1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved 4 * Author(s): Vikas Manocha, <vikas.manocha@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