pl310.c revision 244914
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: head/sys/arm/arm/pl310.c 244914 2012-12-31 21:19:44Z gonzo $");
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
76void omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size);
77void omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size);
78void omap4_l2cache_wb_range(vm_paddr_t physaddr, vm_size_t size);
79void omap4_l2cache_wbinv_all(void);
80void omap4_l2cache_inv_all(void);
81void omap4_l2cache_wb_all(void);
82
83static uint32_t g_l2cache_way_mask;
84
85static const uint32_t g_l2cache_line_size = 32;
86static const uint32_t g_l2cache_align_mask = (32 - 1);
87
88static uint32_t g_l2cache_size;
89
90static struct pl310_softc *pl310_softc;
91
92static int
93pl310_filter(void *arg)
94{
95	struct pl310_softc *sc = arg;
96	uint32_t intr;
97
98	intr = pl310_read4(sc, PL310_INTR_MASK);
99
100	if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
101		/*
102		 * This is for debug purpose, so be blunt about it
103		 * We disable PL310 only when something fishy is going
104		 * on and we need to make sure L2 cache is 100% disabled
105		 */
106		panic("pl310: caches disabled but cache event detected\n");
107	}
108
109	return (FILTER_HANDLED);
110}
111
112static __inline void
113pl310_wait_background_op(uint32_t off, uint32_t mask)
114{
115
116	while (pl310_read4(pl310_softc, off) & mask);
117}
118
119
120/**
121 *	pl310_cache_sync - performs a cache sync operation
122 *
123 *	According to the TRM:
124 *
125 *  "Before writing to any other register you must perform an explicit
126 *   Cache Sync operation. This is particularly important when the cache is
127 *   enabled and changes to how the cache allocates new lines are to be made."
128 *
129 *
130 */
131static __inline void
132pl310_cache_sync(void)
133{
134	if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
135		return;
136
137#ifdef PL310_ERRATA_753970
138	/* Write uncached PL310 register */
139	pl310_write4(pl310_softc, 0x740, 0xffffffff);
140#else
141	pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
142#endif
143}
144
145
146static void
147pl310_wbinv_all(void)
148{
149
150	if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
151		return;
152
153	PL310_LOCK(pl310_softc);
154#ifdef PL310_ERRATA_727915
155	platform_pl310_write_debug(pl310_softc, 3);
156#endif
157	pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
158	pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
159	pl310_cache_sync();
160#ifdef PL310_ERRATA_727915
161	platform_pl310_write_debug(pl310_softc, 0);
162#endif
163	PL310_UNLOCK(pl310_softc);
164}
165
166static void
167pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
168{
169
170	if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
171		return;
172
173	PL310_LOCK(pl310_softc);
174	if (start & g_l2cache_align_mask) {
175		size += start & g_l2cache_align_mask;
176		start &= ~g_l2cache_align_mask;
177	}
178	if (size & g_l2cache_align_mask) {
179		size &= ~g_l2cache_align_mask;
180	   	size += g_l2cache_line_size;
181	}
182
183
184#ifdef PL310_ERRATA_727915
185	platform_pl310_write_debug(pl310_softc, 3);
186#endif
187	while (size > 0) {
188#ifdef PL310_ERRATA_588369
189		/*
190		 * Errata 588369 says that clean + inv may keep the
191		 * cache line if it was clean, the recommanded workaround
192		 * is to clean then invalidate the cache line, with
193		 * write-back and cache linefill disabled
194		 */
195
196		pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
197		pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
198#else
199		pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA, start);
200#endif
201		start += g_l2cache_line_size;
202		size -= g_l2cache_line_size;
203	}
204#ifdef PL310_ERRATA_727915
205	platform_pl310_write_debug(pl310_softc, 0);
206#endif
207
208	pl310_cache_sync();
209	PL310_UNLOCK(pl310_softc);
210}
211
212static void
213pl310_wb_range(vm_paddr_t start, vm_size_t size)
214{
215
216	if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
217		return;
218
219	PL310_LOCK(pl310_softc);
220	if (start & g_l2cache_align_mask) {
221		size += start & g_l2cache_align_mask;
222		start &= ~g_l2cache_align_mask;
223	}
224
225	if (size & g_l2cache_align_mask) {
226		size &= ~g_l2cache_align_mask;
227		size += g_l2cache_line_size;
228	}
229
230	while (size > 0) {
231		pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
232		start += g_l2cache_line_size;
233		size -= g_l2cache_line_size;
234	}
235
236	pl310_cache_sync();
237	PL310_UNLOCK(pl310_softc);
238}
239
240static void
241pl310_inv_range(vm_paddr_t start, vm_size_t size)
242{
243
244	if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
245		return;
246
247	PL310_LOCK(pl310_softc);
248	if (start & g_l2cache_align_mask) {
249		size += start & g_l2cache_align_mask;
250		start &= ~g_l2cache_align_mask;
251	}
252	if (size & g_l2cache_align_mask) {
253		size &= ~g_l2cache_align_mask;
254		size += g_l2cache_line_size;
255	}
256	while (size > 0) {
257		pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
258		start += g_l2cache_line_size;
259		size -= g_l2cache_line_size;
260	}
261
262	pl310_cache_sync();
263	PL310_UNLOCK(pl310_softc);
264}
265
266static int
267pl310_probe(device_t dev)
268{
269
270	if (!ofw_bus_is_compatible(dev, "arm,pl310"))
271		return (ENXIO);
272	device_set_desc(dev, "PL310 L2 cache controller");
273	return (0);
274}
275
276static int
277pl310_attach(device_t dev)
278{
279	struct pl310_softc *sc = device_get_softc(dev);
280	int rid = 0;
281	uint32_t aux_value;
282	uint32_t way_size;
283	uint32_t ways_assoc;
284	uint32_t ctrl_value;
285	uint32_t cache_id;
286
287	sc->sc_dev = dev;
288	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
289	    RF_ACTIVE);
290	if (sc->sc_mem_res == NULL)
291		panic("%s: Cannot map registers", device_get_name(dev));
292
293	/* Allocate an IRQ resource */
294	rid = 0;
295	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
296	                                        RF_ACTIVE | RF_SHAREABLE);
297	if (sc->sc_irq_res == NULL) {
298		panic("Cannot allocate IRQ\n");
299	}
300
301	pl310_softc = sc;
302	mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
303	sc->sc_enabled = pl310_enabled;
304
305	/* activate the interrupt */
306	bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
307				pl310_filter, NULL, sc, &sc->sc_irq_h);
308
309	cache_id = pl310_read4(sc, PL310_CACHE_ID);
310	device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
311	    (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
312	    (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
313	aux_value = pl310_read4(sc, PL310_AUX_CTRL);
314	way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
315	    AUX_CTRL_WAY_SIZE_SHIFT;
316	way_size = 1 << (way_size + 13);
317	if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
318		ways_assoc = 16;
319	else
320		ways_assoc = 8;
321	g_l2cache_way_mask = (1 << ways_assoc) - 1;
322	g_l2cache_size = way_size * ways_assoc;
323	/* Print the information */
324	device_printf(dev, "L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
325	       g_l2cache_line_size, ways_assoc);
326
327	ctrl_value = pl310_read4(sc, PL310_CTRL);
328
329	if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
330		/* Enable the L2 cache if disabled */
331		platform_pl310_write_ctrl(sc, CTRL_ENABLED);
332	}
333
334	if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
335		/*
336		 * Set counters so when cache event happens
337		 * we'll get interrupt and be warned that something
338		 * is off
339		 */
340
341		/* Cache Line Eviction for Counter 0 */
342		pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
343		    EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
344		/* Data Read Request for Counter 1 */
345		pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
346		    EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
347
348		/* Temporary switch on for final flush*/
349		sc->sc_enabled = 1;
350		pl310_wbinv_all();
351		sc->sc_enabled = 0;
352		platform_pl310_write_ctrl(sc, CTRL_DISABLED);
353
354		/* Enable and clear pending interrupts */
355		pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
356		pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
357
358		/* Enable counters and reset C0 and C1 */
359		pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
360		    EVENT_COUNTER_CTRL_ENABLED |
361		    EVENT_COUNTER_CTRL_C0_RESET |
362		    EVENT_COUNTER_CTRL_C1_RESET);
363
364	}
365
366	if (sc->sc_enabled)
367		platform_pl310_init(sc);
368
369	pl310_wbinv_all();
370
371	/* Set the l2 functions in the set of cpufuncs */
372	cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
373	cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
374	cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
375	cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
376
377	return (0);
378}
379
380static device_method_t pl310_methods[] = {
381	DEVMETHOD(device_probe, pl310_probe),
382	DEVMETHOD(device_attach, pl310_attach),
383	{0, 0},
384};
385
386static driver_t pl310_driver = {
387        "l2cache",
388        pl310_methods,
389        sizeof(struct pl310_softc),
390};
391static devclass_t pl310_devclass;
392
393DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
394