summaryrefslogtreecommitdiff
path: root/sys/src/9/teg2/cache-l2-pl310.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:21 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:21 +0100
commitea108c8ca6e726ac008f75775ab83775ec233171 (patch)
tree982816b58d50e1b12b7eeb2c29fe22ca8d9c195b /sys/src/9/teg2/cache-l2-pl310.c
parent43e09c468b4c6562c93c9375a316012e238d21b2 (diff)
add tegra2 soc kernel (from sources)
Diffstat (limited to 'sys/src/9/teg2/cache-l2-pl310.c')
-rw-r--r--sys/src/9/teg2/cache-l2-pl310.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/sys/src/9/teg2/cache-l2-pl310.c b/sys/src/9/teg2/cache-l2-pl310.c
new file mode 100644
index 000000000..275d8699c
--- /dev/null
+++ b/sys/src/9/teg2/cache-l2-pl310.c
@@ -0,0 +1,456 @@
+/*
+ * PL310 level 2 cache (non-architectural bag on the side)
+ *
+ * guaranteed to work incorrectly with default settings; must set Sharovr.
+ *
+ * clean & invalidate (wbinv) is buggy, so we work around erratum 588369
+ * by disabling write-back and cache line-fill before, and restoring after.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "arm.h"
+
+#define NWAYS(l2p) ((l2p)->auxctl & Assoc16way? 16: 8)
+#define L2P ((L2pl310 *)soc.l2cache)
+
+enum {
+ L2size = 1024 * 1024, /* according to the tegra 2 manual */
+ Wayszgran = 16 * KiB, /* granularity of way sizes */
+};
+
+typedef struct L2pl310 L2pl310;
+typedef struct Pl310op Pl310op;
+
+struct Pl310op {
+ ulong pa;
+ ulong _pad;
+ ulong indexway;
+ ulong way;
+};
+
+struct L2pl310 {
+ ulong id;
+ ulong type;
+ uchar _pad0[0x100 - 0x8];
+ ulong ctl;
+ ulong auxctl;
+
+ uchar _pad1[0x730 - 0x108]; /* boring regs */
+ ulong sync;
+ uchar _pad2[0x740 - 0x734];
+ ulong r3p0sync; /* workaround for r3p0 bug */
+ uchar _pad3[0x770 - 0x744];
+ Pl310op inv; /* inv.indexway doesn't exist */
+ uchar _pad4[0x7b0 - 0x780];
+ Pl310op clean;
+ uchar _pad5[0x7f0 - 0x7c0];
+ Pl310op cleaninv;
+ uchar _pad6[0xc00 - 0x7d0];
+ ulong filtstart;
+ ulong filtend;
+ uchar _pad6[0xf40 - 0xc08];
+ ulong debug;
+ /* ... */
+};
+
+enum {
+ /* ctl bits */
+ L2enable = 1,
+
+ /* auxctl bits */
+ Ipref = 1<<29, /* prefetch enables */
+ Dpref = 1<<28,
+ Mbo = 1<<25,
+ Sharovr = 1<<22, /* shared attribute override (i.e., work right!) */
+ Parity = 1<<21,
+ Waycfgshift= 17,
+ Waycfgmask = (1<<3) - 1,
+ Assoc16way = 1<<16,
+ /*
+ * optim'n to 0 cache lines; must be enabled in a9(?!).
+ * set CpAClwr0line on all cpus 1st.
+ */
+ Fullline0= 1<<0,
+
+ /* debug bits */
+ Wt = 1<<1, /* write-through, not write-back */
+ Nolinefill= 1<<0,
+
+ Basecfg = Wt | Nolinefill,
+};
+
+static Lock l2lock;
+static int disallowed; /* by user: *l2off= in plan9.ini */
+static int l2ison;
+static int bg_op_running;
+static ulong waysmask;
+
+static Cacheimpl l2cacheimpl;
+
+static void
+awaitbgop(void)
+{
+ while (bg_op_running)
+ ;
+}
+
+static void
+getlock(void)
+{
+ awaitbgop(); /* wait at normal PL first */
+ ilock(&l2lock);
+ awaitbgop(); /* wait under lock */
+}
+
+static void
+l2pl310sync(void)
+{
+ L2P->sync = 0;
+ coherence();
+}
+
+/* call this first to set sets/ways configuration */
+void
+l2pl310init(void)
+{
+ int waysz, nways;
+ ulong new;
+ L2pl310 *l2p = L2P;
+ static int configed;
+
+ if (getconf("*l2off") != nil) {
+// iprint("l2 cache (pl310) disabled\n");
+ disallowed = 1;
+ return;
+ }
+ if (l2ison || configed)
+ return;
+ l2cache = &l2cacheimpl;
+ cachedwb();
+
+ /*
+ * default config is:
+ * l2: ext unified, 8 ways 512 sets 32 bytes/line => 128KB
+ * but the tegra 2 manual says there's 1MB available.
+ * ways or way-size may be fixed by hardware; the only way to tell
+ * is to try to change the setting and read it back.
+ */
+ l2pl310sync();
+ l2cache->inv();
+
+ /* figure out number of ways */
+ l2pl310sync();
+ nways = NWAYS(l2p);
+ if (!(l2p->auxctl & Assoc16way)) {
+ l2p->auxctl |= Assoc16way;
+ coherence();
+ l2pl310sync();
+ nways = NWAYS(l2p);
+// iprint("\nl2: was set for 8 ways, asked for 16, got %d\n", nways);
+ }
+ waysmask = MASK(nways);
+
+ /* figure out way size (and thus number of sets) */
+ waysz = L2size / nways;
+ new = l2p->auxctl & ~(Waycfgmask << Waycfgshift) |
+ (log2(waysz / Wayszgran) + 1) << Waycfgshift;
+ l2p->auxctl = new;
+ coherence();
+ l2pl310sync();
+ l2cache->inv();
+
+// iprint("\nl2: configed %d ways, %d sets (way size %d)\n", nways,
+// waysz / CACHELINESZ, waysz);
+ if (l2p->auxctl != new)
+ iprint("l2 config %#8.8lux didn't stick; is now %#8.8lux\n",
+ new, l2p->auxctl);
+ configed++;
+}
+
+void
+l2pl310info(Memcache *cp)
+{
+ int pow2;
+ ulong waysz;
+ L2pl310 *l2p = L2P;
+
+ memset(cp, 0, sizeof *cp);
+ if (!l2ison)
+ return;
+
+ l2pl310init();
+ assert((l2p->id >> 24) == 'A');
+ cp->level = 2;
+ cp->type = Unified;
+ cp->external = Extcache;
+ cp->setsways = Cara | Cawa | Cawt | Cawb;
+ cp->l1ip = 3<<14; /* PIPT */
+ cp->setsh = cp->waysh = 0; /* bag on the side */
+
+ cp->linelen = CACHELINESZ;
+ cp->log2linelen = log2(CACHELINESZ);
+
+ cp->nways = NWAYS(l2p);
+ pow2 = ((l2p->auxctl >> Waycfgshift) & Waycfgmask) - 1;
+ if (pow2 < 0)
+ pow2 = 0;
+ waysz = (1 << pow2) * Wayszgran;
+ cp->nsets = waysz / CACHELINESZ;
+}
+
+void
+l2pl310on(void)
+{
+ ulong ctl;
+ L2pl310 *l2p = L2P;
+
+ if (getconf("*l2off") != nil) {
+// iprint("l2 cache (pl310) disabled\n");
+ disallowed = 1;
+ return;
+ }
+ if (l2ison)
+ return;
+
+ l2pl310init();
+ l2cache->inv();
+
+ /*
+ * drain l1. can't turn it off (which would make locks not work)
+ * because doing so makes references below to the l2 registers wedge
+ * the system.
+ */
+ cacheuwbinv();
+ cacheiinv();
+
+ /*
+ * this is only called once, on cpu0 at startup,
+ * so we don't need locks here.
+ * must do all configuration before enabling l2 cache.
+ */
+ l2p->filtend = 0;
+ coherence();
+ l2p->filtstart = 0; /* no enable bit */
+ l2p->debug = 0; /* write-back, line fills allowed */
+ coherence();
+
+ ctl = l2p->auxctl;
+ /* don't change number of sets & ways, but reset all else. */
+ ctl &= Waycfgmask << Waycfgshift | Assoc16way;
+ ctl |= Sharovr; /* actually work correctly for a change */
+ ctl |= Mbo | Ipref | Dpref | Parity | Fullline0;
+ l2p->auxctl = ctl;
+ coherence();
+
+ l2p->ctl |= L2enable;
+ coherence();
+
+ l2ison = 1;
+
+// iprint("l2 cache (pl310) now on\n");
+}
+
+void
+l2pl310off(void)
+{
+ if (!l2ison)
+ return;
+ l2cache->wbinv();
+ getlock();
+ L2P->ctl &= ~L2enable;
+ coherence();
+ l2ison = 0;
+ iunlock(&l2lock);
+}
+
+
+static void
+applyrange(ulong *reg, void *ava, int len)
+{
+ uintptr va, endva;
+
+ if (disallowed || !l2ison)
+ return;
+ if (len < 0)
+ panic("l2cache*se called with negative length");
+ endva = (uintptr)ava + len;
+ for (va = (uintptr)ava & ~(CACHELINESZ-1); va < endva;
+ va += CACHELINESZ)
+ *reg = PADDR(va);
+ l2pl310sync();
+}
+
+void
+l2pl310invse(void *va, int bytes)
+{
+ uintptr start, end;
+ L2pl310 *l2p = L2P;
+
+ /*
+ * if start & end addresses are not on cache-line boundaries,
+ * flush first & last cachelines before invalidating.
+ */
+ start = (uintptr)va;
+ end = start + bytes;
+ getlock();
+ if (start % CACHELINESZ != 0) {
+// iprint("l2pl310invse: unaligned start %#p from %#p\n", start,
+// getcallerpc(&va));
+ applyrange(&l2p->clean.pa, va, 1);
+ }
+ if (end % CACHELINESZ != 0) {
+// iprint("l2pl310invse: unaligned end %#p from %#p\n", end,
+// getcallerpc(&va));
+ applyrange(&l2p->clean.pa, (char *)va + bytes, 1);
+ }
+
+ applyrange(&l2p->inv.pa, va, bytes);
+ iunlock(&l2lock);
+}
+
+void
+l2pl310wbse(void *va, int bytes)
+{
+ getlock();
+ applyrange(&L2P->clean.pa, va, bytes);
+ iunlock(&l2lock);
+}
+
+/*
+ * assume that ldrex/strex (thus locks) won't work when Wt in is effect,
+ * so don't manipulate locks between setting and clearing Wt.
+ */
+void
+l2pl310wbinvse(void *va, int bytes)
+{
+ int odb;
+ L2pl310 *l2p = L2P;
+
+ if (!l2ison)
+ return;
+ getlock();
+ applyrange(&l2p->clean.pa, va, bytes); /* paranoia */
+
+ odb = l2p->debug;
+ l2p->debug |= Wt | Nolinefill; /* erratum workaround */
+ coherence();
+
+ applyrange(&l2p->cleaninv.pa, va, bytes);
+
+ l2p->debug = odb;
+ iunlock(&l2lock);
+}
+
+
+/*
+ * we want to wait for completion at normal PL.
+ * if waiting is interrupted, interrupt code that calls
+ * these ops could deadlock on a uniprocessor, so we only
+ * give up l2lock before waiting on multiprocessors.
+ * in this port, only cpu 0 gets interrupts other than local timer ones.
+ */
+
+void
+l2pl310inv(void)
+{
+ L2pl310 *l2p = L2P;
+
+ if (disallowed)
+ return;
+
+ getlock();
+ bg_op_running = 1;
+ l2p->inv.way = waysmask;
+ coherence();
+ if (conf.nmach > 1)
+ iunlock(&l2lock);
+
+ while (l2p->inv.way & waysmask)
+ ;
+
+ if (conf.nmach > 1)
+ ilock(&l2lock);
+ l2pl310sync();
+ bg_op_running = 0;
+ iunlock(&l2lock);
+}
+
+/*
+ * maximum time seen is 2542µs, typical is 625µs.
+ */
+void
+l2pl310wb(void)
+{
+ L2pl310 *l2p = L2P;
+
+ if (disallowed || !l2ison)
+ return;
+
+ getlock();
+ bg_op_running = 1;
+ l2p->clean.way = waysmask;
+ coherence();
+ if (conf.nmach > 1)
+ iunlock(&l2lock);
+
+ while (l2p->clean.way & waysmask)
+ ;
+
+ if (conf.nmach > 1)
+ ilock(&l2lock);
+ l2pl310sync();
+ bg_op_running = 0;
+ iunlock(&l2lock);
+}
+
+void
+l2pl310wbinv(void)
+{
+ int odb;
+ L2pl310 *l2p = L2P;
+
+ if (disallowed || !l2ison)
+ return;
+
+ l2pl310wb(); /* paranoia */
+
+ getlock();
+ bg_op_running = 1;
+ odb = l2p->debug;
+ l2p->debug |= Wt | Nolinefill; /* erratum workaround */
+ coherence();
+
+ l2p->cleaninv.way = waysmask;
+ coherence();
+ if (conf.nmach > 1)
+ iunlock(&l2lock);
+
+ while (l2p->cleaninv.way & waysmask)
+ ;
+
+ if (conf.nmach > 1)
+ ilock(&l2lock);
+ l2pl310sync();
+ l2p->debug = odb;
+ bg_op_running = 0;
+ iunlock(&l2lock);
+}
+
+static Cacheimpl l2cacheimpl = {
+ .info = l2pl310info,
+ .on = l2pl310on,
+ .off = l2pl310off,
+
+ .inv = l2pl310inv,
+ .wb = l2pl310wb,
+ .wbinv = l2pl310wbinv,
+
+ .invse = l2pl310invse,
+ .wbse = l2pl310wbse,
+ .wbinvse= l2pl310wbinvse,
+};