pl310.c revision 239268
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 239268 2012-08-15 03:03:03Z 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#include <vm/vm.h>
43#include <vm/pmap.h>
44#include <machine/pl310.h>
45#include <machine/bus.h>
46
47#include <dev/fdt/fdt_common.h>
48#include <dev/ofw/openfirm.h>
49#include <dev/ofw/ofw_bus.h>
50#include <dev/ofw/ofw_bus_subr.h>
51
52/**
53 *	PL310 - L2 Cache Controller register offsets.
54 *
55 */
56#define PL310_CACHE_ID              0x000
57#define PL310_CACHE_TYPE            0x004
58#define PL310_CTRL                  0x100
59#define PL310_AUX_CTRL              0x104
60#define PL310_EVENT_COUNTER_CTRL    0x200
61#define PL310_EVENT_COUNTER1_CONF   0x204
62#define PL310_EVENT_COUNTER0_CONF   0x208
63#define PL310_EVENT_COUNTER1_VAL    0x20C
64#define PL310_EVENT_COUNTER0_VAL    0x210
65#define PL310_INTR_MASK             0x214
66#define PL310_MASKED_INTR_STAT      0x218
67#define PL310_RAW_INTR_STAT         0x21C
68#define PL310_INTR_CLEAR            0x220
69#define PL310_CACHE_SYNC            0x730
70#define PL310_INV_LINE_PA           0x770
71#define PL310_INV_WAY               0x77C
72#define PL310_CLEAN_LINE_PA         0x7B0
73#define PL310_CLEAN_LINE_IDX        0x7B8
74#define PL310_CLEAN_WAY             0x7BC
75#define PL310_CLEAN_INV_LINE_PA     0x7F0
76#define PL310_CLEAN_INV_LINE_IDX    0x7F8
77#define PL310_CLEAN_INV_WAY         0x7FC
78#define PL310_LOCKDOWN_D_WAY(x)    (0x900 + ((x) * 8))
79#define PL310_LOCKDOWN_I_WAY(x)    (0x904 + ((x) * 8))
80#define PL310_LOCKDOWN_LINE_ENABLE  0x950
81#define PL310_UNLOCK_ALL_LINES_WAY  0x954
82#define PL310_ADDR_FILTER_START     0xC00
83#define PL310_ADDR_FILTER_END       0xC04
84#define PL310_DEBUG_CTRL            0xF40
85
86
87#define PL310_AUX_CTRL_MASK                      0xc0000fff
88#define PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT       16
89#define PL310_AUX_CTRL_WAY_SIZE_SHIFT            17
90#define PL310_AUX_CTRL_WAY_SIZE_MASK             (0x7 << 17)
91#define PL310_AUX_CTRL_SHARE_OVERRIDE_SHIFT      22
92#define PL310_AUX_CTRL_NS_LOCKDOWN_SHIFT         26
93#define PL310_AUX_CTRL_NS_INT_CTRL_SHIFT         27
94#define PL310_AUX_CTRL_DATA_PREFETCH_SHIFT       28
95#define PL310_AUX_CTRL_INSTR_PREFETCH_SHIFT      29
96#define PL310_AUX_CTRL_EARLY_BRESP_SHIFT         30
97
98
99void omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size);
100void omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size);
101void omap4_l2cache_wb_range(vm_paddr_t physaddr, vm_size_t size);
102void omap4_l2cache_wbinv_all(void);
103void omap4_l2cache_inv_all(void);
104void omap4_l2cache_wb_all(void);
105
106static uint32_t g_l2cache_way_mask;
107
108static const uint32_t g_l2cache_line_size = 32;
109static const uint32_t g_l2cache_align_mask = (32 - 1);
110
111static uint32_t g_l2cache_size;
112
113static struct pl310_softc *pl310_softc;
114
115/**
116 *	pl310_read4 - read a 32-bit value from the PL310 registers
117 *	pl310_write4 - write a 32-bit value from the PL310 registers
118 *	@off: byte offset within the register set to read from
119 *	@val: the value to write into the register
120 *
121 *
122 *	LOCKING:
123 *	None
124 *
125 *	RETURNS:
126 *	nothing in case of write function, if read function returns the value read.
127 */
128static __inline uint32_t
129pl310_read4(bus_size_t off)
130{
131	return bus_read_4(pl310_softc->sc_mem_res, off);
132}
133static __inline void
134pl310_write4(bus_size_t off, uint32_t val)
135{
136	bus_write_4(pl310_softc->sc_mem_res, off, val);
137}
138
139static __inline void
140pl310_wait_background_op(uint32_t off, uint32_t mask)
141{
142	while (pl310_read4(off) & mask);
143}
144
145
146/**
147 *	pl310_cache_sync - performs a cache sync operation
148 *
149 *	According to the TRM:
150 *
151 *  "Before writing to any other register you must perform an explicit
152 *   Cache Sync operation. This is particularly important when the cache is
153 *   enabled and changes to how the cache allocates new lines are to be made."
154 *
155 *
156 */
157static __inline void
158pl310_cache_sync(void)
159{
160	pl310_write4(PL310_CACHE_SYNC, 0);
161}
162
163
164static void
165pl310_wbinv_all(void)
166{
167#if 1
168	pl310_write4(PL310_DEBUG_CTRL, 3);
169#endif
170	pl310_write4(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
171	pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
172	pl310_cache_sync();
173#if 1
174	pl310_write4(PL310_DEBUG_CTRL, 0);
175#endif
176
177}
178
179static void
180pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
181{
182
183	if (size & g_l2cache_align_mask) {
184		size &= ~g_l2cache_align_mask;
185		size += g_l2cache_line_size;
186	}
187#if 1
188
189	pl310_write4(PL310_DEBUG_CTRL, 3);
190#endif
191	while (size > 0) {
192#if 1
193		/*
194		 * Errata 588369 says that clean + inv may keep the
195		 * cache line if it was clean, the recommanded workaround
196		 * is to clean then invalidate the cache line, with
197		 * write-back and cache linefill disabled
198		 */
199
200		pl310_write4(PL310_CLEAN_LINE_PA, start);
201		pl310_write4(PL310_INV_LINE_PA, start);
202#else
203		pl310_write4(PL310_CLEAN_INV_LINE_PA, start);
204#endif
205		start += g_l2cache_line_size;
206		size -= g_l2cache_line_size;
207	}
208#if 1
209	pl310_write4(PL310_DEBUG_CTRL, 0);
210#endif
211	pl310_wait_background_op(PL310_CLEAN_INV_LINE_PA, 1);
212	pl310_cache_sync();
213
214}
215
216static void
217pl310_wb_range(vm_paddr_t start, vm_size_t size)
218{
219
220	if (size & g_l2cache_align_mask) {
221		size &= ~g_l2cache_align_mask;
222		size += g_l2cache_line_size;
223	}
224	while (size > 0) {
225		pl310_write4(PL310_CLEAN_LINE_PA, start);
226		start += g_l2cache_line_size;
227		size -= g_l2cache_line_size;
228	}
229	pl310_cache_sync();
230	pl310_wait_background_op(PL310_CLEAN_LINE_PA, 1);
231
232}
233
234static void
235pl310_inv_range(vm_paddr_t start, vm_size_t size)
236{
237
238	if (size & g_l2cache_align_mask) {
239		size &= ~g_l2cache_align_mask;
240		size += g_l2cache_line_size;
241	}
242	while (size > 0) {
243		pl310_write4(PL310_INV_LINE_PA, start);
244		start += g_l2cache_line_size;
245		size -= g_l2cache_line_size;
246	}
247	pl310_cache_sync();
248	pl310_wait_background_op(PL310_INV_LINE_PA, 1);
249
250}
251
252static int
253pl310_probe(device_t dev)
254{
255
256	if (!ofw_bus_is_compatible(dev, "arm,pl310"))
257		return (ENXIO);
258	device_set_desc(dev, "PL310 L2 cache controller");
259	return (0);
260}
261
262static int
263pl310_attach(device_t dev)
264{
265	struct pl310_softc *sc = device_get_softc(dev);
266	int rid = 0;
267	uint32_t aux_value;
268	uint32_t way_size;
269	uint32_t ways_assoc;
270	uint32_t ctrl_value;
271
272	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
273	    RF_ACTIVE);
274	if (sc->sc_mem_res == NULL)
275		panic("%s: Cannot map registers", device_get_name(dev));
276	pl310_softc = sc;
277
278	platform_init_pl310(sc);
279	aux_value = pl310_read4(PL310_AUX_CTRL);
280	way_size = (aux_value & PL310_AUX_CTRL_WAY_SIZE_MASK) >>
281	    PL310_AUX_CTRL_WAY_SIZE_SHIFT;
282	way_size = 1 << (way_size + 13);
283	if (aux_value & (1 << PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT))
284		ways_assoc = 16;
285	else
286		ways_assoc = 8;
287	g_l2cache_way_mask = (1 << ways_assoc) - 1;
288	g_l2cache_size = way_size * ways_assoc;
289	/* Print the information */
290	printf("  L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
291	       g_l2cache_line_size, ways_assoc);
292	ctrl_value = pl310_read4(PL310_CTRL);
293	if (!(ctrl_value & 0x1)) {
294		/* Enable the L2 cache if disabled */
295		pl310_write4(PL310_CTRL, ctrl_value & 0x1);
296	}
297	pl310_wbinv_all();
298
299	/* Set the l2 functions in the set of cpufuncs */
300	cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
301	cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
302	cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
303	cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
304	return (0);
305}
306
307static device_method_t pl310_methods[] = {
308	DEVMETHOD(device_probe, pl310_probe),
309	DEVMETHOD(device_attach, pl310_attach),
310	{0, 0},
311};
312
313static driver_t pl310_driver = {
314        "l2cache",
315        pl310_methods,
316        sizeof(struct pl310_softc),
317};
318static devclass_t pl310_devclass;
319
320DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
321
322