[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