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