2 * Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org>
4 * Ben Gray <ben.r.gray@gmail.com>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/systm.h>
36 #include <sys/kernel.h>
38 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <machine/intr.h>
43 #include <machine/bus.h>
44 #include <machine/pl310.h>
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>
52 * Define this if you need to disable PL310 for debugging purpose
54 * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
58 * Hardcode errata for now
59 * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
61 #define PL310_ERRATA_588369
62 #define PL310_ERRATA_753970
63 #define PL310_ERRATA_727915
65 #define PL310_LOCK(sc) do { \
66 mtx_lock_spin(&(sc)->sc_mtx); \
69 #define PL310_UNLOCK(sc) do { \
70 mtx_unlock_spin(&(sc)->sc_mtx); \
73 static int pl310_enabled = 1;
74 TUNABLE_INT("pl310.enabled", &pl310_enabled);
76 static uint32_t g_l2cache_way_mask;
78 static const uint32_t g_l2cache_line_size = 32;
79 static const uint32_t g_l2cache_align_mask = (32 - 1);
81 static uint32_t g_l2cache_size;
82 static uint32_t g_way_size;
83 static uint32_t g_ways_assoc;
85 static struct pl310_softc *pl310_softc;
88 pl310_filter(void *arg)
90 struct pl310_softc *sc = arg;
93 intr = pl310_read4(sc, PL310_INTR_MASK);
95 if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
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
101 panic("pl310: caches disabled but cache event detected\n");
104 return (FILTER_HANDLED);
108 pl310_wait_background_op(uint32_t off, uint32_t mask)
111 while (pl310_read4(pl310_softc, off) & mask);
116 * pl310_cache_sync - performs a cache sync operation
118 * According to the TRM:
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."
127 pl310_cache_sync(void)
129 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
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);
138 pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
143 pl310_wbinv_all(void)
146 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
149 PL310_LOCK(pl310_softc);
150 #ifdef PL310_ERRATA_727915
151 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) {
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,
162 PL310_UNLOCK(pl310_softc);
166 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
167 platform_pl310_write_debug(pl310_softc, 3);
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);
172 #ifdef PL310_ERRATA_727915
173 if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
174 platform_pl310_write_debug(pl310_softc, 0);
176 PL310_UNLOCK(pl310_softc);
180 pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
183 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
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;
191 if (size & g_l2cache_align_mask) {
192 size &= ~g_l2cache_align_mask;
193 size += g_l2cache_line_size;
197 #ifdef PL310_ERRATA_727915
198 platform_pl310_write_debug(pl310_softc, 3);
201 #ifdef PL310_ERRATA_588369
202 if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) {
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.
209 pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
210 pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
213 pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA,
215 start += g_l2cache_line_size;
216 size -= g_l2cache_line_size;
218 #ifdef PL310_ERRATA_727915
219 platform_pl310_write_debug(pl310_softc, 0);
223 PL310_UNLOCK(pl310_softc);
227 pl310_wb_range(vm_paddr_t start, vm_size_t size)
230 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
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;
239 if (size & g_l2cache_align_mask) {
240 size &= ~g_l2cache_align_mask;
241 size += g_l2cache_line_size;
245 pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
246 start += g_l2cache_line_size;
247 size -= g_l2cache_line_size;
251 PL310_UNLOCK(pl310_softc);
255 pl310_inv_range(vm_paddr_t start, vm_size_t size)
258 if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
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;
266 if (size & g_l2cache_align_mask) {
267 size &= ~g_l2cache_align_mask;
268 size += g_l2cache_line_size;
271 pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
272 start += g_l2cache_line_size;
273 size -= g_l2cache_line_size;
277 PL310_UNLOCK(pl310_softc);
281 pl310_probe(device_t dev)
284 if (!ofw_bus_is_compatible(dev, "arm,pl310"))
286 device_set_desc(dev, "PL310 L2 cache controller");
291 pl310_attach(device_t dev)
293 struct pl310_softc *sc = device_get_softc(dev);
300 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
302 if (sc->sc_mem_res == NULL)
303 panic("%s: Cannot map registers", device_get_name(dev));
305 /* Allocate an IRQ resource */
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");
314 mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
315 sc->sc_enabled = pl310_enabled;
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);
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))
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);
341 ctrl_value = pl310_read4(sc, PL310_CTRL);
343 if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
344 /* Enable the L2 cache if disabled */
345 platform_pl310_write_ctrl(sc, CTRL_ENABLED);
348 if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
350 * Set counters so when cache event happens
351 * we'll get interrupt and be warned that something
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);
362 /* Temporary switch on for final flush*/
366 platform_pl310_write_ctrl(sc, CTRL_DISABLED);
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);
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);
381 platform_pl310_init(sc);
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;
394 static device_method_t pl310_methods[] = {
395 DEVMETHOD(device_probe, pl310_probe),
396 DEVMETHOD(device_attach, pl310_attach),
400 static driver_t pl310_driver = {
403 sizeof(struct pl310_softc),
405 static devclass_t pl310_devclass;
407 DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);