Home | History | Annotate | Download | only in armv7m
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
      4  * Author(s): Vikas Manocha, <vikas.manocha (at) st.com> for STMicroelectronics.
      5  */
      6 
      7 #include <common.h>
      8 #include <errno.h>
      9 #include <asm/armv7m.h>
     10 #include <asm/io.h>
     11 
     12 /* Cache maintenance operation registers */
     13 
     14 #define V7M_CACHE_REG_ICIALLU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x00))
     15 #define INVAL_ICACHE_POU		0
     16 #define V7M_CACHE_REG_ICIMVALU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x08))
     17 #define V7M_CACHE_REG_DCIMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x0C))
     18 #define V7M_CACHE_REG_DCISW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x10))
     19 #define V7M_CACHE_REG_DCCMVAU		((u32 *)(V7M_CACHE_MAINT_BASE + 0x14))
     20 #define V7M_CACHE_REG_DCCMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x18))
     21 #define V7M_CACHE_REG_DCCSW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x1C))
     22 #define V7M_CACHE_REG_DCCIMVAC		((u32 *)(V7M_CACHE_MAINT_BASE + 0x20))
     23 #define V7M_CACHE_REG_DCCISW		((u32 *)(V7M_CACHE_MAINT_BASE + 0x24))
     24 #define WAYS_SHIFT			30
     25 #define SETS_SHIFT			5
     26 
     27 /* armv7m processor feature registers */
     28 
     29 #define V7M_PROC_REG_CLIDR		((u32 *)(V7M_PROC_FTR_BASE + 0x00))
     30 #define V7M_PROC_REG_CTR		((u32 *)(V7M_PROC_FTR_BASE + 0x04))
     31 #define V7M_PROC_REG_CCSIDR		((u32 *)(V7M_PROC_FTR_BASE + 0x08))
     32 #define MASK_NUM_WAYS			GENMASK(12, 3)
     33 #define MASK_NUM_SETS			GENMASK(27, 13)
     34 #define CLINE_SIZE_MASK			GENMASK(2, 0)
     35 #define NUM_WAYS_SHIFT			3
     36 #define NUM_SETS_SHIFT			13
     37 #define V7M_PROC_REG_CSSELR		((u32 *)(V7M_PROC_FTR_BASE + 0x0C))
     38 #define SEL_I_OR_D			BIT(0)
     39 
     40 enum cache_type {
     41 	DCACHE,
     42 	ICACHE,
     43 };
     44 
     45 /* PoU : Point of Unification, Poc: Point of Coherency */
     46 enum cache_action {
     47 	INVALIDATE_POU,		/* i-cache invalidate by address */
     48 	INVALIDATE_POC,		/* d-cache invalidate by address */
     49 	INVALIDATE_SET_WAY,	/* d-cache invalidate by sets/ways */
     50 	FLUSH_POU,		/* d-cache clean by address to the PoU */
     51 	FLUSH_POC,		/* d-cache clean by address to the PoC */
     52 	FLUSH_SET_WAY,		/* d-cache clean by sets/ways */
     53 	FLUSH_INVAL_POC,	/* d-cache clean & invalidate by addr to PoC */
     54 	FLUSH_INVAL_SET_WAY,	/* d-cache clean & invalidate by set/ways */
     55 };
     56 
     57 #ifndef CONFIG_SYS_DCACHE_OFF
     58 struct dcache_config {
     59 	u32 ways;
     60 	u32 sets;
     61 };
     62 
     63 static void get_cache_ways_sets(struct dcache_config *cache)
     64 {
     65 	u32 cache_size_id = readl(V7M_PROC_REG_CCSIDR);
     66 
     67 	cache->ways = (cache_size_id & MASK_NUM_WAYS) >> NUM_WAYS_SHIFT;
     68 	cache->sets = (cache_size_id & MASK_NUM_SETS) >> NUM_SETS_SHIFT;
     69 }
     70 
     71 /*
     72  * Return the io register to perform required cache action like clean or clean
     73  * & invalidate by sets/ways.
     74  */
     75 static u32 *get_action_reg_set_ways(enum cache_action action)
     76 {
     77 	switch (action) {
     78 	case INVALIDATE_SET_WAY:
     79 		return V7M_CACHE_REG_DCISW;
     80 	case FLUSH_SET_WAY:
     81 		return V7M_CACHE_REG_DCCSW;
     82 	case FLUSH_INVAL_SET_WAY:
     83 		return V7M_CACHE_REG_DCCISW;
     84 	default:
     85 		break;
     86 	};
     87 
     88 	return NULL;
     89 }
     90 
     91 /*
     92  * Return the io register to perform required cache action like clean or clean
     93  * & invalidate by adddress or range.
     94  */
     95 static u32 *get_action_reg_range(enum cache_action action)
     96 {
     97 	switch (action) {
     98 	case INVALIDATE_POU:
     99 		return V7M_CACHE_REG_ICIMVALU;
    100 	case INVALIDATE_POC:
    101 		return V7M_CACHE_REG_DCIMVAC;
    102 	case FLUSH_POU:
    103 		return V7M_CACHE_REG_DCCMVAU;
    104 	case FLUSH_POC:
    105 		return V7M_CACHE_REG_DCCMVAC;
    106 	case FLUSH_INVAL_POC:
    107 		return V7M_CACHE_REG_DCCIMVAC;
    108 	default:
    109 		break;
    110 	}
    111 
    112 	return NULL;
    113 }
    114 
    115 static u32 get_cline_size(enum cache_type type)
    116 {
    117 	u32 size;
    118 
    119 	if (type == DCACHE)
    120 		clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
    121 	else if (type == ICACHE)
    122 		setbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
    123 	/* Make sure cache selection is effective for next memory access */
    124 	dsb();
    125 
    126 	size = readl(V7M_PROC_REG_CCSIDR) & CLINE_SIZE_MASK;
    127 	/* Size enocoded as 2 less than log(no_of_words_in_cache_line) base 2 */
    128 	size = 1 << (size + 2);
    129 	debug("cache line size is %d\n", size);
    130 
    131 	return size;
    132 }
    133 
    134 /* Perform the action like invalidate/clean on a range of cache addresses */
    135 static int action_cache_range(enum cache_action action, u32 start_addr,
    136 			      int64_t size)
    137 {
    138 	u32 cline_size;
    139 	u32 *action_reg;
    140 	enum cache_type type;
    141 
    142 	action_reg = get_action_reg_range(action);
    143 	if (!action_reg)
    144 		return -EINVAL;
    145 	if (action == INVALIDATE_POU)
    146 		type = ICACHE;
    147 	else
    148 		type = DCACHE;
    149 
    150 	/* Cache line size is minium size for the cache action */
    151 	cline_size = get_cline_size(type);
    152 	/* Align start address to cache line boundary */
    153 	start_addr &= ~(cline_size - 1);
    154 	debug("total size for cache action = %llx\n", size);
    155 	do {
    156 		writel(start_addr, action_reg);
    157 		size -= cline_size;
    158 		start_addr += cline_size;
    159 	} while (size > cline_size);
    160 
    161 	/* Make sure cache action is effective for next memory access */
    162 	dsb();
    163 	isb();	/* Make sure instruction stream sees it */
    164 	debug("cache action on range done\n");
    165 
    166 	return 0;
    167 }
    168 
    169 /* Perform the action like invalidate/clean on all cached addresses */
    170 static int action_dcache_all(enum cache_action action)
    171 {
    172 	struct dcache_config cache;
    173 	u32 *action_reg;
    174 	int i, j;
    175 
    176 	action_reg = get_action_reg_set_ways(action);
    177 	if (!action_reg)
    178 		return -EINVAL;
    179 
    180 	clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
    181 	/* Make sure cache selection is effective for next memory access */
    182 	dsb();
    183 
    184 	get_cache_ways_sets(&cache);	/* Get number of ways & sets */
    185 	debug("cache: ways= %d, sets= %d\n", cache.ways + 1, cache.sets + 1);
    186 	for (i = cache.sets; i >= 0; i--) {
    187 		for (j = cache.ways; j >= 0; j--) {
    188 			writel((j << WAYS_SHIFT) | (i << SETS_SHIFT),
    189 			       action_reg);
    190 		}
    191 	}
    192 
    193 	/* Make sure cache action is effective for next memory access */
    194 	dsb();
    195 	isb();	/* Make sure instruction stream sees it */
    196 
    197 	return 0;
    198 }
    199 
    200 void dcache_enable(void)
    201 {
    202 	if (dcache_status())	/* return if cache already enabled */
    203 		return;
    204 
    205 	if (action_dcache_all(INVALIDATE_SET_WAY)) {
    206 		printf("ERR: D-cache not enabled\n");
    207 		return;
    208 	}
    209 
    210 	setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
    211 
    212 	/* Make sure cache action is effective for next memory access */
    213 	dsb();
    214 	isb();	/* Make sure instruction stream sees it */
    215 }
    216 
    217 void dcache_disable(void)
    218 {
    219 	if (!dcache_status())
    220 		return;
    221 
    222 	/* if dcache is enabled-> dcache disable & then flush */
    223 	if (action_dcache_all(FLUSH_SET_WAY)) {
    224 		printf("ERR: D-cache not flushed\n");
    225 		return;
    226 	}
    227 
    228 	clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
    229 
    230 	/* Make sure cache action is effective for next memory access */
    231 	dsb();
    232 	isb();	/* Make sure instruction stream sees it */
    233 }
    234 
    235 int dcache_status(void)
    236 {
    237 	return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_DCACHE)) != 0;
    238 }
    239 
    240 void invalidate_dcache_range(unsigned long start, unsigned long stop)
    241 {
    242 	if (action_cache_range(INVALIDATE_POC, start, stop - start)) {
    243 		printf("ERR: D-cache not invalidated\n");
    244 		return;
    245 	}
    246 }
    247 
    248 void flush_dcache_range(unsigned long start, unsigned long stop)
    249 {
    250 	if (action_cache_range(FLUSH_POC, start, stop - start)) {
    251 		printf("ERR: D-cache not flushed\n");
    252 		return;
    253 	}
    254 }
    255 void flush_dcache_all(void)
    256 {
    257 	if (action_dcache_all(FLUSH_SET_WAY)) {
    258 		printf("ERR: D-cache not flushed\n");
    259 		return;
    260 	}
    261 }
    262 
    263 void invalidate_dcache_all(void)
    264 {
    265 	if (action_dcache_all(INVALIDATE_SET_WAY)) {
    266 		printf("ERR: D-cache not invalidated\n");
    267 		return;
    268 	}
    269 }
    270 #else
    271 void dcache_enable(void)
    272 {
    273 	return;
    274 }
    275 
    276 void dcache_disable(void)
    277 {
    278 	return;
    279 }
    280 
    281 int dcache_status(void)
    282 {
    283 	return 0;
    284 }
    285 
    286 void flush_dcache_all(void)
    287 {
    288 }
    289 
    290 void invalidate_dcache_all(void)
    291 {
    292 }
    293 #endif
    294 
    295 #ifndef CONFIG_SYS_ICACHE_OFF
    296 
    297 void invalidate_icache_all(void)
    298 {
    299 	writel(INVAL_ICACHE_POU, V7M_CACHE_REG_ICIALLU);
    300 
    301 	/* Make sure cache action is effective for next memory access */
    302 	dsb();
    303 	isb();	/* Make sure instruction stream sees it */
    304 }
    305 
    306 void icache_enable(void)
    307 {
    308 	if (icache_status())
    309 		return;
    310 
    311 	invalidate_icache_all();
    312 	setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
    313 
    314 	/* Make sure cache action is effective for next memory access */
    315 	dsb();
    316 	isb();	/* Make sure instruction stream sees it */
    317 }
    318 
    319 int icache_status(void)
    320 {
    321 	return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_ICACHE)) != 0;
    322 }
    323 
    324 void icache_disable(void)
    325 {
    326 	if (!icache_status())
    327 		return;
    328 
    329 	isb();	/* flush pipeline */
    330 	clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
    331 	isb();	/* subsequent instructions fetch see cache disable effect */
    332 }
    333 #else
    334 void icache_enable(void)
    335 {
    336 	return;
    337 }
    338 
    339 void icache_disable(void)
    340 {
    341 	return;
    342 }
    343 
    344 int icache_status(void)
    345 {
    346 	return 0;
    347 }
    348 #endif
    349 
    350 void enable_caches(void)
    351 {
    352 #ifndef CONFIG_SYS_ICACHE_OFF
    353 	icache_enable();
    354 #endif
    355 #ifndef CONFIG_SYS_DCACHE_OFF
    356 	dcache_enable();
    357 #endif
    358 }
    359