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