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