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
signature.asc
Description: OpenPGP digital signature
_______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot