]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/arm/arm/pl310.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / 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$");
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);