plan9fox/sys/src/9/ppc/m8260.c
cinap_lenrek 128ea44a89 kernel: expose no execute bit to portable mmu code as SG_NOEXEC / PTENOEXEC, add PTECACHED bits
a portable SG_NOEXEC segment attribute was added to allow
non-executable (physical) segments. which will set the
PTENOEXEC bits for putmmu().

in the future, this can be used to make non-executable
stack / bss segments.

the SG_DEVICE attribute was added to distinguish between
mmio regions and uncached memory. only matterns on arm64.

on arm, theres the issue that PTEUNCACHED would have
no bits set when using the hardware bit definitions.
this is the reason bcm, kw, teg2 and omap kernels use
arteficial PTE constants. on zynq, the XN bit was used
as a hack to give PTEUNCACHED a non-zero value and when
the bit is clear then cache attributes where added to
the pte.

to fix this, PTECACHED constant was added.

the portable mmu code in fault.c will now explicitely set
PTECACHED bits for cached memory and PTEUNCACHED for
uncached memory. that way the hardware bit definitions
can be used everywhere.
2019-08-26 22:34:38 +02:00

662 lines
14 KiB
C

/*
* 8260 specific stuff:
* Interrupt handling
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "io.h"
#include "fns.h"
#include "m8260.h"
enum {
Pin4 = BIT(4),
};
static union {
struct {
ulong hi;
ulong lo;
};
uvlong val;
} ticks;
struct {
ulong hi;
ulong lo;
} vec2mask[64] = {
[0] = {0, 0 }, /* Error, No interrupt */
[1] = {0, BIT(16) }, /* I2C */
[2] = {0, BIT(17) }, /* SPI */
[3] = {0, BIT(18) }, /* Risc Timers */
[4] = {0, BIT(19) }, /* SMC1 */
[5] = {0, BIT(20) }, /* SMC2 */
[6] = {0, BIT(21) }, /* IDMA1 */
[7] = {0, BIT(22) }, /* IDMA2 */
[8] = {0, BIT(23) }, /* IDMA3 */
[9] = {0, BIT(24) }, /* IDMA4 */
[10] = {0, BIT(25) }, /* SDMA */
[11] = {0, 0 }, /* Reserved */
[12] = {0, BIT(27) }, /* Timer1 */
[13] = {0, BIT(28) }, /* Timer2 */
[14] = {0, BIT(29) }, /* Timer3 */
[15] = {0, BIT(30) }, /* Timer4 */
[16] = {BIT(29), 0 }, /* TMCNT */
[17] = {BIT(30), 0 }, /* PIT */
[18] = {0, 0 }, /* Reserved */
[19] = {BIT(17), 0 }, /* IRQ1 */
[20] = {BIT(18), 0 }, /* IRQ2 */
[21] = {BIT(19), 0 }, /* IRQ3 */
[22] = {BIT(20), 0 }, /* IRQ4 */
[23] = {BIT(21), 0 }, /* IRQ5 */
[24] = {BIT(22), 0 }, /* IRQ6 */
[25] = {BIT(23), 0 }, /* IRQ7 */
[26] = {0, 0 }, /* Reserved */
[27] = {0, 0 }, /* Reserved */
[28] = {0, 0 }, /* Reserved */
[29] = {0, 0 }, /* Reserved */
[30] = {0, 0 }, /* Reserved */
[31] = {0, 0 }, /* Reserved */
[32] = {0, BIT(0) }, /* FCC1 */
[33] = {0, BIT(1) }, /* FCC2 */
[34] = {0, BIT(2) }, /* FCC3 */
[35] = {0, 0 }, /* Reserved */
[36] = {0, BIT(4) }, /* MCC1 */
[37] = {0, BIT(5) }, /* MCC2 */
[38] = {0, 0 }, /* Reserved */
[39] = {0, 0 }, /* Reserved */
[40] = {0, BIT(8) }, /* SCC1 */
[41] = {0, BIT(9) }, /* SCC2 */
[42] = {0, BIT(10) }, /* SCC3 */
[43] = {0, BIT(11) }, /* SCC4 */
[44] = {0, 0 }, /* Reserved */
[45] = {0, 0 }, /* Reserved */
[46] = {0, 0 }, /* Reserved */
[47] = {0, 0 }, /* Reserved */
[48] = {BIT(15), 0 }, /* PC15 */
[49] = {BIT(14), 0 }, /* PC14 */
[50] = {BIT(13), 0 }, /* PC13 */
[51] = {BIT(12), 0 }, /* PC12 */
[52] = {BIT(11), 0 }, /* PC11 */
[53] = {BIT(10), 0 }, /* PC10 */
[54] = {BIT(9), 0 }, /* PC9 */
[55] = {BIT(8), 0 }, /* PC8 */
[56] = {BIT(7), 0 }, /* PC7 */
[57] = {BIT(6), 0 }, /* PC6 */
[58] = {BIT(5), 0 }, /* PC5 */
[59] = {BIT(4), 0 }, /* PC4 */
[60] = {BIT(3), 0 }, /* PC3 */
[61] = {BIT(2), 0 }, /* PC2 */
[62] = {BIT(1), 0 }, /* PC1 */
[63] = {BIT(0), 0 }, /* PC0 */
};
/* Blast memory layout:
* CS0: FE000000 -> FFFFFFFF (Flash)
* CS1: FC000000 -> FCFFFFFF (DSP hpi)
* CS2: 00000000 -> 03FFFFFF (60x sdram)
* CS3: 04000000 -> 04FFFFFF (FPGA)
* CS4: 05000000 -> 06FFFFFF (local bus sdram)
* CS5: 07000000 -> 0700FFFF (eeprom - not populated)
* CS6: E0000000 -> E0FFFFFF (FPGA - 64bits)
*
* Main Board memory layout:
* CS0: FE000000 -> FEFFFFFF (16 M FLASH)
* CS1: FC000000 -> FCFFFFFF (16 M DSP1)
* CS2: 00000000 -> 03FFFFFF (64 M SDRAM)
* CS3: 04000000 -> 04FFFFFF (16M DSP2)
* CS4: 05000000 -> 06FFFFFF (32 M Local SDRAM)
* CS5: 07000000 -> 0700FFFF (eeprom - not populated)
* CS6: unused
* CS7: E0000000 -> E0FFFFFF (16 M FPGA)
*/
IMM* iomem = (IMM*)IOMEM;
static Lock cpmlock;
void
machinit(void)
{
ulong scmr;
int pllmf;
extern char* plan9inistr;
memset(m, 0, sizeof(*m));
m->cputype = getpvr()>>16; /* pvr = 0x00810101 for the 8260 */
m->imap = (Imap*)INTMEM;
m->loopconst = 1096;
/* Make sure Ethernet is disabled (boot code may have buffers allocated anywhere in memory) */
iomem->fcc[0].gfmr &= ~(BIT(27)|BIT(26));
iomem->fcc[1].gfmr &= ~(BIT(27)|BIT(26));
iomem->fcc[2].gfmr &= ~(BIT(27)|BIT(26));
/* Flashed CS configuration is wrong for DSP2. It's set to 64 bits, should be 16 */
iomem->bank[3].br = 0x04001001; /* Set 16-bit port */
/*
* FPGA is capable of doing 64-bit transfers. To use these, set br to 0xe0000001.
* Currently we use 32-bit transfers, because the 8260 does not easily do 64-bit operations.
*/
iomem->bank[6].br = 0xe0001801;
iomem->bank[6].or = 0xff000830; /* Was 0xff000816 */
/*
* All systems with rev. A.1 (0K26N) silicon had serious problems when doing
* DMA transfers with data cache enabled (usually this shows when using
* one of the FCC's with some traffic on the ethernet). Allocating FCC buffer
* descriptors in main memory instead of DP ram solves this problem.
*/
/* Guess at clocks based upon the PLL configuration from the
* power-on reset.
*/
scmr = iomem->scmr;
/* The EST8260 is typically run using either 33 or 66 MHz
* external clock. The configuration byte in the Flash will
* tell us which is configured. The blast appears to be slightly
* overclocked at 72 MHz (if set to 66 MHz, the uart runs too fast)
*/
m->clkin = CLKIN;
pllmf = scmr & 0xfff;
/* This is arithmetic from the 8260 manual, section 9.4.1. */
/* Collect the bits from the scmr.
*/
m->vco_out = m->clkin * (pllmf + 1);
if (scmr & BIT(19)) /* plldf (division factor is 1 or 2) */
m->vco_out >>= 1;
m->cpmhz = m->vco_out >> 1; /* cpm hz is half of vco_out */
m->brghz = m->vco_out >> (2 * ((iomem->sccr & 0x3) + 1));
m->bushz = m->vco_out / (((scmr & 0x00f00000) >> 20) + 1);
/* Serial init sets BRG clock....I don't know how to compute
* core clock from core configuration, but I think I know the
* mapping....
*/
switch(scmr >> (31-7)){
case 0x0a:
m->cpuhz = m->clkin * 2;
break;
case 0x0b:
m->cpuhz = (m->clkin >> 1) * 5;
break;
default:
case 0x0d:
m->cpuhz = m->clkin * 3;
break;
case 0x14:
m->cpuhz = (m->clkin >> 1) * 7;
break;
case 0x1c:
m->cpuhz = m->clkin * 4;
break;
}
m->cyclefreq = m->bushz / 4;
/* Expect:
intfreq 133 m->cpuhz
busfreq 33 m->bushz
cpmfreq 99 m->cpmhz
brgfreq 49.5 m->brghz
vco 198
*/
active.machs[0] = 1;
active.exiting = 0;
putmsr(getmsr() | MSR_ME);
/*
* turn on data cache before instruction cache;
* for some reason which I don't understand,
* you can't turn on both caches at once
*/
icacheenb();
dcacheenb();
kfpinit();
/* Plan9.ini location in flash is FLASHMEM+PLAN9INI
* if PLAN9INI == ~0, it's not stored in flash or there is no flash
* if *cp == 0xff, flash memory is not initialized
*/
if (PLAN9INI == ~0 || *(plan9inistr = (char*)(FLASHMEM+PLAN9INI)) == 0xff){
/* No plan9.ini in flash */
plan9inistr =
"console=0\n"
"ether0=type=fcc port=0 ea=00601d051dd8\n"
"flash0=mem=0xfe000000\n"
"fs=135.104.9.42\n"
"auth=135.104.9.7\n"
"authdom=cs.bell-labs.com\n"
"sys=blast\n"
"ntp=135.104.9.52\n";
}
}
void
fpgareset(void)
{
print("fpga reset\n");
ioplock();
iomem->port[1].pdat &= ~Pin4; /* force reset signal to 0 */
delay(100);
iomem->port[1].pdat |= Pin4; /* force reset signal back to one */
iopunlock();
}
void
hwintrinit(void)
{
iomem->sicr = 2 << 8;
/* Write ones into most bits of the interrupt pending registers to clear interrupts */
iomem->sipnr_h = ~7;
iomem->sipnr_h = ~1;
/* Clear the interrupt masks, thereby disabling all interrupts */
iomem->simr_h = 0;
iomem->simr_l = 0;
iomem->sypcr &= ~2; /* cause a machine check interrupt on memory timeout */
/* Initialize fpga reset pin */
iomem->port[1].pdir |= Pin4; /* 1 is an output */
iomem->port[1].ppar &= ~Pin4;
iomem->port[1].pdat |= Pin4; /* force reset signal back to one */
}
int
vectorenable(Vctl *v)
{
ulong hi, lo;
if (v->irq & ~0x3f){
print("m8260enable: interrupt vector %d out of range\n", v->irq);
return -1;
}
hi = vec2mask[v->irq].hi;
lo = vec2mask[v->irq].lo;
if (hi == 0 && lo == 0){
print("m8260enable: nonexistent vector %d\n", v->irq);
return -1;
}
ioplock();
/* Clear the interrupt before enabling */
iomem->sipnr_h |= hi;
iomem->sipnr_l |= lo;
/* Enable */
iomem->simr_h |= hi;
iomem->simr_l |= lo;
iopunlock();
return v->irq;
}
void
vectordisable(Vctl *v)
{
ulong hi, lo;
if (v->irq & ~0x3f){
print("m8260disable: interrupt vector %d out of range\n", v->irq);
return;
}
hi = vec2mask[v->irq].hi;
lo = vec2mask[v->irq].lo;
if (hi == 0 && lo == 0){
print("m8260disable: nonexistent vector %d\n", v->irq);
return;
}
ioplock();
iomem->simr_h &= ~hi;
iomem->simr_l &= ~lo;
iopunlock();
}
int
intvec(void)
{
return iomem->sivec >> 26;
}
void
intend(int vno)
{
/* Clear interrupt */
ioplock();
iomem->sipnr_h |= vec2mask[vno].hi;
iomem->sipnr_l |= vec2mask[vno].lo;
iopunlock();
}
int
m8260eoi(int)
{
return 0;
}
int
m8260isr(int)
{
return 0;
}
void
flashprogpower(int)
{
}
enum {
TgcrCas = 0x80,
TgcrGm = 0x08,
TgcrStp = 0x2, /* There are two of these, timer-2 bits are bits << 4 */
TgcrRst = 0x1,
TmrIclkCasc = 0x00<<1,
TmrIclkIntclock = 0x01<<1,
TmrIclkIntclock16 = 0x02<<1,
TmrIclkTin = 0x03<<1,
TmrCERising = 0x1 << 6,
TmrCEFalling = 0x2 << 6,
TmrCEAny = 0x3 << 6,
TmrFrr = SBIT(12),
TmrOri = SBIT(11),
TerRef = SBIT(14),
TerCap = SBIT(15),
};
uvlong
fastticks(uvlong *hz)
{
ulong count;
static Lock fasttickslock;
if (hz)
*hz = m->clkin>>1;
ilock(&fasttickslock);
count = iomem->tcnl1;
if (count < ticks.lo)
ticks.hi += 1;
ticks.lo = count;
iunlock(&fasttickslock);
return ticks.val;
}
void
timerset(uvlong next)
{
long offset;
uvlong now;
static int cnt;
now = fastticks(nil);
offset = next - now;
if (offset < 2500)
next = now + 2500; /* 10000 instructions */
else if (offset > m->clkin / HZ){
print("too far in the future: offset %llux, now %llux\n", next, now);
next = now + m->clkin / HZ;
}
iomem->trrl1 = next;
}
void
m8260timerintr(Ureg *u, void*)
{
iomem->ter2 |= TerRef | TerCap; /* Clear interrupt */
timerintr(u, 0);
}
void
timerinit(void)
{
iomem->tgcr1 = TgcrCas | TgcrGm; /* cascade timers 1 & 2, normal gate mode */
iomem->tcnl1 = 0;
iomem->trrl1 = m->clkin / HZ; /* first capture in 1/HZ seconds */
iomem->tmr1 = TmrIclkCasc;
iomem->tmr2 = TmrIclkIntclock | TmrOri;
intrenable(13, m8260timerintr, nil, "timer"); /* Timer 2 interrupt is on 13 */
iomem->tgcr1 |= TgcrRst << 4;
}
static void
addseg(char *name, ulong start, ulong length)
{
Physseg segbuf;
memset(&segbuf, 0, sizeof(segbuf));
segbuf.attr = SG_PHYSICAL | SG_DEVICE | SG_NOEXEC;
kstrdup(&segbuf.name, name);
segbuf.pa = start;
segbuf.size = length;
if (addphysseg(&segbuf) == nil) {
print("addphysseg: %s\n", name);
return;
}
}
void
sharedseginit(void)
{
int i, j;
ulong base, size;
char name[16], *a, *b, *s;
static char *segnames[] = {
"fpga",
"dsp",
};
for (j = 0; j < nelem(segnames); j++){
for (i = 0; i < 8; i++){
snprint(name, sizeof name, "%s%d", segnames[j], i);
if ((a = getconf(name)) == nil)
continue;
if ((b = strstr(a, "mem=")) == nil){
print("blastseginit: %s: no base\n", name);
continue;
}
b += 4;
base = strtoul(b, nil, 0);
if (base == 0){
print("blastseginit: %s: bad base: %s\n", name, b);
continue;
}
if ((s = strstr(a, "size=")) == nil){
print("blastseginit: %s: no size\n", name);
continue;
}
s += 5;
size = strtoul(s, nil, 0);
if (size == 0){
print("blastseginit: %s: bad size: %s\n", name, s);
continue;
}
addseg(name, base, size);
}
}
}
void
cpmop(int op, int dev, int mcn)
{
ioplock();
eieio();
while(iomem->cpcr & 0x10000)
eieio();
iomem->cpcr = dev<<(31-10) | mcn<<(31-25) | op | 0x10000;
eieio();
while(iomem->cpcr & 0x10000)
eieio();
iopunlock();
}
/*
* connect SCCx clocks in NSMI mode (x=1 for USB)
*/
void
sccnmsi(int x, int rcs, int tcs)
{
ulong v;
int sh;
sh = (x-1)*8; /* each SCCx field in sicr is 8 bits */
v = (((rcs&7)<<3) | (tcs&7)) << sh;
iomem->sicr = (iomem->sicr & ~(0xFF<<sh)) | v;
}
/*
* lock the shared IO memory and return a reference to it
*/
void
ioplock(void)
{
ilock(&cpmlock);
}
/*
* release the lock on the shared IO memory
*/
void
iopunlock(void)
{
eieio();
iunlock(&cpmlock);
}
BD*
bdalloc(int n)
{
static BD *palloc = ((Imap*)INTMEM)->bd;
BD *p;
p = palloc;
if (palloc > ((Imap*)INTMEM)->bd + nelem(((Imap*)INTMEM)->bd)){
print("bdalloc: out of BDs\n");
return nil;
}
palloc += n;
return p;
}
/*
* Initialise receive and transmit buffer rings. Only used for FCC
* Ethernet now.
*
* Ioringinit will allocate the buffer descriptors in normal memory
* and NOT in Dual-Ported Ram, as prescribed by the MPC8260
* PowerQUICC II manual (Section 28.6). When they are allocated
* in DPram and the Dcache is enabled, the processor will hang.
* This has been observed for the FCCs, it may or may not be true
* for SCCs or DMA.
* The SMC Uart buffer descriptors are not allocated here; (1) they
* can ONLY be in DPram and (2) they are not configured as a ring.
*/
int
ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
{
int i, x;
static uchar *dpmallocaddr;
static uchar *dpmallocend;
if (dpmallocaddr == nil){
dpmallocaddr = m->imap->dpram1;
dpmallocend = dpmallocaddr + sizeof(m->imap->dpram1);
}
/* the ring entries must be aligned on sizeof(BD) boundaries */
r->nrdre = nrdre;
if(r->rdr == nil)
r->rdr = xspanalloc(nrdre*sizeof(BD), 0, 8);
if(r->rdr == nil)
return -1;
if(r->rrb == nil && bufsize){
r->rrb = xspanalloc(nrdre*bufsize, 0, CACHELINESZ);
if(r->rrb == nil)
return -1;
}
x = bufsize ? PADDR(r->rrb) : 0;
for(i = 0; i < nrdre; i++){
r->rdr[i].length = 0;
r->rdr[i].addr = x;
r->rdr[i].status = BDEmpty|BDInt;
x += bufsize;
}
r->rdr[i-1].status |= BDWrap;
r->rdrx = 0;
r->ntdre = ntdre;
if(r->tdr == nil)
r->tdr = xspanalloc(ntdre*sizeof(BD), 0, 8);
if(r->txb == nil)
r->txb = xspanalloc(ntdre*sizeof(Block*), 0, CACHELINESZ);
if(r->tdr == nil || r->txb == nil)
return -1;
for(i = 0; i < ntdre; i++){
r->txb[i] = nil;
r->tdr[i].addr = 0;
r->tdr[i].length = 0;
r->tdr[i].status = 0;
}
r->tdr[i-1].status |= BDWrap;
r->tdrh = 0;
r->tdri = 0;
r->ntq = 0;
return 0;
}
void
trapinit(void)
{
int i;
/*
* set all exceptions to trap
*/
for(i = 0x0; i < 0x2000; i += 0x100)
sethvec(i, trapvec);
setmvec(0x1000, imiss, tlbvec);
setmvec(0x1100, dmiss, tlbvec);
setmvec(0x1200, dmiss, tlbvec);
/* Useful for avoiding assembler miss handling:
sethvec(0x1000, tlbvec);
sethvec(0x1100, tlbvec);
sethvec(0x1200, tlbvec);
/* */
dcflush(KADDR(0), 0x2000);
icflush(KADDR(0), 0x2000);
putmsr(getmsr() & ~MSR_IP);
}
void
reboot(void*, void*, ulong)
{
ulong *p;
int x;
p = (ulong*)0x90000000;
x = splhi();
iomem->sypcr |= 0xc0;
print("iomem->sypcr = 0x%lux\n", iomem->sypcr);
*p = 0;
print("still alive\n");
splx(x);
}