summaryrefslogtreecommitdiffstats
path: root/sys/arm/arm/pl310.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/arm/pl310.c')
-rw-r--r--sys/arm/arm/pl310.c272
1 files changed, 166 insertions, 106 deletions
diff --git a/sys/arm/arm/pl310.c b/sys/arm/arm/pl310.c
index 7c43b31..a5af23c 100644
--- a/sys/arm/arm/pl310.c
+++ b/sys/arm/arm/pl310.c
@@ -39,62 +39,39 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/intr.h>
-#include <vm/vm.h>
-#include <vm/pmap.h>
-#include <machine/pl310.h>
+
#include <machine/bus.h>
+#include <machine/pl310.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-/**
- * PL310 - L2 Cache Controller register offsets.
- *
+/*
+ * Define this if you need to disable PL310 for debugging purpose
+ * Spec:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
+ */
+
+/*
+ * Hardcode errata for now
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
*/
-#define PL310_CACHE_ID 0x000
-#define PL310_CACHE_TYPE 0x004
-#define PL310_CTRL 0x100
-#define PL310_AUX_CTRL 0x104
-#define PL310_EVENT_COUNTER_CTRL 0x200
-#define PL310_EVENT_COUNTER1_CONF 0x204
-#define PL310_EVENT_COUNTER0_CONF 0x208
-#define PL310_EVENT_COUNTER1_VAL 0x20C
-#define PL310_EVENT_COUNTER0_VAL 0x210
-#define PL310_INTR_MASK 0x214
-#define PL310_MASKED_INTR_STAT 0x218
-#define PL310_RAW_INTR_STAT 0x21C
-#define PL310_INTR_CLEAR 0x220
-#define PL310_CACHE_SYNC 0x730
-#define PL310_INV_LINE_PA 0x770
-#define PL310_INV_WAY 0x77C
-#define PL310_CLEAN_LINE_PA 0x7B0
-#define PL310_CLEAN_LINE_IDX 0x7B8
-#define PL310_CLEAN_WAY 0x7BC
-#define PL310_CLEAN_INV_LINE_PA 0x7F0
-#define PL310_CLEAN_INV_LINE_IDX 0x7F8
-#define PL310_CLEAN_INV_WAY 0x7FC
-#define PL310_LOCKDOWN_D_WAY(x) (0x900 + ((x) * 8))
-#define PL310_LOCKDOWN_I_WAY(x) (0x904 + ((x) * 8))
-#define PL310_LOCKDOWN_LINE_ENABLE 0x950
-#define PL310_UNLOCK_ALL_LINES_WAY 0x954
-#define PL310_ADDR_FILTER_START 0xC00
-#define PL310_ADDR_FILTER_END 0xC04
-#define PL310_DEBUG_CTRL 0xF40
-
-
-#define PL310_AUX_CTRL_MASK 0xc0000fff
-#define PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT 16
-#define PL310_AUX_CTRL_WAY_SIZE_SHIFT 17
-#define PL310_AUX_CTRL_WAY_SIZE_MASK (0x7 << 17)
-#define PL310_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22
-#define PL310_AUX_CTRL_NS_LOCKDOWN_SHIFT 26
-#define PL310_AUX_CTRL_NS_INT_CTRL_SHIFT 27
-#define PL310_AUX_CTRL_DATA_PREFETCH_SHIFT 28
-#define PL310_AUX_CTRL_INSTR_PREFETCH_SHIFT 29
-#define PL310_AUX_CTRL_EARLY_BRESP_SHIFT 30
+#define PL310_ERRATA_588369
+#define PL310_ERRATA_753970
+#define PL310_ERRATA_727915
+
+#define PL310_LOCK(sc) do { \
+ mtx_lock_spin(&(sc)->sc_mtx); \
+} while(0);
+#define PL310_UNLOCK(sc) do { \
+ mtx_unlock_spin(&(sc)->sc_mtx); \
+} while(0);
+
+static int pl310_enabled = 1;
+TUNABLE_INT("pl310.enabled", &pl310_enabled);
void omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size);
void omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size);
@@ -112,34 +89,31 @@ static uint32_t g_l2cache_size;
static struct pl310_softc *pl310_softc;
-/**
- * pl310_read4 - read a 32-bit value from the PL310 registers
- * pl310_write4 - write a 32-bit value from the PL310 registers
- * @off: byte offset within the register set to read from
- * @val: the value to write into the register
- *
- *
- * LOCKING:
- * None
- *
- * RETURNS:
- * nothing in case of write function, if read function returns the value read.
- */
-static __inline uint32_t
-pl310_read4(bus_size_t off)
-{
- return bus_read_4(pl310_softc->sc_mem_res, off);
-}
-static __inline void
-pl310_write4(bus_size_t off, uint32_t val)
+static int
+pl310_filter(void *arg)
{
- bus_write_4(pl310_softc->sc_mem_res, off, val);
+ struct pl310_softc *sc = arg;
+ uint32_t intr;
+
+ intr = pl310_read4(sc, PL310_INTR_MASK);
+
+ if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
+ /*
+ * This is for debug purpose, so be blunt about it
+ * We disable PL310 only when something fishy is going
+ * on and we need to make sure L2 cache is 100% disabled
+ */
+ panic("pl310: caches disabled but cache event detected\n");
+ }
+
+ return (FILTER_HANDLED);
}
static __inline void
pl310_wait_background_op(uint32_t off, uint32_t mask)
{
- while (pl310_read4(off) & mask);
+
+ while (pl310_read4(pl310_softc, off) & mask);
}
@@ -157,29 +131,46 @@ pl310_wait_background_op(uint32_t off, uint32_t mask)
static __inline void
pl310_cache_sync(void)
{
- pl310_write4(PL310_CACHE_SYNC, 0);
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+#ifdef PL310_ERRATA_753970
+ /* Write uncached PL310 register */
+ pl310_write4(pl310_softc, 0x740, 0xffffffff);
+#else
+ pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
+#endif
}
static void
pl310_wbinv_all(void)
{
-#if 1
- pl310_write4(PL310_DEBUG_CTRL, 3);
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
+#ifdef PL310_ERRATA_727915
+ platform_pl310_write_debug(pl310_softc, 3);
#endif
- pl310_write4(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
+ pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_cache_sync();
-#if 1
- pl310_write4(PL310_DEBUG_CTRL, 0);
+#ifdef PL310_ERRATA_727915
+ platform_pl310_write_debug(pl310_softc, 0);
#endif
-
+ PL310_UNLOCK(pl310_softc);
}
static void
pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
{
-
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
@@ -188,12 +179,13 @@ pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
-#if 1
- pl310_write4(PL310_DEBUG_CTRL, 3);
+
+#ifdef PL310_ERRATA_727915
+ platform_pl310_write_debug(pl310_softc, 3);
#endif
while (size > 0) {
-#if 1
+#ifdef PL310_ERRATA_588369
/*
* Errata 588369 says that clean + inv may keep the
* cache line if it was clean, the recommanded workaround
@@ -201,48 +193,58 @@ pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
* write-back and cache linefill disabled
*/
- pl310_write4(PL310_CLEAN_LINE_PA, start);
- pl310_write4(PL310_INV_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
#else
- pl310_write4(PL310_CLEAN_INV_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA, start);
#endif
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
-#if 1
- pl310_write4(PL310_DEBUG_CTRL, 0);
+#ifdef PL310_ERRATA_727915
+ platform_pl310_write_debug(pl310_softc, 0);
#endif
- pl310_wait_background_op(PL310_CLEAN_INV_LINE_PA, 1);
+
pl310_cache_sync();
-
+ PL310_UNLOCK(pl310_softc);
}
static void
pl310_wb_range(vm_paddr_t start, vm_size_t size)
{
-
+
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
+
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
+
while (size > 0) {
- pl310_write4(PL310_CLEAN_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
- pl310_cache_sync();
- pl310_wait_background_op(PL310_CLEAN_LINE_PA, 1);
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
}
static void
pl310_inv_range(vm_paddr_t start, vm_size_t size)
{
+ if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
+ return;
+
+ PL310_LOCK(pl310_softc);
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
@@ -252,13 +254,13 @@ pl310_inv_range(vm_paddr_t start, vm_size_t size)
size += g_l2cache_line_size;
}
while (size > 0) {
- pl310_write4(PL310_INV_LINE_PA, start);
+ pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
- pl310_cache_sync();
- pl310_wait_background_op(PL310_INV_LINE_PA, 1);
+ pl310_cache_sync();
+ PL310_UNLOCK(pl310_softc);
}
static int
@@ -280,39 +282,98 @@ pl310_attach(device_t dev)
uint32_t way_size;
uint32_t ways_assoc;
uint32_t ctrl_value;
+ uint32_t cache_id;
+ sc->sc_dev = dev;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->sc_mem_res == NULL)
panic("%s: Cannot map registers", device_get_name(dev));
- pl310_softc = sc;
- platform_init_pl310(sc);
- aux_value = pl310_read4(PL310_AUX_CTRL);
- way_size = (aux_value & PL310_AUX_CTRL_WAY_SIZE_MASK) >>
- PL310_AUX_CTRL_WAY_SIZE_SHIFT;
+ /* Allocate an IRQ resource */
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL) {
+ panic("Cannot allocate IRQ\n");
+ }
+
+ pl310_softc = sc;
+ mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
+ sc->sc_enabled = pl310_enabled;
+
+ /* activate the interrupt */
+ bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ pl310_filter, NULL, sc, &sc->sc_irq_h);
+
+ cache_id = pl310_read4(sc, PL310_CACHE_ID);
+ device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
+ (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
+ (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
+ aux_value = pl310_read4(sc, PL310_AUX_CTRL);
+ way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
+ AUX_CTRL_WAY_SIZE_SHIFT;
way_size = 1 << (way_size + 13);
- if (aux_value & (1 << PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT))
+ if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
ways_assoc = 16;
else
ways_assoc = 8;
g_l2cache_way_mask = (1 << ways_assoc) - 1;
g_l2cache_size = way_size * ways_assoc;
/* Print the information */
- printf(" L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
+ device_printf(dev, "L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
g_l2cache_line_size, ways_assoc);
- ctrl_value = pl310_read4(PL310_CTRL);
- if (!(ctrl_value & 0x1)) {
+
+ ctrl_value = pl310_read4(sc, PL310_CTRL);
+
+ if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
/* Enable the L2 cache if disabled */
- pl310_write4(PL310_CTRL, ctrl_value & 0x1);
+ platform_pl310_write_ctrl(sc, CTRL_ENABLED);
+ }
+
+ if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
+ /*
+ * Set counters so when cache event happens
+ * we'll get interrupt and be warned that something
+ * is off
+ */
+
+ /* Cache Line Eviction for Counter 0 */
+ pl310_write4(sc, PL310_EVENT_COUNTER0_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
+ /* Data Read Request for Counter 1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER1_CONF,
+ EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
+
+ /* Temporary switch on for final flush*/
+ sc->sc_enabled = 1;
+ pl310_wbinv_all();
+ sc->sc_enabled = 0;
+ platform_pl310_write_ctrl(sc, CTRL_DISABLED);
+
+ /* Enable and clear pending interrupts */
+ pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
+ pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
+
+ /* Enable counters and reset C0 and C1 */
+ pl310_write4(sc, PL310_EVENT_COUNTER_CTRL,
+ EVENT_COUNTER_CTRL_ENABLED |
+ EVENT_COUNTER_CTRL_C0_RESET |
+ EVENT_COUNTER_CTRL_C1_RESET);
+
}
+
+ if (sc->sc_enabled)
+ platform_pl310_init(sc);
+
pl310_wbinv_all();
-
+
/* Set the l2 functions in the set of cpufuncs */
cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
+
return (0);
}
@@ -330,4 +391,3 @@ static driver_t pl310_driver = {
static devclass_t pl310_devclass;
DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
-
OpenPOWER on IntegriCloud