diff options
Diffstat (limited to 'os/cerf405')
35 files changed, 10335 insertions, 0 deletions
diff --git a/os/cerf405/NOTICE b/os/cerf405/NOTICE new file mode 100644 index 00000000..0a613c43 --- /dev/null +++ b/os/cerf405/NOTICE @@ -0,0 +1,3 @@ +Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved. +PowerPC support Copyright © 1995-1997,2002,2003 C H Forsyth (forsyth@terzarima.net). All rights reserved. +405EP Inferno PowerPC port Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. diff --git a/os/cerf405/README b/os/cerf405/README new file mode 100644 index 00000000..c526927b --- /dev/null +++ b/os/cerf405/README @@ -0,0 +1,45 @@ +Cerfcube 405EP + +This is the basis of a port to a range of IBM PowerPC 405 devices. +At some point all the common code will move to ../ppc, as is done +for SA1110, MPC8xx, and others. + +Currently untested: + - both EMAC0 and EMAC1 are initialised, but EMAC1 hasn't been tested (i need to get a cable made); + the PHY for it is detected and initialised, so i think it should work + - pci has not been tested since i haven't yet got the mini-PCI card i need + +Will change: + - devuart.c/uart.h will be replaced by ../port/devuart.c and uart405.c eventually + - power control will be extended + +mk in this directory should produce a file `icerfhd', which is icerf parcelled as ppcboot/uboot expects it. +put it where your tftp server can find it. + +on the machine to be used as file server, run inferno and start svc/net +to serve Styx + +To boot Inferno: + 1. set up a dhcp/tftp server for the EMAC0 MAC address, referring to the icerfhd file as the bootfile. + 2. connect a b115200 l8 pn serial port to talk to the ppcboot/uboot bootstrap + 3. reset the 405, and interrupt the automatic boot to get to the bootstrap's prompt + 4. type + bootp + 5. if dhcp is set correctly, that should load the Inferno boot image into memory at 0x300000 + 6. type + bootm + to boot that image. + +To put Inferno kernel image (icerfhd) on NAND flash: + - the boot area is 0 to 0x100000 on NAND (perhaps a bit more, but 0x100000 is currently fine) + 1. get to the bootstrap's prompt. + 2. nand erase 0 0x100000 + 3. bootp # loads the image to 0x300000 by tftp + 4. nand write 0x300000 0 0x100000 + 5. a subsequent {nand; bootm} (as for auto boot) should run that kernel. + +To initialise a file system on the NAND flash: + 1. set logfsformat=yes in the environment + 2. boot inferno + 3. choose remote file system or kernel file system when offered + 4. the empty flash file system should be in /n/local diff --git a/os/cerf405/cerf b/os/cerf405/cerf new file mode 100644 index 00000000..64265586 --- /dev/null +++ b/os/cerf405/cerf @@ -0,0 +1,139 @@ +# Intrinsyc Cerf Cube 405EP +dev + root + cons + env + mnt + pipe + prog + rtc iic + srv + dup + ssl + cap + sign + + ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + ether netif netaux + uart + flash + + logfs + i2c iic + pci pci inb + + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + keyring + sec + mp + math + kern + logfs + nandfs + +link + etheremac ethermii + flashamd29f0x0 + flashnand nand + ethermedium + +mod + math + sys + keyring + +port + alarm + alloc + allocb + chan + dev + dial + dis + discall + exception + exportfs + inferno + latin1 + nocache + nodynld + parse + pgrp + print + proc + qio + qlock + random + sysfile + taslock + xalloc + +code + int cflag = 0; + int consoleprint = 1; + int panicreset = 0; + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + void screeninit(void){} + +init + cerf405 + +root + /chan / + /dev / + /boot / + /env / + /fd / + /net / + /prog / + /root / + /nvfs / + /osinit.dis + /tmp / + +# files used by osinit.dis during bootstrap + /boot/n / + /boot/n/local / + /boot/n/remote / +# authentication + /boot/nvfs/default /usr/inferno/keyring/default + /boot/dis/lib/auth.dis /dis/lib/auth.dis + /boot/dis/lib/ssl.dis /dis/lib/ssl.dis +# dhcp + /boot/dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis + /boot/dis/lib/ip.dis /dis/lib/ip.dis + +# and other files used to poke round during development + /boot/dis/cat.dis /dis/cat.dis + /boot/dis/echo.dis /dis/echo.dis + /boot/dis/lib/arg.dis /dis/lib/arg.dis + + /boot/dis/sh.dis /dis/sh.dis + /boot/dis/lib/bufio.dis /dis/lib/bufio.dis + /boot/dis/lib/filepat.dis /dis/lib/filepat.dis + /boot/dis/lib/readdir.dis /dis/lib/readdir.dis + /boot/dis/lib/string.dis /dis/lib/string.dis + + /boot/dis/cd.dis /dis/cd.dis + /boot/dis/bind.dis /dis/bind.dis + /boot/dis/dd.dis /dis/dd.dis + /boot/dis/p.dis /dis/p.dis + /boot/dis/ls.dis /dis/ls.dis + /boot/dis/lib/daytime.dis /dis/lib/daytime.dis + /boot/dis/time.dis /dis/time.dis + /boot/dis/xd.dis /dis/xd.dis diff --git a/os/cerf405/clock.c b/os/cerf405/clock.c new file mode 100644 index 00000000..d830c18c --- /dev/null +++ b/os/cerf405/clock.c @@ -0,0 +1,159 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +#include <isa.h> +#include <interp.h> + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +ulong clkrelinq; +void (*kproftick)(ulong); /* set by devkprof.c when active */ +void (*archclocktick)(void); /* set by arch*.c when desired */ + +Timer* +addclock0link(void (*clock)(void), int) +{ + Clock0link *lp; + + if((lp = malloc(sizeof(Clock0link))) == 0){ + print("addclock0link: too many links\n"); + return nil; + } + ilock(&clock0lock); + lp->clock = clock; + lp->link = clock0link; + clock0link = lp; + iunlock(&clock0lock); + return nil; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +void +microdelay(int l) +{ + ulong i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +enum { + Timebase = 1, /* system clock cycles per time base cycle */ + + Wp17= 0<<30, /* watchdog period (2^x clocks) */ + Wp21= 1<<30, + Wp25= 2<<30, + Wp29= 3<<30, + Wrnone= 0<<28, /* no watchdog reset */ + Wrcore= 1<<28, /* core reset */ + Wrchip= 2<<28, /* chip reset */ + Wrsys= 3<<28, /* system reset */ + Wie= 1<<27, /* watchdog interrupt enable */ + Pie= 1<<26, /* enable PIT interrupt */ + Fit9= 0<<24, /* fit period (2^x clocks) */ + Fit13= 1<<24, + Fit17= 2<<24, + Fit21= 3<<24, + Fie= 1<<23, /* fit interrupt enable */ + Are= 1<<22, /* auto reload enable */ + + /* dcr */ + Boot= 0x0F1, + Epctl= 0x0F3, + Pllmr0= 0x0F0, + Pllmr1= 0x0F4, + Ucr= 0x0F5, +}; + +void +clockinit(void) +{ + long x; + + m->delayloop = m->cpuhz/1000; /* initial estimate */ + do { + x = gettbl(); + delay(10); + x = gettbl() - x; + } while(x < 0); + + /* + * fix count + */ + m->delayloop = ((vlong)m->delayloop*(10*(vlong)m->clockgen/1000))/(x*Timebase); + if((int)m->delayloop <= 0) + m->delayloop = 20000; + + x = (m->clockgen/Timebase)/HZ; + putpit(x); +iprint("pit value=%.8lux [%lud]\n", x, x); + puttsr(~0); + puttcr(Pie|Are); +iprint("boot=%.8lux epctl=%.8lux pllmr0=%.8lux pllmr1=%.8lux ucr=%.8lux\n", + getdcr(Boot), getdcr(Epctl), getdcr(Pllmr0), getdcr(Pllmr1), getdcr(Ucr)); +} + +void +clockintr(Ureg *ur) +{ + Clock0link *lp; + + /* PIT was set to reload automatically */ + puttsr(~0); + m->ticks++; + + if(up) + up->pc = ur->pc; + + if(archclocktick != nil) + archclocktick(); + checkalarms(); + if(m->machno == 0) { + if(kproftick != nil) + (*kproftick)(ur->pc); + lock(&clock0lock); + for(lp = clock0link; lp; lp = lp->link) + lp->clock(); + unlock(&clock0lock); + } + + if(up && up->state == Running){ + if(cflag && up->type == Interp && tready(nil)) + ur->cr |= 1; /* set flag in condition register for ../../interp/comp-power.c:/^schedcheck */ + if(anyready()) + sched(); + } +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} diff --git a/os/cerf405/compile.c b/os/cerf405/compile.c new file mode 100644 index 00000000..da9976f2 --- /dev/null +++ b/os/cerf405/compile.c @@ -0,0 +1,34 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define MAXDCR 0x220 + +#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5)) +#define MTDCR(s,n) ((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1)) +#define MFDCR(n,t) ((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1)) +#define RETURN 0x4e800020 +ulong _getdcr[MAXDCR][2]; +ulong _putdcr[MAXDCR][2]; + +void +compiledcr(void) +{ + ulong *p; + int i; + + for(i=0; i<MAXDCR; i++){ + p = _getdcr[i]; + p[0] = MFDCR(i, 3); + p[1] = RETURN; + p = _putdcr[i]; + p[0] = MTDCR(3, i); + p[1] = RETURN; + } + dcflush(_getdcr, sizeof(_getdcr)); + dcflush(_putdcr, sizeof(_putdcr)); + /* no need to flush icache since they won't be there */ +} diff --git a/os/cerf405/dat.h b/os/cerf405/dat.h new file mode 100644 index 00000000..12af446e --- /dev/null +++ b/os/cerf405/dat.h @@ -0,0 +1,159 @@ +typedef struct Conf Conf; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Irqctl Irqctl; +typedef struct ISAConf ISAConf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Map Map; +typedef struct Pcidev Pcidev; +typedef struct Power Power; +typedef struct RMap RMap; +typedef struct Ureg Ureg; + +typedef ulong Instr; + +#define MACHP(n) (n==0? &mach0 : *(Mach**)0) + +struct Lock +{ + ulong key; + ulong pc; + ulong sr; + int pri; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +/* + * Proc.fpstate + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ +struct FPenv +{ + union { + double fpscrd; + struct { + ulong pad; + ulong fpscr; + }; + }; + int fpistate; /* emulated fp */ + ulong emreg[32][3]; /* emulated fp */ +}; +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + double fpreg[32]; + FPenv env; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong ialloc; /* max interrupt time allocation in bytes */ +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/dat.h + */ + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor (unused) */ + ulong splpc; /* pc of last caller to splhi (unused) */ + int mmask; /* 1<<m->machno (unused) */ + + /* ordering from here on irrelevant */ + ulong ticks; /* of the clock since boot time */ + Proc *proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int nrdy; + int speed; /* general system clock in MHz */ + long oscclk; /* oscillator frequency (MHz) */ + long cpuhz; /* general system clock (cycles) */ + long clockgen; /* clock generator frequency (cycles) */ + long vcohz; + long pllhz; + long plbhz; + long opbhz; + long epbhz; + long pcihz; + int cputype; + ulong delayloop; + + /* MUST BE LAST */ + int stack[1]; +}; +extern Mach mach0; + + +/* + * a parsed .ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char* type; + ulong port; + ulong irq; + ulong mem; + int dma; + ulong size; + ulong freq; + uchar bus; + + int nopt; + char* opt[NISAOPT]; +}; + +struct Map { + int size; + ulong addr; +}; + +struct RMap { + char* name; + Map* map; + Map* mapend; + + Lock; +}; + +struct Power { + Dev* dev; + int (*powerdown)(Power*); + int (*powerup)(Power*); + int state; + void* arg; +}; + +extern register Mach *m; +extern register Proc *up; diff --git a/os/cerf405/devboot.c b/os/cerf405/devboot.c new file mode 100644 index 00000000..126fe6e6 --- /dev/null +++ b/os/cerf405/devboot.c @@ -0,0 +1,132 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qboot, + Qmem, +}; + +Dirtab bootdir[]={ + ".", {Qdir,0,QTDIR}, 0, 0555, + "boot", {Qboot}, 0, 0666, + "mem", {Qmem}, 0, 0666, +}; + +#define NBOOT (sizeof bootdir/sizeof(Dirtab)) + +static void +bootreset(void) +{ +} + +static Chan* +bootattach(char *spec) +{ + return devattach('B', spec); +} + +static Walkqid* +bootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, bootdir, NBOOT, devgen); +} + +static int +bootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, bootdir, NBOOT, devgen); +} + +static Chan* +bootopen(Chan *c, int omode) +{ + return devopen(c, omode, bootdir, NBOOT, devgen); +} + +static void +bootclose(Chan*) +{ +} + +static long +bootread(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + + switch((ulong)c->qid.path){ + + case Qdir: + return devdirread(c, buf, n, bootdir, NBOOT, devgen); + + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove(buf, (char*)offset, n); + return n; + } + error(Ebadarg); + } + + error(Egreg); + return 0; /* not reached */ +} + +static long +bootwrite(Chan *c, void *buf, long n, vlong off) +{ + ulong offset = off; + ulong pc; + uchar *p; + + switch((ulong)c->qid.path){ + case Qmem: + /* kernel memory */ + if(offset>=KZERO && offset<KZERO+conf.npage*BY2PG){ + if(offset+n > KZERO+conf.npage*BY2PG) + n = KZERO+conf.npage*BY2PG - offset; + memmove((char*)offset, buf, n); + segflush((void*)offset, n); + return n; + } + error(Ebadarg); + + case Qboot: + p = (uchar*)buf; + pc = (((((p[0]<<8)|p[1])<<8)|p[2])<<8)|p[3]; + if(pc < KZERO || pc >= KZERO+conf.npage*BY2PG) + error(Ebadarg); + splhi(); + segflush((void*)pc, 64*1024); + gotopc(pc); + } + error(Ebadarg); + return 0; /* not reached */ +} + +Dev bootdevtab = { + 'B', + "boot", + + bootreset, + devinit, + devshutdown, + bootattach, + bootwalk, + bootstat, + bootopen, + devcreate, + bootclose, + bootread, + devbread, + bootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/cerf405/devether.c b/os/cerf405/devether.c new file mode 100644 index 00000000..eb4ed283 --- /dev/null +++ b/os/cerf405/devether.c @@ -0,0 +1,617 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +static Ether *etherxx[MaxEther]; + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether; + + ctlrno = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) + error(Ebadarg); + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + chan = devattach('l', spec); + chan->dev = ctlrno; + if(ether->attach) + ether->attach(etherxx[ctlrno]); + poperror(); + runlock(ether); + return chan; +} + +static void +ethershutdown(void) +{ + Ether *ether; + int i; + + for(i=0; i<MaxEther; i++){ + ether = etherxx[i]; + if(ether != nil && ether->detach != nil) + ether->detach(ether); + } +} + +static Walkqid* +etherwalk(Chan* chan, Chan *nchan, char **name, int nname) +{ + Walkqid *wq; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname); + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + s = netifstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return s; +} + +static Chan* +etheropen(Chan* chan, int omode) +{ + Chan *c; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +ethercreate(Chan*, char*, int, ulong) +{ +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + netifclose(ether, chan); + poperror(); + runlock(ether); +} + +static long +etherread(Chan* chan, void* buf, long n, vlong off) +{ + Ether *ether; + ulong offset = off; + long r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if((chan->qid.type & QTDIR) == 0 && ether->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(chan->qid.path) == Nifstatqid){ + r = ether->ifstat(ether, buf, n, offset); + goto out; + } + if(NETTYPE(chan->qid.path) == Nstatqid) + ether->ifstat(ether, buf, 0, offset); + } + r = netifread(ether, chan, buf, n, offset); +out: + poperror(); + runlock(ether); + return r; +} + +static Block* +etherbread(Chan* chan, long n, ulong offset) +{ + Block *b; + Ether *ether; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + b = netifbread(ether, chan, n, offset); + poperror(); + runlock(ether); + return b; +} + +static int +etherwstat(Chan* chan, uchar* dp, int n) +{ + Ether *ether; + int r; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + r = netifwstat(ether, chan, dp, n); + poperror(); + runlock(ether); + return r; +} + +static void +etherrtrace(Netfile* f, Etherpkt* pkt, int len) +{ + int i, n; + Block *bp; + + if(qwindow(f->in) <= 0) + return; + if(len > 58) + n = 58; + else + n = len; + bp = iallocb(64); + if(bp == nil) + return; + memmove(bp->wp, pkt->d, n); + i = TK2MS(MACHP(0)->ticks); + bp->wp[58] = len>>8; + bp->wp[59] = len; + bp->wp[60] = i>>24; + bp->wp[61] = i>>16; + bp->wp[62] = i>>8; + bp->wp[63] = i; + bp->wp += 64; + qpass(f->in, bp); +} + +Block* +etheriq(Ether* ether, Block* bp, int fromwire) +{ + Etherpkt *pkt; + ushort type; + int len, multi, tome, fromme; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + fx = 0; + ep = ðer->f[Ntypes]; + + multi = pkt->d[0] & 1; + /* check for valid multcast addresses */ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ + if(fromwire){ + freeb(bp); + bp = 0; + } + return bp; + } + } + + /* is it for me? */ + tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + + /* + * Multiplex the packet to all the connections which want it. + * If the packet is not to be used subsequently (fromwire != 0), + * attempt to simply pass it into one of the connections, thereby + * saving a copy of the data (usual case hopefully). + */ + for(fp = ether->f; fp < ep; fp++){ + if((f = *fp) && (f->type == type || f->type < 0)) + if(tome || multi || f->prom){ + /* Don't want to hear bridged packets */ + if(f->bridge && !fromwire && !fromme) + continue; + if(!f->headersonly){ + if(fromwire && fx == 0) + fx = f; + else if(xbp = iallocb(len)){ + memmove(xbp->wp, pkt, len); + xbp->wp += len; + if(qpass(f->in, xbp) < 0) + ether->soverflows++; + } + else + ether->soverflows++; + } + else + etherrtrace(f, pkt, len); + } + } + + if(fx){ + if(qpass(fx->in, bp) < 0) + ether->soverflows++; + return 0; + } + if(fromwire){ + freeb(bp); + return 0; + } + + return bp; +} + +static int +etheroq(Ether* ether, Block* bp) +{ + int len, loopback, s; + Etherpkt *pkt; + + ether->outpackets++; + + /* + * Check if the packet has to be placed back onto the input queue, + * i.e. if it's a loopback or broadcast packet or the interface is + * in promiscuous mode. + * If it's a loopback packet indicate to etheriq that the data isn't + * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are fed back. + */ + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ + s = splhi(); + etheriq(ether, bp, 0); + splx(s); + } + + if(!loopback){ + qbwrite(ether->oq, bp); + if(ether->transmit != nil) + ether->transmit(ether); + }else + freeb(bp); + + return len; +} + +static long +etherwrite(Chan* chan, void* buf, long n, vlong) +{ + Ether *ether; + Block *bp; + int onoff; + Cmdbuf *cb; + long l; + + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(NETTYPE(chan->qid.path) != Ndataqid) { + l = netifwrite(ether, chan, buf, n); + if(l >= 0) + goto out; + cb = parsecmd(buf, n); + if(strcmp(cb->f[0], "nonblocking") == 0){ + if(cb->nf <= 1) + onoff = 1; + else + onoff = atoi(cb->f[1]); + qnoblock(ether->oq, onoff); + free(cb); + goto out; + } + free(cb); + if(ether->ctl!=nil){ + l = ether->ctl(ether,buf,n); + goto out; + } + error(Ebadctl); + } + + if(n > ether->maxmtu) + error(Etoobig); + if(n < ether->minmtu) + error(Etoosmall); + bp = allocb(n); + if(waserror()){ + freeb(bp); + nexterror(); + } + memmove(bp->rp, buf, n); + memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + poperror(); + + l = etheroq(ether, bp); +out: + poperror(); + runlock(ether); + return l; +} + +static long +etherbwrite(Chan* chan, Block* bp, ulong) +{ + Ether *ether; + long n; + + n = BLEN(bp); + if(NETTYPE(chan->qid.path) != Ndataqid){ + if(waserror()) { + freeb(bp); + nexterror(); + } + n = etherwrite(chan, bp->rp, n, 0); + poperror(); + freeb(bp); + return n; + } + ether = etherxx[chan->dev]; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + if(n > ether->maxmtu){ + freeb(bp); + error(Etoobig); + } + if(n < ether->minmtu){ + freeb(bp); + error(Etoosmall); + } + n = etheroq(ether, bp); + poperror(); + runlock(ether); + return n; +} + +static struct { + char* type; + int (*reset)(Ether*); +} cards[MaxEther+1]; + +void +addethercard(char* t, int (*r)(Ether*)) +{ + static int ncard; + + if(ncard == MaxEther) + panic("too many ether cards"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} + +int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < Eaddrlen; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; +} + +static void +etherreset(void) +{ + Ether *ether; + int i, n, ctlrno; + char name[KNAMELEN], buf[128]; + + for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + if(ether == 0) + ether = malloc(sizeof(Ether)); + memset(ether, 0, sizeof(Ether)); + ether->ctlrno = ctlrno; + ether->mbps = 10; + ether->minmtu = ETHERMINTU; + ether->maxmtu = ETHERMAXTU; + ether->tbdf = BUSUNKNOWN; + + if(archether(ctlrno, ether) <= 0) + continue; + + for(n = 0; cards[n].type; n++){ + if(cistrcmp(cards[n].type, ether->type)) + continue; + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "ea=", 3) == 0){ + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Eaddrlen); + }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 || + cistrcmp(ether->opt[i], "10BASE-TFD") == 0) + ether->fullduplex = 1; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + ether->mbps = 100; + } + if(cards[n].reset(ether)) + break; + snprint(name, sizeof(name), "ether%d", ctlrno); + + if(ether->interrupt != nil) + intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name); + + i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem)); + if(ether->size) + i += sprint(buf+i, " size 0x%luX", ether->size); + i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX", + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + sprint(buf+i, "\n"); + print(buf); + + if(ether->mbps == 100){ + netifinit(ether, name, Ntypes, 256*1024); + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); + } + else{ + netifinit(ether, name, Ntypes, 64*1024); + if(ether->oq == 0) + ether->oq = qopen(64*1024, Qmsg, 0, 0); + } + if(ether->oq == 0) + panic("etherreset %s", name); + ether->alen = Eaddrlen; + memmove(ether->addr, ether->ea, Eaddrlen); + memset(ether->bcast, 0xFF, Eaddrlen); + + etherxx[ctlrno] = ether; + ether = 0; + break; + } + } + if(ether) + free(ether); +} + +static void +etherpower(int on) +{ + int i; + Ether *ether; + + for(i = 0; i < MaxEther; i++){ + if((ether = etherxx[i]) == nil || ether->power == nil) + continue; + if(on){ + if(canrlock(ether)) + continue; + if(ether->power != nil) + ether->power(ether, on); + wunlock(ether); + }else{ + if(!canrlock(ether)) + continue; + wlock(ether); + if(ether->power != nil) + ether->power(ether, on); + /* Keep locked until power goes back on */ + } + } +} + +#define POLY 0xedb88320 + +/* really slow 32 bit crc for ethers */ +ulong +ethercrc(uchar *p, int len) +{ + int i, j; + ulong crc, b; + + crc = 0xffffffff; + for(i = 0; i < len; i++){ + b = *p++; + for(j = 0; j < 8; j++){ + crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0); + b >>= 1; + } + } + return crc; +} + +Dev etherdevtab = { + 'l', + "ether", + + etherreset, + devinit, + ethershutdown, + etherattach, + etherwalk, + etherstat, + etheropen, + ethercreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/cerf405/devrtc.c b/os/cerf405/devrtc.c new file mode 100644 index 00000000..f743f1e0 --- /dev/null +++ b/os/cerf405/devrtc.c @@ -0,0 +1,369 @@ +/* + * DS1339 Timekeeper (on I2C) + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "io.h" + +typedef struct Rtc Rtc; +typedef struct Rtcreg Rtcreg; + +struct Rtc +{ + int sec; + int min; + int hour; + int wday; + int mday; + int mon; + int year; +}; + +struct Rtcreg +{ + uchar sec; + uchar min; + uchar hour; + uchar wday; /* 1=Sun */ + uchar mday; /* 00-31 */ + uchar mon; /* 1-12 */ + uchar year; +}; + +enum{ + Qdir = 0, + Qrtc, + + Rtclen= 7, /* bytes read and written to timekeeper */ +}; + +static QLock rtclock; /* mutex on nvram operations */ +static I2Cdev rtdev; + +static Dirtab rtcdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "rtc", {Qrtc, 0}, 0, 0664, +}; + +static ulong rtc2sec(Rtc*); +static void sec2rtc(ulong, Rtc*); +static void setrtc(Rtc*); + +static void +rtcreset(void) +{ + rtdev.addr = 0x68; + rtdev.salen = 1; + i2csetup(1); +} + +static Chan* +rtcattach(char *spec) +{ + return devattach('r', spec); +} + +static Walkqid* +rtcwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); +} + +static int +rtcstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); +} + +static Chan* +rtcopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch((ulong)c->qid.path){ + case Qrtc: + if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) + error(Eperm); + break; + } + return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); +} + +static void +rtcclose(Chan*) +{ +} + +static long +rtcread(Chan *c, void *buf, long n, vlong offset) +{ + ulong t, ot; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); + + switch((ulong)c->qid.path){ + case Qrtc: + qlock(&rtclock); + t = rtctime(); + do{ + ot = t; + t = rtctime(); /* make sure there's no skew */ + }while(t != ot); + qunlock(&rtclock); + return readnum(offset, buf, n, t, 12); + } + error(Egreg); + return -1; /* never reached */ +} + +static long +rtcwrite(Chan *c, void *buf, long n, vlong off) +{ + Rtc rtc; + ulong secs; + char *cp, sbuf[32]; + ulong offset = off; + + switch((ulong)c->qid.path){ + case Qrtc: + if(offset!=0 || n >= sizeof(sbuf)-1) + error(Ebadarg); + memmove(sbuf, buf, n); + sbuf[n] = '\0'; + /* + * read the time + */ + cp = sbuf; + while(*cp){ + if(*cp>='0' && *cp<='9') + break; + cp++; + } + secs = strtoul(cp, 0, 0); + /* + * convert to bcd + */ + sec2rtc(secs, &rtc); + /* + * write it + */ + setrtc(&rtc); + return n; + } + error(Egreg); + return -1; /* never reached */ +} + +Dev rtcdevtab = { + 'r', + "rtc", + + rtcreset, + devinit, + devshutdown, + rtcattach, + rtcwalk, + rtcstat, + rtcopen, + devcreate, + rtcclose, + rtcread, + devbread, + rtcwrite, + devbwrite, + devremove, + devwstat, +}; + +static int +getbcd(int bcd) +{ + return (bcd&0x0f) + 10 * (bcd>>4); +} + +static int +putbcd(int val) +{ + return (val % 10) | (((val/10) % 10) << 4); +} + +long +rtctime(void) +{ + Rtc rtc; + Rtcreg d; + int h; + + if(waserror()){ + iprint("rtc: err %s\n", up->env->errstr); + return 0; + } + if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen) + return 0; + poperror(); + rtc.sec = getbcd(d.sec); + rtc.min = getbcd(d.min); + if(d.hour & (1<<6)){ /* 12 hour clock */ + h = d.hour & 0x1F; + if(d.hour & (1<<5)) + h += 0x12; + rtc.hour = getbcd(h); + }else + rtc.hour = getbcd(d.hour); + rtc.mday = getbcd(d.mday); + rtc.mon = getbcd(d.mon & 0x7f); + rtc.year = getbcd(d.year); + if(rtc.mon < 1 || rtc.mon > 12) + return 0; + if(d.mon & (1<<7)) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + +static void +setrtc(Rtc *rtc) +{ + Rtcreg d; + + memset(&d, 0, sizeof(d)); + d.year = putbcd(rtc->year % 100); + d.mon = putbcd(rtc->mon); + if(rtc->year >= 2000) + d.mon |= 1<<7; + d.wday = rtc->wday+1; + d.mday = putbcd(rtc->mday); + d.hour = putbcd(rtc->hour); + d.min = putbcd(rtc->min); + d.sec = putbcd(rtc->sec); + i2csend(&rtdev, &d, Rtclen, 0); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int y) +{ + + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 + */ +static ulong +rtc2sec(Rtc *rtc) +{ + ulong secs; + int i; + int *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +/* + * compute rtc from seconds since Jan 1 1970 + */ +static void +sec2rtc(ulong secs, Rtc *rtc) +{ + int d; + long hms, day; + int *d2m; + + /* + * break initial number into days + */ + hms = secs % SEC2DAY; + day = secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + rtc->wday = (day + 7340036L) % 7; + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for (d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; +} diff --git a/os/cerf405/devuart.c b/os/cerf405/devuart.c new file mode 100644 index 00000000..eba92cba --- /dev/null +++ b/os/cerf405/devuart.c @@ -0,0 +1,1064 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the uart. + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Fdma= (1<<3), /* dma on */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ringl= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 2, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + uchar sticky[8]; /* sticky write register values */ + void* regs; + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*); + +/* + * pick up architecture specific routines and definitions + */ +#include "uart.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + p->freq = archuartclock(p->port, rate); + if(p->freq == 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + uartwr(p, Dmsb, (brconst>>8) & 0xff); + uartwr(p, Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +uartdsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +uartdcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +uartparity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +uartdtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +uartrts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +uartfifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// uartfifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartportpower(p, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + uartfifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = 0; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartportpower(p, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +uartkick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + uartwr(p, Data, *p->op++); + } +} + +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + uartkick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +uartsetup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + + uartsetbaud(p, 9600); + + p->iq = qopen(4*1024, 0, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); + if(p->iq == nil || p->oq == nil) + panic("uartsetup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by uartinstall() to create a new duart + */ +void +uartsetup(ulong port, void *regs, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->regs = regs; + p->freq = freq; + uartsetup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p) +{ + uchar ch; + int s, l; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + uartkick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + iprint("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + uartrts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + uartkick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + uartinstall(); /* architecture specific */ + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ringl) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'C': + case 'c': + uartdcdhup(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'E': + case 'e': + uartdsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + uartfifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, +}; diff --git a/os/cerf405/etheremac.c b/os/cerf405/etheremac.c new file mode 100644 index 00000000..484c4205 --- /dev/null +++ b/os/cerf405/etheremac.c @@ -0,0 +1,829 @@ +/* + * ethernet + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "ethermii.h" +#include "etherif.h" +#include "ureg.h" + +/* + * TO DO: + * - test EMAC1 + */ + +#define DBG if(0)iprint +#define MIIDBG if(0)iprint + +enum { + Nrdre = 64, /* receive descriptor ring entries */ + Ntdre = 32, /* transmit descriptor ring entries */ + Nrxchan = 2, + Ntxchan = 2, /* there are actually 4 but we only use 2 now */ + + Rbsize = ETHERMAXTU, /* ring buffer size */ + Bufsize = (Rbsize+CACHELINESZ-1)&~(CACHELINESZ-1), /* aligned */ +}; + +enum { + /* emac-specific Rx BD bits */ + RxOverrun= 1<<9, /* not enough empty space in FIFO */ + RxPause= 1<<8, /* control pause packet */ + RxBad= 1<<7, /* packet error */ + RxRunt= 1<<6, + RxShort= 1<<5, + RxAlign= 1<<4, + RxFCS= 1<<3, + RxLong= 1<<2, + RxRange= 1<<1, /* out of range error */ + RxInRange= 1<<0, /* in range error */ + RxError= (0x3FF & ~RxPause), /* error flags */ + + /* emac-specific Tx BD bits */ + /* write access */ + TxFCS= 1<<9, /* generate FCS */ + TxPad= 1<<8, /* pad short frames */ + TxInsSA= 1<<7, /* insert source address */ + TxRepSA= 1<<6, /* replace source address */ + TxInsVLAN= 1<<5, /* insert VLAN tag */ + TxRepVLAN= 1<<4, /* replace VLAN tag */ + + /* read access (status) */ + TxBadFCS= 1<<9, + TxBadPrev= 1<<8, /* bad previous packet in dependent mode */ + TxLostCarrier= 1<<7, + TxEDef= 1<<6, /* excessive deferral */ + TxECol= 1<<5, /* excessive collisions */ + TxLateCol= 1<<4, /* late collision (half-duplex only) */ + TxManyCol= 1<<3, /* more than 1 but less than 16 collisions */ + TxCollision= 1<<2, /* single collision */ + TxUnderrun= 1<<1, /* didn't fill FIFO in time */ + TxSQE= 1<<0, /* signal quality test failed (10mbit half-duplex only) */ + TxError= 0x3FF, /* error flags */ +}; + +typedef struct Emac Emac; +struct Emac { + ulong mr0; /* mode register 0 [see 19-48] */ + ulong mr1; /* mode register 1 [Reset] */ + ulong tmr0; /* transmit mode register 0 [see 19-28] */ + ulong tmr1; /* transmit mode register 1 [see 19-28] */ + ulong rmr; /* receive mode register [Reset] */ + ulong isr; /* interrupt status register [Always] */ + ulong iser; /* interrupt status enable register [Reset] */ + ulong iahr; /* individual address high [Reset, R, T]*/ + ulong ialr; /* individual address low [Reset, R, T] */ + ulong vtpid; /* VLAN Tag Protocol Identifier [Reset, R, T] */ + ulong vtci; /* VLAN Tag Control Information [Reset, R, T] */ + ulong ptr; /* pause timer [Reset, T] */ + ulong iaht[4]; /* individual address hash table [Reset, R] */ + ulong gaht[4]; /* group address hash table [Reset, R] */ + ulong lsah; /* last source address high */ + ulong lsal; /* last source address low */ + ulong ipgvr; /* inter-packet gap value [Reset, T] */ + ulong stacr; /* STA control register [see 19-41] */ + ulong trtr; /* transmit request threshold register [see 19-42] */ + ulong rwmr; /* receive low/high water mark [Reset] */ + ulong octx; /* bytes transmitted */ + ulong ocrx; /* bytes received */ +}; + +enum { + /* mode register 0 */ + Mr0Rxi= 1<<31, /* receive MAC idle */ + Mr0Txi= 1<<30, /* transmit MAC idle */ + Mr0Srst= 1<<29, /* soft reset; soft reset in progress */ + Mr0Txe= 1<<28, /* tx MAC enable */ + Mr0Rxe= 1<<27, /* rx MAC enable */ + Mr0Wke= 1<<26, /* enable wake-up packets */ + + /* mode register 1 */ + Mr1Fde= 1<<31, /* full-duplex enable */ + Mr1Ile= 1<<30, /* internal loop-back enable */ + Mr1Vle= 1<<29, /* VLAN enable */ + Mr1Eifc= 1<<28, /* enable integrated flow control */ + Mr1App= 1<<27, /* allow pause packets */ + Mr1Ist= 1<<24, /* ignore sqe test (all but half-duplex 10m/bit) */ + Mr1Mf10= 0<<22, /* medium [MII] frequency is 10 mbps */ + Mr1Mf100= 1<<22, /* medium frequency is 100 mbps */ + Mr1Rfs512= 0<<20, /* RX FIFO size (512 bytes) */ + Mr1Rfs1024= 1<<20, + Mr1Rfs2048= 2<<20, + Mr1Rfs4096= 3<<20, + Mr1Tfs1024= 1<<18, /* TX FIFO size (1024 bytes) */ + Mr1Tfs2048= 2<<18, + Mr1Tr0sp= 0<<15, /* transmit request 0: single packet */ + Mr1Tr0mp= 1<<15, /* multiple packets */ + Mr1Tr0dm= 2<<15, /* dependent mode */ + Mr1Tr1sp= 0<<13, /* transmit request 1: single packet */ + Mr1Tr1mp= 1<<13, /* multiple packets */ + Mr1Tr1dm= 2<<13, /* dependent mode */ + + /* transmit mode register 0 */ + Tmr0Gnp0= 1<<31, /* get new packet channel 0 */ + Tmr0Gnp1= 1<<30, /* get new packet channel 1 */ + Tmr0Gnpd= 1<<29, /* get new packet dependent mode */ + Tmr0Fc= 1<<28, /* first channel (dependent mode) */ + + /* transmit mode register 1 */ + Tmr1Trl_s= 27, /* transmit low request (shift) */ + Tmr1Tur_s= 16, /* transmit urgent request (shift) */ + + /* receive mode register */ + RmrSp= 1<<31, /* strip pad/FCS bytes */ + RmrSfcs= 1<<30, /* strip FCS */ + RmrRrp= 1<<29, /* receive runt packets */ + RmrRfp= 1<<28, /* receive packets with FCS error */ + RmrRop= 1<<27, /* receive oversize packets */ + RmrRpir= 1<<26, /* receive packets with in range error */ + RmrPpp= 1<<25, /* propagate pause packet */ + RmrPme= 1<<24, /* promiscuous mode enable */ + RmrPmme= 1<<23, /* promiscuous mode multicast enable */ + RmrIae= 1<<22, /* individual address enable */ + RmrMiae= 1<<21, /* multiple individual address enable */ + RmrBae= 1<<20, /* broadcast address enable */ + RmrMae= 1<<19, /* multicast address enable */ + + /* interrupt status register */ + IsrOvr= 1<<25, /* overrun error */ + IsrPp= 1<<24, /* pause packet */ + IsrBp= 1<<23, /* bad packet */ + IsrRp= 1<<22, /* runt packet */ + IsrSe= 1<<21, /* short event */ + IsrAle= 1<<20, /* alignment error */ + IsrBfcs= 1<<19, /* bad FCS */ + IsrPtle= 1<<18, /* packet too long error */ + IsrOre= 1<<17, /* out of range error */ + IsrIre= 1<<16, /* in range error */ + IsrDbdm= 1<<9, /* dead bit dependent mode */ + IsrDb0= 1<<8, /* dead bit 0 */ + IsrSe0= 1<<7, /* sqe 0 */ + IsrTe0= 1<<6, /* tx error 0 */ + IsrDb1= 1<<5, /* dead bit 1 */ + IsrSe1= 1<<4, /* sqe 1 */ + IsrTe1= 1<<3, /* tx error 1 */ + IsrMos= 1<<1, /* MMA operation succeeded */ + IsrMof= 1<<0, /* MMA operation failed */ + + /* STA control register */ + StaOc= 1<<15, /* operation complete */ + StaPhye= 1<<14, /* PHY error */ + StaRead= 1<<12, /* STA read */ + StaWrite= 2<<12, /* STA write */ + StaOpb50= 0<<10, /* OPB frequency */ + StaOpb66= 1<<10, + StaOpb83= 2<<10, + StaOpb100= 3<<10, + + /* transmit request threshold */ + TrtrTrt_s= 27, /* threshold (shift) -- and the value is (threshold/64)-1 */ + + /* receive low/high water mark register */ + RwmrRlwm_s= 23, /* low water mark (shift) */ + RwmrRhwm_s= 7, /* high water mark (shift) */ +}; + +typedef struct { + Lock; + int port; + int init; + int active; + Emac *regs; + Emac *miiregs; + Mal* rx; + Mal* tx; + + Mii *mii; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static void dumpemac(Emac*); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + if(!ctlr->active){ + malrxenable(ctlr->rx); + maltxenable(ctlr->tx); + eieio(); + ctlr->regs->mr0 = Mr0Txe | Mr0Rxe; + eieio(); + ctlr->active = 1; + } + iunlock(ctlr); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + if(ctlr->active){ + ilock(ctlr); +iprint("ether closed\n"); + ctlr->regs->mr0 &= ~(Mr0Txe | Mr0Rxe); /* reset enable bits */ + /* TO DO: reset ring */ + /* TO DO: could wait */ + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->regs->rmr |= RmrPme | RmrPmme; + else + ctlr->regs->rmr &= ~(RmrPme | RmrPmme); + iunlock(ctlr); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + Ether *ether; + Ctlr *ctlr; + + USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(ether->prom || ether->nmaddr) + ctlr->regs->rmr |= RmrPmme; + else + ctlr->regs->rmr &= ~RmrPmme; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + while(ctlr->ntq < ctlr->ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + if(dre->status & BDReady) + panic("ether: txstart"); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + len = BLEN(b); + if(ctlr->txb[ctlr->tdrh] != nil) + panic("etheremac: txstart"); + ctlr->txb[ctlr->tdrh] = b; + dre->addr = PADDR(b->rp); + dre->length = len; + dcflush(b->rp, len); + eieio(); + dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast|TxFCS|TxPad; + eieio(); + ctlr->regs->tmr0 = Tmr0Gnp0; /* TO DO: several channels */ + eieio(); + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +static Block* +clallocb(void) +{ + Block *b; + + b = iallocb(Bufsize+CACHELINESZ-1); + if(b == nil) + return b; + dcflush(b->base, BALLOC(b)); + b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1)); + return b; +} + + +static void +rxring(Ureg*, void *arg) +{ + Ether *ether; + ulong status; + Ctlr *ctlr; + BD *dre; + Block *b, *rb; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + * We rely on the descriptor accesses being uncached. + */ + dre = &ctlr->rdr[ctlr->rdrx]; + while(((status = dre->status) & BDEmpty) == 0){ + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxShort|RxLong)) + ether->buffs++; + if(status & (RxBad|RxAlign|RxRange|RxInRange)) + ether->frames++; + if(status & RxFCS) + ether->crcs++; + if(status & RxOverrun) + ether->overflows++; + iprint("eth rx: %lux\n", status); + }else if((status & RxPause) == 0){ + /* + * We have a packet. Read it in. + */ + b = clallocb(); + if(b != nil){ + rb = ctlr->rxb[ctlr->rdrx]; + rb->wp += dre->length; + ctlr->rxb[ctlr->rdrx] = b; + ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp); + etheriq(ether, rb, 1); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->status = (status & BDWrap) | BDEmpty | BDInt; + eieio(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } +} + +static void +txring(Ureg*, void *arg) +{ + Ether *ether; + ulong status; + Ctlr *ctlr; + BD *dre; + Block *b; + + ether = arg; + ctlr = ether->ctlr; + ctlr->interrupts++; + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + lock(ctlr); + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + status = dre->status; + if(status & BDReady) + break; + if(status & TxEDef) + ctlr->deferred++; + if(status & TxLateCol) + ctlr->latecoll++; + if(status & TxECol) + ctlr->retrylim++; + if(status & TxUnderrun) + ctlr->underrun++; + if(status & (TxManyCol|TxCollision)) + ctlr->retrycount++; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("etheremac: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre); + } + txstart(ether); + unlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + Ether *ether; + ulong events; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + events = ctlr->regs->isr; + eieio(); + ctlr->regs->isr = events; + eieio(); + ctlr->interrupts++; +//iprint("eth: %8.8lux\n", events); + if(!ctlr->active || events == 0) + return; + + if(events & IsrOvr) + ctlr->overrun++; + if(events & (IsrTe0|IsrTe1)) + ether->oerrs++; + + rxring(nil, arg); + txring(nil, arg); + ctlr->interrupts -= 2; + + /* TO DO: restart tx/rx on error */ +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len; + Ctlr *ctlr; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); + len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); + len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); + len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); + len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static QLock miilock; /* the PHY are both on EMAC0's MII bus */ + +static int +miird(Mii *mii, int pa, int ra) +{ + Ctlr *ctlr; + Emac *em; + ulong r; + int i; + + if(up) + qlock(&miilock); + ctlr = mii->ctlr; + em = ctlr->miiregs; + MIIDBG("r: %x.%x:", pa, ra); + if((em->stacr & StaOc) == 0) + iprint("mii-not oc\n"); + em->stacr = StaRead | StaOpb66 | (pa<<5) | ra; + for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) + microdelay(1); + r = em->stacr; + if(up) + qunlock(&miilock); + if((r & StaOc) == 0) + iprint("mii'-not oc\n"); + if(r & StaPhye) + return -1; + MIIDBG(" %8.8lux\n", r); + return r >> 16; +} + +static int +miiwr(Mii *mii, int pa, int ra, int v) +{ + Ctlr *ctlr; + Emac *em; + ulong r; + int i; + + if(up) + qlock(&miilock); + ctlr = mii->ctlr; + em = ctlr->miiregs; + if((em->stacr & StaOc) == 0) + iprint("miiw-not oc\n"); + em->stacr = (v<<16) | StaWrite | StaOpb66 | (pa<<5) | ra; + for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) + microdelay(1); + r = em->stacr; + if(up) + qunlock(&miilock); + if((r & StaOc) == 0) + iprint("miiw'-not oc\n"); + if(r & StaPhye) + return -1; + MIIDBG("w: %x.%x: %8.8lux\n", pa, ra, r); + return 0; +} + +static int +emacmii(Ctlr *ctlr) +{ + MiiPhy *phy; + int i; + + MIIDBG("mii\n"); + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = miird; + ctlr->mii->miw = miiwr; + + if(mii(ctlr->mii, 1<<(ctlr->port+1)) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + iprint("oui %X phyno %d\n", phy->oui, phy->phyno); + if(miistatus(ctlr->mii) < 0){ + + miireset(ctlr->mii); + MIIDBG("miireset\n"); + if(miiane(ctlr->mii, ~0, 0, ~0) < 0){ + iprint("miiane failed\n"); + return -1; + } + MIIDBG("miistatus...\n"); + miistatus(ctlr->mii); + if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){ + for(i=0;; i++){ + if(i > 600){ + iprint("emac%d: autonegotiation failed\n", ctlr->port); + break; + } + if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc) + break; + delay(10); + } + if(miistatus(ctlr->mii) < 0) + iprint("miistatus failed\n"); + }else{ + iprint("emac%d: no link\n", ctlr->port); + phy->speed = 10; /* simple default */ + } + } + + iprint("emac%d mii: fd=%d speed=%d tfc=%d rfc=%d\n", ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc); + + MIIDBG("mii done\n"); + + return 0; +} + +static void +emacsetup(Ctlr *ctlr, Ether *ether) +{ + int i; + Emac *em; + ulong mode; + MiiPhy *phy; + + /* apparently don't need to set any Alt1 in GPIO */ + + em = ctlr->regs; + + /* errata emac_8 */ + if(em->mr0 & Mr0Rxe){ /* probably never happens in our config */ + em->mr0 &= ~Mr0Rxe; + eieio(); + for(i=0; (em->mr0 & Mr0Rxi) == 0; i++){ + if(i > 100){ + iprint("ethermac: Rxe->Rxi timed out\n"); + break; /* we'll try soft reset anyway */ + } + microdelay(100); + } + } + + /* soft reset */ + em->mr0 = Mr0Srst; + eieio(); + for(i=0; em->mr0 & Mr0Srst; i++){ + if(i > 20){ + iprint("ethermac: reset (PHY clocks not running?)"); + i=0; + } + microdelay(100); + } +iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr)); +//if(ctlr->port)return; + + malrxinit(ctlr->rx, ctlr, Bufsize/16); + maltxinit(ctlr->tx, ctlr); + malrxreset(ctlr->rx); + maltxreset(ctlr->tx); + + em->mr0 = 0; + mode = Mr1Rfs4096 | Mr1Tfs2048 | Mr1Tr0mp; + if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){ + if(phy->speed == 10){ + mode |= Mr1Mf10; + if(phy->fd) + mode |= Mr1Ist; + }else + mode |= Mr1Mf100 | Mr1Ist; + if(phy->fd) + mode |= Mr1Fde; + /* errata emac_9 suggests not using integrated flow control (it's broken); so don't negotiate it */ + if(0 && (phy->rfc || phy->tfc)) + mode |= Mr1App | Mr1Eifc; + ether->mbps = phy->speed; + ether->fullduplex = phy->fd; + }else{ + iprint("mii: didn't work: default 100FD\n"); + mode |= Mr1Mf100 | Mr1Ist | Mr1Fde; + ether->mbps = 100; + ether->fullduplex = 1; + } + + em->mr1 = mode; + em->tmr1 = (9<<Tmr1Trl_s) | (256<<Tmr1Tur_s); /* TO DO: validate these sizes */ + em->rmr = RmrSp | RmrSfcs | RmrIae | RmrBae; + em->iahr = (ether->ea[0]<<8) | ether->ea[1]; + em->ialr = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5]; + em->vtpid = 0; + em->vtci = 0; + em->ptr = 1; /* pause timer [Reset, T] */ + for(i=0; i<4; i++){ + em->iaht[i] = 0; /* individual address hash table */ + em->gaht[i] = 0; /* group address hash table */ + } + em->ipgvr = (96/8)/3; /* minimise bit times between packets */ + em->trtr = ((256/64)-1)<<TrtrTrt_s; /* transmission threshold (probably could be smaller) */ + em->rwmr = (32<<RwmrRlwm_s) | (128<<RwmrRhwm_s); /* receive low/high water mark (TO DO: check) */ + /* 0x0f002000? */ + //dumpemac(em); + //dumpmal(); + eieio(); + em->isr = em->isr; /* clear all events */ + eieio(); + em->iser = IsrOvr | IsrBp | IsrSe | IsrSe0 | IsrTe0 | IsrSe1 | IsrTe1; /* enable various error interrupts */ + /* packet tx/rx interrupts come from MAL */ + eieio(); + + /* tx/rx enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + Ctlr *ctlr; + int i; + + ioringreserve(Nrxchan, Nrdre, Ntxchan, Ntdre); + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + ctlr = malloc(sizeof(*ctlr)); + ctlr->port = ether->port; + + switch(ether->port){ + case 0: + ctlr->regs = KADDR(PHYSEMAC0); + ctlr->miiregs = ctlr->regs; + ctlr->rx = malchannel(0, 0, rxring, ether); + ctlr->tx = malchannel(0, 1, txring, ether); + ether->irq = VectorEMAC0; + break; + case 1: + ctlr->regs = KADDR(PHYSEMAC1); + ctlr->miiregs = KADDR(PHYSEMAC0); /* p. 19-41: ``only the MDIO interface for EMAC0 is pinned out'' */ + ctlr->rx = malchannel(1, 0, rxring, ether); + ctlr->tx = malchannel(2, 1, txring, ether); + ether->irq = VectorEMAC1; + break; + default: + print("%s ether: no port %lud\n", ether->type, ether->port); + free(ctlr); + return -1; + } + + if(emacmii(ctlr) < 0){ + free(ctlr); + return -1; + } + + ether->ctlr = ctlr; + + if(ioringinit(ctlr, Nrdre, Ntdre) < 0) /* TO DO: there are two transmit rings*/ + panic("etheremac initring"); + + for(i = 0; i < ctlr->nrdre; i++){ + ctlr->rxb[i] = clallocb(); + ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp); + } + + emacsetup(ctlr, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->interrupt = interrupt; /* oddly, it's only error interrupts; see malchannel call above for tx/rx */ + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +void +etheremaclink(void) +{ + addethercard("EMAC", reset); +} + +static void +dumpemac(Emac *r) +{ + iprint("mr0=%8.8lux\n", r->mr0); /* mode register 0 [see 19-48] */ + iprint("mr1=%8.8lux\n", r->mr1); /* mode register 1 [Reset] */ + iprint("tmr0=%8.8lux\n", r->tmr0); /* transmit mode register 0 [see 19-28] */ + iprint("tmr1=%8.8lux\n", r->tmr1); /* transmit mode register 1 [see 19-28] */ + iprint("rmr=%8.8lux\n", r->rmr); /* receive mode register [Reset] */ + iprint("isr=%8.8lux\n", r->isr); /* interrupt status register [Always] */ + iprint("iser=%8.8lux\n", r->iser); /* interrupt status enable register [Reset] */ + iprint("iahr=%8.8lux\n", r->iahr); /* individual address high [Reset, R, T]*/ + iprint("ialr=%8.8lux\n", r->ialr); /* individual address low [Reset, R, T] */ + iprint("vtpid=%8.8lux\n", r->vtpid); /* VLAN Tag Protocol Identifier [Reset, R, T] */ + iprint("vtci=%8.8lux\n", r->vtci); /* VLAN Tag Control Information [Reset, R, T] */ + iprint("ptr=%8.8lux\n", r->ptr); /* pause timer [Reset, T] */ + iprint("lsah=%8.8lux\n", r->lsah); /* last source address high */ + iprint("lsal=%8.8lux\n", r->lsal); /* last source address low */ + iprint("ipgvr=%8.8lux\n", r->ipgvr); /* inter-packet gap value [Reset, T] */ + iprint("stacr=%8.8lux\n", r->stacr); /* STA control register [see 19-41] */ + iprint("trtr=%8.8lux\n", r->trtr); /* transmit request threshold register [see 19-42] */ + iprint("rwmr=%8.8lux\n", r->rwmr); /* receive low/high water mark [Reset] */ + iprint("octx=%8.8lux\n", r->octx); /* bytes transmitted */ + iprint("ocrx=%8.8lux\n", r->ocrx); /* bytes received */ +} diff --git a/os/cerf405/etherif.h b/os/cerf405/etherif.h new file mode 100644 index 00000000..26f0a764 --- /dev/null +++ b/os/cerf405/etherif.h @@ -0,0 +1,37 @@ +enum { + MaxEther = 4, + Ntypes = 8, +}; + +typedef struct Ether Ether; +struct Ether { +RWlock; /* TO DO */ + ISAConf; /* hardware info */ + int ctlrno; + int tbdf; /* type+busno+devno+funcno */ + int minmtu; + int maxmtu; + uchar ea[Eaddrlen]; + int encry; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*closed)(Ether*); + void (*detach)(Ether*); + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); + long (*ctl)(Ether*, void*, long); /* custom ctl messages */ + void (*power)(Ether*, int); /* power on/off */ + void (*shutdown)(Ether*); /* shutdown hardware before reboot */ + void *ctlr; + int pcmslot; /* PCMCIA */ + int fullduplex; /* non-zero if full duplex */ + + Queue* oq; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/cerf405/fns.h b/os/cerf405/fns.h new file mode 100644 index 00000000..4c6a3fa1 --- /dev/null +++ b/os/cerf405/fns.h @@ -0,0 +1,147 @@ +#include "../port/portfns.h" + +void addpower(Power*); +void archbacklight(int); +void archconfinit(void); +int archconfval(char**, char**, int); +void archdisableuart(int); +void archdisableusb(void); +void archdisablevideo(void); +void archenableuart(int, int); +void archenableusb(int, int); +void archenablevideo(void); +void archkbdinit(void); +void archresetvideo(void); +void archinit(void); +int archoptionsw(void); +void archreboot(void); +ulong archuartclock(int, int); +void archuartdma(int, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() /* nothing needed for uniprocessor */ +void compiledcr(void); +void cpuidprint(void); +void* dcflush(void*, ulong); +void dcinval(void*, ulong); +void delay(int); +void dtlbmiss(void); +void dumplongs(char*, ulong*, int); +void dumpregs(Ureg*); +void eieio(void); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +void fprestore(FPU*); +void fpsave(FPU*); +ulong fpstatus(void); +char* getconf(char*); +ulong getccr0(void); +ulong getdar(void); +ulong getdcr(int); +ulong getdear(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getesr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpit(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +ulong gettsr(void); +void gotopc(ulong); +void icflush(void*, ulong); +void idle(void); +void idlehands(void); +int inb(int); +ulong inl(int); +int ins(int); +void insb(int, void*, int); +void insl(int, void*, int); +void inss(int, void*, int); +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +void intrcvec(void); +void ioinit(void); +void ioreset(void); +int isaconfig(char*, int, ISAConf*); +int isvalid_va(void*); +void itlbmiss(void); +void kbdinit(void); +void kbdreset(void); +void* kmapphys(void*, ulong, ulong, ulong, ulong); +void lcdpanel(int); +void links(void); +void mapfree(RMap*, ulong, int); +void mapinit(RMap*, Map*, int); +void mathinit(void); +void mmuinit(void); +void* mmucacheinhib(void*, ulong); +ulong mmumapsize(ulong); +void pcimapinit(void); +int pciscan(int, Pcidev **); +ulong pcibarsize(Pcidev *, int); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev *, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +void pcisetbme(Pcidev*); +void procsave(Proc*); +void procsetup(Proc*); +void putdcr(int, ulong); +void putesr(ulong); +void putevpr(ulong); +void putmsr(ulong); +void putpit(ulong); +void puttcr(ulong); +void puttsr(ulong); +void puttwb(ulong); +ulong rmapalloc(RMap*, ulong, int, int); +long rtctime(void); +void screeninit(void); +int screenprint(char*, ...); /* debugging */ +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void toggleled(int); +void setpanic(void); +ulong _tas(ulong*); +ulong tlbrehi(int); +ulong tlbrelo(int); +int tlbsxcc(void*); +void tlbwehi(int, ulong); +void tlbwelo(int, ulong); +void trapinit(void); +void trapvec(void); +void trapcvec(void); +void uartinstall(void); +void uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void uartwait(void); /* debugging */ +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +ulong getcallerpc(void*); + +#define isphys(a) (((ulong)(a)&KSEGM)!=KSEG0 && ((ulong)(a)&KSEGM)!=KSEG1) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) (isphys(a)?(ulong)(a):((ulong)(a)&~KSEGM)) + +/* IBM bit field order */ +#define IBIT(b) (((ulong)(1<<31))>>(b)) +#define SIBIT(n) ((ushort)1<<(15-(n))) +#define CIBIT(n) ((uchar)1<<(7-(n))) + diff --git a/os/cerf405/fpi.h b/os/cerf405/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/cerf405/fpi.h @@ -0,0 +1,61 @@ +typedef long Word; +typedef unsigned long Single; +typedef struct { + unsigned long h; + unsigned long l; +} Double; + +enum { + FractBits = 28, + CarryBit = 0x10000000, + HiddenBit = 0x08000000, + MsBit = HiddenBit, + NGuardBits = 3, + GuardMask = 0x07, + LsBit = (1<<NGuardBits), + + SingleExpBias = 127, + SingleExpMax = 255, + DoubleExpBias = 1023, + DoubleExpMax = 2047, + + ExpBias = DoubleExpBias, + ExpInfinity = DoubleExpMax, +}; + +typedef struct { + unsigned char s; + short e; + long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */ + long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */ +} Internal; + +#define IsWeird(n) ((n)->e >= ExpInfinity) +#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0) +#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0) +#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l)) +#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \ + (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0) +#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0) +#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0) + +/* + * fpi.c + */ +extern void fpiround(Internal *); +extern void fpiadd(Internal *, Internal *, Internal *); +extern void fpisub(Internal *, Internal *, Internal *); +extern void fpimul(Internal *, Internal *, Internal *); +extern void fpidiv(Internal *, Internal *, Internal *); +extern int fpicmp(Internal *, Internal *); +extern void fpinormalise(Internal*); + +/* + * fpimem.c + */ +extern void fpis2i(Internal *, void *); +extern void fpid2i(Internal *, void *); +extern void fpiw2i(Internal *, void *); +extern void fpii2s(void *, Internal *); +extern void fpii2d(void *, Internal *); +extern void fpii2w(Word *, Internal *); diff --git a/os/cerf405/fpipower.c b/os/cerf405/fpipower.c new file mode 100644 index 00000000..ec4363b2 --- /dev/null +++ b/os/cerf405/fpipower.c @@ -0,0 +1,970 @@ +/* + * this doesn't attempt to implement Power architecture floating-point properties + * that aren't visible in the Inferno environment. + * all arithmetic is done in double precision. + * the FP trap status isn't updated. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +#include "fpi.h" + +#define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) +#define FR(x) (*(Internal*)em->fr[(x)&0x1F]) +#define REGSP 1 /* stack pointer */ + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getulong(a) (*(ulong*)(a)) + +enum { + CRLT = 1<<31, + CRGT = 1<<30, + CREQ = 1<<29, + CRSO = 1<<28, + CRFU = CRSO, + + CRFX = 1<<27, + CRFEX = 1<<26, + CRVX = 1<<25, + CROX = 1<<24, +}; + +#define getCR(x,w) (((w)>>(28-(x*4)))&0xF) +#define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) + +#define simm(xx, ii) xx = (short)(ii&0xFFFF); +#define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) +#define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) + +#define FPS_FX (1<<31) /* exception summary (sticky) */ +#define FPS_EX (1<<30) /* enabled exception summary */ +#define FPS_VX (1<<29) /* invalid operation exception summary */ +#define FPS_OX (1<<28) /* overflow exception OX (sticky) */ +#define FPS_UX (1<<27) /* underflow exception UX (sticky) */ +#define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ +#define FPS_XX (1<<25) /* inexact exception XX (sticky) */ +#define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ +#define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ +#define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ +#define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ +#define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ +#define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ +#define FPS_FR (1<<18) /* fraction rounded */ +#define FPS_FI (1<<17) /* fraction inexact */ +#define FPS_FPRF (1<<16) /* floating point result class */ +#define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ +#define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ +#define FPS_VE (1<<7) /* invalid operation exception enable */ +#define FPS_OE (1<<6) /* enable overflow exceptions */ +#define FPS_UE (1<<5) /* enable underflow */ +#define FPS_ZE (1<<4) /* enable zero divide */ +#define FPS_XE (1<<3) /* enable inexact exceptions */ +#define FPS_RN (3<<0) /* rounding mode */ + +typedef struct Emreg Emreg; + +struct Emreg { + Ureg* ur; + ulong (*fr)[3]; + FPenv* ufp; + ulong ir; + char* name; +}; + +int fpemudebug = 0; + +#undef OFR +#define OFR(X) ((ulong)&((Ureg*)0)->X) + +static int roff[] = { + OFR(r0), OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(r29), OFR(r30), OFR(r31), +}; + +/* + * initial FP register values assumed by qc's code + */ +static Internal fpreginit[] = { + /* s, e, l, h */ + {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ + {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ + {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ +}; + +static void +fadd(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + (a.s == b.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fsub(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + b.s ^= 1; + (b.s == a.s? fpiadd: fpisub)(&b, &a, d); +} + +static void +fmul(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpimul(&b, &a, d); +} + +static void +fdiv(Emreg *em, Internal *d, int ra, int rb) +{ + Internal a, b; + + a = FR(ra); + b = FR(rb); + fpidiv(&b, &a, d); +} + +static void +fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + b.s ^= 1; + (b.s == t.s? fpiadd: fpisub)(&b, &t, d); +} + +static void +fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) +{ + Internal a, c, b, t; + + a = FR(ra); + c = FR(rc); + b = FR(rb); + fpimul(&a, &c, &t); + (t.s == b.s? fpiadd: fpisub)(&b, &t, d); +} + +static ulong setfpscr(Emreg*); +static void setfpcc(Emreg*, int); + +static void +unimp(Emreg *em, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +/* + * floating load/store + */ + +static void +fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) +{ + ulong ea; + long imm; + int ra, rd, upd; + + getairr(ir); + ea = imm; + upd = (ir&(1L<<26))!=0; + if(ra) { + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ + REG(ra) = ea; + } + } else { + if(upd) + unimp(em, ir); + } + *rdp = rd; + *eap = (void*)ea; + if(fpemudebug) + print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); +} + +static void +fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) +{ + ulong ea; + int ra, rb, rd; + + getarrr(ir); + ea = REG(rb); + if(ra){ + ea += REG(ra); + if(upd){ + if(ra == REGSP) + panic("fpemu: r1 update"); + REG(ra) = ea; + } + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); + } else { + if(upd) + unimp(em, ir); + if(fpemudebug) + print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); + } + *eap = (void*)ea; + *rdp = rd; +} + +static void +lfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfs"; + fpeairr(em, ir, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfsx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); + fpis2i(&FR(rd), (void*)ea); +} + +static void +lfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfd"; + fpeairr(em, ir, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +lfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + + em->name = "lfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); + fpid2i(&FR(rd), (void*)ea); +} + +static void +stfs(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfs"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfsx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfsx"; + fpearrr(em, ir, getxo(ir)==695, &ea, &rd); + tmp = FR(rd); + fpii2s(ea, &tmp); +} + +static void +stfd(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfd"; + fpeairr(em, ir, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +stfdx(Emreg *em, ulong ir) +{ + void *ea; + int rd; + Internal tmp; + + em->name = "stfdx"; + fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); + tmp = FR(rd); + fpii2d(ea, &tmp); +} + +static void +mcrfs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + static ulong fpscr0[] ={ + FPS_FX|FPS_OX, + FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, + FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, + FPS_VXVC, + 0, + FPS_VXCVI, + }; + + getarrr(ir); + if(rb || ra&3 || rd&3) + unimp(em, ir); + ra >>= 2; + rd >>= 2; + em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); + em->ufp->fpscr &= ~fpscr0[ra]; + if(fpemudebug) + print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); +} + +static void +mffs(Emreg *em, ulong ir) +{ + int rd, ra, rb; + Double dw; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + dw.h = 0; + dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; + fpid2i(&FR(rd), &dw); + /* it's anyone's guess how CR1 should be set when ir&1 */ + em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ + if(fpemudebug) + print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb1(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr |= (1L << (31-rd)); + /* BUG: should set summary bits */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsb0(Emreg *em, ulong ir) +{ + int rd, ra, rb; + + getarrr(ir); + if(ra || rb) + unimp(em, ir); + em->ufp->fpscr &= ~(1L << (31-rd)); + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); +} + +static void +mtfsf(Emreg *em, ulong ir) +{ + int fm, rb, i; + ulong v; + Internal b; + Double db; + + if(ir & ((1L << 25)|(1L << 16))) + unimp(em, ir); + rb = (ir >> 11) & 0x1F; + fm = (ir >> 17) & 0xFF; + b = FR(rb); + fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ + v = db.l; + for(i=0; i<8; i++) + if(fm & (1 << (7-i))) + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); +} + +static void +mtfsfi(Emreg *em, ulong ir) +{ + int imm, rd; + + if(ir & ((0x7F << 16)|(1L << 11))) + unimp(em, ir); + rd = (ir >> 23) & 0xF; + imm = (ir >> 12) & 0xF; + em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); + /* BUG: should set FEX and VX `according to the usual rule' */ + if(ir & 1) + em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ + if(fpemudebug) + print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); +} + +static void +fcmp(Emreg *em, ulong ir) +{ + int fc, rd, ra, rb, sig, i; + + getarrr(ir); + if(rd & 3) + unimp(em, ir); + rd >>= 2; + sig = 0; + switch(getxo(ir)) { + default: + unimp(em, ir); + case 32: + if(fpemudebug) + print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + sig = 1; + break; + case 0: + if(fpemudebug) + print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); + break; + } + if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { + if(sig){ + ; /* BUG: should trap if not masked ... */ + } + fc = CRFU; + } else { + i = fpicmp(&FR(ra), &FR(rb)); + if(i > 0) + fc = CRGT; + else if(i == 0) + fc = CREQ; + else + fc = CRLT; + } + fc >>= 28; + em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); + /* BUG: update FX, VXSNAN, VXVC */ +} + +static void +fariths(Emreg *em, ulong ir) +{ + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + Internal *d; + + fmt = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* fsqrts */ + case 24: /* fres */ + default: + unimp(em, ir); + return; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdivs"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsubs"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadds"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmuls"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsubs"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadds"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsubs"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadds"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith(Emreg *em, ulong ir) +{ + Word w; + Double dv; + int rd, ra, rb, rc, fmt; + char *cc, *n; + ulong fpscr; + int nocc; + Internal *d; + + fmt = 0; + nocc = 0; + rc = (ir>>6)&0x1F; + getarrr(ir); + d = &FR(rd); + switch(getxo(ir)&0x1F) { /* partial XO decode */ + case 22: /* frsqrt */ + case 23: /* fsel */ + case 26: /* fsqrte */ + default: + unimp(em, ir); + return; + case 12: /* frsp */ + *d = FR(rb); /* BUG: doesn't round to single precision */ + fmt = 1; + n = "frsp"; + break; + case 14: /* fctiw */ /* BUG: ignores rounding mode */ + case 15: /* fctiwz */ + fpii2w(&w, &FR(rb)); + dv.h = 0; + dv.l = w; + fpid2i(d, &dv); + fmt = 1; + nocc = 1; + n = "fctiw"; + break; + case 18: + if(IsZero(&FR(rb))) { + em->ufp->fpscr |= FPS_ZX | FPS_FX; + error("sys: fp: zero divide"); + } + fdiv(em, d, ra, rb); + n = "fdiv"; + break; + case 20: + fsub(em, d, ra, rb); + n = "fsub"; + break; + case 21: + fadd(em, d, ra, rb); + n = "fadd"; + break; + case 25: + fmul(em, d, ra, rc); + rb = rc; + n = "fmul"; + break; + case 28: + fmsub(em, d, ra, rc, rb); + fmt = 2; + n = "fmsub"; + break; + case 29: + fmadd(em, d, ra, rc, rb); + fmt = 2; + n = "fmadd"; + break; + case 30: + fmsub(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmsub"; + break; + case 31: + fmadd(em, d, ra, rc, rb); + d->s ^= 1; + fmt = 2; + n = "fnmadd"; + break; + } + if(fmt==1 && ra) + unimp(em, ir); + fpscr = setfpscr(em); + if(nocc == 0) + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) { + switch(fmt) { + case 0: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); + break; + case 1: + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); + break; + case 2: + print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); + break; + } + } +} + +static void +farith2(Emreg *em, ulong ir) +{ + int rd, ra, rb; + char *cc, *n; + ulong fpscr; + Internal *d, *b; + + getarrr(ir); + if(ra) + unimp(em, ir); + d = &FR(rd); + b = &FR(rb); + switch(getxo(ir)) { /* full XO decode */ + default: + unimp(em, ir); + case 40: + *d = *b; + d->s ^= 1; + n = "fneg"; + break; + case 72: + *d = *b; + n = "fmr"; + break; + case 136: + *d = *b; + d->s = 1; + n = "fnabs"; + break; + case 264: + *d = *b; + d->s = 0; + n = "fabs"; + break; + } + fpscr = setfpscr(em); + setfpcc(em, rd); + cc = ""; + if(ir & 1) { + cc = "."; + em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); + } + if(fpemudebug) + print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); +} + +static ulong +setfpscr(Emreg *em) +{ + ulong fps, fpscr; + + fps = 0; /* BUG: getfsr() */ + fpscr = em->ufp->fpscr; + if(fps & FPAOVFL) + fpscr |= FPS_OX; + if(fps & FPAINEX) + fpscr |= FPS_XX; + if(fps & FPAUNFL) + fpscr |= FPS_UX; + if(fps & FPAZDIV) + fpscr |= FPS_ZX; + if(fpscr != em->ufp->fpscr) { + fpscr |= FPS_FX; + em->ufp->fpscr = fpscr; + } + return fpscr; +} + +static void +setfpcc(Emreg *em, int r) +{ + int c; + Internal *d; + + d = &FR(r); + c = 0; + if(IsZero(d)) + c |= 2; + else if(d->s == 1) + c |= 4; + else + c |= 8; + if(IsNaN(d)) + c |= 1; + em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ +} + +static uchar op63flag[32] = { +[12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, +[23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, +}; + +/* + * returns the number of FP instructions emulated + */ +int +fpipower(Ureg *ur) +{ + ulong op; + int xo; + Emreg emreg, *em; + FPenv *ufp; + int n; + + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + em = &emreg; + em->ur = ur; + em->fr = ufp->emreg; + em->ufp = ufp; + em->name = nil; + if(em->ufp->fpistate != FPACTIVE) { + em->ufp->fpistate = FPACTIVE; + em->ufp->fpscr = 0; /* TO DO */ + for(n = 0; n < nelem(fpreginit); n++) + FR(31-n) = fpreginit[n]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + em->ir = op; + if(fpemudebug > 1) + print("%8.8lux %8.8lux: ", ur->pc, op); + switch(op>>26){ + default: + return n; + case 48: /* lfs */ + case 49: /* lfsu */ + lfs(em, op); + break; + case 50: /* lfd */ + case 51: /* lfdu */ + lfd(em, op); + break; + case 52: /* stfs */ + case 53: /* stfsu */ + stfs(em, op); + break; + case 54: /* stfd */ + case 55: /* stfdu */ + stfd(em, op); + break; + case 31: /* indexed load/store */ + xo = getxo(op); + if((xo & 0x300) != 0x200) + return n; + switch(xo){ + default: + return n; + case 535: /* lfsx */ + case 567: /* lfsux */ + lfsx(em, op); + break; + case 599: /* lfdx */ + case 631: /* lfdux */ + lfdx(em, op); + break; + case 663: /* stfsx */ + case 695: /* stfsux */ + stfsx(em, op); + break; + case 727: /* stfdx */ + case 759: /* stfdux */ + stfdx(em, op); + break; + } + break; + case 63: /* double precision */ + xo = getxo(op); + if(op63flag[xo & 0x1F]){ + farith(em, op); + break; + } + switch(xo){ + default: + return n; + case 0: /* fcmpu */ + case 32: /* fcmpo */ + fcmp(em, op); + break; + case 40: /* fneg */ + case 72: /* fmr */ + case 136: /* fnabs */ + case 264: /* fabs */ + farith2(em, op); + break; + case 38: + mtfsb1(em, op); + break; + case 64: + mcrfs(em, op); + break; + case 70: + mtfsb0(em, op); + break; + case 134: + mtfsfi(em, op); + break; + case 583: + mffs(em, op); + break; + case 711: + mtfsf(em, op); + break; + } + break; + case 59: /* single precision */ + fariths(em, op); + break; + } + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} + +/* +50: lfd frD,d(rA) +51: lfdu frD,d(rA) +31,631: lfdux frD,rA,rB +31,599: lfdx frD,rA,rB +48: lfs frD,d(rA) +49: lfsu frD,d(rA) +31,567: lfsux frD,rA,rB +31,535: lfsx frD,rA,rB + +54: stfd frS,d(rA) +55: stfdu frS,d(rA) +31,759: stfdux frS,rA,rB +31,727: stfdx frS,rA,rB +52: stfs frS,d(rA) +53: stfsu frS,d(rA) +31,695: stfsux frS,rA,rB +31,663: stfsx frS,rA,rB + +63,64: mcrfs crfD,crfS +63,583: mffs[.] frD +63,70: mtfsb0[.] crbD +63,38: mtfsb1[.] crbD +63,711: mtfsf[.] FM,frB +63,134: mtfsfi[.] crfD,IMM +*/ + +/* +float to int: + FMOVD g+0(SB),F1 + FCTIWZ F1,F4 + FMOVD F4,.rathole+0(SB) + MOVW .rathole+4(SB),R7 + MOVW R7,l+0(SB) +*/ + +/* +int to float: + MOVW $1127219200,R9 + MOVW l+0(SB),R7 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R7,R6 + MOVW R6,.rathole+4(SB) + FMOVD .rathole+0(SB),F0 + FSUB F27,F0 + +unsigned to float: + MOVW ul+0(SB),R5 + MOVW R9,.rathole+0(SB) + XOR $-2147483648,R5,R4 + MOVW R4,.rathole+4(SB) + FMOVD .rathole+0(SB),F3 + FSUB F27,F3 + FCMPU F3,F28 + BGE ,3(PC) + FMOVD $4.29496729600000000e+09,F2 + FADD F2,F3 + FMOVD F3,g+0(SB) +*/ diff --git a/os/cerf405/gpio.c b/os/cerf405/gpio.c new file mode 100644 index 00000000..a4821b1c --- /dev/null +++ b/os/cerf405/gpio.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define GPIOREGS ((Gpioregs*)KADDR(PHYSGPIO)) + +static ulong gpioreserved; +static Lock gpiolock; + +void +gpioreserve(ulong mask) +{ + ilock(&gpiolock); + if(gpioreserved & mask) + panic("gpioreserve: duplicate use of 0x%.8lux", gpioreserved & mask); + gpioreserved |= mask; + iunlock(&gpiolock); +} + +/* + * expand each of the bottom 16 bits into a two bit field + * with the bit as low order bit in the field + */ +static ulong +inflate(ulong m) +{ + m = ((m & 0xFF00) << 8) | (m & 0x00FF); + m = ((m << 4) | m) & 0x0F0F0F0F; + m = ((m << 2) | m) & 0x33333333; + return ((m << 1) | m) & 0x55555555; +} + +/* + * set tcr, osr[hl], tsr[hl], odr, isr1[hl] for gpio bits in m, + * following the configuration bits in cfg. when setting + * a gpio pin as output, set the right output value in OR first. + */ +void +gpioconfig(ulong m, ulong cfg) +{ + Gpioregs *g; + ulong h, hm, l, lm; + + h = inflate(m>>16); + hm = h | (h<<1); + l = inflate(m); + lm = l | (l<<1); + ilock(&gpiolock); + g = GPIOREGS; + /* + * tsr has a setting ``Alt1 three-state source'' but + * table 23-7 sets it to zero (use TCR) and sets TCR. + * thus, it seems never really to be needed. + */ + g->tsrh &= ~hm; + g->tsrl &= ~lm; + /* always select pin input (don't care for outputs) */ + g->isr1h = (g->isr1h & ~hm) | h; + g->isr1l = (g->isr1l & ~lm) | l; + if(cfg & Gpio_Alt1){ /* table 23-7 */ + g->osrh = (g->osrh & ~hm) | h; /* alt1 source */ + g->osrl = (g->osrl & ~lm) | l; + }else{ + g->osrh &= ~hm; /* GPIO_OR source */ + g->osrl &= ~lm; + } + if(cfg & Gpio_OD) + g->odr |= m; + else + g->odr &= ~m; + if(cfg & Gpio_in || cfg & Gpio_Tri) + g->tcr &= ~m; + else + g->tcr |= m; + iunlock(&gpiolock); +} + +ulong +gpioget(ulong mask) +{ + return GPIOREGS->ir & mask; +} + +void +gpioset(ulong mask, ulong out) +{ + Gpioregs *g; + + ilock(&gpiolock); + g = GPIOREGS; + g->or = (g->or & ~mask) | (out & mask); + iunlock(&gpiolock); +} + +void +gpiorelease(ulong mask) +{ + ilock(&gpiolock); + if((gpioreserved & mask) != mask) + panic("gpiorelease: unexpected release of 0x%.8lux", ~gpioreserved & mask); + gpioreserved &= ~mask; + iunlock(&gpiolock); +} diff --git a/os/cerf405/iic.c b/os/cerf405/iic.c new file mode 100644 index 00000000..22d5e51f --- /dev/null +++ b/os/cerf405/iic.c @@ -0,0 +1,605 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * basic read/write interface to 4xx IIC bus (master mode) + * ``referred to as IIC to distinguish it from the Phillips IC bus [itself]'' + * + * TO DO: + * power management ref count + * power up/power down on timer? + */ + +typedef struct Ctlr Ctlr; +typedef struct IICregs IICregs; + +struct IICregs { + uchar mdbuf; /* master data buffer */ + uchar rsvd0; + uchar sdbuf; /* slave data buffer */ + uchar rsvd1; + uchar lmadr; /* low master address */ + uchar hmadr; /* high master address */ + uchar cntl; /* control */ + uchar mdcntl; /* mode control */ + uchar sts; /* status */ + uchar extsts; /* extended status */ + uchar lsadr; /* low slave address */ + uchar hsadr; /* high slave address */ + uchar clkdiv; /* clock divide */ + uchar intrmsk; /* interrupt mask */ + uchar xfrcnt; /* transfer count */ + uchar xtcntlss; /* extended control and slave status */ + uchar directcntl; /* direct control */ +}; + +enum { + /* cntl */ + Hmt= 1<<7, /* halt master transfer */ + Amd10= 1<<6, /* =0, 7-bit; =1, 10-bit addressing */ + /* 5,4: two bit transfer count (n-1)&3 bytes */ + Rpst= 1<<3, /* =0, normal start; =1, repeated Start, transfer should be followed by another start */ + Cht= 1<<2, /* chain transfer; not the last */ + Write= 0<<1, /* transfer is a write */ + Read= 1<<1, /* transfer is a read */ + Pt= 1<<0, /* =0, most recent transfer complete; =1, start transfer if bus free */ + + /* mdcntl */ + Fsdb= 1<<7, /* flush slave data buffer */ + Fmdb= 1<<6, /* flush master data buffer */ + Fsm= 1<<4, /* =0, 100 kHz standard mode; =1, 400 Khz fast mode */ + Esm= 1<<3, /* enable slave mode */ + Eint= 1<<2, /* enable interrupt */ + Eubs= 1<<1, /* exit unknown bus state */ + Hscl= 1<<0, /* hold IIC serial clock low */ + + /* sts */ + Sss= 1<<7, /* slave status set (slave operation in progress) */ + Slpr= 1<<6, /* sleep mode */ + Mdbs= 1<<5, /* master data buffer has data */ + Mdbf= 1<<4, /* master data buffer is full */ + Scmp= 1<<3, /* stop complete */ + Err= 1<<2, /* error set in extsts */ + Irqa= 1<<1, /* IRQ active */ + /* Pt as above */ + + /* extsts */ + Irqp= 1<<7, /* IRQ pending */ + Bcs= 7<<4, + Bcs_ssel= 1<<4, /* slave-selected state */ + Bcs_sio= 2<<4, /* slave transfer state */ + Bcs_mio= 3<<4, /* master transfer state */ + Bcs_free= 4<<4, /* bus is free */ + Bcs_busy= 5<<4, /* bus is busy */ + Bcs_gok= 6<<4, /* unknown state */ + Irqd= 1<<3, /* IRQ on deck */ + La= 1<<2, /* lost arbitration */ + Ict= 1<<1, /* incomplete transfer */ + Xfra= 1<<0, /* transfer aborted */ + + /* intrmsk */ + Eirc= 1<<7, /* slave read complete */ + Eirs= 1<<6, /* slave read needs service */ + Eiwc= 1<<5, /* slave write complete */ + Eiws= 1<<4, /* slave write needs service */ + Eihe= 1<<3, /* halt executed */ + Eiic= 1<<2, /* incomplete transfer */ + Eita= 1<<1, /* transfer aborted */ + Eimtc= 1<<0, /* master transfer complete */ + + /* xtcntlss */ + Src= 1<<7, /* slave read complete; =1, NAK or Stop, or repeated Start ended op */ + Srs= 1<<6, /* slave read needs service */ + Swc= 1<<5, /* slave write complete */ + Sws= 1<<4, /* slave write needs service */ + Sdbd= 1<<3, /* slave buffer has data */ + Sdbf= 1<<2, /* slave buffer is full */ + Epi= 1<<1, /* enable pulsed IRQ on transfer aborted */ + Srst= 1<<0, /* soft reset */ + + /* directcntl */ + Sdac= 1<<3, /* SDA output */ + Scc= 1<<2, /* SCL output */ + Msda= 1<<1, /* SDA input */ + Msc= 1<<0, /* SCL input */ + + /* others */ + Rbit = 1<<0, /* bit in address byte denoting read */ + FIFOsize= 4, /* most to be written at once */ + + MaxIO = 8192, /* largest transfer done at once (can change) */ + MaxSA= 2, /* largest subaddress; could be FIFOsize */ + Bufsize = MaxIO, /* subaddress bytes don't go in buffer */ + Freq = 100000, + I2Ctimeout = 125, /* msec (can change) */ + + Chatty = 0, +}; + +#define DPRINT if(Chatty)print + +/* + * I2C software structures + */ + +struct Ctlr { + Lock; + QLock io; + int init; + int polling; /* eg, when running before system set up */ + IICregs* regs; /* hardware registers */ + + /* controller state (see below) */ + int status; + int phase; + Rendez r; + + /* transfer parameters */ + int cntl; /* everything but transfer length */ + int rdcount; /* requested read transfer size */ + Block* b; +}; + +enum { + /* Ctlr.state */ + Idle, + Done, + Failed, + Busy, + Halting, +}; + +static Ctlr iicctlr[1]; + +static void interrupt(Ureg*, void*); +static int readyxfer(Ctlr*); +static void rxstart(Ctlr*); +static void txstart(Ctlr*); +static void stopxfer(Ctlr*); +static void txoffset(Ctlr*, ulong, int); +static int idlectlr(Ctlr*); + +static void +iicdump(char *t, IICregs *iic) +{ + iprint("iic %s: lma=%.2ux hma=%.2ux im=%.2ux mdcntl=%.2ux sts=%.2ux ests=%.2ux cntl=%.2ux\n", + t, iic->lmadr, iic->hmadr, iic->intrmsk, iic->mdcntl, iic->sts, iic->extsts, iic->cntl); +} + +static void +initialise(IICregs *iic, int intrmsk) +{ + int d; + + d = (m->opbhz-1000000)/10000000; + if(d <= 0) + d = 1; /* just in case OPB freq < 20 Mhz */ + /* initialisation (see 22.4, p. 22-23) */ + iic->lmadr = 0; + iic->hmadr = 0; + iic->sts = Scmp|Irqa; + iic->extsts = Irqp | Irqd | La | Ict | Xfra; + iic->clkdiv = d; + iic->intrmsk = 0; /* see below */ + iic->xfrcnt = 0; + iic->xtcntlss = Src | Srs | Swc | Sws; + iic->mdcntl = Fsdb | Fmdb | Eubs; /* reset; standard mode */ + iic->cntl = 0; + eieio(); + iic->mdcntl = 0; + eieio(); + if(intrmsk){ + iic->intrmsk = intrmsk; + iic->mdcntl = Eint; + } +} + +/* + * called by the reset routine of any driver using the IIC + */ +void +i2csetup(int polling) +{ + IICregs *iic; + Ctlr *ctlr; + + ctlr = iicctlr; + ctlr->polling = polling; + iic = (IICregs*)KADDR(PHYSIIC); + ctlr->regs = iic; + if(!polling){ + if(ctlr->init == 0){ + initialise(iic, Eihe | Eiic | Eita | Eimtc); + ctlr->init = 1; + intrenable(VectorIIC, interrupt, iicctlr, BUSUNKNOWN, "iic"); + } + }else + initialise(iic, 0); +} + +static void +interrupt(Ureg*, void *arg) +{ + int sts, nb, ext, avail; + Ctlr *ctlr; + Block *b; + IICregs *iic; + + ctlr = arg; + iic = ctlr->regs; + if(0) + iicdump("intr", iic); + sts = iic->sts; + if(sts & Pt) + iprint("iic: unexpected status: %.2ux", iic->sts); + ext = iic->extsts; + if(sts & Mdbs) + nb = iic->xfrcnt & 7; + else + nb = 0; + eieio(); + iic->sts = sts; + if(sts & Err && (ext & (La|Xfra)) != 0) + iprint("iic: s=%.2ux es=%.2ux (IO)\n", sts, ext); + ctlr->status = ext; + switch(ctlr->phase){ + default: + iprint("iic: unexpected interrupt: p-%d s=%.2ux es=%.2ux\n", ctlr->phase, sts, ext); + break; + + case Halting: + ctlr->phase = Idle; + break; + + case Busy: + b = ctlr->b; + if(b == nil) + panic("iic: no buffer"); + if(ctlr->cntl & Read){ + /* copy data in from FIFO */ + avail = b->lim - b->wp; + if(nb > avail) + nb = avail; + while(--nb >= 0) + *b->wp++ = iic->mdbuf; /* ``the IIC interface handles the [FIFO] latency'' (22-4) */ + if(sts & Err || ctlr->rdcount <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + break; + } + rxstart(ctlr); + }else{ + /* account for data transmitted */ + if((b->rp += nb) > b->wp) + b->rp = b->wp; + if(sts & Err || BLEN(b) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + break; + } + txstart(ctlr); + } + } +} + +static int +done(void *a) +{ + return ((Ctlr*)a)->phase < Busy; +} + +static int +i2cerror(char *s) +{ + DPRINT("iic error: %s\n", s); + if(up) + error(s); + /* no current process, don't call error */ + return -1; +} + +static char* +startxfer(I2Cdev *d, int op, void (*xfer)(Ctlr*), Block *b, int n, ulong offset) +{ + IICregs *iic; + Ctlr *ctlr; + int i, cntl, p, s; + + ctlr = iicctlr; + if(up){ + qlock(&ctlr->io); + if(waserror()){ + qunlock(&ctlr->io); + nexterror(); + } + } + ilock(ctlr); + if(!idlectlr(ctlr)){ + iunlock(ctlr); + if(up) + error("bus confused"); + return "bus confused"; + } + if(ctlr->phase >= Busy) + panic("iic: ctlr busy"); + cntl = op | Pt; + if(d->tenbit) + cntl |= Amd10; + ctlr->cntl = cntl; + ctlr->b = b; + ctlr->rdcount = n; + ctlr->phase = Busy; + iic = ctlr->regs; + if(d->tenbit){ + iic->hmadr = 0xF0 | (d->addr>>7); /* 2 higher bits of address, LSB don't care */ + iic->lmadr = d->addr; + }else{ + iic->hmadr = 0; + iic->lmadr = d->addr<<1; /* 7-bit address */ + } + if(d->salen) + txoffset(ctlr, offset, d->salen); + else + (*xfer)(ctlr); + iunlock(ctlr); + + /* wait for it */ + if(ctlr->polling){ + for(i=0; !done(ctlr); i++){ + delay(2); + interrupt(nil, ctlr); + } + }else + tsleep(&ctlr->r, done, ctlr, I2Ctimeout); + + ilock(ctlr); + p = ctlr->phase; + s = ctlr->status; + ctlr->b = nil; + if(ctlr->phase != Done && ctlr->phase != Idle) + stopxfer(ctlr); + iunlock(ctlr); + + if(up){ + poperror(); + qunlock(&ctlr->io); + } + if(p != Done || s & (La|Xfra)){ /* CHECK; time out */ + if(s & La) + return "iic lost arbitration"; + if(s & Xfra) + return "iic transfer aborted"; + if(p != Done) + return "iic timed out"; + sprint(up->genbuf, "iic error: phase=%d estatus=%.2ux", p, s); + return up->genbuf; + } + return nil; +} + +long +i2csend(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + memmove(b->wp, buf, n); + b->wp += n; + e = startxfer(d, Write, txstart, b, 0, offset); + if(up) + poperror(); + n -= BLEN(b); /* residue */ + freeb(b); + if(e) + return i2cerror(e); + return n; +} + +long +i2crecv(I2Cdev *d, void *buf, long n, ulong offset) +{ + Block *b; + long nr; + char *e; + + if(n <= 0) + return 0; + if(n > MaxIO) + n = MaxIO; + + if(up){ + b = allocb(n); + if(b == nil) + error(Enomem); + if(waserror()){ + freeb(b); + nexterror(); + } + }else{ + b = iallocb(n); + if(b == nil) + return -1; + } + e = startxfer(d, Read, rxstart, b, n, offset); + nr = BLEN(b); + if(nr > 0) + memmove(buf, b->rp, nr); + if(up) + poperror(); + freeb(b); + if(e) + return i2cerror(e); + return nr; +} + +/* + * the controller must be locked for the following functions + */ + +static int +readyxfer(Ctlr *ctlr) +{ + IICregs *iic; + + iic = ctlr->regs; + iic->sts = Scmp | Err; + if((iic->sts & Pt) != 0){ + ctlr->phase = Failed; + wakeup(&ctlr->r); + return 0; + } + iic->mdcntl |= Fmdb; + return 1; +} + +/* + * start a master transfer to receive the next chunk of data + */ +static void +rxstart(Ctlr *ctlr) +{ + Block *b; + int cntl; + long nb; + + b = ctlr->b; + if(b == nil || (nb = ctlr->rdcount) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + return; + } + if(!readyxfer(ctlr)) + return; + cntl = ctlr->cntl; + if(nb > FIFOsize){ + nb = FIFOsize; + cntl |= Cht; /* more to come */ + } + ctlr->rdcount -= nb; + ctlr->regs->cntl = cntl | ((nb-1)<<4); +} + +/* + * start a master transfer to send the next chunk of data + */ +static void +txstart(Ctlr *ctlr) +{ + Block *b; + int cntl, i; + long nb; + IICregs *iic; + + b = ctlr->b; + if(b == nil || (nb = BLEN(b)) <= 0){ + ctlr->phase = Done; + wakeup(&ctlr->r); + return; + } + if(!readyxfer(ctlr)) + return; + cntl = ctlr->cntl; + if(nb > FIFOsize){ + nb = FIFOsize; + cntl |= Cht; /* more to come */ + } + iic = ctlr->regs; + for(i=0; i<nb; i++) + iic->mdbuf = *b->rp++; /* load the FIFO */ + iic->cntl = cntl | ((nb-1)<<4); +} + +/* + * start a master transfer to send a sub-addressing offset; + * if subsequently receiving, use Rpst to cause the next transfer to include a Start; + * if subsequently sending, use Cht to chain the transfer without a Start. + */ +static void +txoffset(Ctlr *ctlr, ulong offset, int len) +{ + int i, cntl; + IICregs *iic; + + if(!readyxfer(ctlr)) + return; + iic = ctlr->regs; + for(i=len*8; (i -= 8) >= 0;) + iic->mdbuf = offset>>i; /* load offset bytes into FIFO */ + cntl = ctlr->cntl & Amd10; + if(ctlr->cntl & Read) + cntl |= Rpst; + else + cntl |= Cht; + iic->cntl = cntl | ((len-1)<<4) | Write | Pt; +} + +/* + * stop a transfer if one is in progress + */ +static void +stopxfer(Ctlr *ctlr) +{ + IICregs *iic; + int ext; + + iic = ctlr->regs; + ext = iic->extsts; + eieio(); + iic->sts = Scmp | Irqa; + eieio(); + if((iic->sts & Pt) == 0){ + ctlr->phase = Idle; + return; + } + if((ext & Bcs) == Bcs_mio && ctlr->phase != Halting){ + ctlr->phase = Halting; /* interrupt will clear the state */ + iic->cntl = Hmt; + } +} + +static int +idlectlr(Ctlr *ctlr) +{ + IICregs *iic; + + iic = ctlr->regs; + if((iic->extsts & Bcs) == Bcs_free){ + if((iic->sts & Pt) == 0){ + ctlr->phase = Idle; + return 1; + } + iprint("iic: bus free, ctlr busy: s=%.2ux es=%.2ux\n", iic->sts, iic->extsts); + } + /* hit it with the hammer, soft reset */ + iprint("iic: soft reset\n"); + iic->xtcntlss = Srst; + iunlock(ctlr); + delay(1); + ilock(ctlr); + initialise(iic, Eihe | Eiic | Eita | Eimtc); + ctlr->phase = Idle; + return (iic->extsts & Bcs) == Bcs_free && (iic->sts & Pt) == 0; +} diff --git a/os/cerf405/inb.s b/os/cerf405/inb.s new file mode 100644 index 00000000..b664f85e --- /dev/null +++ b/os/cerf405/inb.s @@ -0,0 +1,127 @@ +#include "mem.h" + +/* + * the tlb entry is configured for little-endian access, + * so byte-reversing loads aren't needed + */ + +#define ISAIO PHYSPCIIO0 + +#define BDNZ BC 16,0, + +TEXT inb(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +insb1: + EIEIO + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVHZ (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVH R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAIO, R3 + EIEIO + MOVW (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAIO, R3 + EIEIO + MOVW R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAIO, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/os/cerf405/io.h b/os/cerf405/io.h new file mode 100644 index 00000000..1f779dfc --- /dev/null +++ b/os/cerf405/io.h @@ -0,0 +1,301 @@ +typedef struct BD BD; +typedef struct Ring Ring; +typedef struct MALdev MALdev; +typedef struct I2Cdev I2Cdev; + +enum +{ + /* 405EP UIC interrupt vectors (IBM bit numbering) */ + VectorUIC= 0, + VectorUART0=VectorUIC, + VectorUART1, + VectorIIC, + VectorPCIECW, + VectorRsvd1, + VectorDMA0, + VectorDMA1, + VectorDMA2, + VectorDMA3, + VectorEtherwake, + VectorMALSERR, + VectorMALTXEOB, + VectorMALRXEOB, + VectorMALTXDE, + VectorMALRXDE, + VectorEMAC0, + VectorPCISERR, + VectorEMAC1, + VectorPCIPM, + VectorGPT0, + VectorGPT1, + VectorGPT2, + VectorGPT3, + VectorGPT4, + /* 1 reserved */ + VectorIRQ= VectorUIC+25, /* IRQ0 to IRQ6 */ + MaxVector= VectorIRQ+7, + + /* some flags to change polarity and sensitivity */ + IRQmask= 0xFF, /* actual vector address */ + IRQactivelow= 1<<8, + IRQedge= 1<<9, + IRQcritical= 1<<10, +}; + +/* + * these are defined to keep the interface compatible with other + * architectures, but only BUSUNKNOWN is currently used + */ +#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) +#define BUSFNO(tbdf) (((tbdf)>>8)&0x07) +#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F) +#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF) +#define BUSTYPE(tbdf) ((tbdf)>>24) +#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00) +#define BUSUNKNOWN (-1) + +enum { + BusOPB, + BusPLB, + BusPCI, + MaxBus +}; + +/* + * MAL Buffer Descriptors and IO Rings + */ + +struct BD { + ushort status; + ushort length; + ulong addr; +}; +#define MAXIORING 256 /* hardware limit to ring size */ +#define BDBUFLIM (4096-16) /* no MAL buffer larger than this */ + +BD* bdalloc(ulong); +void bdfree(BD*, int); +void dumpbd(char*, BD*, int); + +enum { + /* Rx BDs, bits common to all protocols */ + BDEmpty= 1<<15, + BDWrap= 1<<14, /* end of ring */ + BDContin= 1<<13, /* continuous mode */ + BDLast= 1<<12, /* last buffer in current packet */ + BDFirst= 1<<11, /* first buffer in current packet (set by MAL) */ + BDInt= 1<<10, /* interrupt when done */ + + /* Tx BDs */ + BDReady= 1<<15, /* ready to transmit; set by driver, cleared by MAL */ + /* BDWrap, BDInt, BDLast as above */ +}; + +struct Ring { + BD* rdr; /* receive descriptor ring */ + Block** rxb; /* receive ring buffers */ + int rdrx; /* index into rdr */ + int nrdre; /* length of rdr */ + + BD* tdr; /* transmit descriptor ring */ + Block** txb; /* transmit ring buffers */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntdre; /* length of tdr */ + int ntq; /* pending transmit requests */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +/* + * one per mal channel + */ +typedef struct Mal Mal; +struct Mal { + int n; + int len; + int tx; + ulong mask; + + void* arg; + void (*interrupt)(Ureg*, void*); +}; + +Mal* malchannel(int, int, void (*)(Ureg*, void*), void*); +void maltxreset(Mal*); +void maltxinit(Mal*, Ring*); +void maltxenable(Mal*); +void malrxreset(Mal*); +void malrxinit(Mal*, Ring*, ulong); +void malrxenable(Mal*); +void ioringreserve(int, ulong, int, ulong); +int ioringinit(Ring*, int, int); + +typedef struct Gpioregs Gpioregs; +struct Gpioregs { + ulong or; /* output register */ + ulong tcr; /* tristate control */ + ulong osrh; /* output select high (0-15) */ + ulong osrl; /* output select low (16-31) */ + ulong tsrh; /* tristate select high (0-15) */ + ulong tsrl; /* tristate select low (16-31) */ + ulong odr; /* open drain */ + ulong ir; /* input */ + ulong rr1; /* receive register */ + ulong pad[3]; + ulong isr1h; /* input select 1 high (0-15) */ + ulong isr1l; /* input select 1 low (16-31) */ +}; + +enum { + /* software configuration bits for gpioconfig */ + Gpio_Alt1= 1<<0, /* implies specific settings of all the others, but include in or out */ + Gpio_OD= 1<<1, + Gpio_Tri= 1<<2, + Gpio_in= 1<<4, + Gpio_out= 1<<5, +}; + +void gpioreserve(ulong); +void gpioconfig(ulong, ulong); +ulong gpioget(ulong); +void gpioset(ulong, ulong); +void gpiorelease(ulong); + +/* + * used by ../port/devi2c.c and iic.c + */ +struct I2Cdev { + int addr; + int salen; /* length in bytes of subaddress, if used; 0 otherwise */ + int tenbit; /* 10-bit addresses */ +}; + +long i2crecv(I2Cdev*, void*, long, ulong); +long i2csend(I2Cdev*, void*, long, ulong); +void i2csetup(int); + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +typedef struct Pcisiz Pcisiz; +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +typedef struct Pcidev Pcidev; +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + ulong pcr; +}; + +#define PCIWINDOW 0x80000000 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) diff --git a/os/cerf405/l.s b/os/cerf405/l.s new file mode 100644 index 00000000..28990ef6 --- /dev/null +++ b/os/cerf405/l.s @@ -0,0 +1,795 @@ +#include "mem.h" + +#define MB (1024*1024) + +/* + * common ppc special purpose registers + */ +#define DSISR 18 +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define SPRG0 272 /* Supervisor Private Registers */ +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 285 /* Time base Upper/Lower (Writing) */ +#define TBWL 284 +#define PVR 287 /* Processor Version */ + +/* + * 4xx-specific special purpose registers of interest here + */ +#define ICCR 1019 /* instruction cache control */ +#define DCCR 1018 /* data cache control */ +#define DBCR0 1010 /* debug control register 0 */ +#define DCWR 964 /* data cache write-through */ +#define PID 945 /* TLB process ID */ +#define CCR0 947 /* core configuration register 0 */ +#define SLER 955 /* storage little-endian */ +#define SU0R 956 /* storage user-defined 0 */ +#define SRR2 990 +#define SRR3 991 +/* SPRGn up to 7, if needed, on the 400 series */ +#define DEAR 961 /* data error address */ +#define ESR 980 /* exception syndrome */ +#define EVPR 982 /* exception vector prefix */ +#define PIT 987 /* interval timer */ +#define SGR 953 /* storage guarded */ +#define TCR 986 /* timer control */ +#define TSR 984 /* timer status */ +#define ZPR 944 /* zone protection */ + +/* + * 4xx-specific(?) device control registers + */ +#define OCM0_DSCNTL 0x1B /* OCM data-side control register */ + +/* use of SPRG registers in save/restore */ +#define SAVER0 SPRG0 +#define SAVER1 SPRG1 +#define SAVELR SPRG2 +#define SAVEXX SPRG3 + +/* special instruction definitions */ +#define BDNZ BC 16,0, +#define BDNE BC 0,2, +#define TLBIA WORD $((31<<26)|(370<<1)) +#define TLBSYNC WORD $((31<<26)|(566<<1)) +#define MFTB(tbr,d) WORD $((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1)) + +/* 603/603e specific: load tlb entries */ +#define TLBLD(x) WORD $((31<<26)|(978<<1)|((x&0x1F)<<11)) +#define TLBLI(x) WORD $((31<<26)|(1010<<1)|((x&0x1F)<<11)) + +/* 400 models; perhaps others */ +#define ICCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(966<<1)) +#define DCCCI(a,b) WORD $((31<<26)|((a)<<16)|((b)<<11)|(454<<1)) +/* these follow the source -> dest ordering */ +#define DCREAD(s,t) WORD $((31<<26)|((t)<<21)|((s)<<11)|(486<<1)) +#define DCRF(n) ((((n)>>5)&0x1F)|(((n)&0x1F)<<5)) +#define MTDCR(s,n) WORD $((31<<26)|((s)<<21)|(DCRF(n)<<11)|(451<<1)) +#define MFDCR(n,t) WORD $((31<<26)|((t)<<21)|(DCRF(n)<<11)|(323<<1)) +#define TLBRELO(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1)) +#define TLBREHI(a,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1)) +#define TLBWELO(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1)) +#define TLBWEHI(s,a) WORD $((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1)) +#define TLBSX(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)) +#define TLBSXCC(a,b,t) WORD $((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1) +#define WRTMSR_EE(s) WORD $((31<<26)|((s)<<21)|(131<<1)) +#define WRTMSR_EEI(e) WORD $((31<<26)|((e)<<16)|(163<<1)) + +/* on some models mtmsr doesn't synchronise enough (eg, 603e) */ +#define MSRSYNC SYNC; ISYNC + +/* on the 400 series, the prefetcher madly fetches across RFI, sys call, and others; use BR 0(PC) to stop */ +#define RFI WORD $((19<<26)|(50<<1)); BR 0(PC) +#define RFCI WORD $((19<<26)|(51<<1)); BR 0(PC) + +#define UREGSPACE (UREGSIZE+8) + +/* could define STEP to set an LED to mark progress */ +#define STEP(x) + +/* + * Boot first processor + */ + TEXT start(SB), $-4 + + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R3 + OR $MSR_ME, R3 + ISYNC + MOVW R3, MSR /* turn off interrupts but enable traps */ + MSRSYNC + MOVW $0, R0 /* except during trap handling, R0 is zero from now on */ + MOVW R0, CR + + MOVW $setSB-KZERO(SB), R2 /* SB until mmu on */ + +/* + * reset the caches and disable them until mmu on + */ + MOVW R0, SPR(ICCR) + ICCCI(0, 2) /* the errata reveals that EA is used; we'll use SB */ + ISYNC + + MOVW $((CACHEWAYSIZE/CACHELINESZ)-1), R3 + MOVW R3, CTR + MOVW R0, R3 +dcinv: + DCCCI(0,3) + ADD $32, R3 + BDNZ dcinv + + /* cache is copy-back, disabled; no user-defined 0; big endian throughout */ + MOVW R0, SPR(DCWR) + MOVW R0, SPR(DCCR) + MOVW R0, SPR(SU0R) + MOVW R0, SPR(SLER) + ISYNC + + /* guard everything above 0x20000000 */ + MOVW $~(0xF000<<16), R3 + MOVW R3, SPR(SGR) + ISYNC + + /* set access to LED */ + MOVW $PHYSGPIO, R4 + MOVW $(1<<31), R6 + MOVW $(0xC000<<16), R5 + MOVW 4(R4), R3 + OR R6, R3 + MOVW R3, 4(R4) /* tcr set */ + MOVW 0x18(R4), R3 + ANDN R6, R3 + MOVW R3, 0x18(R4) /* odr reset */ + MOVW 8(R4), R3 + ANDN R5, R3 + MOVW R3, 8(R4) /* osrh uses or */ + MOVW 0x10(R4), R3 + ANDN R5, R3 + MOVW R3, 0x10(R4) /* tsr uses tcr */ + + MOVW $(1<<31), R4 /* reset MAL */ + MTDCR(0x180, 4) + +/* + MOVW $'H', R3 + BL uartputc(SB) + MOVW $'\n', R3 + BL uartputc(SB) +*/ + +/* + * set other system configuration values + */ + MOVW R0, SPR(PIT) + MOVW $~0, R3 + MOVW R3, SPR(TSR) + +STEP(1) + + BL kernelmmu(SB) + /* now running with correct addresses, mmu on */ + + MOVW $setSB(SB), R2 + + /* enable caches for kernel 128mb in real mode; data is copy-back */ + MOVW R0, SPR(DCWR) + MOVW $(1<<31), R3 + MOVW R3, SPR(DCCR) + MOVW R3, SPR(ICCR) + +/* + BL ledoff(SB) + + MOVW $0x800, R8 + MOVW R8, LR + BL (LR) + BR 0(PC) +*/ + +STEP(2) + /* no kfpinit on 4xx */ + + MOVW $mach0(SB), R(MACH) + ADD $(MACHSIZE-8), R(MACH), R1 + SUB $4, R(MACH), R3 + ADD $4, R1, R4 +clrmach: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrmach + + MOVW R0, R(USER) + MOVW R0, 0(R(MACH)) + + MOVW $edata(SB), R3 + MOVW $end(SB), R4 + ADD $4, R4 + SUB $4, R3 +clrbss: + MOVWU R0, 4(R3) + CMP R3, R4 + BNE clrbss + +STEP(3) + BL main(SB) + BR 0(PC) + +TEXT kernelmmu(SB), $-4 + TLBIA + ISYNC + SYNC + + /* make following TLB entries shared, TID=PID=0 */ + MOVW R0, SPR(PID) + + /* all zones are supervisor, access controlled by TLB */ + MOVW R0, SPR(ZPR) + + /* map various things 1:1 */ + MOVW $tlbtab-KZERO(SB), R4 + MOVW $tlbtabe-KZERO(SB), R5 + SUB R4, R5 + MOVW $(2*4), R6 + DIVW R6, R5 + SUB $4, R4 + MOVW R5, CTR + MOVW R0, R3 +ltlb: + MOVWU 4(R4), R5 /* TLBHI */ + TLBWEHI(5,3) + MOVWU 4(R4), R5 /* TLBLO */ + TLBWELO(5,3) + ADD $1, R3 + BDNZ ltlb + + MOVW LR, R3 + OR $KZERO, R3 + MOVW R3, SPR(SRR0) + MOVW MSR, R4 + OR $(MSR_IR|MSR_DR), R4 + MOVW R4, SPR(SRR1) + + RFI /* resume in kernel mode in caller */ + +TEXT ledoff(SB), $0 + MOVW $PHYSGPIO, R4 + MOVW 0(R4), R3 + RLWNM $0, R3, $~(1<<31), R3 /* LED off */ + MOVW R3, 0(R4) + RETURN + +TEXT splhi(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R4 + SYNC + MOVW R4, MSR + MSRSYNC + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + RETURN + +TEXT splx(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + RLWNMCC $0, R3, $MSR_EE, R5 + BNE splx0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ +splx0: + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT splxpc(SB), $0 + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + RLWNMCC $0, R3, $MSR_EE, R5 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spllo(SB), $0 + MFTB(TBRL, 3) + MOVW R3, spltbl(SB) + MOVW MSR, R3 + OR $MSR_EE, R3, R4 + SYNC + MOVW R4, MSR + MSRSYNC + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN + +TEXT tlbwelo(SB), $-4 + MOVW v+4(FP), R5 + SYNC + TLBWELO(5, 3) + ISYNC + SYNC + RETURN + +TEXT tlbwehi(SB), $-4 + MOVW v+4(FP), R5 + SYNC + TLBWEHI(5, 3) + ISYNC + SYNC + RETURN + +TEXT tlbrehi(SB), $-4 + TLBREHI(3, 3) + RETURN + +TEXT tlbrelo(SB), $-4 + TLBRELO(3, 3) + RETURN + +TEXT tlbsxcc(SB), $-4 + TLBSXCC(0, 3, 3) + BEQ tlbsxcc0 + MOVW $-1, R3 /* not found */ +tlbsxcc0: + RETURN + +/* + * enter with stack set and mapped. + * on return, SB (R2) has been set, and R3 has the Ureg*, + * the MMU has been re-enabled, kernel text and PC are in KSEG, + * R(MACH) has been set, and R0 contains 0. + * + * this can be simplified in the Inferno regime + */ +TEXT saveureg(SB), $-4 +/* + * save state + */ + MOVMW R2, 48(R1) /* r2:r31 */ + MOVW $setSB(SB), R2 + MOVW SPR(SAVER1), R4 + MOVW R4, 44(R1) + MOVW SPR(SAVER0), R5 + MOVW R5, 40(R1) + MOVW CTR, R6 + MOVW R6, 36(R1) + MOVW XER, R4 + MOVW R4, 32(R1) + MOVW CR, R5 + MOVW R5, 28(R1) + MOVW SPR(SAVELR), R6 /* LR */ + MOVW R6, 24(R1) + /* pad at 20(R1) */ + /* old PC(16) and status(12) saved earlier */ + MOVW SPR(SAVEXX), R0 + MOVW R0, 8(R1) /* cause/vector */ + ADD $8, R1, R3 /* Ureg* */ + STWCCC R3, (R1) /* break any pending reservations */ + MOVW $0, R0 /* compiler/linker expect R0 to be zero */ + + MOVW MSR, R5 + OR $(MSR_IR|MSR_DR), R5 /* enable MMU */ + MOVW R5, SPR(SRR1) + MOVW LR, R31 + OR $KZERO, R31 /* return PC in KSEG0 */ + MOVW R31, SPR(SRR0) + SYNC + ISYNC + RFI /* returns to trap handler */ + +TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */ + MOVW n+4(FP), R4 + CMP R4, R0 + BLE icf1 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +icf0: ICBI (R5) + ADD $CACHELINESZ, R5 + BDNZ icf0 +icf1: + ISYNC + RETURN + +/* + * flush to store and invalidate globally + */ +TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dcf1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dcf0: DCBF (R5) + ADD $CACHELINESZ, R5 + BDNZ dcf0 + SYNC +dcf1: + ISYNC + MOVW R5, R3 /* check its operation */ + RETURN + +/* + * invalidate without flush, globally + */ +TEXT dcinval(SB), $-4 /* dcinval(virtaddr, count) */ + SYNC + MOVW n+4(FP), R4 + RLWNM $0, R3, $~(CACHELINESZ-1), R5 + CMP R4, $0 + BLE dci1 + SUB R5, R3 + ADD R3, R4 + ADD $(CACHELINESZ-1), R4 + SRAW $CACHELINELOG, R4 + MOVW R4, CTR +dci0: DCBI (R5) + ADD $CACHELINESZ, R5 + BDNZ dci0 + SYNC + ISYNC +dci1: + RETURN + +TEXT dccci(SB), $-4 + SYNC + DCCCI(0, 3) + ISYNC + RETURN + +TEXT _tas(SB), $0 + SYNC + MOVW R3, R4 + MOVW $0xdeaddead,R5 +tas1: + DCBF (R4) /* fix for 603x bug */ + LWAR (R4), R3 + CMP R3, $0 + BNE tas0 + STWCCC R5, (R4) + BNE tas1 +tas0: + SYNC + ISYNC + RETURN + +TEXT gettbl(SB), $0 + MFTB(TBRL, 3) + RETURN + +TEXT gettbu(SB), $0 + MFTB(TBRU, 3) + RETURN + +TEXT getpvr(SB), $0 + MOVW SPR(PVR), R3 + RETURN + +TEXT getcallerpc(SB), $-4 + MOVW 0(R1), R3 + RETURN + +TEXT getdear(SB), $0 + MOVW SPR(DEAR), R3 + RETURN + +TEXT getdsisr(SB), $0 + MOVW SPR(DSISR), R3 + RETURN + +TEXT getmsr(SB), $0 + MOVW MSR, R3 + RETURN + +TEXT putmsr(SB), $0 + SYNC + MOVW R3, MSR + MSRSYNC + RETURN + +TEXT putevpr(SB), $0 + MOVW R3, SPR(EVPR) + RETURN + +TEXT getesr(SB), $0 + MOVW SPR(ESR), R3 + RETURN + +TEXT putesr(SB), $0 + MOVW R3, SPR(ESR) + RETURN + +TEXT getpit(SB), $0 + MOVW SPR(PIT), R3 + RETURN + +TEXT putpit(SB), $0 + MOVW R3, SPR(PIT) + RETURN + +TEXT gettsr(SB), $0 + MOVW SPR(TSR), R3 + RETURN + +TEXT puttsr(SB), $0 + MOVW R3, SPR(TSR) + RETURN + +TEXT puttcr(SB), $0 + MOVW R3, SPR(TCR) + RETURN + +TEXT eieio(SB), $0 + EIEIO + RETURN + +TEXT gotopc(SB), $0 + MOVW R3, CTR + MOVW LR, R31 /* for trace back */ + BR (CTR) + +TEXT getccr0(SB), $-4 + MOVW SPR(CCR0), R3 + RETURN + +TEXT dcread(SB), $-4 + MOVW 4(FP), R4 + MOVW SPR(CCR0), R5 + RLWNM $0, R5, $~0xFF, R5 + OR R4, R5 + MOVW R5, SPR(CCR0) + SYNC + ISYNC + DCREAD(3, 3) + RETURN + +TEXT getdcr(SB), $-4 + MOVW $_getdcr(SB), R5 + SLW $3, R3 + ADD R3, R5 + MOVW R5, CTR + BR (CTR) + +TEXT putdcr(SB), $-4 + MOVW $_putdcr(SB), R5 + SLW $3, R3 + ADD R3, R5 + MOVW R5, CTR + MOVW 8(R1), R3 + BR (CTR) + +TEXT firmware(SB), $0 + MOVW $(3<<28), R3 + MOVW R3, SPR(DBCR0) /* system reset */ + BR 0(PC) + +/* + * byte swapping of arrays of long and short; + * could possibly be avoided with more changes to drivers + */ +TEXT swabl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $2, R5, R5 + MOVW R5, CTR + SUB $4, R4 + SUB $4, R3 +swabl1: + ADD $4, R3 + MOVWU 4(R4), R7 + MOVWBR R7, (R3) + BDNZ swabl1 + RETURN + +TEXT swabs(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + SRAW $1, R5, R5 + MOVW R5, CTR + SUB $2, R4 + SUB $2, R3 +swabs1: + ADD $2, R3 + MOVHZU 2(R4), R7 + MOVHBR R7, (R3) + BDNZ swabs1 + RETURN + +TEXT legetl(SB), $0 + MOVWBR (R3), R3 + RETURN + +TEXT lesetl(SB), $0 + MOVW v+4(FP), R4 + MOVWBR R4, (R3) + RETURN + +TEXT legets(SB), $0 + MOVHBR (R3), R3 + RETURN + +TEXT lesets(SB), $0 + MOVW v+4(FP), R4 + MOVHBR R4, (R3) + RETURN + +TEXT itlbmiss(SB), $-4 + BR traps + +TEXT dtlbmiss(SB), $-4 + BR traps + +/* + * traps force memory mapping off. + * this code goes to much effort to restore it; + * (a little more effort than needed for the Inferno environment) + */ +TEXT trapvec(SB), $-4 +traps: + MOVW LR, R0 + +pagefault: + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ +trapcomm: + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $(MSR_DR|MSR_ME), R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MOVW SPR(SRR0), R0 /* save SRR0/SRR1 now, since DLTB might be missing stack page */ + MOVW R0, LR + MOVW SPR(SRR1), R0 + RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */ + MOVW R0, 12(R1) /* save status: could take DLTB miss here */ + MOVW LR, R0 + MOVW R0, 16(R1) /* old PC */ + BL saveureg(SB) + BL trap(SB) + BR restoreureg + +/* + * critical trap/interrupt + */ +TEXT trapcvec(SB), $-4 + MOVW LR, R0 + /* for now we'll just restore the state to the conventions that trap expects, since we don't use critical intrs yet */ + MOVW R0, SPR(SAVEXX) + MOVW SPR(SRR2), R0 + MOVW R0, SPR(SRR0) + MOVW SPR(SRR3), R0 + MOVW R0, SPR(SRR1) + BR trapcomm + +TEXT intrvec(SB), $-4 + MOVW LR, R0 + +/* + * map data virtually and make space to save + */ + MOVW R0, SPR(SAVEXX) /* vector */ + MOVW R1, SPR(SAVER1) + SYNC + ISYNC + MOVW MSR, R0 + OR $MSR_DR, R0 /* make data space usable */ + SYNC + MOVW R0, MSR + MSRSYNC + SUB $UREGSPACE, R1 + + MFTB(TBRL, 0) + MOVW R0, intrtbl(SB) + + MOVW SPR(SRR0), R0 + MOVW R0, LR + MOVW SPR(SRR1), R0 + RLWNM $0, R0, $~MSR_WE, R0 /* remove wait state */ + MOVW R0, 12(R1) + MOVW LR, R0 + MOVW R0, 16(R1) + BL saveureg(SB) + + MFTB(TBRL, 5) + MOVW R5, isavetbl(SB) + + BL intr(SB) + +/* + * restore state from Ureg and return from trap/interrupt + */ +restoreureg: + MOVMW 48(R1), R2 /* r2:r31 */ + /* defer R1 */ + MOVW 40(R1), R0 + MOVW R0, SPR(SAVER0) + MOVW 36(R1), R0 + MOVW R0, CTR + MOVW 32(R1), R0 + MOVW R0, XER + MOVW 28(R1), R0 + MOVW R0, CR /* CR */ + MOVW 24(R1), R0 + MOVW R0, SPR(SAVELR) /* LR */ + /* pad, skip */ + MOVW 16(R1), R0 + MOVW R0, SPR(SRR0) /* old PC */ + MOVW 12(R1), R0 + MOVW R0, SPR(SRR1) /* old MSR */ + /* cause, skip */ + MOVW 44(R1), R1 /* old SP */ + MOVW SPR(SAVELR), R0 + MOVW R0, LR + MOVW SPR(SAVER0), R0 + RFI + +TEXT mul64fract(SB), $0 + MOVW a0+8(FP), R9 + MOVW a1+4(FP), R10 + MOVW b0+16(FP), R4 + MOVW b1+12(FP), R5 + + MULLW R10, R5, R13 /* c2 = lo(a1*b1) */ + + MULLW R10, R4, R12 /* c1 = lo(a1*b0) */ + MULHWU R10, R4, R7 /* hi(a1*b0) */ + ADD R7, R13 /* c2 += hi(a1*b0) */ + + MULLW R9, R5, R6 /* lo(a0*b1) */ + MULHWU R9, R5, R7 /* hi(a0*b1) */ + ADDC R6, R12 /* c1 += lo(a0*b1) */ + ADDE R7, R13 /* c2 += hi(a0*b1) + carry */ + + MULHWU R9, R4, R7 /* hi(a0*b0) */ + ADDC R7, R12 /* c1 += hi(a0*b0) */ + ADDE R0, R13 /* c2 += carry */ + + MOVW R12, 4(R3) + MOVW R13, 0(R3) + RETURN + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL spltbl+0(SB), $4 +GLOBL intrtbl+0(SB), $4 +GLOBL isavetbl+0(SB), $4 diff --git a/os/cerf405/main.c b/os/cerf405/main.c new file mode 100644 index 00000000..11e172fc --- /dev/null +++ b/os/cerf405/main.c @@ -0,0 +1,719 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../ip/ip.h" +#include "version.h" + +#define MAXCONF 32 + +extern ulong kerndate; +extern int cflag; +int remotedebug; + +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); +void eepromscan(void); + +static void +options(void) +{ +// nconf = archconfval(confname, confval, sizeof(confname)); +} + +void +doc(char *m) +{ + USED(m); + iprint("%s...\n", m); +} + +void +idoc(char *m) +{ + uartputs(m, strlen(m)); +} + +static void +poolsizeinit(void) +{ + ulong nb; + + nb = conf.npage*BY2PG; + poolsize(mainmem, (nb*main_pool_pcnt)/100, 0); + poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0); + poolsize(imagmem, (nb*image_pool_pcnt)/100, 1); +} + +static void +serialconsole(void) +{ + char *p; + int port, baud; + + p = getconf("console"); + if(p == nil) + p = "0"; + if(p != nil && !remotedebug){ + port = strtol(p, nil, 0); + baud = 115200; + p = getconf("baud"); + if(p != nil){ + baud = strtol(p, nil, 0); + if(baud < 9600) + baud = 9600; + } + uartspecial(port, baud, &kbdq, &printq, kbdcr2nl); + } +} + +void +main(void) +{ + idoc("machinit...\n"); + machinit(); + idoc("options...\n"); + compiledcr(); + options(); +// archinit(); + quotefmtinstall(); + idoc("confinit...\n"); + confinit(); + xinit(); + poolsizeinit(); + poolinit(); + idoc("trapinit...\n"); + trapinit(); + mmuinit(); + ioinit(); + printinit(); + uartinstall(); + serialconsole(); + pcimapinit(); + eepromscan(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); + cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); + + eve = strdup("inferno"); + + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + doc("userinit"); + userinit(); + doc("schedinit"); + schedinit(); +} + +//ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2 + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<<m->machno; + m->cputype = getpvr()>>16; + m->delayloop = 20000; /* initial estimate only; set by clockinit */ + m->speed = 266; /* initial estimate only; set by archinit */ + m->cpuhz = 266333333; + m->vcohz = 799000000; + m->pllhz = 266333333; + m->plbhz = 133166666; + m->opbhz = 66600000; + m->epbhz = 44*MHz; + m->pcihz = 66600000; + m->clockgen = m->cpuhz; /* it's the internal cpu clock */ +} + +void +init0(void) +{ + Osenv *o; + int i; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0"); + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + o = up->env; + o->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(o->pgrp->slash->name); + o->pgrp->slash->name = newcname("/"); + o->pgrp->dot = cclone(o->pgrp->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "power", 0); + snprint(buf, sizeof(buf), "power %s", conffile); + ksetenv("terminal", buf, 0); + poperror(); + } + for(i = 0; i < nconf; i++) + if(confname[i][0] != '*'){ + if(!waserror()){ + ksetenv(confname[i], confval[i], 0); + poperror(); + } + } + + poperror(); + disinit("/osinit.dis"); +} + +void +userinit(void) +{ + Proc *p; + Osenv *o; + + p = newproc(); + o = p->env; + + o->fgrp = newfgrp(nil); + o->pgrp = newpgrp(); + o->egrp = newegrp(); + kstrdup(&o->user, eve); + + strcpy(p->text, "interp"); + + /* + * Kernel Stack + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK; + + ready(p); +} + +Conf conf; + +void +addconf(char *name, char *val) +{ + if(nconf >= MAXCONF) + return; + confname[nconf] = name; + confval[nconf] = val; + nconf++; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +void +confinit(void) +{ + char *p; + int pcnt; + + + if(p = getconf("*kernelpercent")) + pcnt = 100 - strtol(p, 0, 0); + else + pcnt = 0; + + archconfinit(); + + conf.npage = conf.npage0 + conf.npage1; + if(pcnt < 10) + pcnt = 70; + conf.ialloc = (((conf.npage*(100-pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = MAXMACH; + +} + +static void +twinkle(void) +{ + if(m->ticks%MS2TK(1000) == 0) + ((Gpioregs*)PHYSGPIO)->or ^= 1<<31; +} + +void (*archclocktick)(void) = twinkle; + +void +exit(int ispanic) +{ + up = 0; + spllo(); + print("cpu %d exiting\n", m->machno); + + /* Shutdown running devices */ + chandevshutdown(); + + delay(1000); + splhi(); + if(ispanic) + for(;;); + archreboot(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + print("cpu halted\n"); + microdelay(1000); + for(;;) + ; +} + +/* + * kept in case it's needed for PCI/ISA devices + */ +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[KNAMELEN], *p; + int i; + + snprint(cc, sizeof cc, "%s%d", class, ctlrno); + p = getconf(cc); + if(p == nil) + return 0; + + isa->nopt = tokenize(p, isa->opt, NISAOPT); + for(i = 0; i < isa->nopt; i++){ + p = isa->opt[i]; + if(cistrncmp(p, "type=", 5) == 0) + isa->type = p + 5; + else if(cistrncmp(p, "port=", 5) == 0) + isa->port = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "irq=", 4) == 0) + isa->irq = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "mem=", 4) == 0) + isa->mem = strtoul(p+4, &p, 0); + else if(cistrncmp(p, "size=", 5) == 0) + isa->size = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "freq=", 5) == 0) + isa->freq = strtoul(p+5, &p, 0); + else if(cistrncmp(p, "dma=", 4) == 0) + isa->dma = strtoul(p+4, &p, 0); + } + return 1; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc*) +{ +} + +void +idlehands(void) +{ + putmsr(getmsr() | MSR_WE | MSR_EE | MSR_CE); /* MSR_DE as well? */ +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +/* + * some of this is possibly ice-cube specific + */ + +enum { + Cpc0Pllmr0= 0xF0, /* PLL mode register 0 */ + Cpc0Boot= 0xF1, /* clock status */ + Cpc0Pllmr1= 0xF4, /* PLL mode register 1 */ + Cpc0Srr= 0xF6, /* PCI soft reset */ + Cpc0PCI= 0xF9, /* PCI control */ +}; +/* +00f0 = 00011101 +00f1 = 00000025 +00f2 = 00000000 +00f3 = 00000000 +00f4 = 8085523e +00f5 = 00000017 +00f6 = 00000000 +ccdv=1 cbdv=2 opdv=2 epdv=3 mpdv=1 ppdv=2 +fbmul=8 fwdva=5 fwdvb=5 tun=257 m=40 +*/ +void +archconfinit(void) +{ + ulong ktop; + + conf.npage0 = (32*1024*1024)/BY2PG; + conf.base0 = 0; + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop) - conf.base0; + conf.npage0 -= ktop/BY2PG; + conf.base0 += ktop; + + {int i; for(i=0xF0; i<=0xF6; i++){iprint("%.4ux = %.8lux\n", i, getdcr(i));}} + { + int ccdv, cbdv, opdv, epdv, mpdv, ppdv; + int fbmul, fwdva, fwdvb, tun; + ulong mr0, mr1; + + mr0 = getdcr(Cpc0Pllmr0); + ccdv = ((mr0>>20)&3)+1; + cbdv = ((mr0>>16)&3)+1; + opdv = ((mr0>>12)&3)+1; + epdv = ((mr0>>8)&3)+2; + mpdv = ((mr0>>4)&3)+1; + ppdv = (mr0&3)+1; + iprint("ccdv=%d cbdv=%d opdv=%d epdv=%d mpdv=%d ppdv=%d\n", + ccdv, cbdv, opdv, epdv, mpdv, ppdv); + mr1 = getdcr(Cpc0Pllmr1); + fbmul = (mr1>>20) & 0xF; + if(fbmul == 0) + fbmul = 16; + fwdva = (mr1>>16) & 7; + if(fwdva == 0) + fwdva = 8; + fwdvb = (mr1>>12) & 7; + if(fwdvb == 0) + fwdvb = 8; + tun = mr0 & 0x3FF; + iprint("fbmul=%d fwdva=%d fwdvb=%d tun=%d m=%d\n", + fbmul, fwdva, fwdvb, tun, fbmul*fwdva); + } +} + +void +archreboot(void) +{ + putevpr(~0); + firmware(0); + for(;;); +} + +void +clockcheck(void) +{ +} + +void +cpuidprint(void) +{ + iprint("PowerPC 405EP pvr=%8.8lux\n", getpvr()); + /* TO DO */ +} + +#include "../port/flashif.h" + +/* + * for 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) +{ + switch(bank){ + case 0: + f->type = "AMD29F0x0"; /* not right, but will do for now */ + f->addr = (void*)PHYSFLASH; + f->size = FLASHSIZE; + f->width = 2; + return 0; + case 1: + f->type = "nand"; + f->addr = (void*)PHYSNAND; + f->size = 0; /* done by probe */ + f->width = 1; + return 0; + default: + return -1; + } +} + +void +archflashwp(Flash*, int) +{ +} + +#include "../port/netif.h" +#include "etherif.h" + +enum { + /* EMAC-PHY control, tucked away in CPC0 */ + Cpc0Epctl= 0xF3, /* EMAC-PHY ctl */ + + E0Nf= 1<<31, /* Emac0 noise filter enable */ + E1Nf= 1<<30, /* Emac1 noise filter enable */ + E1pr= 1<<7, /* Emac1 packet reject is active high */ + E0pr= 1<<6, /* Emac 0 packet reject is active high */ + E1rm= 1<<5, /* enable Emac 1 packet removal */ + E0rm= 1<<4, /* enable Emac 0 packet removal */ + E1pci= 1<<1, /* Emac 1 clock source is Tx clock output (loopback) */ + E0pci= 1<<0, /* Emac 0 clock source is Tx clock output (loopback) */ +}; + +int +archether(int ctlno, Ether *ether) +{ + char name[KNAMELEN], *p; + int s; + + if(ctlno > 1) + return -1; + ether->type = "EMAC"; + ether->port = ctlno; + if(ctlno != 0) + snprint(name, sizeof(name), "eth%daddr", ctlno); + else + strcpy(name, "ethaddr"); + p = getconf(name); + if(p == 0){ + iprint("ether%d: no %s in EEPROM env\n", ctlno, name); + return -1; + } + parsemac(ether->ea, p, Eaddrlen); + s = splhi(); + putdcr(Cpc0Epctl, getdcr(Cpc0Epctl) | (ctlno?E1Nf:E0Nf)); + splx(s); + return 1; +} + +enum { + /* UART control */ + Cpc0Ucr= 0xF5, /* UART control register */ + + U0Dc= 1<<21, /* UART0 DMA clear enable */ + U0Dt= 1<<20, /* enable UART0 DMA transmit channel */ + U0Dr= 1<<19, /* enable UART0 DMA receive channel */ + U1Dc= 1<<18, /* UART1 DMA clear enable */ + U1Dt= 1<<17, /* enable UART1 DMA transmit channel */ + U1Dr= 1<<16, /* enable UART1 DMA receive channel */ + U1Div_s= 8, /* UART1 serial clock divisor (shift) */ + U1Stop= 1<<8, + U0Div_s= 0, /* UART0 serial clock divisor (shift) */ + U0Stop= 1<<0, + UDiv_m= 0x7F, /* UARTx divisor mask */ +}; + +static ulong +findserialclock(int rate, ulong *freq) +{ + ulong d, b; + ulong serialclock; + int actual, e, beste, bestd; + + *freq = 0; + if(rate == 0) + return 0; + d = ((m->pllhz+m->opbhz-1)/m->opbhz)*2; /* double to allow for later rounding */ + beste = 0; + bestd = -1; + for(; d<=128; d++){ + serialclock = (2*m->pllhz)/d; + b = ((serialclock+8*rate-1)/(rate*16))>>1; + actual = ((serialclock+8*b-1)/(b*16))>>1; + e = rate-actual; + if(e < 0) + e = -e; + if(bestd < 0 || e < beste || e == beste && (bestd&1) && (d&1)==0){ + beste = e; + bestd = d; + } + } + if(bestd > 0) + *freq = m->pllhz/bestd; + return bestd; +} + +/* + * return a value for UARTn's baud rate generator, and + * set a corresponding divsor in the UARTn clock generator + * (between 2 and 128) + */ +ulong +archuartclock(int n, int rate) +{ + int d, s; + ulong m, freq; + + d = findserialclock(rate, &freq); + if(d <= 0) + d = U0Stop; + m = UDiv_m; + if(n){ + d <<= U1Div_s; + m <<= U1Div_s; + } + s = splhi(); + putdcr(Cpc0Ucr, (getdcr(Cpc0Ucr) & ~m) | d); + splx(s); + return freq; +} + +void +archuartdma(int n, int on) +{ + ulong r; + int s; + + r = n? (U1Dc|U1Dt|U1Dr): (U0Dc|U0Dt|U0Dr); + if(on){ + s = splhi(); + putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) | r); + splx(s); + }else{ + s = splhi(); + putdcr(Cpc0Ucr, getdcr(Cpc0Ucr) & ~r); + splx(s); + } +} + +/* + * boot environment in eeprom + */ + +enum { + EEpromHdr= 8, /* bytes */ + Envsize= 0x400, +}; + +static I2Cdev eedev; +static struct { + uchar buf[Envsize]; + int size; +} bootenv; + +static int +eepromitem(uchar *buf, int lim, ulong *off) +{ + int l; + uchar b; + + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = b; + if(l & 0x80){ + if(i2crecv(&eedev, &b, 1, (*off)++) != 1) + return -1; + l = ((l & 0x7F)<<8) | b; + } + if(buf == nil) + return l; + if(l > lim) + l = lim; + return i2crecv(&eedev, buf, l, *off); +} + +void +eepromscan(void) +{ + int n, l; + ulong off; + uchar buf[2]; + char *p, *ep, *v; + + eedev.addr = 0x50; + eedev.salen = 2; + i2csetup(1); + n = i2crecv(&eedev, buf, sizeof(buf), 0); + if(n <= 0){ + iprint("eepromscan: %d\n", n); + return; + } + if(buf[0] != 0xEF || buf[1] != 0xBE){ + iprint("eeprom invalid\n"); + return; + } + bootenv.size = 0; + for(off = EEpromHdr; off < 16384;){ + l = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); /* key */ + if(l <= 0) + break; + off += l; + if(l == 7 && memcmp(bootenv.buf, "PPCBOOT", 7) == 0){ /* intrinsyc key */ + bootenv.size = eepromitem(bootenv.buf, sizeof(bootenv.buf), &off); + break; + } + l = eepromitem(nil, 0, &off); /* skip value */ + if(l < 0) + break; + off += l+2; /* 2 byte crc */ + } + p = (char*)bootenv.buf+4; /* skip crc */ + ep = p+bootenv.size; + for(; p < ep && *p; p += l){ + l = strlen(p)+1; + v = strchr(p, '='); + if(v != nil) + *v++ = 0; + else + v = ""; + addconf(p, v); + if(0) + iprint("%q = %q\n", p, v); + } +} + +ulong +logfsnow(void) +{ + return rtctime(); +} diff --git a/os/cerf405/mal.c b/os/cerf405/mal.c new file mode 100644 index 00000000..8a5018d3 --- /dev/null +++ b/os/cerf405/mal.c @@ -0,0 +1,334 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * on the 405EP the MAL is used only by the Ethernet + * but we keep it separate even so + */ + +enum { + Nrxchan= 2, + Ntxchan= 4, + Maxchan = 4 +}; + +enum { + /* device control registers */ + Cfg= 0x180, /* configuration register */ + Esr= 0x181, /* error status register */ + Ier= 0x182, /* interrupt enable register */ + Txcasr= 0x184, /* transmit channel active set register */ + Txcarr= 0x185, /* transmit channel active reset register */ + Txeobisr= 0x186, /* transmit end of buffer interrupt status register */ + Txdeir= 0x187, /* transmit descriptor error interrupt register */ + Rxcasr= 0x190, /* receive channel active set register */ + Rxcarr= 0x191, /* receive channel active reset register */ + Rxeobisr= 0x192, /* receive channel descriptor error interrupt register */ + Rxdeir= 0x193, /* receive descriptor error interrupt register */ +}; + +#define TXCTPR(n) (0x1A0+(n)) /* transmit channel table pointer register */ +#define RXCTPR(n) (0x1C0+(n)) /* receive channel table pointer register */ +#define RCBS(n) (0x1E0+(n)) /* receive channel buffer size register */ + +enum { + /* configuration */ + CfgSr= 1<<31, /* software reset */ + CfgPlbp0= 0<<22, /* PLB priority (0=lowest) */ + CfgPlbp1= 1<<22, + CfgPlbp2= 2<<22, + CfgPlbp3= 3<<22, + CfgGa= 1<<21, /* guarded */ + CfgOa= 1<<20, /* ordered */ + CfgPlble= 1<<19, /* lock error */ + CfgPlbt_f= 0xF<<15, /* latency timer field */ + CfgPlbt_s= 15, /* latency timer (shift) */ + CfgPlbb= 1<<14, /* burst enable */ + CfgOpbbl= 1<<7, /* OPB locked */ + CfgOepie= 1<<2, /* interrupt on every end of packet */ + CfgLea= 1<<1, /* locked error active */ + CfgSd= 1<<0, /* scroll to next packet on early termination */ + + /* error status */ + EsrEvb= 1<<31, /* error valid bit */ + EsrCid_f= 0x7F<<25, /* field: channel ID causing lock error */ + EsrDe= 1<<20, /* descriptor error */ + EsrOne= 1<<19, /* OPB non-fullword error */ + EsrOte= 1<<18, /* OPB timeout error */ + EsrOse= 1<<17, /* OPB slave error */ + EsrPein= 1<<16, /* PLB bus error indication */ + EsrDei= 1<<4, /* descriptor error interrupt */ + EsrOnei= 1<<3, /* OPB non-fulword error interrupt */ + EsrOtei= 1<<2, /* OPB timeout error interrupt */ + EsrOsei= 1<<1, /* OPB slave error interrupt */ + EsrPbei= 1<<0, /* OPB bus error interrupt */ + +}; + +typedef struct Malmem Malmem; +struct Malmem { + Lock; + BD* base; + BD* limit; + BD* avail; +}; + +static Malmem malmem; + +static Mal* malchans[2][Maxchan]; + +static void +errorintr(Ureg*, void*) +{ + ulong esr, rxdeir, txdeir; + + /* mal de tête */ + esr = getdcr(Esr); + txdeir = getdcr(Txdeir); + rxdeir = getdcr(Rxdeir); + iprint("mal: esr=%8.8lux txdeir=%8.8lux rxdeir=%8.8lux\n", esr, txdeir, rxdeir); + putdcr(Rxdeir, rxdeir); + putdcr(Txdeir, txdeir); + putdcr(Esr, esr); +} + +static void +scanintr(Ureg *ur, ulong ir, Mal *chans[]) +{ + Mal *ml; + int i; + + for(i=0; ir != 0 && i < Maxchan; i++) + if(ir & IBIT(i)){ + ir &= ~IBIT(i); + ml = chans[i]; + if(ml != nil && ml->interrupt != nil) + ml->interrupt(ur, ml->arg); + /* unexpected interrupt otherwise */ + } +} + +static void +txinterrupt(Ureg *ur, void*) +{ + ulong ir; + + ir = getdcr(Txeobisr); + putdcr(Txeobisr, ir); + scanintr(ur, ir, malchans[1]); +} + +static void +rxinterrupt(Ureg *ur, void*) +{ + ulong ir; + + ir = getdcr(Rxeobisr); + putdcr(Rxeobisr, ir); + scanintr(ur, ir, malchans[0]); +} + +void +ioinit(void) +{ + int i; + + putdcr(Txcarr, ~0); + putdcr(Rxcarr, ~0); + + /* reset */ + putdcr(Cfg, CfgSr); + while(getdcr(Cfg) & CfgSr) + ; /* at most one system clock */ + + /* clear these out whilst we're at it */ + for(i=0; i<Nrxchan; i++){ + putdcr(RCBS(i), 0); + putdcr(RXCTPR(i), 0); + } + for(i=0; i<Ntxchan; i++) + putdcr(TXCTPR(i), 0); + + putdcr(Cfg, (0xF<<CfgPlbt_s)|CfgPlbb); /* TO DO: check */ + + /* Ier */ + intrenable(VectorMALSERR, errorintr, nil, BUSUNKNOWN, "malserr"); + intrenable(VectorMALTXDE, errorintr, nil, BUSUNKNOWN, "maltxde"); + intrenable(VectorMALRXDE, errorintr, nil, BUSUNKNOWN, "malrxde"); + intrenable(VectorMALTXEOB, txinterrupt, nil, BUSUNKNOWN, "maltxeob"); + intrenable(VectorMALRXEOB, rxinterrupt, nil, BUSUNKNOWN, "malrxeob"); + putdcr(Ier, EsrDei | EsrOnei | EsrOtei | EsrOsei | EsrPbei); +} + +Mal* +malchannel(int n, int tx, void (*intr)(Ureg*, void*), void *arg) +{ + Mal *ml; + + if((ml = malchans[tx][n]) == nil){ + ml = malloc(sizeof(*m)); + malchans[tx][n] = ml; + } + ml->n = n; + ml->tx = tx; + ml->len = 1; + ml->arg = arg; + ml->interrupt = intr; + return ml; +} + +void +maltxreset(Mal *ml) +{ + putdcr(Txcarr, IBIT(ml->n)); +} + +void +maltxinit(Mal *ml, Ring *r) +{ + putdcr(TXCTPR(ml->n), PADDR(r->tdr)); +} + +void +maltxenable(Mal *ml) +{ + putdcr(Txcasr, getdcr(Txcasr) | IBIT(ml->n)); +} + +void +malrxreset(Mal *ml) +{ + putdcr(Rxcarr, IBIT(ml->n)); +} + +void +malrxinit(Mal *ml, Ring *r, ulong limit) +{ + putdcr(RXCTPR(ml->n), PADDR(r->rdr)); + putdcr(RCBS(ml->n), limit); +} + +void +malrxenable(Mal *ml) +{ + putdcr(Rxcasr, getdcr(Rxcasr) | IBIT(ml->n)); +} + +/* + * initialise receive and transmit buffer rings + * to use both Emacs, or two channels per emac, we'll need + * to allocate all rx descriptors at once, and all tx descriptors at once, + * in a region where all addresses have the same bits 0-12(!); + * see p 20-34. of the MAL chapter. + * + * the ring entries must be aligned on sizeof(BD) boundaries + * rings must be uncached, and buffers must align with cache lines since the cache doesn't snoop + * + * thus, we initialise it once for all, then hand it out as requested. + */ +void +ioringreserve(int nrx, ulong nrb, int ntx, ulong ntb) +{ + ulong nb, nbd; + + lock(&malmem); + if(malmem.base == nil){ + nbd = nrx*nrb + ntx*ntb; + nb = mmumapsize(nbd*sizeof(BD)); + /* + * the data sheet says in the description of buffer tables that they must be on a 4k boundary, + * but the pointer register descriptions say 8 bytes; it seems to be the latter. + */ + malmem.base = mmucacheinhib(xspanalloc(nb, nb, 1<<19), nb); + malmem.limit = malmem.base + nbd; + malmem.avail = malmem.base; + if((PADDR(malmem.base)&~0x7FFFF) != (PADDR(malmem.base)&~0x7FFFF)) + print("mal: trouble ahead?\n"); + } + unlock(&malmem); + if(malmem.base == nil) + panic("ioringreserve"); +} + +BD* +bdalloc(ulong nd) +{ + BD *b; + + lock(&malmem); + b = malmem.avail; + if(b+nd > malmem.limit) + b = nil; + else + malmem.avail = b+nd; + unlock(&malmem); + return b; +} + +int +ioringinit(Ring* r, int nrdre, int ntdre) +{ + int i; + + /* buffers must align with cache lines since the cache doesn't snoop */ + r->nrdre = nrdre; + if(r->rdr == nil) + r->rdr = bdalloc(nrdre); + if(r->rxb == nil) + r->rxb = malloc(nrdre*sizeof(Block*)); + if(r->rdr == nil || r->rxb == nil) + return -1; + for(i = 0; i < nrdre; i++){ + r->rxb[i] = nil; + r->rdr[i].length = 0; + r->rdr[i].addr = 0; + r->rdr[i].status = BDEmpty|BDInt; + } + r->rdr[i-1].status |= BDWrap; + r->rdrx = 0; + + r->ntdre = ntdre; + if(r->tdr == nil) + r->tdr = bdalloc(ntdre); + if(r->txb == nil) + r->txb = malloc(ntdre*sizeof(Block*)); + 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 +dumpmal(void) +{ + int i; + + iprint("Cfg=%8.8lux\n", getdcr(Cfg)); + iprint("Esr=%8.8lux\n", getdcr(Esr)); + iprint("Ier=%8.8lux\n", getdcr(Ier)); + iprint("Txcasr=%8.8lux\n", getdcr(Txcasr)); + iprint("Txcarr=%8.8lux\n", getdcr(Txcarr)); + iprint("Txeobisr=%8.8lux\n", getdcr(Txeobisr)); + iprint("Txdeir=%8.8lux\n", getdcr(Txdeir)); + iprint("Rxcasr=%8.8lux\n", getdcr(Rxcasr)); + iprint("Rxcarr=%8.8lux\n", getdcr(Rxcarr)); + iprint("Rxeobisr=%8.8lux\n", getdcr(Rxeobisr)); + iprint("Rxdeir=%8.8lux\n", getdcr(Rxdeir)); + for(i=0; i<Nrxchan; i++) + iprint("Rxctpr[%d]=%8.8lux Rcbs[%d]=%8.8lux\n", i, getdcr(RXCTPR(i)), i, getdcr(RCBS(i))); + for(i=0;i<Ntxchan; i++) + iprint("Txctpr[%d]=%8.8lux\n", i, getdcr(TXCTPR(i))); +} diff --git a/os/cerf405/mem.h b/os/cerf405/mem.h new file mode 100644 index 00000000..fa0aa82b --- /dev/null +++ b/os/cerf405/mem.h @@ -0,0 +1,147 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per double word */ +#define BY2PG 4096 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define CACHELINELOG 5 +#define CACHELINESZ (1<<CACHELINELOG) +#define CACHESIZE 16384 +#define CACHEWAYSIZE (CACHESIZE/2) /* 2-way set associative */ + +#define MAXMACH 1 /* max # cpus system can run */ +#define MACHSIZE BY2PG + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define MS2TK(t) ((t)/MS2HZ) /* milliseconds to ticks */ +#define MHz 1000000 + +/* + * 4xx MSR bits + */ + +#define MSR_WE 0x40000 /* wait state enable */ +#define MSR_CE 0x20000 /* critical interrupt enable */ +#define MSR_EE 0x08000 /* enable external/decrementer interrupts */ +#define MSR_PR 0x04000 /* =1, user mode */ +#define MSR_ME 0x01000 /* enable machine check exceptions */ +#define MSR_DWE 0x00400 /* debug wait enable */ +#define MSR_DE 0x00200 /* debug interrupts enable */ +#define MSR_IR 0x00020 /* enable instruction address translation */ +#define MSR_DR 0x00010 /* enable data address translation */ + +#define KMSR (MSR_ME) +#define UMSR (MSR_PR|MSR_DE|MSR_CE|MSR_EE|MSR_IR|MSR_DR) + +/* + * Magic registers + */ + +#define MACH 30 /* R30 is m-> */ +#define USER 29 /* R29 is up-> */ + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + */ + +/* TLBHI */ +#define TLBEPN(x) ((x) & ~0x3FF) +#define TLB1K (0<<7) +#define TLB4K (1<<7) +#define TLB16K (2<<7) +#define TLB64K (3<<7) +#define TLB256K (4<<7) +#define TLB1MB (5<<7) +#define TLB4MB (6<<7) +#define TLB16MB (7<<7) +#define TLBVALID (1<<6) +#define TLBLE (1<<5) /* little-endian */ +#define TLBU0 (1<<4) /* user-defined attribute */ + +/* TLBLO */ +#define TLBRPN(x) ((x) & ~0x3FF) +#define TLBEX (1<<9) /* execute enable */ +#define TLBWR (1<<8) /* write enable */ +#define TLBZONE(x) ((x)<<4) +#define TLBW (1<<3) /* write-through */ +#define TLBI (1<<2) /* cache inhibit */ +#define TLBM (1<<1) /* memory coherent */ +#define TLBG (1<<0) /* guarded */ + +/* + * Address spaces + */ + +#define KUSEG 0x00000000 +#define KSEG0 0x20000000 +#define KSEG1 0x60000000 /* uncached alias for KSEG0 */ +#define KSEGM 0xE0000000 /* mask to check which seg */ + +#define KZERO KSEG0 /* base of kernel address space */ +#define KTZERO (KZERO+0x3000) /* first address in kernel text */ +#define KSTACK 8192 /* Size of kernel stack */ + +#define OCMZERO 0x40000000 /* on-chip memory (virtual and physical--see p 5-1) */ + +/* + * Exception codes (trap vectors) + */ +#define CRESET 0x01 +#define CMCHECK 0x02 +#define CDSI 0x03 +#define CISI 0x04 +#define CEI 0x05 +#define CALIGN 0x06 +#define CPROG 0x07 +/* 0x08 (fpu) not used */ +/* 0x09 (dec) not used */ +#define CSYSCALL 0x0C +/* 0x0D (trace) not used */ +/* 0x0E (fpa) not used */ +#define CPIT 0x10 +/* FIT is 0x1010 */ +/* WDT is 0x1020 */ +#define CDMISS 0x11 +#define CIMISS 0x12 +#define CDEBUG 0x20 + +/* + * exception syndrome register + */ +#define ESR_MCI 0x80000000 /* instruction machine check */ +#define ESR_PIL 0x08000000 /* program interrupt: illegal instruction */ +#define ESR_PPR 0x04000000 /* program interrupt: privileged */ +#define ESR_PTR 0x02000000 /* program intterupt: trap with successful compare */ +#define ESR_DST 0x00800000 /* data storage interrupt: store fault */ +#define ESR_DIZ 0x00400000 /* data/instruction storage interrupt: zone fault */ +#define ESR_U0F 0x00008000 /* data storage interrupt: u0 fault */ + +#include "physmem.h" + +/* cerf-cube specific */ +#define PHYSDRAM 0 +#define PHYSFLASH 0xFFE00000 +#define FLASHSIZE 0x200000 +#define PHYSNAND 0x60000000 diff --git a/os/cerf405/mkfile b/os/cerf405/mkfile new file mode 100644 index 00000000..f43b9232 --- /dev/null +++ b/os/cerf405/mkfile @@ -0,0 +1,104 @@ +SYSTARG=Inferno +OBJTYPE=power +<../../mkconfig + +#Configurable parameters + +CONF=cerf #default configuration +CONFLIST=cerf +KZERO=0x20003020 + +SYSTARG=$OSTARG +OBJTYPE=power +INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed +#INSTALLDIR=/$OBJTYPE + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS + +OBJ=\ + l.$O\ + tlb.$O\ + nofp.$O\ + clock.$O\ + compile.$O\ + fpi.$O\ + fpimem.$O\ + fpipower.$O\ + gpio.$O\ + main.$O\ + mal.$O\ + mmu.$O\ + rmap.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $VGAS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} + +HFILES=\ + mem.h\ + physmem.h\ + dat.h\ + fns.h\ + io.h\ + +CFLAGS=-wFV -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +#default:V: i$CONF.sq +default:V: i${CONF}hd + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KZERO -l -R4 $OBJ $CONF.$O $LIBFILES + $KSIZE $target + +i$CONF.sq: i$CONF + sqz -w i$CONF >$target + +out=i${CONF}hd + +i${CONF}hd: i$CONF + mkppcimage i$CONF $out + +install:V: i$CONF # i$CONF.sq + cp i$CONF $INSTALLDIR/i$CONF + #cp i$CONF.sq $INSTALLDIR/i$CONF.sq + +uninstall:V: + rm -f $ROOT/$OBJDIR/bin/i$CONF + rm -f $ROOT/$OBJDIR/bin/i$CONF.sq + +<../port/portmkfile + +../init/$INIT.dis: ../init/$INIT.b + cd ../init; mk $INIT.dis + +clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +faultpower.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +devether.$O $ETHERS: etherif.h ../port/netif.h +archipe.$O: screen.h archipe.h + +#$VGAS: screen.h vga.h +$IP devip.$O: ../ip/ip.h + +devboot.$O: devboot.c + $CC $CFLAGS devboot.c + +devuart.$O: devuart.c + $CC $CFLAGS devuart.c diff --git a/os/cerf405/mmu.c b/os/cerf405/mmu.c new file mode 100644 index 00000000..ae0e0be9 --- /dev/null +++ b/os/cerf405/mmu.c @@ -0,0 +1,122 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +extern ulong tlbtab[], tlbtabe[]; +static int tlbx; /* index of next free entry in TLB */ + +enum +{ + /* on-chip memory dcr */ + Isarc= 0x018, /* instruction-side address range compare */ + Iscntl= 0x019, /* instruction-side control register */ + Isen= 1<<31, /* enable */ + Dsarc= 0x01A, /* data-side address range compare register */ + Dscntl= 0x01B, /* data-side control register */ + Dsen= 1<<31, /* enable */ + Dof= 1<<30, /* must be one (p. 5-7) */ +}; + +void +mmuinit(void) +{ + int i; + + /* + * the l.s initial TLB settings do nearly all that is needed initially. + * clear invalid entries (just for clarity) and record the address + * of the first available + */ + tlbx = -1; + for(i = 0; i < 64; i++) + if((tlbrehi(i) & TLBVALID) == 0){ + if(tlbx < 0) + tlbx = i; + tlbwelo(i, 0); + tlbwehi(i, 0); + } + + iprint("ccr0=%8.8lux\n", getccr0()); + + /* + * set OCM mapping, assuming: + * caches were invalidated earlier; + * and we aren't currently using it + * must also set a tlb entry that validates the virtual address but + * the translation is not used (see p. 5-2) + */ + putdcr(Isarc, OCMZERO); + putdcr(Dsarc, OCMZERO); + putdcr(Iscntl, Isen); + putdcr(Iscntl, Dsen|Dof); + tlbwelo(tlbx, OCMZERO|TLBZONE(0)|TLBWR|TLBEX|TLBI); + tlbwehi(tlbx, OCMZERO|TLB4K|TLBVALID); + tlbx++; +} + +int +segflush(void *a, ulong n) +{ + /* flush dcache then invalidate icache */ + dcflush(a, n); + icflush(a, n); + return 0; +} + +/* + * return required size and alignment to map n bytes in a tlb entry + */ +ulong +mmumapsize(ulong n) +{ + ulong size; + int i; + + size = 1024; + for(i = 0; i < 8 && size < n; i++) + size <<= 2; + return size; +} + +/* + * map a physical addresses at pa to va, with the given attributes. + * the virtual address must not be mapped already. + * if va is nil, map it at pa in virtual space. + */ +void* +kmapphys(void *va, ulong pa, ulong nb, ulong attr, ulong le) +{ + int s, i; + ulong size; + + if(va == nil) + va = (void*)pa; /* simplest is to use a 1-1 map */ + size = 1024; + for(i = 0; i < 8 && size < nb; i++) + size <<= 2; + if(i >= 8) + return nil; + s = splhi(); + tlbwelo(tlbx, pa | TLBZONE(0) | attr); + tlbwehi(tlbx, (ulong)va | (i<<7) | TLBVALID | le); + tlbx++; + splx(s); + return va; +} + +/* + * return an uncached alias for the memory at a + */ +void* +mmucacheinhib(void *a, ulong nb) +{ + ulong p; + + if(a == nil) + return nil; + dcflush(a, nb); + p = PADDR(a); + return kmapphys((void*)(KSEG1|p), p, nb, TLBWR | TLBI | TLBG, 0); +} diff --git a/os/cerf405/nand.c b/os/cerf405/nand.c new file mode 100644 index 00000000..5567574c --- /dev/null +++ b/os/cerf405/nand.c @@ -0,0 +1,96 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "flashif.h" + +/* + * Cerf405-specific NAND flash interface + */ + +#define BE(n) (1<<(31-(n))) /* big-endian bit numbering */ + +enum { + /* GPIO lines */ + Gpio_CLE_o_b= 31, + Gpio_ALE_o_b= 30, + Gpio_NCE_o_b= 24, /* CE#, active low */ + Gpio_RDY_i_b= 23, + + /* bit masks */ + Gpio_CLE_o= BE(Gpio_CLE_o_b), + Gpio_ALE_o= BE(Gpio_ALE_o_b), + Gpio_NCE_o= BE(Gpio_NCE_o_b), + Gpio_RDY_i= BE(Gpio_RDY_i_b), + + Gpio_NAND_o= Gpio_CLE_o | Gpio_ALE_o | Gpio_NCE_o, + + CS_NAND= 1, + Gpio_PerCS1_o= BE(10), +}; + +void +archnand_init(Flash*) +{ + gpioreserve(Gpio_NAND_o | Gpio_RDY_i); + gpioset(Gpio_NAND_o, Gpio_NCE_o); + gpioconfig(Gpio_NAND_o, Gpio_out); + gpioconfig(Gpio_RDY_i, Gpio_in); +} + +void +archnand_claim(Flash*, int claim) +{ + gpioset(Gpio_NCE_o, claim? 0: Gpio_NCE_o); +} + +void +archnand_setCLEandALE(Flash*, int cle, int ale) +{ + ulong v; + + v = 0; + if(cle) + v |= Gpio_CLE_o; + if(ale) + v |= Gpio_ALE_o; + gpioset(Gpio_CLE_o | Gpio_ALE_o, v); +} + +/* + * could unroll the loops + */ + +void +archnand_read(Flash *f, void *buf, int len) +{ + uchar *p, *bp; + + p = f->addr; + if(buf != nil){ + bp = buf; + while(--len >= 0) + *bp++ = *p; + }else{ + int junk; + while(--len >= 0){ + junk = *p; + USED(junk); + } + } +} + +void +archnand_write(Flash *f, void *buf, int len) +{ + uchar *p, *bp; + + p = f->addr; + bp = buf; + while(--len >= 0) + *p = *bp++; +} diff --git a/os/cerf405/nofp.s b/os/cerf405/nofp.s new file mode 100644 index 00000000..f23d49b0 --- /dev/null +++ b/os/cerf405/nofp.s @@ -0,0 +1,31 @@ +/* + * stubs when no floating-point hardware + */ + +TEXT kfpinit(SB), $0 + RETURN + +TEXT getfpscr(SB), $8 + MOVW $0, R3 + RETURN + +TEXT fpsave(SB), $0 + RETURN + +TEXT fprestore(SB), $0 + RETURN + +TEXT clrfptrap(SB), $0 + RETURN + +TEXT fpinit(SB), $0 + RETURN + +TEXT fpoff(SB), $0 + RETURN + +TEXT FPsave(SB), 1, $0 + RETURN + +TEXT FPrestore(SB), 1, $0 + RETURN diff --git a/os/cerf405/pci.c b/os/cerf405/pci.c new file mode 100644 index 00000000..30908dc0 --- /dev/null +++ b/os/cerf405/pci.c @@ -0,0 +1,981 @@ +/* + * PCI support code. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define DBG if(0) pcilog + +typedef struct Pcicfg Pcicfg; +struct Pcicfg { + ulong addr; + union { + ulong l; + uchar b[4]; + ushort s[2]; + } data; +}; + +static Pcicfg* pcicfg; +static ulong* pciack; +static ulong* pcimem; + +struct +{ + char output[16384]; + int ptr; +}PCICONS; + +int +pcilog(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + memmove(PCICONS.output+PCICONS.ptr, buf, n); + PCICONS.ptr += n; + return n; +} + +enum +{ /* configuration mechanism #1 */ + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static QLock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxbno = 7; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +static char* bustypes[] = { +[BusOPB] "OPB", +[BusPLB] "PLB", +[BusPCI] "PCI", +}; + +#pragma varargck type "T" int + +static int +tbdffmt(Fmt* fmt) +{ + char *p; + int l, r, type, tbdf; + + if((p = malloc(READSTR)) == nil) + return fmtstrcpy(fmt, "(tbdfconv)"); + + switch(fmt->r){ + case 'T': + tbdf = va_arg(fmt->args, int); + type = BUSTYPE(tbdf); + if(type < nelem(bustypes)) + l = snprint(p, READSTR, bustypes[type]); + else + l = snprint(p, READSTR, "%d", type); + snprint(p+l, READSTR-l, ".%d.%d.%d", + BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); + break; + + default: + snprint(p, READSTR, "(tbdfconv)"); + break; + } + r = fmtstrcpy(fmt, p); + free(p); + + return r; +} + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static int +pcisizcmp(void *a, void *b) +{ + Pcisiz *aa, *bb; + + aa = a; + bb = b; + return aa->siz - bb->siz; +} + +static ulong +pcimask(ulong v) +{ + ulong m; + + m = BI2BY*sizeof(v); + for(m = 1<<(m-1); m != 0; m >>= 1) { + if(m & v) + break; + } + + m--; + if((v & m) == 0) + return v; + + v |= m; + return v+1; +} + +static void +pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) +{ + Pcidev *p; + int ntb, i, size, rno, hole; + ulong v, mema, ioa, sioa, smema, base, limit; + Pcisiz *table, *tptr, *mtb, *itb; + extern void qsort(void*, long, long, int (*)(void*, void*)); + + ioa = *pioa; + mema = *pmema; + + DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", + wrreg, root->tbdf, mema, ioa); + + ntb = 0; + for(p = root; p != nil; p = p->link) + ntb++; + + ntb *= (PciCIS-PciBAR0)/4; + table = malloc(2*ntb*sizeof(Pcisiz)); + itb = table; + mtb = table+ntb; + + /* + * Build a table of sizes + */ + for(p = root; p != nil; p = p->link) { + if(p->ccrb == 0x06) { + if(p->ccru == 0x04 && p->bridge != nil) { + sioa = ioa; + smema = mema; + pcibusmap(p->bridge, &smema, &sioa, 0); + + hole = pcimask(smema-mema); + if(hole < (1<<20)) + hole = 1<<20; + p->mema.size = hole; + + hole = pcimask(sioa-ioa); + if(hole < (1<<12)) + hole = 1<<12; + + p->ioa.size = hole; + + itb->dev = p; + itb->bar = -1; + itb->siz = p->ioa.size; + itb++; + + mtb->dev = p; + mtb->bar = -1; + mtb->siz = p->mema.size; + mtb++; + } + if((pcicfgr8(p, PciHDT)&0x7f) != 0) + continue; + } + + for(i = 0; i <= 5; i++) { + rno = PciBAR0 + i*4; + v = pcicfgrw32(p->tbdf, rno, 0, 1); + size = pcibarsize(p, rno); + if(size == 0) + continue; + + if(v & 1) { + itb->dev = p; + itb->bar = i; + itb->siz = size; + itb++; + } + else { + mtb->dev = p; + mtb->bar = i; + mtb->siz = size; + mtb++; + } + + p->mem[i].size = size; + } + } + + /* + * Sort both tables IO smallest first, Memory largest + */ + qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); + tptr = table+ntb; + qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); + + /* + * Allocate IO address space on this bus + */ + for(tptr = table; tptr < itb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<12; + ioa = (ioa+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->ioa.bar = ioa; + else { + p->pcr |= IOen; + p->mem[tptr->bar].bar = ioa|1; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); + } + + ioa += tptr->siz; + } + + /* + * Allocate Memory address space on this bus + */ + for(tptr = table+ntb; tptr < mtb; tptr++) { + hole = tptr->siz; + if(tptr->bar == -1) + hole = 1<<20; + mema = (mema+hole-1) & ~(hole-1); + + p = tptr->dev; + if(tptr->bar == -1) + p->mema.bar = mema; + else { + p->pcr |= MEMen; + p->mem[tptr->bar].bar = mema; + if(wrreg) + pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); + } + mema += tptr->siz; + } + + *pmema = mema; + *pioa = ioa; + free(table); + + if(wrreg == 0) + return; + + /* + * Finally set all the bridge addresses & registers + */ + for(p = root; p != nil; p = p->link) { + if(p->bridge == nil) { + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + p->pcr |= MASen; + pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0); + continue; + } + + base = p->ioa.bar; + limit = base+p->ioa.size-1; + v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1); + v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); + pcicfgrw32(p->tbdf, PciBAR3, v, 0); + v = (limit & 0xFFFF0000)|(base>>16); + pcicfgrw32(p->tbdf, 0x30, v, 0); + + base = p->mema.bar; + limit = base+p->mema.size-1; + v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); + pcicfgrw32(p->tbdf, PciBAR4, v, 0); + + /* + * Disable memory prefetch + */ + pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0); + pcicfgrw8(p->tbdf, PciLTR, 64, 0); + + /* + * Enable the bridge + */ + v = 0xFFFF0000 | IOen | MEMen | MASen; + pcicfgrw32(p->tbdf, PciPCR, v, 0); + + sioa = p->ioa.bar; + smema = p->mema.bar; + pcibusmap(p->bridge, &smema, &sioa, 1); + } +} + +static int +pcilscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the + * bus+device+function triplet needed to address it + * and try to read the vendor and device ID. + * If successful, allocate a device struct and + * start to fill it in with some useful information + * from the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = malloc(sizeof(*p)); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccru = pcicfgr8(p, PciCCRu); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccrb) { + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x06: /* bridge device */ + case 0x07: /* simple comm. controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++) { + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccrb != 0x06 || p->ccru != 0x04) + continue; + + /* + * If the secondary or subordinate bus number is not + * initialised try to do what the PCI BIOS should have + * done and fill in the numbers as the tree is descended. + * On the way down the subordinate bus number is set to + * the maximum as it's not known how many buses are behind + * this one; the final value is set on the way back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + + if(sbn == 0 || ubn == 0) { + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are + * off, set the primary, secondary and subordinate + * bus numbers and clear the secondary status before + * attempting to scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pcilscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + + pcicfgw32(p, PciPBN, l); + } + else { + maxubn = ubn; + pcilscan(sbn, &p->bridge); + } + } + + return maxubn; +} + +int +pciscan(int bno, Pcidev **list) +{ + int ubn; + + qlock(&pcicfginitlock); + ubn = pcilscan(bno, list); + qunlock(&pcicfginitlock); + return ubn; +} + +static void +pcicfginit(void) +{ + char *p; + int bno; + Pcidev **list; + ulong mema, ioa; + + qlock(&pcicfginitlock); + if(pcicfgmode != -1) + goto out; + + //pcimmap(); + + pcicfgmode = 1; + pcimaxdno = 31; + +// fmtinstall('T', tbdffmt); + + if(p = getconf("*pcimaxbno")) + pcimaxbno = strtoul(p, 0, 0); + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + + list = &pciroot; + for(bno = 0; bno <= pcimaxbno; bno++) { + int sbno = bno; + bno = pcilscan(bno, list); + + while(*list) + list = &(*list)->link; + + if (sbno == 0) { + Pcidev *pci; + + /* + * If we have found a PCI-to-Cardbus bridge, make sure + * it has no valid mappings anymore. + */ + pci = pciroot; + while (pci) { + if (pci->ccrb == 6 && pci->ccru == 7) { + ushort bcr; + + /* reset the cardbus */ + bcr = pcicfgr16(pci, PciBCR); + pcicfgw16(pci, PciBCR, 0x40 | bcr); + delay(50); + } + pci = pci->link; + } + } + } + + if(pciroot == nil) + goto out; + + /* + * Work out how big the top bus is + */ + mema = 0; + ioa = 0; + pcibusmap(pciroot, &mema, &ioa, 0); + + DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", + mema, pcimask(mema), ioa); + + /* + * Align the windows and map it + */ + ioa = 0x1000; + mema = 0; + + pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); + + pcibusmap(pciroot, &mema, &ioa, 1); + DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); + +out: + qunlock(&pcicfginitlock); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = rno & 0x03; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.b[o]; /* TO DO: perhaps o^3 */ + else + pcicfg->data.b[o] = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + o = (rno >> 1) & 1; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.s[o]; + else + pcicfg->data.s[o] = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int x; + + if(pcicfgmode == -1) + pcicfginit(); + + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + eieio(); + if(read) + x = pcicfg->data.l; + else + pcicfg->data.l = data; + eieio(); + pcicfg->addr = 0; + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +uchar +pciipin(Pcidev *pci, uchar pin) +{ + if (pci == nil) + pci = pcilist; + + while (pci) { + uchar intl; + + if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) + return pci->intl; + + if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) + return intl; + + pci = pci->list; + } + return 0; +} + +static void +pcilhinv(Pcidev* p) +{ + int i; + Pcidev *t; + + if(p == nil) { + putstrn(PCICONS.output, PCICONS.ptr); + p = pciroot; + print("bus dev type vid did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", + BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), + t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + if(t->ioa.bar || t->ioa.size) + print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); + if(t->mema.bar || t->mema.size) + print("mema:%.8lux %d ", t->mema.bar, t->mema.size); + if(t->bridge) + print("->%d", BUSBNO(t->bridge->tbdf)); + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcilhinv(p->bridge); + p = p->link; + } +} + +void +pcihinv(Pcidev* p) +{ + if(pcicfgmode == -1) + pcicfginit(); + qlock(&pcicfginitlock); + pcilhinv(p); + qunlock(&pcicfginitlock); +} + +void +pcishutdown(void) +{ + Pcidev *p; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + /* don't mess with the bridges */ + if(p->ccrb == 0x06) + continue; + pciclrbme(p); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= MASen; + pcicfgw16(p, PciPCR, pcr); +} + +void +pciclrbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr &= ~MASen; + pcicfgw16(p, PciPCR, pcr); +} + +/* + * 405EP specific + */ + +typedef struct Pciplbregs Pciplbregs; +struct Pciplbregs { + struct { + ulong la; + ulong ma; + ulong pcila; + ulong pciha; + } pmm[3]; + struct { + ulong ms; + ulong la; + } ptm[2]; +}; + +enum { /* mask/attribute registers */ + Pre= 1<<1, /* enable prefetching (PMM only) */ + Ena= 1<<0, /* enable PLB to PCI map (PMM); enable PCI to PLB (PTM) */ +}; + +enum { /* DCR */ + Cpc0Srr= 0xF6, /* PCI soft reset */ + Rpci= 1<<18, /* reset PCI bridge */ + Cpc0PCI= 0xF9, /* PCI control */ + Spe= 1<<4, /* PCIINT/WE select */ + Hostcfgen= 1<<3, /* enable host config */ + Arben= 1<<1, /* enable internal arbiter */ +}; + +enum { + /* PciPCR */ + Se= 1<<8, /* enable PCISErr# when parity error detected as target */ + Per= 1<<6, /* enable PERR# on parity errors */ + Me= 1<<2, /* enable bridge-to-master cycles */ + Ma= 1<<1, /* enable memory access (when PCI memory target) */ + + /* PciPSR */ + Depe= 1<<15, /* parity error */ + Sse= 1<<14, /* signalled system error */ + Rma= 1<<13, /* received master abort */ + Rta= 1<<12, /* received target abort */ + Sta= 1<<11, /* signalled target abort */ + Dpe= 1<<8, /* data parity error */ + F66C= 1<<5, /* 66MHz capable */ + + /* PciBARn */ + Pf= 1<<3, /* prefetchable */ +}; + +/* +pmm 0: la=00000080 ma=010000c0 pcila=00000080 pciha=00000000 +pmm 1: la=00000000 ma=00000000 pcila=00000000 pciha=00000000 +pmm 2: la=00000000 ma=00000000 pcila=00000000 pciha=00000000 +ptm 0: ms=01000080 la=00000000 +ptm 1: ms=00000000 la=00000000 +*/ + +static void +pcidumpdev(ulong tbdf) +{ + int i; + + for(i=0; i<0x68; i+=4) + iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1)); +} + +void +pcimapinit(void) +{ + Pciplbregs *pm; + int i, bridge, psr; + + pm = kmapphys(nil, PHYSPCIBCFG, sizeof(Pciplbregs), TLBWR | TLBI | TLBG, TLBLE); + if(0){ + /* see what the bootstrap left */ + iprint("PCI:\n"); + for(i=0; i<3; i++) + iprint("pmm %d: la=%.8lux ma=%.8lux pcila=%.8lux pciha=%.8lux\n", + i, pm->pmm[i].la, pm->pmm[i].ma, pm->pmm[i].pcila, pm->pmm[i].pciha); + for(i=0; i<2; i++) + iprint("ptm %d: ms=%.8lux la=%.8lux\n", + i, pm->ptm[i].ms, pm->ptm[i].la); + } + putdcr(Cpc0Srr, Rpci); + delay(1); + putdcr(Cpc0Srr, 0); + + kmapphys((void*)PHYSPCIIO0, PHYSPCIIO0, 64*1024, TLBWR | TLBI | TLBG, TLBLE); + pcicfg = kmapphys(nil, PHYSPCIADDR, sizeof(Pcicfg), TLBWR | TLBI | TLBG, TLBLE); + pciack = kmapphys(nil, PHYSPCIACK, sizeof(ulong), TLBWR | TLBI | TLBG, TLBLE); + eieio(); + + /* + * PLB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb + * are mapped to PCI memory at 0. + */ + pm->pmm[0].ma = 0; /* disable during update */ + eieio(); + pm->pmm[0].la = PHYSPCIBRIDGE; + pm->pmm[0].pcila = 0; + pm->pmm[0].pciha = 0; + eieio(); + pm->pmm[0].ma = 0xFC000000 | Ena; /* enable prefetch? */ + for(i=1; i<3; i++) + pm->pmm[i].ma = 0; /* disable the others */ + eieio(); + pcimem = kmapphys((void*)PHYSPCIBRIDGE, PHYSPCIBRIDGE, 0x4000000, TLBWR | TLBI | TLBG, TLBLE); + + /* + * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+1Gb + * are mapped to physical memory. + */ + pm->ptm[0].ms = 0; + eieio(); + pm->ptm[0].la = PCIWINDOW; + eieio(); + pm->ptm[0].ms = 0xC0000000 | Ena; /* always enabled by hardware */ + pm->ptm[1].ms = 0; + eieio(); + + iprint("cpc0pci=%.8lux\n", getdcr(Cpc0PCI)); + + /* + * the 405ep's pci bridge contains IBM vendor & devid, but + * ppcboot rather annoyingly clears them; put them back. + */ + pcicfgmode = 1; + pcimaxdno = 31; + + bridge = MKBUS(BusPCI, 0, 0, 0); + pcicfgrw16(bridge, PciPCR, Me | Ma, 0); + pcicfgrw16(bridge, PciVID, 0x1014, 0); + pcicfgrw16(bridge, PciDID, 0x0156, 0); + pcicfgrw8(bridge, PciCCRb, 0x06, 0); /* bridge */ + pcicfgrw32(bridge, PciBAR1, Pf, 0); + psr = pcicfgrw16(bridge, PciPSR, 0, 1); + if(m->pcihz >= 66000000) + psr |= F66C; /* 66 MHz */ + pcicfgrw16(bridge, PciPSR, psr, 0); /* reset error status */ + if(0){ + iprint("pci bridge':\n"); + pcidumpdev(bridge); + } + + pcicfgmode = -1; +} diff --git a/os/cerf405/physmem.h b/os/cerf405/physmem.h new file mode 100644 index 00000000..9fde7920 --- /dev/null +++ b/os/cerf405/physmem.h @@ -0,0 +1,22 @@ +/* + * Memory-mapped IO + */ + +#define PHYSPCIBRIDGE 0x80000000 +#define PHYSMMIO 0xEF600000 +#define MMIO(i) (PHYSMMIO+(i)*0x100) +#define PHYSGPT MMIO(0) +#define PHYSUART0 MMIO(3) +#define PHYSUART1 MMIO(4) +#define PHYSIIC MMIO(5) +#define PHYSOPB MMIO(6) +#define PHYSGPIO MMIO(7) +#define PHYSEMAC0 MMIO(8) +#define PHYSEMAC1 MMIO(9) + +#define PHYSPCIIO0 0xE8000000 /* for 64M */ +#define PHYSPCIMEM 0x80000000 +#define PHYSPCIADDR 0xEEC00000 /* for 8 bytes */ +#define PHYSPCIDATA 0xEEC00004 +#define PHYSPCIACK 0xEED00000 /* interrupt acknowledge */ +#define PHYSPCIBCFG 0xEF400000 /* bridge configuration registers */ diff --git a/os/cerf405/powerbreak.c b/os/cerf405/powerbreak.c new file mode 100644 index 00000000..fc5970ef --- /dev/null +++ b/os/cerf405/powerbreak.c @@ -0,0 +1,123 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ureg.h" + +extern int (*breakhandler)(Ureg *ur, Proc*); /* trap.c */ +extern Instr BREAK; /* trap.c */ +extern void portbreakinit(void); + +#define getop(i) ((i>>26)&0x3F) +#define getxo(i) ((i>>1)&0x3FF) +#define getbobi(i) bo = (i>>21)&0x1f; bi = (i>>16)&0x1f; xx = (i>>11)&0x1f; + +void +machbreakinit(void) +{ + portbreakinit(); + breakhandler = breakhit; +} + +Instr +machinstr(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + return *(Instr*)addr; +} + +void +machbreakset(ulong addr) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = BREAK; + segflush((void*)addr, sizeof(Instr)); +} + +void +machbreakclear(ulong addr, Instr i) +{ + if (addr < KTZERO) + error(Ebadarg); + *(Instr*)addr = i; + segflush((void*)addr, sizeof(Instr)); +} + +static int +condok(Ureg *ur, ulong ir, int ctrok) +{ + int bo, bi, xx; + ulong ctrval; + + ctrval = ur->ctr; + getbobi(ir); + if(xx) + return 0; /* illegal */ + if((bo & 0x4) == 0) { + if(!ctrok) + return 0; /* illegal */ + ctrval--; + } + if(bo & 0x4 || (ctrval!=0)^((bo>>1)&1)) { + if(bo & 0x10 || (((ur->cr & (1L<<(31-bi))!=0)==((bo>>3)&1)))) + return 1; + } + return 0; +} + +/* + * Return the address of the instruction that will be executed after the + * instruction at ur->pc, accounting for current branch conditions. + */ +ulong +machnextaddr(Ureg *ur) +{ + long imm; + ulong ir; + + ir = *(ulong*)ur->pc; + switch(getop(ir)) { + case 18: /* branch */ + imm = ir & 0x03FFFFFC; + if(ir & 0x02000000) + imm |= 0xFC000000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + + case 16: /* conditional branch */ + if(condok(ur, ir&0xFFFF0000, 1)){ + imm = ir & 0xFFFC; + if(ir & 0x08000) + imm |= 0xFFFF0000; /* sign extended */ + if((ir & 2) == 0) /* relative address */ + return ur->pc + imm; + return imm; + } + break; + + case 19: /* conditional branch to register */ + switch(getxo(ir)){ + case 528: /* bcctr */ + if(condok(ur, ir, 0)) + return ur->ctr & ~3; + break; + case 16: /* bclr */ + if(condok(ur, ir, 1)) + return ur->lr & ~3; + break; + } + break; + } + return ur->pc+4; /* next instruction */ +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KTZERO; +} diff --git a/os/cerf405/rmap.c b/os/cerf405/rmap.c new file mode 100644 index 00000000..f521c24d --- /dev/null +++ b/os/cerf405/rmap.c @@ -0,0 +1,106 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +void +mapinit(RMap *rmap, Map *map, int size) +{ + lock(rmap); + rmap->map = map; + rmap->mapend = map+(size/sizeof(Map)); + unlock(rmap); +} + +void +mapfree(RMap* rmap, ulong addr, int size) +{ + Map *mp; + ulong t; + + if(size <= 0) + return; + + lock(rmap); + for(mp = rmap->map; mp->addr <= addr && mp->size; mp++) + ; + + if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){ + (mp-1)->size += size; + if(addr+size == mp->addr){ + (mp-1)->size += mp->size; + while(mp->size){ + mp++; + (mp-1)->addr = mp->addr; + (mp-1)->size = mp->size; + } + } + } + else{ + if(addr+size == mp->addr && mp->size){ + mp->addr -= size; + mp->size += size; + } + else do{ + if(mp >= rmap->mapend){ + print("mapfree: %s: losing 0x%luX, %d\n", + rmap->name, addr, size); + break; + } + t = mp->addr; + mp->addr = addr; + addr = t; + t = mp->size; + mp->size = size; + mp++; + }while(size = t); + } + unlock(rmap); +} + +ulong +rmapalloc(RMap* rmap, ulong addr, int size, int align) +{ + Map *mp; + ulong maddr, oaddr; + + lock(rmap); + for(mp = rmap->map; mp->size; mp++){ + maddr = mp->addr; + + if(addr){ + if(maddr > addr) + break; + if(maddr+mp->size < addr) + continue; + if(addr+size > maddr+mp->size) + break; + maddr = addr; + } + + if(align > 0) + maddr = ((maddr+align-1)/align)*align; + if(mp->addr+mp->size-maddr < size) + continue; + + oaddr = mp->addr; + mp->addr = maddr+size; + mp->size -= maddr-oaddr+size; + if(mp->size == 0){ + do{ + mp++; + (mp-1)->addr = mp->addr; + }while((mp-1)->size = mp->size); + } + + unlock(rmap); + if(oaddr != maddr) + mapfree(rmap, oaddr, maddr-oaddr); + + return maddr; + } + unlock(rmap); + + return 0; +} diff --git a/os/cerf405/tlb.s b/os/cerf405/tlb.s new file mode 100644 index 00000000..98abd3fd --- /dev/null +++ b/os/cerf405/tlb.s @@ -0,0 +1,30 @@ +#include "mem.h" +#define MB (1024*1024) + +/* + * TLB prototype entries, loaded once-for-all at startup, + * remaining unchanged thereafter. + * Limit the table size to ensure it fits in small TLBs. + */ +#define TLBE(hi, lo) WORD $(hi); WORD $(lo) + +TEXT tlbtab(SB), $-4 + + /* tlbhi tlblo */ + + /* DRAM, 32MB */ + TLBE(KZERO|PHYSDRAM|TLB16MB|TLBVALID, PHYSDRAM|TLBZONE(0)|TLBWR|TLBEX) + TLBE(KZERO|(PHYSDRAM+16*MB)|TLB16MB|TLBVALID, (PHYSDRAM+16*MB)|TLBZONE(0)|TLBWR|TLBEX) + + /* memory-mapped IO, 4K */ + TLBE(PHYSMMIO|TLB4K|TLBVALID, PHYSMMIO|TLBZONE(0)|TLBWR|TLBI|TLBG) + + /* NAND flash access (4K?) */ + TLBE(PHYSNAND|TLB4K|TLBVALID, PHYSNAND|TLBZONE(0)|TLBWR|TLBI|TLBG) + + /* NOR flash, 2MB */ + TLBE(PHYSFLASH|TLB1MB|TLBVALID, PHYSFLASH|TLBZONE(0)|TLBWR|TLBEX) + TLBE((PHYSFLASH+MB)|TLB1MB|TLBVALID, (PHYSFLASH+MB)|TLBZONE(0)|TLBWR|TLBEX) + +TEXT tlbtabe(SB), $-4 + RETURN diff --git a/os/cerf405/trap.c b/os/cerf405/trap.c new file mode 100644 index 00000000..846cc097 --- /dev/null +++ b/os/cerf405/trap.c @@ -0,0 +1,581 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + +enum +{ + Maxhandler= MaxVector /* max number of interrupt handlers, assuming none shared */ +}; + +enum { + /* UIC registers (p. 10-4) */ + Usr = 0xC0, /* status */ + Uer = 0xC2, /* enable */ + Ucr = 0xC3, /* critical interrupts */ + Upr = 0xC4, /* priority */ + Utr = 0xC5, /* trigger */ + Umsr = 0xC6, /* masked status */ + Uvr = 0xC7, /* vector */ + Uvcr = 0xC8, /* vector configuration */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + char name[KNAMELEN]; + Handler *next; + int edge; + ulong nintr; + ulong ticks; + int maxtick; +}; + +static Lock veclock; + +static struct +{ + Handler *ivec[MaxVector]; + Handler h[Maxhandler]; + int free; + Handler* freelist; +} halloc; + +Instr BREAK = 0x7fe00008; +int (*breakhandler)(Ureg*, Proc*); + +void kernfault(Ureg*, int); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "i/o controller interface error", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", + "reserved 15", + "reserved 16", + "reserved 17", + "reserved 18", + "reserved 19", + "reserved 1A", + "reserved 1B", + "data breakpoint", + "instruction breakpoint", + "peripheral breakpoint", + "development port", + /* the following are made up on a program exception */ + "floating point exception", /* 20: FPEXC */ + "illegal instruction", /* 21 */ + "privileged instruction", /* 22 */ + "trap", /* 23 */ + "illegal operation", /* 24 */ + "breakpoint", /* 25 */ +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = (ulong*)KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far: running from ROM */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|((ulong)r&~KSEGM)|2; /* ba */ + dcflush(vp, sizeof(*vp)); +} + +static void +faultpower(Ureg *ur, ulong addr, int read) +{ + char buf[ERRMAX]; + + if(up == nil){ + dumpregs(ur); + panic("kernel fault"); + } + + up->dbgreg = ur; /* For remote ACID */ + spllo(); + + sprint(buf, "trap: fault %s pc=0x%lux addr=0x%lux", + read? "read": "write", ur->pc, addr); + if(up->type == Interp){ + if(addr == ~0) + disfault(ur, "dereference of nil"); + disfault(ur, buf); + } + dumpregs(ur); + panic("fault: %s\n", buf); +} + +void +trap(Ureg *ur) +{ + int ecode, s; + ulong w, esr; + char buf[ERRMAX]; + + ecode = ur->cause >> 8; + if(ecode < 0 || ecode >= 0x1F) + ecode = 0x1F; + esr = getesr(); + putesr(0); + switch(ecode){ + case CPIT: + clockintr(ur); + preemption(1); + break; + + case CMCHECK: + if(esr & ESR_MCI){ + faultpower(ur, ur->pc, 1); + break; + } + /* FALL THROUGH */ + case CDSI: + faultpower(ur, getdear(), !(esr&ESR_DST)); + break; + + case CDMISS: + faultpower(ur, getdear(), 1); + break; + + case CISI: + case CIMISS: + faultpower(ur, ur->pc, 1); + break; + + case CPROG: + if(esr & ESR_PIL){ + if(up == nil) + goto Default; + if((ulong)(ur+1) != ur->r1) + panic("fp emu stack"); + spllo(); + if(waserror()){ + if(up->type == Interp) + disfault(ur, up->env->errstr); + panic("%s", up->env->errstr); + } + if(fpipower(ur) == 0){ + splhi(); + poperror(); + print("pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); /* temporary */ + goto Default; + } + poperror(); + break; + } + /* TO DO: 4xx variant for the following */ + if(ur->status & (1<<19)) { + ecode = 0x20; + w = ur->pc; + if(ur->status & (1<<16)) + w += 4; + if(*(ulong*)w == 0x7fe00008){ /* tw 31,0,0 */ + if(breakhandler){ + s = (*breakhandler)(ur, up); + if(s == BrkSched){ + if(up){ + up->preempted = 0; + sched(); + splhi(); + } + }else if(s == BrkNoSched){ + if(up){ + up->preempted = 1; /* stop it being preempted until next instruction */ + up->dbgreg = 0; + } + } + break; + } + ecode = 0x1D; /* breakpoint */ + } + } + if(ur->status & (1<<18)) + ecode = 0x21; + if(ur->status & (1<<17)) + ecode = 0x22; + /* FALL THROUGH */ + + Default: + default: + if(up && up->type == Interp) { + spllo(); + snprint(buf, sizeof buf, "sys: trap: %s pc=0x%lux", excname[ecode], ur->pc); + error(buf); + break; + } + print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + splhi(); +} + +void +trapinit(void) +{ + int i; + + putdcr(Uer, 0); /* none enabled */ + putdcr(Ucr, 0); /* none are critical by default */ + putdcr(Upr, ~IBIT(VectorPCISERR)); /* default is active high (except PCISERR) */ + putdcr(Utr, 0); /* all are level sensitive by default */ + putdcr(Usr, getdcr(Usr)); /* reset interrupts */ + putdcr(Uvcr, 0); /* 31 is highest priority */ + eieio(); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x3000; i += 0x100) + sethvec(i, trapvec); + + /* on the 405, several traps are critical interrupts with different SRRs */ + sethvec(0x0100, trapcvec); + sethvec(0x0200, trapcvec); + + sethvec(CEI<<8, intrvec); + /* TO DO: FIT and WDT */ + //sethvec2(CIMISS<<8, itlbmiss); + //sethvec2(CDMISS<<8, dtlbmiss); + + putevpr(0); /* use our vectors */ +} + +void +intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h; + ulong w, f, bit; + + f = v; + v &= IRQmask; + bit = IBIT(v); + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v); + ilock(&veclock); + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->arg = arg; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* + * enable corresponding interrupt in UIC + */ + w = getdcr(Ucr); + if(f & IRQcritical) + putdcr(Ucr, w | bit); + else + putdcr(Ucr, w & ~bit); + if(v >= VectorIRQ){ + /* (only) these have got choice of polarity, etc. */ + w = getdcr(Utr); + h->edge = (f & IRQedge) != 0; + if(h->edge) + putdcr(Utr, w | bit); + else + putdcr(Utr, w & ~bit); + w = getdcr(Upr); + if(f & IRQactivelow) + putdcr(Upr, w | bit); + else + putdcr(Upr, w & ~bit); + } + eieio(); + putdcr(Uer, getdcr(Uer) | bit); + eieio(); + iunlock(&veclock); +} + +static void +irqdisable(int v) +{ + putdcr(Uer, getdcr(Uer) & ~IBIT(v)); +} + +void +intrdisable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h, **hp; + + v &= IRQmask; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrdisable(%d)", v); + ilock(&veclock); + for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next) + if(h->r == r && h->arg == arg && strcmp(h->name, name) == 0){ + *hp = h->next; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil) + irqdisable(v); + iunlock(&veclock); +} + +/* + * called directly by l.s:/intrvec. on a multiprocessor we'd need to lock veclock. + */ +void +intr(Ureg *ur) +{ + ulong msr, b; + int v; + Handler *h; + long t0; + Proc *oup; + + oup = up; + up = nil; /* no process at interrupt level */ + while((msr = getdcr(Umsr)) != 0){ + for(v=0; msr!=0 && v<32; v++){ + b = IBIT(v); + if((msr & b) == 0) + continue; + msr &= ~b; + ur->cause = (CEI<<8) | v; + h = halloc.ivec[v]; + if(h == nil){ + iprint("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + irqdisable(v); + continue; + } + + /* + * call the interrupt handlers + */ + do { + if(h->edge) + putdcr(Usr, b); + h->nintr++; + t0 = getpit(); + (*h->r)(ur, h->arg); + t0 -= getpit(); + h->ticks += t0; + if(h->maxtick < t0) + h->maxtick = t0; + if(!h->edge) + putdcr(Usr, b); + h = h->next; + } while(h != nil); + } + } + up = oup; + preemption(0); +} + +int +intrstats(char *buf, int bsize) +{ + Handler *h; + int i, n; + + n = 0; + for(i=0; i<nelem(halloc.ivec) && n < bsize; i++) + if((h = halloc.ivec[i]) != nil && h->nintr) + n += snprint(buf+n, bsize-n, "%3d %lud %lud %ud\n", i, h->nintr, h->ticks, h->maxtick); + return n; +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<<i)) + s = fpcause[i]; + if(s == 0) + return "no floating point exception"; + sprint(buf, "%s fppc=0x%lux", s, fppc); + return buf; +} + +#define KERNPC(x) (KTZERO<(ulong)(x)&&(ulong)(x)<(ulong)etext) + +void +kernfault(Ureg *ur, int code) +{ + Label l; + + print("panic: kfault %s dear=0x%lux esr=0x%8.8lux\n", excname[code], getdear(), getesr()); + print("u=0x%lux status=0x%lux pc=0x%lux sp=0x%lux\n", + up, ur->status, ur->pc, ur->sp); + dumpregs(ur); + l.sp = ur->sp; + l.pc = ur->pc; + dumpstack(); + setpri(PriBackground); /* Let the debugger in */ + for(;;) + sched(); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%lux=%lux, ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->usp < (ulong)up->kstack || + ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + l = &ur->cause; + for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2) + print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]); +} + +static void +linkproc(void) +{ + spllo(); + (*up->kpfun)(up->arg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->arg = arg; +} + +void +setpanic(void) +{ + consoleprint = 1; +iprint("panic\n"); +} + +void +dumplongs(char*, ulong*, int) +{ +} diff --git a/os/cerf405/uart.c b/os/cerf405/uart.c new file mode 100644 index 00000000..3d90f5ad --- /dev/null +++ b/os/cerf405/uart.c @@ -0,0 +1,139 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +/* + * uart for initial port + */ + +typedef struct Uartregs Uartregs; +struct Uartregs { + uchar rbr; +#define thr rbr +#define dll rbr + uchar ier; +#define dlm ier + uchar fcr; +#define iir fcr + uchar lcr; + uchar mcr; + uchar lsr; + uchar msr; + uchar scr; +}; + +#define UARTREGS(n) ((Uartregs*)(PHYSUART0+(n)*0x100)) + +enum { + /* ier */ + Edssi= 1<<3, + Elsi= 1<<2, + Etbei= 1<<1, + Erbfi= 1<<0, + + /* iir */ + Fci0= 0<<4, + Fci3= 3<<4, + Ipl= 7<<1, + Ip= 1<<0, + + /* fcr */ + Rftl1= 0<<6, /* receiver trigger level 1, 16, 32, 56 */ + Rftl16= 1<<6, + Rftl32= 2<<6, + Rftl56= 3<<6, + Dms= 1<<3, /* =0, single transfer; =1, multiple transfers */ + Tfr= 1<<2, /* transmitter fifo reset */ + Rfr= 1<<1, /* receiver fifo reset */ + Fifoe= 1<<0, /* =0, disable fifos; =1, enable fifos */ + + /* lcr */ + Dlab= 1<<7, /* =0, normal; =1, dll/dlm visible */ + Sb= 1<<6, /* set break */ + Sp= 1<<5, /* =1, enable sticky parity */ + Eps= 1<<4, /* =1, generate even parity */ + Pen= 1<<3, /* =1, enable parity checking */ + Sbs= 1<<2, /* =0, 1 stop bit; =1, 1.5 or 2 stop bits (see Wls) */ + Wls= 3<<0, /* set to nbit-5 for nbit characters */ + + /* mcr */ + Afc= 1<<5, /* =1, auto flow control enabled */ + Loop= 1<<4, /* =1, loop back mode enabled */ + Out2= 1<<3, /* =0, OUT2# active */ + Out1= 1<<2, /* =0, OUT1# active */ + Rts= 1<<1, /* =0, RTS# inactive (1); =1, RTS# active (0) */ + Dtr= 1<<0, /* =0, DTS# inactive; =1, DTS# active */ + + /* lsr */ + Rfe= 1<<7, /* error instances in fifo */ + Temt= 1<<6, /* transmitter empty */ + Thre= 1<<5, /* transmitter holding register/fifo empty */ + Be= 1<<4, /* break (interrupt) */ + Fe= 1<<3, /* framing error */ + Pe= 1<<2, /* parity error */ + Oe= 1<<1, /* overrun error */ + Dr= 1<<0, /* receiver data ready */ + + /* msr */ + Dcd= 1<<7, /* follows Out2 */ + Ri= 1<<6, /* follows Out1 */ + Dsr= 1<<5, /* follows Dtr */ + Cts= 1<<4, /* follows Rts */ + Ddcd= 1<<3, /* Dcd input changed */ + Teri= 1<<2, /* Ri changed from 0 to 1 */ + Ddsr= 1<<1, /* Dsr input changed */ + Dcts= 1<<0, /* Cts input changed */ +}; + +void (*serwrite)(char*, int) = uartputs; + +void +uartinstall(void) +{ +} + +void +uartspecial(int, int, Queue**, Queue**, int (*)(Queue*, int)) +{ +} + +void +uartputc(int c) +{ + Uartregs *r; + + if(c == 0) + return; + r = UARTREGS(0); + while((r->lsr & Thre) == 0) + {} + r->thr = c; + if(c == '\n') + while((r->lsr & Thre) == 0) /* flush xmit fifo */ + {} +} + +void +uartputs(char *data, int len) +{ + int s; + +// if(!uartspcl && !redirectconsole) +// return; + s = splhi(); + while(--len >= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +void +uartwait(void) +{ +} diff --git a/os/cerf405/uart.h b/os/cerf405/uart.h new file mode 100644 index 00000000..28037473 --- /dev/null +++ b/os/cerf405/uart.h @@ -0,0 +1,101 @@ +/* + * 405EP specific code for its uart. + */ + +#define UR(p,r) ((uchar*)(p))[r] +#define uartwr(u,r,v) (UR(u->regs,r) = (v)) +#define uartwrreg(u,r,v) (UR(u->regs,r)= (u)->sticky[r] | (v)) +#define uartrdreg(u,r) UR(u->regs,r) + +extern void uartsetup(ulong, void*, ulong, char*); +extern void uartclock(void); + +static void +uartportpower(Uart*, int) +{ + /* TO DO: power control */ +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintrx(Ureg*, void* arg) +{ + uartintr(arg); +} + +/* + * install the uarts (called by reset) + */ +void +uartinstall(void) +{ + static int already; + + if(already) + return; + already = 1; + + /* first two ports are always there */ + uartsetup(0, (void*)PHYSUART0, 0, "eia0"); + intrenable(VectorUART0, uartintrx, uart[0], BUSUNKNOWN, "uart0"); + uartsetup(1, (void*)PHYSUART1, 0, "eia1"); + intrenable(VectorUART1, uartintrx, uart[1], BUSUNKNOWN, "uart1"); + addclock0link(uartclock, 22); +} + +/* + * If the UART's receiver can be connected to a DMA channel, + * this function does what is necessary to create the + * connection and returns the DMA channel number. + * If the UART's receiver cannot be connected to a DMA channel, + * a -1 is returned. + */ +char +uartdmarcv(int dev) +{ + + USED(dev); + return -1; +} + +void +uartputc(int c) +{ + uchar *p; + + if(c == 0) + return; + p = (uchar*)PHYSUART0; + while((UR(p,Lstat) & Outready) == 0){ + ; + } + UR(p,Data) = c; + eieio(); + if(c == '\n') + while((UR(p,Lstat) & Outready) == 0){ /* let fifo drain */ + ; + } +} + +void +uartputs(char *data, int len) +{ + int s; + +// if(!uartspcl && !redirectconsole) +// return; + s = splhi(); + while(--len >= 0){ + if(*data == '\n') + uartputc('\r'); + uartputc(*data++); + } + splx(s); +} + +void +uartwait(void) +{ +} |
