Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 *      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;
uchar etheraddr[6] = { 0x90, 0x85, 0x82, 0x32, 0x83, 0x00};

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 = 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 ||
            (uchar)*(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;
}

ulong multiplier;

ulong
µs(void)
{
        uvlong x;

        if(multiplier == 0){
                multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
                print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
        }
        cycles(&x);
        return (x*multiplier) >> 16;
}

void
timerset(Tval 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) == -1) {
                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);
}