![cinap_lenrek](/assets/img/avatar_default.png)
we might as well handle the per process cycle counter in the portable part instead of duplicating the code in every arch and have inconsistent implementations. we now have a portable kenter() and kexit() function, that is ment to be used in trap/syscall from user, which updates the counters. some kernels missed initializing Mach.cyclefreq.
510 lines
10 KiB
C
510 lines
10 KiB
C
/*
|
|
* stuff specific to marvell's kirkwood architecture
|
|
* as seen in the sheevaplug
|
|
*/
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
#include "io.h"
|
|
|
|
#include "../port/netif.h"
|
|
#include "../port/etherif.h"
|
|
#include "../port/flashif.h"
|
|
|
|
#include "arm.h"
|
|
|
|
enum {
|
|
L2writeback = 1,
|
|
Debug = 0,
|
|
};
|
|
|
|
typedef struct GpioReg GpioReg;
|
|
struct GpioReg {
|
|
ulong dataout;
|
|
ulong dataoutena;
|
|
ulong blinkena;
|
|
ulong datainpol;
|
|
ulong datain;
|
|
ulong intrcause;
|
|
ulong intrmask;
|
|
ulong intrlevelmask;
|
|
};
|
|
|
|
typedef struct L2uncache L2uncache;
|
|
typedef struct L2win L2win;
|
|
struct L2uncache {
|
|
struct L2win {
|
|
ulong base; /* phys addr */
|
|
ulong size;
|
|
} win[4];
|
|
};
|
|
|
|
enum {
|
|
/* L2win->base bits */
|
|
L2enable = 1<<0,
|
|
};
|
|
|
|
typedef struct Dramctl Dramctl;
|
|
struct Dramctl {
|
|
ulong ctl;
|
|
ulong ddrctllo;
|
|
struct {
|
|
ulong lo;
|
|
ulong hi;
|
|
} time;
|
|
ulong addrctl;
|
|
ulong opagectl;
|
|
ulong oper;
|
|
ulong mode;
|
|
ulong extmode;
|
|
ulong ddrctlhi;
|
|
ulong ddr2timelo;
|
|
ulong operctl;
|
|
struct {
|
|
ulong lo;
|
|
ulong hi;
|
|
} mbusctl;
|
|
ulong mbustimeout;
|
|
ulong ddrtimehi;
|
|
ulong sdinitctl;
|
|
ulong extsdmode1;
|
|
ulong extsdmode2;
|
|
struct {
|
|
ulong lo;
|
|
ulong hi;
|
|
} odtctl;
|
|
ulong ddrodtctl;
|
|
ulong rbuffsel;
|
|
|
|
ulong accalib;
|
|
ulong dqcalib;
|
|
ulong dqscalib;
|
|
};
|
|
|
|
/* unused so far */
|
|
typedef struct SDramdReg SDramdReg;
|
|
struct SDramdReg {
|
|
struct {
|
|
ulong base;
|
|
ulong size;
|
|
} win[4];
|
|
};
|
|
|
|
typedef struct Addrmap Addrmap;
|
|
typedef struct Addrwin Addrwin;
|
|
struct Addrmap {
|
|
struct Addrwin {
|
|
ulong ctl; /* see Winenable in io.h */
|
|
ulong base;
|
|
ulong remaplo;
|
|
ulong remaphi;
|
|
} win[8];
|
|
ulong dirba; /* device internal reg's base addr.: PHYSIO */
|
|
};
|
|
|
|
Soc soc = {
|
|
.cpu = PHYSIO+0x20100,
|
|
.devid = PHYSIO+0x10034,
|
|
.l2cache = PHYSIO+0x20a00, /* uncachable addrs for L2 */
|
|
.sdramc = PHYSIO+0x01400,
|
|
// .sdramd = PHYSIO+0x01500, /* unused */
|
|
|
|
.iocfg = PHYSIO+0x100e0,
|
|
.addrmap = PHYSIO+0x20000,
|
|
.intr = PHYSIO+0x20200,
|
|
.nand = PHYSIO+0x10418,
|
|
.cesa = PHYSIO+0x30000, /* crypto accelerator */
|
|
.ehci = PHYSIO+0x50000,
|
|
.spi = PHYSIO+0x10600,
|
|
.twsi = PHYSIO+0x11000,
|
|
|
|
.analog = PHYSIO+0x1007c,
|
|
.pci = PHYSIO+0x40000,
|
|
.pcibase = PHYSIO+0x41800,
|
|
|
|
.rtc = PHYSIO+0x10300,
|
|
.clock = PHYSIO+0x20300,
|
|
// .clockctl = PHYSIO+0x1004c, /* unused */
|
|
|
|
.ether = { PHYSIO+0x72000, PHYSIO+0x76000, },
|
|
.sata = { PHYSIO+0x80000, /* sata config reg here */
|
|
PHYSIO+0x82000, /* edma config reg here */
|
|
PHYSIO+0x84000, /* edma config reg here */
|
|
},
|
|
.uart = { PHYSIO+0x12000, PHYSIO+0x12100, },
|
|
.gpio = { PHYSIO+0x10100, PHYSIO+0x10140, },
|
|
};
|
|
|
|
/*
|
|
* sheeva/openrd u-boot leaves us with this address map:
|
|
*
|
|
* 0 targ 4 attr 0xe8 size 256MB addr 0x9:: remap addr 0x9:: pci mem
|
|
* 1 targ 1 attr 0x2f size 8MB addr 0xf9:: remap addr 0xf9:: nand flash
|
|
* 2 targ 4 attr 0xe0 size 16MB addr 0xf:: remap addr 0xc:: pci i/o
|
|
* 3 targ 1 attr 0x1e size 16MB addr 0xf8:: remap addr 0x0 spi flash
|
|
* 4 targ 1 attr 0x1d size 16MB addr 0xff:: boot rom
|
|
* 5 targ 1 attr 0x1e size 128MB addr 0xe8:: disabled spi flash
|
|
* 6 targ 1 attr 0x1d size 128MB addr 0xf:: disabled boot rom
|
|
* 7 targ 3 attr 0x1 size 64K addr 0xfb:: crypto sram
|
|
*/
|
|
#define WINTARG(ctl) (((ctl) >> 4) & 017)
|
|
#define WINATTR(ctl) (((ctl) >> 8) & 0377)
|
|
#define WIN64KSIZE(ctl) (((ctl) >> 16) + 1)
|
|
|
|
static void
|
|
praddrwin(Addrwin *win, int i)
|
|
{
|
|
ulong ctl, targ, attr, size64k;
|
|
|
|
if (!Debug) {
|
|
USED(win, i);
|
|
return;
|
|
}
|
|
ctl = win->ctl;
|
|
targ = WINTARG(ctl);
|
|
attr = WINATTR(ctl);
|
|
size64k = WIN64KSIZE(ctl);
|
|
print("cpu addr map: %s window %d: targ %ld attr %#lux size %,ld addr %#lux",
|
|
ctl & Winenable? "enabled": "disabled", i, targ, attr,
|
|
size64k * 64*1024, win->base);
|
|
if (i < 4)
|
|
print(" remap addr %#llux", (uvlong)win->remaphi<<32 |
|
|
win->remaplo);
|
|
print("\n");
|
|
}
|
|
|
|
static void
|
|
fixaddrmap(void)
|
|
{
|
|
int i;
|
|
ulong ctl, targ, attr, size64k;
|
|
Addrmap *map;
|
|
Addrwin *win;
|
|
|
|
map = (Addrmap *)soc.addrmap;
|
|
for (i = 0; i < nelem(map->win); i++) {
|
|
win = &map->win[i];
|
|
ctl = win->ctl;
|
|
targ = WINTARG(ctl);
|
|
attr = WINATTR(ctl);
|
|
size64k = WIN64KSIZE(ctl);
|
|
|
|
USED(attr, size64k);
|
|
if (targ == Targcesasram) {
|
|
win->ctl |= Winenable;
|
|
win->base = PHYSCESASRAM;
|
|
coherence();
|
|
praddrwin(win, i);
|
|
}
|
|
}
|
|
if (map->dirba != PHYSIO)
|
|
panic("dirba not %#ux", PHYSIO);
|
|
}
|
|
|
|
static void
|
|
praddrmap(void)
|
|
{
|
|
int i;
|
|
Addrmap *map;
|
|
|
|
map = (Addrmap *)soc.addrmap;
|
|
for (i = 0; i < nelem(map->win); i++)
|
|
praddrwin(&map->win[i], i);
|
|
}
|
|
|
|
int
|
|
ispow2(uvlong ul)
|
|
{
|
|
/* see Hacker's Delight if this isn't obvious */
|
|
return (ul & (ul - 1)) == 0;
|
|
}
|
|
|
|
/*
|
|
* return exponent of smallest power of 2 ≥ n
|
|
*/
|
|
int
|
|
log2(ulong n)
|
|
{
|
|
int i;
|
|
|
|
i = 31 - clz(n);
|
|
if (!ispow2(n) || n == 0)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
void
|
|
cacheinfo(int level, int kind, Memcache *cp) /* l1 only */
|
|
{
|
|
uint len, assoc, size;
|
|
ulong setsways;
|
|
|
|
/* get cache types & sizes (read-only reg) */
|
|
setsways = cprdsc(0, CpID, CpIDidct, CpIDct);
|
|
|
|
cp->level = level;
|
|
cp->kind = kind;
|
|
|
|
if ((setsways & (1<<24)) == 0)
|
|
kind = Unified;
|
|
if (kind != Icache)
|
|
setsways >>= 12;
|
|
|
|
assoc = (setsways >> 3) & MASK(3);
|
|
cp->nways = 1 << assoc;
|
|
size = (setsways >> 6) & MASK(4);
|
|
cp->size = 1 << (size + 9);
|
|
len = setsways & MASK(2);
|
|
cp->log2linelen = len + 3;
|
|
cp->linelen = 1 << cp->log2linelen;
|
|
cp->setsways = setsways;
|
|
|
|
cp->nsets = 1 << (size + 6 - assoc - len);
|
|
cp->setsh = cp->log2linelen;
|
|
cp->waysh = 32 - log2(cp->nways);
|
|
}
|
|
|
|
static char *
|
|
wbtype(uint type)
|
|
{
|
|
static char *types[] = {
|
|
"write-through",
|
|
"read data block",
|
|
"reg 7 ops, no lock-down",
|
|
[06] "reg 7 ops, format A",
|
|
[07] "reg 7 ops, format B deprecated",
|
|
[016] "reg 7 ops, format C",
|
|
[05] "reg 7 ops, format D",
|
|
};
|
|
|
|
if (type >= nelem(types) || types[type] == nil)
|
|
return "GOK";
|
|
return types[type];
|
|
}
|
|
|
|
static void
|
|
prcache(Memcache *mcp)
|
|
{
|
|
int type;
|
|
char id;
|
|
|
|
if (mcp->kind == Unified)
|
|
id = 'U';
|
|
else if (mcp->kind == Icache)
|
|
id = 'I';
|
|
else if (mcp->kind == Dcache)
|
|
id = 'D';
|
|
else
|
|
id = '?';
|
|
print("l%d %c: %d bytes, %d ways %d sets %d bytes/line",
|
|
mcp->level, id, mcp->size, mcp->nways, mcp->nsets,
|
|
mcp->linelen);
|
|
if (mcp->linelen != CACHELINESZ)
|
|
print(" *should* be %d", CACHELINESZ);
|
|
type = (mcp->setsways >> 25) & MASK(4);
|
|
if (type == 0)
|
|
print("; write-through only");
|
|
else
|
|
print("; write-back type `%s' (%#o) possible",
|
|
wbtype(type), type);
|
|
if (mcp->setsways & (1<<11))
|
|
print("; page table mapping restrictions apply");
|
|
if (mcp->setsways & (1<<2))
|
|
print("; M bit is set in cache type reg");
|
|
print("\n");
|
|
}
|
|
|
|
static void
|
|
prcachecfg(void)
|
|
{
|
|
Memcache mc;
|
|
|
|
cacheinfo(1, Dcache, &mc);
|
|
prcache(&mc);
|
|
cacheinfo(1, Icache, &mc);
|
|
prcache(&mc);
|
|
}
|
|
|
|
void
|
|
l2cacheon(void)
|
|
{
|
|
ulong cfg;
|
|
CpucsReg *cpu;
|
|
L2uncache *l2p;
|
|
|
|
cacheuwbinv();
|
|
l2cacheuwbinv();
|
|
l1cachesoff(); /* turns off L2 as a side effect */
|
|
|
|
cpwrsc(CpDef, CpCLD, 0, 0, 0); /* GL-CPU-100: set D cache lockdown reg. */
|
|
|
|
/* marvell guideline GL-CPU-130 */
|
|
cpu = (CpucsReg *)soc.cpu;
|
|
cfg = cpu->cpucfg | L2exists | L2ecc | Cfgiprefetch | Cfgdprefetch;
|
|
|
|
if (L2writeback)
|
|
cfg &= ~L2writethru; /* see PTE Cached & Buffered bits */
|
|
else
|
|
cfg |= L2writethru;
|
|
cpu->l2cfg = cfg;
|
|
coherence(); /* force l2 cache to pay attention */
|
|
cpu->l2tm1 = cpu->l2tm0 = 0x66666666; /* marvell guideline GL-CPU-120 */
|
|
coherence();
|
|
|
|
cpwrsc(CpL2, CpTESTCFG, CpTCl2waylck, CpTCl2waylock, 0);
|
|
|
|
cachedinv();
|
|
l2cacheuinv();
|
|
|
|
/* disable l2 caching of i/o registers */
|
|
l2p = (L2uncache *)soc.l2cache;
|
|
memset(l2p, 0, sizeof *l2p);
|
|
/*
|
|
* l2: don't cache upper half of address space.
|
|
* the L2 cache is PIPT, so the addresses are physical.
|
|
*/
|
|
l2p->win[0].base = 0x80000000 | L2enable; /* 64K multiple */
|
|
l2p->win[0].size = (32*1024-1) << 16; /* 64K multiples */
|
|
coherence();
|
|
|
|
l2cachecfgon();
|
|
l1cacheson(); /* turns L2 on as a side effect */
|
|
print("l2 cache: 256K or 512K: 4 ways, 32-byte lines, write-%s, sdram only\n",
|
|
cpu->l2cfg & L2writethru? "through": "back");
|
|
}
|
|
|
|
/* called late in main */
|
|
void
|
|
archconfinit(void)
|
|
{
|
|
m->cpuhz = Frequency;
|
|
m->cyclefreq = m->cpuhz;
|
|
m->delayloop = m->cpuhz/2000; /* initial estimate */
|
|
fixaddrmap();
|
|
if (Debug)
|
|
praddrmap();
|
|
prcachecfg();
|
|
|
|
l2cacheon();
|
|
}
|
|
|
|
void
|
|
archkwlink(void)
|
|
{
|
|
}
|
|
|
|
/* LED/USB gpios */
|
|
enum {
|
|
/*
|
|
* the bit assignments are MPP pin numbers from the last page of the
|
|
* sheevaplug 6.0.1 schematic.
|
|
*/
|
|
KWOEValHigh = 1<<(49-32), /* pin 49: LED pin */
|
|
KWOEValLow = 1<<29, /* pin 29: USB_PWEN, pin 28: usb_pwerr */
|
|
KWOELow = ~0,
|
|
KWOEHigh = ~0,
|
|
};
|
|
|
|
/* called early in main */
|
|
void
|
|
archreset(void)
|
|
{
|
|
ulong clocks;
|
|
CpucsReg *cpu;
|
|
Dramctl *dram;
|
|
GpioReg *gpio;
|
|
|
|
clockshutdown(); /* watchdog disabled */
|
|
|
|
/* configure gpios */
|
|
gpio = (GpioReg*)soc.gpio[0];
|
|
gpio->dataout = KWOEValLow;
|
|
coherence();
|
|
gpio->dataoutena = KWOELow;
|
|
|
|
gpio = (GpioReg*)soc.gpio[1];
|
|
gpio->dataout = KWOEValHigh;
|
|
coherence();
|
|
gpio->dataoutena = KWOEHigh;
|
|
coherence();
|
|
|
|
cpu = (CpucsReg *)soc.cpu;
|
|
cpu->mempm = 0; /* turn everything on */
|
|
coherence();
|
|
|
|
clocks = MASK(10);
|
|
clocks |= MASK(21) & ~MASK(14);
|
|
clocks &= ~(1<<18 | 1<<1); /* reserved bits */
|
|
cpu->clockgate |= clocks; /* enable all the clocks */
|
|
cpu->l2cfg |= L2exists; /* when L2exists is 0, the l2 ignores us */
|
|
coherence();
|
|
|
|
dram = (Dramctl *)soc.sdramc;
|
|
dram->ddrctllo &= ~(1<<6); /* marvell guideline GL-MEM-70 */
|
|
|
|
*(ulong *)soc.analog = 0x68; /* marvell guideline GL-MISC-40 */
|
|
coherence();
|
|
}
|
|
|
|
void
|
|
archreboot(void)
|
|
{
|
|
CpucsReg *cpu;
|
|
|
|
iprint("reset!\n");
|
|
delay(10);
|
|
|
|
cpu = (CpucsReg *)soc.cpu;
|
|
cpu->rstout = RstoutSoft;
|
|
cpu->softreset = ResetSystem;
|
|
coherence();
|
|
cpu->cpucsr = Reset;
|
|
coherence();
|
|
delay(500);
|
|
|
|
splhi();
|
|
iprint("waiting...");
|
|
for(;;)
|
|
idlehands();
|
|
}
|
|
|
|
void
|
|
archconsole(void)
|
|
{
|
|
// uartconsole(0, "b115200");
|
|
//serialputs("uart0 console @ 115200\n", strlen("uart0 console @ 115200\n"));
|
|
}
|
|
|
|
void
|
|
archflashwp(Flash*, int)
|
|
{
|
|
}
|
|
|
|
int flashat(Flash *f, uintptr pa);
|
|
|
|
/*
|
|
* for ../port/devflash.c:/^flashreset
|
|
* retrieve flash type, virtual base and length and return 0;
|
|
* return -1 on error (no flash)
|
|
*/
|
|
int
|
|
archflashreset(int bank, Flash *f)
|
|
{
|
|
if(bank != 0)
|
|
return -1;
|
|
f->type = "nand";
|
|
if (flashat(f, PHYSNAND1))
|
|
f->addr = (void*)PHYSNAND1;
|
|
else if (flashat(f, PHYSNAND2))
|
|
f->addr = (void*)PHYSNAND2;
|
|
else
|
|
f->addr = nil;
|
|
f->size = 0; /* done by probe */
|
|
f->width = 1;
|
|
f->interleave = 0;
|
|
return 0;
|
|
}
|