Am 09.09.2016 um 15:44 schrieb Paul Burton:
> This patch adds support for initialising & maintaining L2 caches on MIPS
> systems. The L2 cache configuration may be advertised through either
> coprocessor 0 or the MIPS Coherence Manager depending upon the system,
> and support for both is included.
> 
> If the L2 can be bypassed then we bypass it early in boot & initialise
> the L1 caches first, such that we can start making use of the L1
> instruction cache as early as possible. Otherwise we initialise the L2
> first such that the L1s have no opportunity to generate access to the
> uninitialised L2.

Some questions:

Does it make sense to bypass L2 in early boot and mips_cache_reset() and
to initialize and enable L2 after board_init_f()? Then you could
implement the L2 code completely in C and avoid adding a lot of new
assembly code.

Can't you make all flush_cache() functions a no-op when CONFIG_MIPS_CM
is enabled?

> 
> Signed-off-by: Paul Burton <paul.bur...@imgtec.com>
> ---
> 
> Changes in v2: None
> 
>  arch/mips/Kconfig                   |   6 ++
>  arch/mips/include/asm/cm.h          |  38 ++++++++
>  arch/mips/include/asm/global_data.h |   3 +
>  arch/mips/include/asm/mipsregs.h    |   5 +
>  arch/mips/lib/cache.c               |  62 ++++++++++++-
>  arch/mips/lib/cache_init.S          | 180 
> +++++++++++++++++++++++++++++++++++-
>  6 files changed, 288 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
> index d1cd6f1..ff86ad2 100644
> --- a/arch/mips/Kconfig
> +++ b/arch/mips/Kconfig
> @@ -300,6 +300,12 @@ config MIPS_L1_CACHE_SHIFT
>       default "4" if MIPS_L1_CACHE_SHIFT_4
>       default "5"
>  
> +config MIPS_L2_CACHE
> +     bool
> +     help
> +       Select this if your system includes an L2 cache and you want U-Boot
> +       to initialise & maintain it.
> +
>  config DYNAMIC_IO_PORT_BASE
>       bool
>  
> diff --git a/arch/mips/include/asm/cm.h b/arch/mips/include/asm/cm.h
> index 0261733..62ecef2 100644
> --- a/arch/mips/include/asm/cm.h
> +++ b/arch/mips/include/asm/cm.h
> @@ -12,8 +12,46 @@
>  #define GCR_BASE                     0x0008
>  #define GCR_BASE_UPPER                       0x000c
>  #define GCR_REV                              0x0030
> +#define GCR_L2_CONFIG                        0x0130
> +#define GCR_L2_TAG_ADDR                      0x0600
> +#define GCR_L2_TAG_ADDR_UPPER                0x0604
> +#define GCR_L2_TAG_STATE             0x0608
> +#define GCR_L2_TAG_STATE_UPPER               0x060c
> +#define GCR_L2_DATA                  0x0610
> +#define GCR_L2_DATA_UPPER            0x0614
>  
>  /* GCR_REV CM versions */
>  #define GCR_REV_CM3                  0x0800
>  
> +/* GCR_L2_CONFIG fields */
> +#define GCR_L2_CONFIG_ASSOC_SHIFT    0
> +#define GCR_L2_CONFIG_ASSOC_BITS     8
> +#define GCR_L2_CONFIG_LINESZ_SHIFT   8
> +#define GCR_L2_CONFIG_LINESZ_BITS    4
> +#define GCR_L2_CONFIG_SETSZ_SHIFT    12
> +#define GCR_L2_CONFIG_SETSZ_BITS     4
> +#define GCR_L2_CONFIG_BYPASS         (1 << 20)
> +
> +#ifndef __ASSEMBLY__
> +
> +#include <asm/io.h>
> +
> +static inline void *mips_cm_base(void)
> +{
> +     return (void *)CKSEG1ADDR(CONFIG_MIPS_CM_BASE);
> +}
> +
> +static inline unsigned long mips_cm_l2_line_size(void)
> +{
> +     unsigned long l2conf, line_sz;
> +
> +     l2conf = __raw_readl(mips_cm_base() + GCR_L2_CONFIG);
> +
> +     line_sz = l2conf >> GCR_L2_CONFIG_LINESZ_SHIFT;
> +     line_sz &= GENMASK(GCR_L2_CONFIG_LINESZ_BITS - 1, 0);
> +     return line_sz ? (2 << line_sz) : 0;
> +}
> +
> +#endif /* !__ASSEMBLY__ */
> +
>  #endif /* __MIPS_ASM_CM_H__ */
> diff --git a/arch/mips/include/asm/global_data.h 
> b/arch/mips/include/asm/global_data.h
> index 8533b69..0078bbe 100644
> --- a/arch/mips/include/asm/global_data.h
> +++ b/arch/mips/include/asm/global_data.h
> @@ -25,6 +25,9 @@ struct arch_global_data {
>       unsigned short l1i_line_size;
>       unsigned short l1d_line_size;
>  #endif
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     unsigned short l2_line_size;
> +#endif
>  };
>  
>  #include <asm-generic/global_data.h>
> diff --git a/arch/mips/include/asm/mipsregs.h 
> b/arch/mips/include/asm/mipsregs.h
> index cd4f952..b4c2dff 100644
> --- a/arch/mips/include/asm/mipsregs.h
> +++ b/arch/mips/include/asm/mipsregs.h
> @@ -485,9 +485,13 @@
>  #define MIPS_CONF1_TLBS_SIZE    (6)
>  #define MIPS_CONF1_TLBS         (_ULCAST_(63) << MIPS_CONF1_TLBS_SHIFT)
>  
> +#define MIPS_CONF2_SA_SHF    0
>  #define MIPS_CONF2_SA                (_ULCAST_(15) << 0)
> +#define MIPS_CONF2_SL_SHF    4
>  #define MIPS_CONF2_SL                (_ULCAST_(15) << 4)
> +#define MIPS_CONF2_SS_SHF    8
>  #define MIPS_CONF2_SS                (_ULCAST_(15) << 8)
> +#define MIPS_CONF2_L2B               (_ULCAST_(1) << 12)
>  #define MIPS_CONF2_SU                (_ULCAST_(15) << 12)
>  #define MIPS_CONF2_TA                (_ULCAST_(15) << 16)
>  #define MIPS_CONF2_TL                (_ULCAST_(15) << 20)
> @@ -551,6 +555,7 @@
>  #define MIPS_CONF5_MVH               (_ULCAST_(1) << 5)
>  #define MIPS_CONF5_FRE               (_ULCAST_(1) << 8)
>  #define MIPS_CONF5_UFE               (_ULCAST_(1) << 9)
> +#define MIPS_CONF5_L2C               (_ULCAST_(1) << 10)
>  #define MIPS_CONF5_MSAEN     (_ULCAST_(1) << 27)
>  #define MIPS_CONF5_EVA               (_ULCAST_(1) << 28)
>  #define MIPS_CONF5_CV                (_ULCAST_(1) << 29)
> diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c
> index d8baf08..bd14ba6 100644
> --- a/arch/mips/lib/cache.c
> +++ b/arch/mips/lib/cache.c
> @@ -7,10 +7,44 @@
>  
>  #include <common.h>
>  #include <asm/cacheops.h>
> +#include <asm/cm.h>
>  #include <asm/mipsregs.h>
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> +static void probe_l2(void)
> +{
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     unsigned long conf2, sl;
> +     bool l2c = false;
> +
> +     if (!(read_c0_config1() & MIPS_CONF_M))
> +             return;
> +
> +     conf2 = read_c0_config2();
> +
> +     if (__mips_isa_rev >= 6) {
> +             l2c = conf2 & MIPS_CONF_M;
> +             if (l2c)
> +                     l2c = read_c0_config3() & MIPS_CONF_M;
> +             if (l2c)
> +                     l2c = read_c0_config4() & MIPS_CONF_M;
> +             if (l2c)
> +                     l2c = read_c0_config5() & MIPS_CONF5_L2C;
> +     }
> +
> +     if (l2c && config_enabled(CONFIG_MIPS_CM)) {
> +             gd->arch.l2_line_size = mips_cm_l2_line_size();
> +     } else if (l2c) {
> +             /* We don't know how to retrieve L2 config on this system */
> +             BUG();
> +     } else {
> +             sl = (conf2 & MIPS_CONF2_SL) >> MIPS_CONF2_SL_SHF;
> +             gd->arch.l2_line_size = sl ? (2 << sl) : 0;
> +     }
> +#endif
> +}
> +
>  void mips_cache_probe(void)
>  {
>  #ifdef CONFIG_SYS_CACHE_SIZE_AUTO
> @@ -24,6 +58,7 @@ void mips_cache_probe(void)
>       gd->arch.l1i_line_size = il ? (2 << il) : 0;
>       gd->arch.l1d_line_size = dl ? (2 << dl) : 0;
>  #endif
> +     probe_l2();
>  }
>  
>  static inline unsigned long icache_line_size(void)
> @@ -44,6 +79,15 @@ static inline unsigned long dcache_line_size(void)
>  #endif
>  }
>  
> +static inline unsigned long scache_line_size(void)
> +{
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     return gd->arch.l2_line_size;
> +#else
> +     return 0;
> +#endif
> +}
> +
>  #define cache_loop(start, end, lsize, ops...) do {                   \
>       const void *addr = (const void *)(start & ~(lsize - 1));        \
>       const void *aend = (const void *)((end - 1) & ~(lsize - 1));    \
> @@ -60,12 +104,13 @@ void flush_cache(ulong start_addr, ulong size)
>  {
>       unsigned long ilsize = icache_line_size();
>       unsigned long dlsize = dcache_line_size();
> +     unsigned long slsize = scache_line_size();
>  
>       /* aend will be miscalculated when size is zero, so we return here */
>       if (size == 0)
>               return;
>  
> -     if (ilsize == dlsize) {
> +     if ((ilsize == dlsize) && !slsize) {
>               /* flush I-cache & D-cache simultaneously */
>               cache_loop(start_addr, start_addr + size, ilsize,
>                          HIT_WRITEBACK_INV_D, HIT_INVALIDATE_I);
> @@ -75,6 +120,11 @@ void flush_cache(ulong start_addr, ulong size)
>       /* flush D-cache */
>       cache_loop(start_addr, start_addr + size, dlsize, HIT_WRITEBACK_INV_D);
>  
> +     /* flush L2 cache */
> +     if (slsize)
> +             cache_loop(start_addr, start_addr + size, slsize,
> +                        HIT_WRITEBACK_INV_SD);
> +
>       /* flush I-cache */
>       cache_loop(start_addr, start_addr + size, ilsize, HIT_INVALIDATE_I);
>  }
> @@ -82,21 +132,31 @@ void flush_cache(ulong start_addr, ulong size)
>  void flush_dcache_range(ulong start_addr, ulong stop)
>  {
>       unsigned long lsize = dcache_line_size();
> +     unsigned long slsize = scache_line_size();
>  
>       /* aend will be miscalculated when size is zero, so we return here */
>       if (start_addr == stop)
>               return;
>  
>       cache_loop(start_addr, stop, lsize, HIT_WRITEBACK_INV_D);
> +
> +     /* flush L2 cache */
> +     if (slsize)
> +             cache_loop(start_addr, stop, slsize, HIT_WRITEBACK_INV_SD);
>  }
>  
>  void invalidate_dcache_range(ulong start_addr, ulong stop)
>  {
>       unsigned long lsize = dcache_line_size();
> +     unsigned long slsize = scache_line_size();
>  
>       /* aend will be miscalculated when size is zero, so we return here */
>       if (start_addr == stop)
>               return;
>  
> +     /* invalidate L2 cache */
> +     if (slsize)
> +             cache_loop(start_addr, stop, slsize, HIT_INVALIDATE_SD);
> +
>       cache_loop(start_addr, stop, lsize, HIT_INVALIDATE_D);
>  }
> diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S
> index 2df3a82..599a85f 100644
> --- a/arch/mips/lib/cache_init.S
> +++ b/arch/mips/lib/cache_init.S
> @@ -13,6 +13,7 @@
>  #include <asm/mipsregs.h>
>  #include <asm/addrspace.h>
>  #include <asm/cacheops.h>
> +#include <asm/cm.h>
>  
>  #ifndef CONFIG_SYS_MIPS_CACHE_MODE
>  #define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT
> @@ -95,14 +96,135 @@
>   * with good parity is available. This routine will initialise an area of
>   * memory starting at location zero to be used as a source of parity.
>   *
> + * Note that this function does not follow the standard calling convention &
> + * may clobber typically callee-saved registers.
> + *
>   * RETURNS: N/A
>   *
>   */
> -#define R_IC_SIZE    t2
> -#define R_IC_LINE    t8
> -#define R_DC_SIZE    t3
> -#define R_DC_LINE    t9
> +#define R_RETURN     s0
> +#define R_IC_SIZE    s1
> +#define R_IC_LINE    s2
> +#define R_DC_SIZE    s3
> +#define R_DC_LINE    s4
> +#define R_L2_SIZE    s5
> +#define R_L2_LINE    s6
> +#define R_L2_BYPASSED        s7
> +#define R_L2_L2C     t8
>  LEAF(mips_cache_reset)
> +     move    R_RETURN, ra
> +
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     /*
> +      * For there to be an L2 present, Config2 must be present. If it isn't
> +      * then we proceed knowing there's no L2 cache.
> +      */
> +     move    R_L2_SIZE, zero
> +     move    R_L2_LINE, zero
> +     move    R_L2_BYPASSED, zero
> +     move    R_L2_L2C, zero
> +     mfc0    t0, CP0_CONFIG, 1
> +     bgez    t0, l2_probe_done
> +
> +     /*
> +      * From MIPSr6 onwards the L2 cache configuration might not be reported
> +      * by Config2. The Config5.L2C bit indicates whether this is the case,
> +      * and if it is then we need knowledge of where else to look. For cores
> +      * from Imagination Technologies this is a CM GCR.
> +      */
> +# if __mips_isa_rev >= 6
> +     /* Check that Config5 exists */
> +     mfc0    t0, CP0_CONFIG, 2
> +     bgez    t0, l2_probe_cop0
> +     mfc0    t0, CP0_CONFIG, 3
> +     bgez    t0, l2_probe_cop0
> +     mfc0    t0, CP0_CONFIG, 4
> +     bgez    t0, l2_probe_cop0
> +
> +     /* Check Config5.L2C is set */
> +     mfc0    t0, CP0_CONFIG, 5
> +     and     R_L2_L2C, t0, MIPS_CONF5_L2C
> +     beqz    R_L2_L2C, l2_probe_cop0
> +
> +     /* Config5.L2C is set */
> +#  ifdef CONFIG_MIPS_CM
> +     /* The CM will provide L2 configuration */
> +     PTR_LI  t0, CKSEG1ADDR(CONFIG_MIPS_CM_BASE)
> +     lw      t1, GCR_L2_CONFIG(t0)
> +     bgez    t1, l2_probe_done
> +
> +     ext     R_L2_LINE, t1, \
> +             GCR_L2_CONFIG_LINESZ_SHIFT, GCR_L2_CONFIG_LINESZ_BITS
> +     beqz    R_L2_LINE, l2_probe_done
> +     li      t2, 2
> +     sllv    R_L2_LINE, t2, R_L2_LINE
> +
> +     ext     t2, t1, GCR_L2_CONFIG_ASSOC_SHIFT, GCR_L2_CONFIG_ASSOC_BITS
> +     addiu   t2, t2, 1
> +     mul     R_L2_SIZE, R_L2_LINE, t2
> +
> +     ext     t2, t1, GCR_L2_CONFIG_SETSZ_SHIFT, GCR_L2_CONFIG_SETSZ_BITS
> +     sllv    R_L2_SIZE, R_L2_SIZE, t2
> +     li      t2, 64
> +     mul     R_L2_SIZE, R_L2_SIZE, t2
> +
> +     /* Bypass the L2 cache so that we can init the L1s early */
> +     or      t1, t1, GCR_L2_CONFIG_BYPASS
> +     sw      t1, GCR_L2_CONFIG(t0)
> +     sync
> +     li      R_L2_BYPASSED, 1
> +
> +     /* Zero the L2 tag registers */
> +     sw      zero, GCR_L2_TAG_ADDR(t0)
> +     sw      zero, GCR_L2_TAG_ADDR_UPPER(t0)
> +     sw      zero, GCR_L2_TAG_STATE(t0)
> +     sw      zero, GCR_L2_TAG_STATE_UPPER(t0)
> +     sw      zero, GCR_L2_DATA(t0)
> +     sw      zero, GCR_L2_DATA_UPPER(t0)
> +     sync
> +#  else
> +     /* We don't know how to retrieve L2 configuration on this system */
> +#  endif
> +     b       l2_probe_done
> +# endif
> +
> +     /*
> +      * For pre-r6 systems, or r6 systems with Config5.L2C==0, probe the L2
> +      * cache configuration from the cop0 Config2 register.
> +      */
> +l2_probe_cop0:
> +     mfc0    t0, CP0_CONFIG, 2
> +
> +     srl     R_L2_LINE, t0, MIPS_CONF2_SL_SHF
> +     andi    R_L2_LINE, R_L2_LINE, MIPS_CONF2_SL >> MIPS_CONF2_SL_SHF
> +     beqz    R_L2_LINE, l2_probe_done
> +     li      t1, 2
> +     sllv    R_L2_LINE, t1, R_L2_LINE
> +
> +     srl     t1, t0, MIPS_CONF2_SA_SHF
> +     andi    t1, t1, MIPS_CONF2_SA >> MIPS_CONF2_SA_SHF
> +     addiu   t1, t1, 1
> +     mul     R_L2_SIZE, R_L2_LINE, t1
> +
> +     srl     t1, t0, MIPS_CONF2_SS_SHF
> +     andi    t1, t1, MIPS_CONF2_SS >> MIPS_CONF2_SS_SHF
> +     sllv    R_L2_SIZE, R_L2_SIZE, t1
> +     li      t1, 64
> +     mul     R_L2_SIZE, R_L2_SIZE, t1
> +
> +     /* Attempt to bypass the L2 so that we can init the L1s early */
> +     or      t0, t0, MIPS_CONF2_L2B
> +     mtc0    t0, CP0_CONFIG, 2
> +     ehb
> +     mfc0    t0, CP0_CONFIG, 2
> +     and     R_L2_BYPASSED, t0, MIPS_CONF2_L2B
> +
> +     /* Zero the L2 tag registers */
> +     mtc0    zero, CP0_TAGLO, 4
> +     ehb
> +l2_probe_done:
> +#endif
> +
>  #ifndef CONFIG_SYS_CACHE_SIZE_AUTO
>       li      R_IC_SIZE, CONFIG_SYS_ICACHE_SIZE
>       li      R_IC_LINE, CONFIG_SYS_ICACHE_LINE_SIZE
> @@ -142,11 +264,33 @@ LEAF(mips_cache_reset)
>  
>  #endif /* CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD */
>  
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     /*
> +      * If the L2 is bypassed, init the L1 first so that we can execute the
> +      * rest of the cache initialisation using the L1 instruction cache.
> +      */
> +     bnez            R_L2_BYPASSED, l1_init
> +
> +l2_init:
> +     PTR_LI          t0, INDEX_BASE
> +     PTR_ADDU        t1, t0, R_L2_SIZE
> +1:   cache           INDEX_STORE_TAG_SD, 0(t0)
> +     PTR_ADDU        t0, t0, R_L2_LINE
> +     bne             t0, t1, 1b
> +
> +     /*
> +      * If the L2 was bypassed then we already initialised the L1s before
> +      * the L2, so we are now done.
> +      */
> +     bnez            R_L2_BYPASSED, return
> +#endif
> +
>       /*
>        * The TagLo registers used depend upon the CPU implementation, but the
>        * architecture requires that it is safe for software to write to both
>        * TagLo selects 0 & 2 covering supported cases.
>        */
> +l1_init:
>       mtc0            zero, CP0_TAGLO
>       mtc0            zero, CP0_TAGLO, 2
>  
> @@ -206,8 +350,34 @@ LEAF(mips_cache_reset)
>       PTR_LI          t0, INDEX_BASE
>       cache_loop      t0, t1, R_DC_LINE, INDEX_STORE_TAG_D
>  #endif
> +3:
> +
> +#ifdef CONFIG_MIPS_L2_CACHE
> +     /* If the L2 isn't bypassed then we're done */
> +     beqz            R_L2_BYPASSED, return
>  
> -3:   jr      ra
> +     /* The L2 is bypassed - un-bypass it then initialise it */
> +# if __mips_isa_rev >= 6
> +     beqz            R_L2_L2C, 1f
> +
> +     li              t0, CKSEG1ADDR(CONFIG_MIPS_CM_BASE)
> +     lw              t1, GCR_L2_CONFIG(t0)
> +     xor             t1, t1, GCR_L2_CONFIG_BYPASS
> +     sw              t1, GCR_L2_CONFIG(t0)
> +     sync
> +     ehb
> +     b               l2_init
> +# endif
> +1:
> +     mfc0            t0, CP0_CONFIG, 2
> +     xor             t0, t0, MIPS_CONF2_L2B
> +     mtc0            t0, CP0_CONFIG, 2
> +     ehb
> +     b               l2_init
> +#endif
> +
> +return:
> +     jr      ra
>       END(mips_cache_reset)
>  
>  /*
> 

-- 
- Daniel

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to