[U-Boot] [PATCH v2 09/10] MIPS: L2 cache support
Daniel Schwierzeck
daniel.schwierzeck at gmail.com
Sat Sep 10 18:55:08 CEST 2016
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.burton at 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: OpenPGP digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20160910/c068dad8/attachment.sig>
More information about the U-Boot
mailing list