Home | History | Annotate | Download | only in lib
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2003
      4  * Wolfgang Denk, DENX Software Engineering, <wd (at) denx.de>
      5  */
      6 
      7 #include <common.h>
      8 #include <asm/cacheops.h>
      9 #ifdef CONFIG_MIPS_L2_CACHE
     10 #include <asm/cm.h>
     11 #endif
     12 #include <asm/io.h>
     13 #include <asm/mipsregs.h>
     14 #include <asm/system.h>
     15 
     16 DECLARE_GLOBAL_DATA_PTR;
     17 
     18 static void probe_l2(void)
     19 {
     20 #ifdef CONFIG_MIPS_L2_CACHE
     21 	unsigned long conf2, sl;
     22 	bool l2c = false;
     23 
     24 	if (!(read_c0_config1() & MIPS_CONF_M))
     25 		return;
     26 
     27 	conf2 = read_c0_config2();
     28 
     29 	if (__mips_isa_rev >= 6) {
     30 		l2c = conf2 & MIPS_CONF_M;
     31 		if (l2c)
     32 			l2c = read_c0_config3() & MIPS_CONF_M;
     33 		if (l2c)
     34 			l2c = read_c0_config4() & MIPS_CONF_M;
     35 		if (l2c)
     36 			l2c = read_c0_config5() & MIPS_CONF5_L2C;
     37 	}
     38 
     39 	if (l2c && config_enabled(CONFIG_MIPS_CM)) {
     40 		gd->arch.l2_line_size = mips_cm_l2_line_size();
     41 	} else if (l2c) {
     42 		/* We don't know how to retrieve L2 config on this system */
     43 		BUG();
     44 	} else {
     45 		sl = (conf2 & MIPS_CONF2_SL) >> MIPS_CONF2_SL_SHF;
     46 		gd->arch.l2_line_size = sl ? (2 << sl) : 0;
     47 	}
     48 #endif
     49 }
     50 
     51 void mips_cache_probe(void)
     52 {
     53 #ifdef CONFIG_SYS_CACHE_SIZE_AUTO
     54 	unsigned long conf1, il, dl;
     55 
     56 	conf1 = read_c0_config1();
     57 
     58 	il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHF;
     59 	dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHF;
     60 
     61 	gd->arch.l1i_line_size = il ? (2 << il) : 0;
     62 	gd->arch.l1d_line_size = dl ? (2 << dl) : 0;
     63 #endif
     64 	probe_l2();
     65 }
     66 
     67 static inline unsigned long icache_line_size(void)
     68 {
     69 #ifdef CONFIG_SYS_CACHE_SIZE_AUTO
     70 	return gd->arch.l1i_line_size;
     71 #else
     72 	return CONFIG_SYS_ICACHE_LINE_SIZE;
     73 #endif
     74 }
     75 
     76 static inline unsigned long dcache_line_size(void)
     77 {
     78 #ifdef CONFIG_SYS_CACHE_SIZE_AUTO
     79 	return gd->arch.l1d_line_size;
     80 #else
     81 	return CONFIG_SYS_DCACHE_LINE_SIZE;
     82 #endif
     83 }
     84 
     85 static inline unsigned long scache_line_size(void)
     86 {
     87 #ifdef CONFIG_MIPS_L2_CACHE
     88 	return gd->arch.l2_line_size;
     89 #else
     90 	return 0;
     91 #endif
     92 }
     93 
     94 #define cache_loop(start, end, lsize, ops...) do {			\
     95 	const void *addr = (const void *)(start & ~(lsize - 1));	\
     96 	const void *aend = (const void *)((end - 1) & ~(lsize - 1));	\
     97 	const unsigned int cache_ops[] = { ops };			\
     98 	unsigned int i;							\
     99 									\
    100 	if (!lsize)							\
    101 		break;							\
    102 									\
    103 	for (; addr <= aend; addr += lsize) {				\
    104 		for (i = 0; i < ARRAY_SIZE(cache_ops); i++)		\
    105 			mips_cache(cache_ops[i], addr);			\
    106 	}								\
    107 } while (0)
    108 
    109 void flush_cache(ulong start_addr, ulong size)
    110 {
    111 	unsigned long ilsize = icache_line_size();
    112 	unsigned long dlsize = dcache_line_size();
    113 	unsigned long slsize = scache_line_size();
    114 
    115 	/* aend will be miscalculated when size is zero, so we return here */
    116 	if (size == 0)
    117 		return;
    118 
    119 	if ((ilsize == dlsize) && !slsize) {
    120 		/* flush I-cache & D-cache simultaneously */
    121 		cache_loop(start_addr, start_addr + size, ilsize,
    122 			   HIT_WRITEBACK_INV_D, HIT_INVALIDATE_I);
    123 		goto ops_done;
    124 	}
    125 
    126 	/* flush D-cache */
    127 	cache_loop(start_addr, start_addr + size, dlsize, HIT_WRITEBACK_INV_D);
    128 
    129 	/* flush L2 cache */
    130 	cache_loop(start_addr, start_addr + size, slsize, HIT_WRITEBACK_INV_SD);
    131 
    132 	/* flush I-cache */
    133 	cache_loop(start_addr, start_addr + size, ilsize, HIT_INVALIDATE_I);
    134 
    135 ops_done:
    136 	/* ensure cache ops complete before any further memory accesses */
    137 	sync();
    138 
    139 	/* ensure the pipeline doesn't contain now-invalid instructions */
    140 	instruction_hazard_barrier();
    141 }
    142 
    143 void flush_dcache_range(ulong start_addr, ulong stop)
    144 {
    145 	unsigned long lsize = dcache_line_size();
    146 	unsigned long slsize = scache_line_size();
    147 
    148 	/* aend will be miscalculated when size is zero, so we return here */
    149 	if (start_addr == stop)
    150 		return;
    151 
    152 	cache_loop(start_addr, stop, lsize, HIT_WRITEBACK_INV_D);
    153 
    154 	/* flush L2 cache */
    155 	cache_loop(start_addr, stop, slsize, HIT_WRITEBACK_INV_SD);
    156 
    157 	/* ensure cache ops complete before any further memory accesses */
    158 	sync();
    159 }
    160 
    161 void invalidate_dcache_range(ulong start_addr, ulong stop)
    162 {
    163 	unsigned long lsize = dcache_line_size();
    164 	unsigned long slsize = scache_line_size();
    165 
    166 	/* aend will be miscalculated when size is zero, so we return here */
    167 	if (start_addr == stop)
    168 		return;
    169 
    170 	/* invalidate L2 cache */
    171 	cache_loop(start_addr, stop, slsize, HIT_INVALIDATE_SD);
    172 
    173 	cache_loop(start_addr, stop, lsize, HIT_INVALIDATE_D);
    174 
    175 	/* ensure cache ops complete before any further memory accesses */
    176 	sync();
    177 }
    178