pl310.c revision 266152
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: stable/10/sys/arm/arm/pl310.c 266152 2014-05-15 16:11:06Z ian $");
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
73static int pl310_enabled = 1;
74TUNABLE_INT("pl310.enabled", &pl310_enabled);
75
76static uint32_t g_l2cache_way_mask;
77
78static const uint32_t g_l2cache_line_size = 32;
79static const uint32_t g_l2cache_align_mask = (32 - 1);
80
81static uint32_t g_l2cache_size;
82static uint32_t g_way_size;
83static uint32_t g_ways_assoc;
84
85static struct pl310_softc *pl310_softc;
86
87static int
88pl310_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
107static __inline void
108pl310_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 */
126static __inline void
127pl310_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
142static void
143pl310_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
179static void
180pl310_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
226static void
227pl310_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
254static void
255pl310_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
280static int
281pl310_probe(device_t dev)
282{
283
284	if (!ofw_bus_status_okay(dev))
285		return (ENXIO);
286
287	if (!ofw_bus_is_compatible(dev, "arm,pl310"))
288		return (ENXIO);
289	device_set_desc(dev, "PL310 L2 cache controller");
290	return (0);
291}
292
293static int
294pl310_attach(device_t dev)
295{
296	struct pl310_softc *sc = device_get_softc(dev);
297	int rid = 0;
298	uint32_t aux_value;
299	uint32_t ctrl_value;
300	uint32_t cache_id;
301
302	sc->sc_dev = dev;
303	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
304	    RF_ACTIVE);
305	if (sc->sc_mem_res == NULL)
306		panic("%s: Cannot map registers", device_get_name(dev));
307
308	/* Allocate an IRQ resource */
309	rid = 0;
310	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
311	                                        RF_ACTIVE | RF_SHAREABLE);
312	if (sc->sc_irq_res == NULL) {
313		panic("Cannot allocate IRQ\n");
314	}
315
316	pl310_softc = sc;
317	mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
318	sc->sc_enabled = pl310_enabled;
319
320	/* activate the interrupt */
321	bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
322				pl310_filter, NULL, sc, &sc->sc_irq_h);
323
324	cache_id = pl310_read4(sc, PL310_CACHE_ID);
325	sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) &
326	    CACHE_ID_RELEASE_MASK;
327	device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
328	    (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
329	    (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
330	aux_value = pl310_read4(sc, PL310_AUX_CTRL);
331	g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
332	    AUX_CTRL_WAY_SIZE_SHIFT;
333	g_way_size = 1 << (g_way_size + 13);
334	if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
335		g_ways_assoc = 16;
336	else
337		g_ways_assoc = 8;
338	g_l2cache_way_mask = (1 << g_ways_assoc) - 1;
339	g_l2cache_size = g_way_size * g_ways_assoc;
340	/* Print the information */
341	device_printf(dev, "L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
342	       g_l2cache_line_size, g_ways_assoc);
343
344	ctrl_value = pl310_read4(sc, PL310_CTRL);
345
346	if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
347		/* invalidate current content */
348		pl310_write4(pl310_softc, PL310_INV_WAY, 0xffff);
349		pl310_wait_background_op(PL310_INV_WAY, 0xffff);
350
351		/* Enable the L2 cache if disabled */
352		platform_pl310_write_ctrl(sc, CTRL_ENABLED);
353		device_printf(dev, "L2 Cache enabled\n");
354	}
355
356	if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
357		/*
358		 * Set counters so when cache event happens
359		 * we'll get interrupt and be warned that something
360		 * is off
361		 */
362
363		/* Cache Line Eviction for Counter 0 */
364		pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
365		    EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
366		/* Data Read Request for Counter 1 */
367		pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
368		    EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
369
370		/* Temporary switch on for final flush*/
371		sc->sc_enabled = 1;
372		pl310_wbinv_all();
373		sc->sc_enabled = 0;
374		platform_pl310_write_ctrl(sc, CTRL_DISABLED);
375
376		/* Enable and clear pending interrupts */
377		pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
378		pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
379
380		/* Enable counters and reset C0 and C1 */
381		pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
382		    EVENT_COUNTER_CTRL_ENABLED |
383		    EVENT_COUNTER_CTRL_C0_RESET |
384		    EVENT_COUNTER_CTRL_C1_RESET);
385
386		device_printf(dev, "L2 Cache disabled\n");
387	}
388
389	if (sc->sc_enabled)
390		platform_pl310_init(sc);
391
392	pl310_wbinv_all();
393
394	/* Set the l2 functions in the set of cpufuncs */
395	cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
396	cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
397	cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
398	cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
399
400	return (0);
401}
402
403static device_method_t pl310_methods[] = {
404	DEVMETHOD(device_probe, pl310_probe),
405	DEVMETHOD(device_attach, pl310_attach),
406	{0, 0},
407};
408
409static driver_t pl310_driver = {
410        "l2cache",
411        pl310_methods,
412        sizeof(struct pl310_softc),
413};
414static devclass_t pl310_devclass;
415
416DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
417