xref: /openbsd-src/sys/arch/arm/cortex/arml2cc.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /* $OpenBSD: arml2cc.c,v 1.5 2016/08/22 01:42:00 jsg Exp $ */
2 /*
3  * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24 #include <sys/socket.h>
25 #include <sys/timeout.h>
26 #include <machine/intr.h>
27 #include <machine/bus.h>
28 #include <arm/cpufunc.h>
29 #include <arm/cortex/cortex.h>
30 #include <arm/cortex/smc.h>
31 
32 #define PL310_ERRATA_727915
33 
34 /* offset from periphbase */
35 #define L2C_ADDR			0x2000
36 #define L2C_SIZE			0x1000
37 
38 /* registers */
39 #define L2C_CACHE_ID			0x000
40 #define L2C_CACHE_TYPE			0x004
41 #define L2C_CTL				0x100
42 #define L2C_AUXCTL			0x104
43 #define L2C_TAG_RAM_CTL			0x108
44 #define L2C_DATA_RAM_CTL		0x10c
45 #define L2C_EVC_CTR_CTL			0x200
46 #define L2C_EVC_CTR0_CTL		0x204
47 #define L2C_EVC_CTR1_CTL		0x208
48 #define L2C_EVC_CTR0_VAL		0x20c
49 #define L2C_EVC_CTR1_VAL		0x210
50 #define L2C_INT_MASK			0x214
51 #define L2C_INT_MASK_STS		0x218
52 #define L2C_INT_RAW_STS			0x21c
53 #define L2C_INT_CLR			0x220
54 #define L2C_CACHE_SYNC			0x730
55 #define L2C_INV_PA			0x770
56 #define L2C_INV_WAY			0x77c
57 #define L2C_CLEAN_PA			0x7b0
58 #define L2C_CLEAN_INDEX			0x7b8
59 #define L2C_CLEAN_WAY			0x7bc
60 #define L2C_CLEAN_INV_PA		0x7f0
61 #define L2C_CLEAN_INV_INDEX		0x7f8
62 #define L2C_CLEAN_INV_WAY		0x7fc
63 #define L2C_D_LOCKDOWN0			0x900
64 #define L2C_I_LOCKDOWN0			0x904
65 #define L2C_D_LOCKDOWN1			0x908
66 #define L2C_I_LOCKDOWN1			0x90c
67 #define L2C_D_LOCKDOWN2			0x910
68 #define L2C_I_LOCKDOWN2			0x914
69 #define L2C_D_LOCKDOWN3			0x918
70 #define L2C_I_LOCKDOWN3			0x91c
71 #define L2C_D_LOCKDOWN4			0x920
72 #define L2C_I_LOCKDOWN4			0x924
73 #define L2C_D_LOCKDOWN5			0x928
74 #define L2C_I_LOCKDOWN5			0x92c
75 #define L2C_D_LOCKDOWN6			0x930
76 #define L2C_I_LOCKDOWN6			0x934
77 #define L2C_D_LOCKDOWN7			0x938
78 #define L2C_I_LOCKDOWN7			0x93c
79 #define L2C_LOCKDOWN_LINE_EN		0x950
80 #define L2C_UNLOCK_WAY			0x954
81 #define L2C_ADDR_FILTER_START		0xc00
82 #define L2C_ADDR_FILTER_END		0xc04
83 #define L2C_DEBUG_CTL			0xf40
84 #define L2C_PREFETCH_CTL		0xf60
85 #define L2C_POWER_CTL			0xf80
86 
87 #define L2C_CACHE_ID_RELEASE_MASK	0x3f
88 #define L2C_CACHE_TYPE_LINESIZE		0x3
89 #define L2C_AUXCTL_ASSOC_SHIFT		16
90 #define L2C_AUXCTL_ASSOC_MASK		0x1
91 
92 #define roundup2(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
93 
94 struct arml2cc_softc {
95 	struct device		sc_dev;
96 	bus_space_tag_t		sc_iot;
97 	bus_space_handle_t	sc_ioh;
98 	uint32_t		sc_enabled;
99 	uint32_t		sc_waymask;
100 	uint32_t		sc_dcache_line_size;
101 };
102 
103 struct arml2cc_softc *arml2cc_sc;
104 
105 int arml2cc_match(struct device *, void *, void *);
106 void arml2cc_attach(struct device *parent, struct device *self, void *args);
107 void arml2cc_enable(struct arml2cc_softc *);
108 void arml2cc_disable(struct arml2cc_softc *);
109 void arml2cc_sdcache_wbinv_all(void);
110 void arml2cc_sdcache_wbinv_range(vaddr_t, paddr_t, psize_t);
111 void arml2cc_sdcache_inv_range(vaddr_t, paddr_t, psize_t);
112 void arml2cc_sdcache_wb_range(vaddr_t, paddr_t, psize_t);
113 void arml2cc_cache_range_op(paddr_t, psize_t, bus_size_t);
114 void arml2cc_cache_way_op(struct arml2cc_softc *, bus_size_t, uint32_t);
115 void arml2cc_cache_op(struct arml2cc_softc *, bus_size_t, uint32_t);
116 void arml2cc_cache_sync(struct arml2cc_softc *);
117 void arml2cc_sdcache_drain_writebuf(void);
118 
119 struct cfattach armliicc_ca = {
120 	sizeof (struct arml2cc_softc), arml2cc_match, arml2cc_attach
121 };
122 
123 struct cfdriver armliicc_cd = {
124 	NULL, "armliicc", DV_DULL
125 };
126 
127 int
128 arml2cc_match(struct device *parent, void *cfdata, void *aux)
129 {
130 	if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
131 		return (1);
132 
133 	return (0);
134 }
135 
136 void
137 arml2cc_attach(struct device *parent, struct device *self, void *args)
138 {
139 	struct cortex_attach_args *ia = args;
140 	struct arml2cc_softc *sc = (struct arml2cc_softc *) self;
141 
142 	sc->sc_iot = ia->ca_iot;
143 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + L2C_ADDR,
144 	    L2C_SIZE, 0, &sc->sc_ioh))
145 		panic("arml2cc_attach: bus_space_map failed!");
146 
147 	printf(": rtl %d", bus_space_read_4(sc->sc_iot, sc->sc_ioh,
148 	    L2C_CACHE_ID) & 0x3f);
149 
150 	arml2cc_sc = sc;
151 
152 	if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CTL))
153 		panic("L2 Cache controller was already enabled\n");
154 
155 	sc->sc_dcache_line_size = 32 << (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CACHE_TYPE) & L2C_CACHE_TYPE_LINESIZE);
156 	sc->sc_waymask = (8 << ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_AUXCTL)
157 			    >> L2C_AUXCTL_ASSOC_SHIFT) & L2C_AUXCTL_ASSOC_MASK)) - 1;
158 	printf(" waymask: 0x%08x\n", sc->sc_waymask);
159 
160 	arml2cc_enable(sc);
161 	sc->sc_enabled = 1;
162 
163 	arml2cc_sdcache_wbinv_all();
164 
165 	cpufuncs.cf_sdcache_wbinv_all = arml2cc_sdcache_wbinv_all;
166 	cpufuncs.cf_sdcache_wbinv_range = arml2cc_sdcache_wbinv_range;
167 	cpufuncs.cf_sdcache_inv_range = arml2cc_sdcache_inv_range;
168 	cpufuncs.cf_sdcache_wb_range = arml2cc_sdcache_wb_range;
169 	cpufuncs.cf_sdcache_drain_writebuf = arml2cc_sdcache_drain_writebuf;
170 }
171 
172 void
173 arml2cc_enable(struct arml2cc_softc *sc)
174 {
175 	int s;
176 	s = splhigh();
177 
178 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL,
179 	    1);
180 
181 	arml2cc_cache_way_op(sc, L2C_INV_WAY, sc->sc_waymask);
182 	arml2cc_cache_sync(sc);
183 
184 	splx(s);
185 }
186 
187 void
188 arml2cc_disable(struct arml2cc_softc *sc)
189 {
190 	int s;
191 	s = splhigh();
192 
193 	arml2cc_cache_way_op(sc, L2C_CLEAN_INV_WAY, sc->sc_waymask);
194 	arml2cc_cache_sync(sc);
195 
196 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL, 0);
197 
198 	splx(s);
199 }
200 
201 void
202 arml2cc_cache_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t val)
203 {
204 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
205 	while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & 1) {
206 		/* spin */
207 	}
208 }
209 
210 void
211 arml2cc_cache_way_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t way_mask)
212 {
213 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, way_mask);
214 	while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & way_mask) {
215 		/* spin */
216 	}
217 }
218 
219 void
220 arml2cc_cache_sync(struct arml2cc_softc *sc)
221 {
222 	/* ARM Errata 753970 */
223 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, 0x740, 0xffffffff);
224 }
225 
226 void
227 arml2cc_sdcache_drain_writebuf(void)
228 {
229 	struct arml2cc_softc * const sc = arml2cc_sc;
230 	if (sc == NULL || !sc->sc_enabled)
231 		return;
232 
233 	arml2cc_cache_sync(sc);
234 }
235 
236 void
237 arml2cc_cache_range_op(paddr_t pa, psize_t len, bus_size_t cache_op)
238 {
239 	struct arml2cc_softc * const sc = arml2cc_sc;
240 	size_t line_size = sc->sc_dcache_line_size;
241 	size_t line_mask = line_size - 1;
242 	paddr_t endpa;
243 
244 	endpa = pa + len;
245 	pa = pa & ~line_mask;
246 
247 	// printf("l2inv op %x %08x %08x incr %d %d\n", cache_op, pa, endpa, line_size, len);
248 	while (endpa > pa) {
249 		arml2cc_cache_op(sc, cache_op, pa);
250 		pa += line_size;
251 	}
252 }
253 
254 void
255 arml2cc_sdcache_wbinv_all(void)
256 {
257 	struct arml2cc_softc *sc = arml2cc_sc;
258 	if (sc == NULL || !sc->sc_enabled)
259 		return;
260 
261 #ifdef PL310_ERRATA_727915
262 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
263 #endif
264 
265 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY, sc->sc_waymask);
266 	while(bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY) & sc->sc_waymask);
267 
268 #ifdef PL310_ERRATA_727915
269 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
270 #endif
271 
272 	arml2cc_cache_sync(sc);
273 }
274 void
275 arml2cc_sdcache_wbinv_range(vaddr_t va, paddr_t pa, psize_t len)
276 {
277 	struct arml2cc_softc *sc = arml2cc_sc;
278 	if (sc == NULL || !sc->sc_enabled)
279 		return;
280 
281 #ifdef PL310_ERRATA_727915
282 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
283 #endif
284 
285 	arml2cc_cache_range_op(pa, len, L2C_CLEAN_INV_PA);
286 	arml2cc_cache_sync(sc);
287 
288 #ifdef PL310_ERRATA_727915
289 	platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
290 #endif
291 }
292 
293 void
294 arml2cc_sdcache_inv_range(vaddr_t va, paddr_t pa, psize_t len)
295 {
296 	struct arml2cc_softc *sc = arml2cc_sc;
297 	if (sc == NULL || !sc->sc_enabled)
298 		return;
299 	arml2cc_cache_range_op(pa, len, L2C_INV_PA);
300 	arml2cc_cache_sync(sc);
301 }
302 
303 void
304 arml2cc_sdcache_wb_range(vaddr_t va, paddr_t pa, psize_t len)
305 {
306 	struct arml2cc_softc *sc = arml2cc_sc;
307 	if (sc == NULL || !sc->sc_enabled)
308 		return;
309 	arml2cc_cache_range_op(pa, len, L2C_CLEAN_PA);
310 	arml2cc_cache_sync(sc);
311 }
312