ref: f6ef250871c6e92e77ff3d92adb29619b79e280b
dir: /sys/src/9/ppc/m8260.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; 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); }