1 /*
2  * Copyright (C) 2012-2014 Panasonic Corporation
3  * Copyright (C) 2015-2016 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <linux/io.h>
11 #include <linux/kernel.h>
12 #include <asm/armv7.h>
13 #include <asm/processor.h>
14 
15 #include "cache-uniphier.h"
16 
17 /* control registers */
18 #define UNIPHIER_SSCC		0x500c0000	/* Control Register */
19 #define    UNIPHIER_SSCC_BST			(0x1 << 20)	/* UCWG burst read */
20 #define    UNIPHIER_SSCC_ACT			(0x1 << 19)	/* Inst-Data separate */
21 #define    UNIPHIER_SSCC_WTG			(0x1 << 18)	/* WT gathering on */
22 #define    UNIPHIER_SSCC_PRD			(0x1 << 17)	/* enable pre-fetch */
23 #define    UNIPHIER_SSCC_ON			(0x1 <<  0)	/* enable cache */
24 #define UNIPHIER_SSCLPDAWCR	0x500c0030	/* Unified/Data Active Way Control */
25 #define UNIPHIER_SSCLPIAWCR	0x500c0034	/* Instruction Active Way Control */
26 
27 /* revision registers */
28 #define UNIPHIER_SSCID		0x503c0100	/* ID Register */
29 
30 /* operation registers */
31 #define UNIPHIER_SSCOPE		0x506c0244	/* Cache Operation Primitive Entry */
32 #define    UNIPHIER_SSCOPE_CM_INV		0x0	/* invalidate */
33 #define    UNIPHIER_SSCOPE_CM_CLEAN		0x1	/* clean */
34 #define    UNIPHIER_SSCOPE_CM_FLUSH		0x2	/* flush */
35 #define    UNIPHIER_SSCOPE_CM_SYNC		0x8	/* sync (drain bufs) */
36 #define    UNIPHIER_SSCOPE_CM_FLUSH_PREFETCH	0x9	/* flush p-fetch buf */
37 #define UNIPHIER_SSCOQM		0x506c0248
38 #define    UNIPHIER_SSCOQM_TID_MASK		(0x3 << 21)
39 #define    UNIPHIER_SSCOQM_TID_LRU_DATA		(0x0 << 21)
40 #define    UNIPHIER_SSCOQM_TID_LRU_INST		(0x1 << 21)
41 #define    UNIPHIER_SSCOQM_TID_WAY		(0x2 << 21)
42 #define    UNIPHIER_SSCOQM_S_MASK		(0x3 << 17)
43 #define    UNIPHIER_SSCOQM_S_RANGE		(0x0 << 17)
44 #define    UNIPHIER_SSCOQM_S_ALL		(0x1 << 17)
45 #define    UNIPHIER_SSCOQM_S_WAY		(0x2 << 17)
46 #define    UNIPHIER_SSCOQM_CE			(0x1 << 15)	/* notify completion */
47 #define    UNIPHIER_SSCOQM_CW			(0x1 << 14)
48 #define    UNIPHIER_SSCOQM_CM_MASK		(0x7)
49 #define    UNIPHIER_SSCOQM_CM_INV		0x0	/* invalidate */
50 #define    UNIPHIER_SSCOQM_CM_CLEAN		0x1	/* clean */
51 #define    UNIPHIER_SSCOQM_CM_FLUSH		0x2	/* flush */
52 #define    UNIPHIER_SSCOQM_CM_PREFETCH		0x3	/* prefetch to cache */
53 #define    UNIPHIER_SSCOQM_CM_PREFETCH_BUF	0x4	/* prefetch to pf-buf */
54 #define    UNIPHIER_SSCOQM_CM_TOUCH		0x5	/* touch */
55 #define    UNIPHIER_SSCOQM_CM_TOUCH_ZERO	0x6	/* touch to zero */
56 #define    UNIPHIER_SSCOQM_CM_TOUCH_DIRTY	0x7	/* touch with dirty */
57 #define UNIPHIER_SSCOQAD	0x506c024c	/* Cache Operation Queue Address */
58 #define UNIPHIER_SSCOQSZ	0x506c0250	/* Cache Operation Queue Size */
59 #define UNIPHIER_SSCOQMASK	0x506c0254	/* Cache Operation Queue Address Mask */
60 #define UNIPHIER_SSCOQWN	0x506c0258	/* Cache Operation Queue Way Number */
61 #define UNIPHIER_SSCOPPQSEF	0x506c025c	/* Cache Operation Queue Set Complete */
62 #define    UNIPHIER_SSCOPPQSEF_FE		(0x1 << 1)
63 #define    UNIPHIER_SSCOPPQSEF_OE		(0x1 << 0)
64 #define UNIPHIER_SSCOLPQS	0x506c0260	/* Cache Operation Queue Status */
65 #define    UNIPHIER_SSCOLPQS_EF			(0x1 << 2)
66 #define    UNIPHIER_SSCOLPQS_EST		(0x1 << 1)
67 #define    UNIPHIER_SSCOLPQS_QST		(0x1 << 0)
68 
69 #define UNIPHIER_SSC_LINE_SIZE		128
70 #define UNIPHIER_SSC_RANGE_OP_MAX_SIZE	(0x00400000 - (UNIPHIER_SSC_LINE_SIZE))
71 
72 #define UNIPHIER_SSCOQAD_IS_NEEDED(op) \
73 		((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_RANGE)
74 #define UNIPHIER_SSCOQWM_IS_NEEDED(op) \
75 		(((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_WAY) || \
76 		 ((op & UNIPHIER_SSCOQM_TID_MASK) == UNIPHIER_SSCOQM_TID_WAY))
77 
78 /* uniphier_cache_sync - perform a sync point for a particular cache level */
79 static void uniphier_cache_sync(void)
80 {
81 	/* drain internal buffers */
82 	writel(UNIPHIER_SSCOPE_CM_SYNC, UNIPHIER_SSCOPE);
83 	/* need a read back to confirm */
84 	readl(UNIPHIER_SSCOPE);
85 }
86 
87 /**
88  * uniphier_cache_maint_common - run a queue operation
89  *
90  * @start: start address of range operation (don't care for "all" operation)
91  * @size: data size of range operation (don't care for "all" operation)
92  * @ways: target ways (don't care for operations other than pre-fetch, touch
93  * @operation: flags to specify the desired cache operation
94  */
95 static void uniphier_cache_maint_common(u32 start, u32 size, u32 ways,
96 					u32 operation)
97 {
98 	/* clear the complete notification flag */
99 	writel(UNIPHIER_SSCOLPQS_EF, UNIPHIER_SSCOLPQS);
100 
101 	do {
102 		/* set cache operation */
103 		writel(UNIPHIER_SSCOQM_CE | operation, UNIPHIER_SSCOQM);
104 
105 		/* set address range if needed */
106 		if (likely(UNIPHIER_SSCOQAD_IS_NEEDED(operation))) {
107 			writel(start, UNIPHIER_SSCOQAD);
108 			writel(size, UNIPHIER_SSCOQSZ);
109 		}
110 
111 		/* set target ways if needed */
112 		if (unlikely(UNIPHIER_SSCOQWM_IS_NEEDED(operation)))
113 			writel(ways, UNIPHIER_SSCOQWN);
114 	} while (unlikely(readl(UNIPHIER_SSCOPPQSEF) &
115 			  (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE)));
116 
117 	/* wait until the operation is completed */
118 	while (likely(readl(UNIPHIER_SSCOLPQS) != UNIPHIER_SSCOLPQS_EF))
119 		cpu_relax();
120 }
121 
122 static void uniphier_cache_maint_all(u32 operation)
123 {
124 	uniphier_cache_maint_common(0, 0, 0, UNIPHIER_SSCOQM_S_ALL | operation);
125 
126 	uniphier_cache_sync();
127 }
128 
129 static void uniphier_cache_maint_range(u32 start, u32 end, u32 ways,
130 				       u32 operation)
131 {
132 	u32 size;
133 
134 	/*
135 	 * If the start address is not aligned,
136 	 * perform a cache operation for the first cache-line
137 	 */
138 	start = start & ~(UNIPHIER_SSC_LINE_SIZE - 1);
139 
140 	size = end - start;
141 
142 	if (unlikely(size >= (u32)(-UNIPHIER_SSC_LINE_SIZE))) {
143 		/* this means cache operation for all range */
144 		uniphier_cache_maint_all(operation);
145 		return;
146 	}
147 
148 	/*
149 	 * If the end address is not aligned,
150 	 * perform a cache operation for the last cache-line
151 	 */
152 	size = ALIGN(size, UNIPHIER_SSC_LINE_SIZE);
153 
154 	while (size) {
155 		u32 chunk_size = min_t(u32, size, UNIPHIER_SSC_RANGE_OP_MAX_SIZE);
156 
157 		uniphier_cache_maint_common(start, chunk_size, ways,
158 					    UNIPHIER_SSCOQM_S_RANGE | operation);
159 
160 		start += chunk_size;
161 		size -= chunk_size;
162 	}
163 
164 	uniphier_cache_sync();
165 }
166 
167 void uniphier_cache_prefetch_range(u32 start, u32 end, u32 ways)
168 {
169 	uniphier_cache_maint_range(start, end, ways,
170 				   UNIPHIER_SSCOQM_TID_WAY |
171 				   UNIPHIER_SSCOQM_CM_PREFETCH);
172 }
173 
174 void uniphier_cache_touch_range(u32 start, u32 end, u32 ways)
175 {
176 	uniphier_cache_maint_range(start, end, ways,
177 				   UNIPHIER_SSCOQM_TID_WAY |
178 				   UNIPHIER_SSCOQM_CM_TOUCH);
179 }
180 
181 void uniphier_cache_touch_zero_range(u32 start, u32 end, u32 ways)
182 {
183 	uniphier_cache_maint_range(start, end, ways,
184 				   UNIPHIER_SSCOQM_TID_WAY |
185 				   UNIPHIER_SSCOQM_CM_TOUCH_ZERO);
186 }
187 
188 void uniphier_cache_inv_way(u32 ways)
189 {
190 	uniphier_cache_maint_common(0, 0, ways,
191 				    UNIPHIER_SSCOQM_S_WAY |
192 				    UNIPHIER_SSCOQM_CM_INV);
193 }
194 
195 void uniphier_cache_set_active_ways(int cpu, u32 active_ways)
196 {
197 	void __iomem *base = (void __iomem *)UNIPHIER_SSCC + 0xc00;
198 
199 	switch (readl(UNIPHIER_SSCID)) { /* revision */
200 	case 0x12:	/* LD4 */
201 	case 0x16:	/* sld8 */
202 		base = (void __iomem *)UNIPHIER_SSCC + 0x840;
203 		break;
204 	default:
205 		base = (void __iomem *)UNIPHIER_SSCC + 0xc00;
206 		break;
207 	}
208 
209 	writel(active_ways, base + 4 * cpu);
210 }
211 
212 static void uniphier_cache_endisable(int enable)
213 {
214 	u32 tmp;
215 
216 	tmp = readl(UNIPHIER_SSCC);
217 	if (enable)
218 		tmp |= UNIPHIER_SSCC_ON;
219 	else
220 		tmp &= ~UNIPHIER_SSCC_ON;
221 	writel(tmp, UNIPHIER_SSCC);
222 }
223 
224 void uniphier_cache_enable(void)
225 {
226 	uniphier_cache_endisable(1);
227 }
228 
229 void uniphier_cache_disable(void)
230 {
231 	uniphier_cache_endisable(0);
232 }
233 
234 #ifdef CONFIG_CACHE_UNIPHIER
235 void v7_outer_cache_flush_all(void)
236 {
237 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_FLUSH);
238 }
239 
240 void v7_outer_cache_inval_all(void)
241 {
242 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_INV);
243 }
244 
245 void v7_outer_cache_flush_range(u32 start, u32 end)
246 {
247 	uniphier_cache_maint_range(start, end, 0, UNIPHIER_SSCOQM_CM_FLUSH);
248 }
249 
250 void v7_outer_cache_inval_range(u32 start, u32 end)
251 {
252 	if (start & (UNIPHIER_SSC_LINE_SIZE - 1)) {
253 		start &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
254 		uniphier_cache_maint_range(start, UNIPHIER_SSC_LINE_SIZE, 0,
255 					   UNIPHIER_SSCOQM_CM_FLUSH);
256 		start += UNIPHIER_SSC_LINE_SIZE;
257 	}
258 
259 	if (start >= end) {
260 		uniphier_cache_sync();
261 		return;
262 	}
263 
264 	if (end & (UNIPHIER_SSC_LINE_SIZE - 1)) {
265 		end &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
266 		uniphier_cache_maint_range(end, UNIPHIER_SSC_LINE_SIZE, 0,
267 					   UNIPHIER_SSCOQM_CM_FLUSH);
268 	}
269 
270 	if (start >= end) {
271 		uniphier_cache_sync();
272 		return;
273 	}
274 
275 	uniphier_cache_maint_range(start, end, 0, UNIPHIER_SSCOQM_CM_INV);
276 }
277 
278 void v7_outer_cache_enable(void)
279 {
280 	uniphier_cache_set_active_ways(0, U32_MAX);	/* activate all ways */
281 	uniphier_cache_enable();
282 }
283 
284 void v7_outer_cache_disable(void)
285 {
286 	uniphier_cache_disable();
287 }
288 #endif
289 
290 void enable_caches(void)
291 {
292 	dcache_enable();
293 }
294