FreeBSD/Linux Kernel Cross Reference
sys/arm/arm/pl310.c
1 /*-
2 * Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org>
3 * Copyright (c) 2011
4 * Ben Gray <ben.r.gray@gmail.com>.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the company nor the name of the author may be used to
16 * endorse or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: releng/10.0/sys/arm/arm/pl310.c 253788 2013-07-29 21:45:39Z cognet $");
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/rman.h>
38 #include <sys/module.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <machine/intr.h>
42
43 #include <machine/bus.h>
44 #include <machine/pl310.h>
45
46 #include <dev/fdt/fdt_common.h>
47 #include <dev/ofw/openfirm.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50
51 /*
52 * Define this if you need to disable PL310 for debugging purpose
53 * Spec:
54 * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
55 */
56
57 /*
58 * Hardcode errata for now
59 * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
60 */
61 #define PL310_ERRATA_588369
62 #define PL310_ERRATA_753970
63 #define PL310_ERRATA_727915
64
65 #define PL310_LOCK(sc) do { \
66 mtx_lock_spin(&(sc)->sc_mtx); \
67 } while(0);
68
69 #define PL310_UNLOCK(sc) do { \
70 mtx_unlock_spin(&(sc)->sc_mtx); \
71 } while(0);
72
73 static int pl310_enabled = 1;
74 TUNABLE_INT("pl310.enabled", &pl310_enabled);
75
76 static uint32_t g_l2cache_way_mask;
77
78 static const uint32_t g_l2cache_line_size = 32;
79 static const uint32_t g_l2cache_align_mask = (32 - 1);
80
81 static uint32_t g_l2cache_size;
82 static uint32_t g_way_size;
83 static uint32_t g_ways_assoc;
84
85 static struct pl310_softc *pl310_softc;
86
87 static int
88 pl310_filter(void *arg)
89 {
90 struct pl310_softc *sc = arg;
91 uint32_t intr;
92
93 intr = pl310_read4(sc, PL310_INTR_MASK);
94
95 if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
96 /*
97 * This is for debug purpose, so be blunt about it
98 * We disable PL310 only when something fishy is going
99 * on and we need to make sure L2 cache is 100% disabled
100 */
101 panic("pl310: caches disabled but cache event detected\n");
102 }
103
104 return (FILTER_HANDLED);
105 }
106
107 static __inline void
108 pl310_wait_background_op(uint32_t off, uint32_t mask)
109 {
110
111 while (pl310_read4(pl310_softc, off) & mask);
112 }
113
114
115 /**
116 * pl310_cache_sync - performs a cache sync operation
117 *
118 * According to the TRM:
119 *
120 * "Before writing to any other register you must perform an explicit
121 * Cache Sync operation. This is particularly important when the cache is
122 * enabled and changes to how the cache allocates new lines are to be made."
123 *
124 *
125 */
126 static __inline void
127 pl310_cache_sync(void)
128 {
129 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
130 return;
131
132 #ifdef PL310_ERRATA_753970
133 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
134 /* Write uncached PL310 register */
135 pl310_write4(pl310_softc, 0x740, 0xffffffff);
136 else
137 #endif
138 pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
139 }
140
141
142 static void
143 pl310_wbinv_all(void)
144 {
145
146 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
147 return;
148
149 PL310_LOCK(pl310_softc);
150 #ifdef PL310_ERRATA_727915
151 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) {
152 int i, j;
153
154 for (i = 0; i < g_ways_assoc; i++) {
155 for (j = 0; j < g_way_size / g_l2cache_line_size; j++) {
156 pl310_write4(pl310_softc,
157 PL310_CLEAN_INV_LINE_IDX,
158 (i << 28 | j << 5));
159 }
160 }
161 pl310_cache_sync();
162 PL310_UNLOCK(pl310_softc);
163 return;
164
165 }
166 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
167 platform_pl310_write_debug(pl310_softc, 3);
168 #endif
169 pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
170 pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
171 pl310_cache_sync();
172 #ifdef PL310_ERRATA_727915
173 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
174 platform_pl310_write_debug(pl310_softc, 0);
175 #endif
176 PL310_UNLOCK(pl310_softc);
177 }
178
179 static void
180 pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
181 {
182
183 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
184 return;
185
186 PL310_LOCK(pl310_softc);
187 if (start & g_l2cache_align_mask) {
188 size += start & g_l2cache_align_mask;
189 start &= ~g_l2cache_align_mask;
190 }
191 if (size & g_l2cache_align_mask) {
192 size &= ~g_l2cache_align_mask;
193 size += g_l2cache_line_size;
194 }
195
196
197 #ifdef PL310_ERRATA_727915
198 platform_pl310_write_debug(pl310_softc, 3);
199 #endif
200 while (size > 0) {
201 #ifdef PL310_ERRATA_588369
202 if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) {
203 /*
204 * Errata 588369 says that clean + inv may keep the
205 * cache line if it was clean, the recommanded
206 * workaround is to clean then invalidate the cache
207 * line, with write-back and cache linefill disabled.
208 */
209 pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
210 pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
211 } else
212 #endif
213 pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA,
214 start);
215 start += g_l2cache_line_size;
216 size -= g_l2cache_line_size;
217 }
218 #ifdef PL310_ERRATA_727915
219 platform_pl310_write_debug(pl310_softc, 0);
220 #endif
221
222 pl310_cache_sync();
223 PL310_UNLOCK(pl310_softc);
224 }
225
226 static void
227 pl310_wb_range(vm_paddr_t start, vm_size_t size)
228 {
229
230 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
231 return;
232
233 PL310_LOCK(pl310_softc);
234 if (start & g_l2cache_align_mask) {
235 size += start & g_l2cache_align_mask;
236 start &= ~g_l2cache_align_mask;
237 }
238
239 if (size & g_l2cache_align_mask) {
240 size &= ~g_l2cache_align_mask;
241 size += g_l2cache_line_size;
242 }
243
244 while (size > 0) {
245 pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
246 start += g_l2cache_line_size;
247 size -= g_l2cache_line_size;
248 }
249
250 pl310_cache_sync();
251 PL310_UNLOCK(pl310_softc);
252 }
253
254 static void
255 pl310_inv_range(vm_paddr_t start, vm_size_t size)
256 {
257
258 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
259 return;
260
261 PL310_LOCK(pl310_softc);
262 if (start & g_l2cache_align_mask) {
263 size += start & g_l2cache_align_mask;
264 start &= ~g_l2cache_align_mask;
265 }
266 if (size & g_l2cache_align_mask) {
267 size &= ~g_l2cache_align_mask;
268 size += g_l2cache_line_size;
269 }
270 while (size > 0) {
271 pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
272 start += g_l2cache_line_size;
273 size -= g_l2cache_line_size;
274 }
275
276 pl310_cache_sync();
277 PL310_UNLOCK(pl310_softc);
278 }
279
280 static int
281 pl310_probe(device_t dev)
282 {
283
284 if (!ofw_bus_is_compatible(dev, "arm,pl310"))
285 return (ENXIO);
286 device_set_desc(dev, "PL310 L2 cache controller");
287 return (0);
288 }
289
290 static int
291 pl310_attach(device_t dev)
292 {
293 struct pl310_softc *sc = device_get_softc(dev);
294 int rid = 0;
295 uint32_t aux_value;
296 uint32_t ctrl_value;
297 uint32_t cache_id;
298
299 sc->sc_dev = dev;
300 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
301 RF_ACTIVE);
302 if (sc->sc_mem_res == NULL)
303 panic("%s: Cannot map registers", device_get_name(dev));
304
305 /* Allocate an IRQ resource */
306 rid = 0;
307 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
308 RF_ACTIVE | RF_SHAREABLE);
309 if (sc->sc_irq_res == NULL) {
310 panic("Cannot allocate IRQ\n");
311 }
312
313 pl310_softc = sc;
314 mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
315 sc->sc_enabled = pl310_enabled;
316
317 /* activate the interrupt */
318 bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
319 pl310_filter, NULL, sc, &sc->sc_irq_h);
320
321 cache_id = pl310_read4(sc, PL310_CACHE_ID);
322 sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) &
323 CACHE_ID_RELEASE_MASK;
324 device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
325 (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
326 (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
327 aux_value = pl310_read4(sc, PL310_AUX_CTRL);
328 g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
329 AUX_CTRL_WAY_SIZE_SHIFT;
330 g_way_size = 1 << (g_way_size + 13);
331 if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
332 g_ways_assoc = 16;
333 else
334 g_ways_assoc = 8;
335 g_l2cache_way_mask = (1 << g_ways_assoc) - 1;
336 g_l2cache_size = g_way_size * g_ways_assoc;
337 /* Print the information */
338 device_printf(dev, "L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
339 g_l2cache_line_size, g_ways_assoc);
340
341 ctrl_value = pl310_read4(sc, PL310_CTRL);
342
343 if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
344 /* Enable the L2 cache if disabled */
345 platform_pl310_write_ctrl(sc, CTRL_ENABLED);
346 }
347
348 if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
349 /*
350 * Set counters so when cache event happens
351 * we'll get interrupt and be warned that something
352 * is off
353 */
354
355 /* Cache Line Eviction for Counter 0 */
356 pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
357 EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
358 /* Data Read Request for Counter 1 */
359 pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
360 EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
361
362 /* Temporary switch on for final flush*/
363 sc->sc_enabled = 1;
364 pl310_wbinv_all();
365 sc->sc_enabled = 0;
366 platform_pl310_write_ctrl(sc, CTRL_DISABLED);
367
368 /* Enable and clear pending interrupts */
369 pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
370 pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
371
372 /* Enable counters and reset C0 and C1 */
373 pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
374 EVENT_COUNTER_CTRL_ENABLED |
375 EVENT_COUNTER_CTRL_C0_RESET |
376 EVENT_COUNTER_CTRL_C1_RESET);
377
378 }
379
380 if (sc->sc_enabled)
381 platform_pl310_init(sc);
382
383 pl310_wbinv_all();
384
385 /* Set the l2 functions in the set of cpufuncs */
386 cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
387 cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
388 cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
389 cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
390
391 return (0);
392 }
393
394 static device_method_t pl310_methods[] = {
395 DEVMETHOD(device_probe, pl310_probe),
396 DEVMETHOD(device_attach, pl310_attach),
397 {0, 0},
398 };
399
400 static driver_t pl310_driver = {
401 "l2cache",
402 pl310_methods,
403 sizeof(struct pl310_softc),
404 };
405 static devclass_t pl310_devclass;
406
407 DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
Cache object: 8b51f4644a4a38dd2fd71ed5c2f1bee0
|