diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /os/manga | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'os/manga')
| -rwxr-xr-x | os/manga/Mk | 8 | ||||
| -rw-r--r-- | os/manga/archmanga.c | 164 | ||||
| -rw-r--r-- | os/manga/clock.c | 166 | ||||
| -rw-r--r-- | os/manga/dat.h | 128 | ||||
| -rw-r--r-- | os/manga/devether.c | 765 | ||||
| -rw-r--r-- | os/manga/devusb.c | 931 | ||||
| -rw-r--r-- | os/manga/eswnotes | 41 | ||||
| -rw-r--r-- | os/manga/ether8139.c | 744 | ||||
| -rw-r--r-- | os/manga/etherif.h | 44 | ||||
| -rw-r--r-- | os/manga/etherks8695.c | 1169 | ||||
| -rw-r--r-- | os/manga/flashif.h | 82 | ||||
| -rw-r--r-- | os/manga/fns.h | 163 | ||||
| -rw-r--r-- | os/manga/fpi.h | 61 | ||||
| -rw-r--r-- | os/manga/fpiarm.c | 483 | ||||
| -rw-r--r-- | os/manga/gpio.c | 75 | ||||
| -rw-r--r-- | os/manga/inb.c | 85 | ||||
| -rw-r--r-- | os/manga/io.h | 320 | ||||
| -rw-r--r-- | os/manga/ioring.c | 72 | ||||
| -rw-r--r-- | os/manga/l.s | 404 | ||||
| -rw-r--r-- | os/manga/main.c | 317 | ||||
| -rw-r--r-- | os/manga/manga | 137 | ||||
| -rw-r--r-- | os/manga/mem.h | 133 | ||||
| -rw-r--r-- | os/manga/mkfile | 89 | ||||
| -rw-r--r-- | os/manga/mmu.c | 244 | ||||
| -rw-r--r-- | os/manga/pci.c | 1007 | ||||
| -rw-r--r-- | os/manga/pinflate | bin | 0 -> 20480 bytes | |||
| -rw-r--r-- | os/manga/trap.c | 498 | ||||
| -rw-r--r-- | os/manga/uartks8695.c | 629 | ||||
| -rw-r--r-- | os/manga/usb.h | 160 | ||||
| -rw-r--r-- | os/manga/usbuhci.c | 1556 |
30 files changed, 10675 insertions, 0 deletions
diff --git a/os/manga/Mk b/os/manga/Mk new file mode 100755 index 00000000..51d69a44 --- /dev/null +++ b/os/manga/Mk @@ -0,0 +1,8 @@ +#!/bin/rc +rfork ne +ROOT=/usr/inferno +fn cd +NPROC=3 +path=(/usr/inferno/Plan9/$cputype/bin $path) +#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig +exec mk $* diff --git a/os/manga/archmanga.c b/os/manga/archmanga.c new file mode 100644 index 00000000..45b0f68c --- /dev/null +++ b/os/manga/archmanga.c @@ -0,0 +1,164 @@ +/* + * Manga Balance Plus + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#include "../port/netif.h" +#include "etherif.h" +#include "../port/flashif.h" + +enum { + /* GPIO assignment ... */ + + Maxmac= 4, /* number of MAC addresses taken from EEPROM */ +}; + +static uchar macaddrs[Maxmac][Eaddrlen] = { +[0] {0x00, 0x10, 0xa1, 0x00, 0x10, 0x01}, +[1] {0x00, 0x11, 0x6E, 0x00, 0x4A, 0xD4}, +[2] {0x00, 0x10, 0xa1, 0x00, 0x20, 0x01}, /* TO DO */ +}; + +void +archreset(void) +{ + /* TO DO: set GPIO and other key registers? */ + GPIOREG->iopm |= (1<<GPIO_status_orange_o)|(1<<GPIO_status_green_o); + GPIOREG->iopm &= ~(1<<GPIO_button_i); + GPIOREG->iopd &= ~(1<<GPIO_status_orange_o); + GPIOREG->iopd &= ~(1<<GPIO_status_green_o); + GPIOREG->iopc |= 0x8888; + m->cpuhz = 166000000; /* system clock is 125 = 5*CLOCKFREQ */ + m->delayloop = m->cpuhz/1000; +/* + uartdebuginit(); +*/ +} + +void +ledset(int n) +{ + int s; + + s = splhi(); + if(n) + GPIOREG->iopd |= 1<<GPIO_status_green_o; + else + GPIOREG->iopd &= ~(1<<GPIO_status_green_o); + splx(s); +} + +void +archconfinit(void) +{ + ulong *p; + + p = KADDR(PHYSMEMCR+0x30); + conf.topofmem = (((p[0]>>22)<<16)|0xFFFF)+1; +// w = PMGRREG->ppcr & 0x1f; +// m->cpuhz = CLOCKFREQ*(27*2*2); +} + +void +archuartpower(int, int) +{ +} + +void +kbdinit(void) +{ +} + +void +archreboot(void) +{ + dcflushall(); + GPIOREG->iopd |= 1<<GPIO_status_green_o; + GPIOREG->iopd &= ~(1<<GPIO_status_orange_o); +// mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */ +// RESETREG->rsrr = 1; /* software reset */ + for(;;) + //spllo(); + splhi(); +} + +void +archflashwp(Flash*, int) +{ +} + +/* + * 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) +{ + ulong *p; + int w; + + p = KADDR(PHYSMEMCR+0x10); +iprint("Flash %8.8lux %8.8lux %8.8lux\n", p[0], p[1], p[4]); + w = p[4]&3; + if(bank > 0 || w == 0) + return -1; + if(w == 3) + w = 4; + f->type = "cfi8"; + f->addr = (void*)FLASHMEM; + f->size = 0; + f->width = w; + f->interleave = 0; + return 0; +} + +/* + * set ether parameters: the contents should be derived from EEPROM or NVRAM + */ +int +archether(int ctlno, Ether *ether) +{ + ether->nopt = 0; + ether->itype = IRQ; + switch(ctlno){ + case 0: + sprint(ether->type, "ks8695"); + ether->mem = PHYSWANDMA; + ether->port = 0; + ether->irq = IRQwmrps; + break; + case 1: + sprint(ether->type, "ks8695"); + ether->mem = PHYSLANDMA; + ether->port = 1; + ether->irq = IRQlmrps; + ether->maxmtu = ETHERMAXTU+4; /* 802.1[pQ] tags */ + break; + case 2: + sprint(ether->type, "rtl8139"); + ether->mem = 0; + ether->port = 0; + ether->irq = -1; + break; + default: + return -1; + } + memmove(ether->ea, macaddrs[ctlno], Eaddrlen); + return 1; +} + +/* + * TO DO: extract some boot data from user area of flash + */ + +void +eepromscan(void) +{ +} diff --git a/os/manga/clock.c b/os/manga/clock.c new file mode 100644 index 00000000..282f32d6 --- /dev/null +++ b/os/manga/clock.c @@ -0,0 +1,166 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "ureg.h" + +enum { + Mclk= 25000000 +}; + +typedef struct Clock0link Clock0link; +typedef struct Clock0link { + void (*clock)(void); + Clock0link* link; +} Clock0link; + +static Clock0link *clock0link; +static Lock clock0lock; +static void (*prof_fcn)(Ureg *, int); + +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; +} + +static void +profintr(Ureg *, void*) +{ + /* TO DO: watchdog, profile on Timer 0 */ +} + +static void +clockintr(Ureg*, void*) +{ + Clock0link *lp; + static int blip, led; + + if(++blip >= HZ){ + blip = 0; + ledset(led ^= 1); + } + m->ticks++; + + checkalarms(); + + if(canlock(&clock0lock)){ + for(lp = clock0link; lp; lp = lp->link) + if(lp->clock) + lp->clock(); + unlock(&clock0lock); + } + + /* round robin time slice is done by trap.c and proc.c */ +} + +void +installprof(void (*pf)(Ureg *, int)) +{ + USED(pf); +} + +void +clockinit(void) +{ + TimerReg *tr; + IntrReg *ir; + ulong l, u; + + m->ticks = 0; + tr = TIMERREG; + tr->enable = 0; + tr->pulse1 = 1; + + /* first tune the delay loop parameter (using a search because the counter doesn't decrement) */ + ir = INTRREG; + tr->count1 = Mclk/1000 - tr->pulse1; /* millisecond */ + u = m->cpuhz/(2*1000); /* over-large estimate for a millisecond */ + l = 10000; + while(l+1 < u){ + m->delayloop = l + (u-l)/2; + ir->st = 1<<IRQtm1; /* reset edge */ + tr->enable = 1<<1; + delay(1); + tr->enable = 0; + if(ir->st & (1<<IRQtm1)) + u = m->delayloop; + else + l = m->delayloop; + } + + intrenable(IRQ, IRQtm1, clockintr, nil, "timer.1"); + tr->count1 = Mclk/HZ - tr->pulse1; + tr->enable = 1<<1; /* enable only Timer 1 */ +} + +void +clockpoll(void) +{ +} + +void +clockcheck(void) +{ +} + +uvlong +fastticks(uvlong *hz) +{ + if(hz) + *hz = HZ; + return m->ticks; +} + +void +microdelay(int l) +{ + int i; + + l *= m->delayloop; + l /= 1000; + if(l <= 0) + l = 1; + for(i = 0; i < l; i++) + ; +} + +void +delay(int l) +{ + ulong i, j; + + j = m->delayloop; + while(l-- > 0) + for(i=0; i < j; i++) + ; +} + +/* + * for devkprof.c + */ +long +archkprofmicrosecondspertick(void) +{ + return MS2HZ*1000; +} + +void +archkprofenable(int) +{ + /* TO DO */ +} diff --git a/os/manga/dat.h b/os/manga/dat.h new file mode 100644 index 00000000..b53c25e5 --- /dev/null +++ b/os/manga/dat.h @@ -0,0 +1,128 @@ +typedef struct Conf Conf; +typedef struct Dma Dma; +typedef struct FPU FPU; +typedef struct FPenv FPenv; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct Ureg Ureg; +typedef struct ISAConf ISAConf; +typedef struct Pcidev Pcidev; + +typedef ulong Instr; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong topofmem; /* highest physical address + 1 */ + 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 */ + + int useminicache; /* use mini cache: screen.c/lcd.c */ + int textwrite; /* writeable text segment, for debug */ + int portrait; /* display orientation */ +}; + +#define NISAOPT 8 +struct ISAConf { + char type[KNAMELEN]; + ulong port; + ulong irq; + int itype; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +/* + * FPenv.status + */ +enum +{ + FPINIT, + FPACTIVE, + FPINACTIVE, +}; + +struct FPenv +{ + ulong status; + ulong control; + ushort fpistate; /* emulated fp */ + ulong regs[8][3]; /* emulated fp */ +}; + +/* + * This structure must agree with fpsave and fprestore asm routines + */ +struct FPU +{ + FPenv env; +}; + +struct Label +{ + ulong sp; + ulong pc; +}; + +struct Lock +{ + ulong key; + ulong sr; + ulong pc; + int pri; +}; + +#include "../port/portdat.h" + +/* + * machine dependent definitions not used by ../port/portdat.h + */ +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + ulong splpc; /* pc of last caller to splhi */ + + /* ordering from here on irrelevant */ + + int machno; /* physical id of processor */ + 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 */ + ulong cpuhz; + ulong delayloop; + + /* stacks for exceptions */ + ulong fiqstack[4]; + ulong irqstack[4]; + ulong abtstack[4]; + ulong undstack[4]; + + int stack[1]; +}; + +#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0) + +extern Mach *m; +extern Proc *up; + +/* + * Layout at virtual address 0. + */ +typedef struct Vectorpage { + void (*vectors[8])(void); + uint vtable[8]; +} Vectorpage; +extern Vectorpage *page0; diff --git a/os/manga/devether.c b/os/manga/devether.c new file mode 100644 index 00000000..18f786e4 --- /dev/null +++ b/os/manga/devether.c @@ -0,0 +1,765 @@ +#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" + +enum { + Type8021Q= 0x8100, /* value of type field for 802.1[pQ] tags */ +}; + +static Ether *etherxx[MaxEther]; /* real controllers */ +static Ether* vlanalloc(Ether*, int); +static void vlanoq(Ether*, Block*); + +Chan* +etherattach(char* spec) +{ + ulong ctlrno; + char *p; + Chan *chan; + Ether *ether, *vlan; + int vlanid; + + ctlrno = 0; + vlanid = 0; + if(spec && *spec){ + ctlrno = strtoul(spec, &p, 0); + if(ctlrno == 0 && p == spec || ctlrno >= MaxEther || *p && *p != '.') + error(Ebadarg); + if(*p == '.'){ /* vlan */ + vlanid = strtoul(p+1, &p, 0); + if(vlanid <= 0 || vlanid > 0xFFF || *p) + error(Ebadarg); + } + } + if((ether = etherxx[ctlrno]) == 0) + error(Enodev); + rlock(ether); + if(waserror()){ + runlock(ether); + nexterror(); + } + if(vlanid){ + if(ether->maxmtu < ETHERMAXTU+4) + error("interface cannot support 802.1 tags"); + vlan = vlanalloc(ether, vlanid); + chan = devattach('l', spec); + chan->dev = ctlrno + (vlanid<<8); + chan->aux = vlan; + poperror(); + runlock(ether); + return chan; + } + chan = devattach('l', spec); + chan->dev = ctlrno; + chan->aux = ether; + if(ether->attach) + ether->attach(ether); + 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 = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + wq = netifwalk(ether, chan, nchan, name, nname); + if(wq && wq->clone != nil && wq->clone != chan) + wq->clone->aux = ether; + poperror(); + runlock(ether); + return wq; +} + +static int +etherstat(Chan* chan, uchar* dp, int n) +{ + int s; + Ether *ether; + + ether = chan->aux; + 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 = chan->aux; + rlock(ether); + if(waserror()) { + runlock(ether); + nexterror(); + } + c = netifopen(ether, chan, omode); + poperror(); + runlock(ether); + return c; +} + +static void +etherclose(Chan* chan) +{ + Ether *ether; + + ether = chan->aux; + 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 = chan->aux; + 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 = chan->aux; + 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 = chan->aux; + 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, vlanid, i; + Netfile **ep, *f, **fp, *fx; + Block *xbp; + Ether *vlan; + + ether->inpackets++; + + pkt = (Etherpkt*)bp->rp; + len = BLEN(bp); + type = (pkt->type[0]<<8)|pkt->type[1]; + if(type == Type8021Q && ether->nvlan){ + vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF; + if(vlanid){ + for(i = 0; i < nelem(ether->vlans); i++){ + vlan = ether->vlans[i]; + if(vlan != nil && vlan->vlanid == vlanid){ + memmove(bp->rp+4, bp->rp, 2*Eaddrlen); + bp->rp += 4; + return etheriq(vlan, bp, fromwire); + } + } + /* allow normal type handling to accept or discard it */ + } + } + + 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){ + if(ether->vlanid){ + /* add tag */ + bp = padblock(bp, 2+2); + memmove(bp->rp, bp->rp+4, 2*Eaddrlen); + hnputs(bp->rp+2*Eaddrlen, Type8021Q); + hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF); /* prio:3 0:1 vid:12 */ + ether = ether->ctlr; + } + 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 = chan->aux; + 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]); + if(ether->oq != nil) + 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 = chan->aux; + 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 void +nop(Ether*) +{ +} + +static long +vlanctl(Ether *ether, void *buf, long n) +{ + uchar ea[Eaddrlen]; + Ether *master; + Cmdbuf *cb; + int i; + + cb = parsecmd(buf, n); + if(cb->nf >= 2 + && strcmp(cb->f[0], "ea")==0 + && parseether(ea, cb->f[1]) == 0){ + free(cb); + memmove(ether->ea, ea, Eaddrlen); + memmove(ether->addr, ether->ea, Eaddrlen); + return 0; + } + if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){ + master = ether->ctlr; + qlock(&master->vlq); + for(i = 0; i < nelem(master->vlans); i++) + if(master->vlans[i] == ether){ + ether->vlanid = 0; + master->nvlan--; + break; + } + qunlock(&master->vlq); + free(cb); + return 0; + } + free(cb); + error(Ebadctl); + return -1; /* not reached */ +} + +static Ether* +vlanalloc(Ether *ether, int id) +{ + Ether *vlan; + int i, fid; + char name[KNAMELEN]; + + qlock(ðer->vlq); + if(waserror()){ + qunlock(ðer->vlq); + nexterror(); + } + fid = -1; + for(i = 0; i < nelem(ether->vlans); i++){ + vlan = ether->vlans[i]; + if(vlan != nil && vlan->vlanid == id){ + poperror(); + qunlock(ðer->vlq); + return vlan; + } + if(fid < 0 && (vlan == nil || vlan->vlanid == 0)) + fid = i; + } + if(fid < 0) + error(Enoifc); + snprint(name, sizeof(name), "ether%d.%d", ether->ctlrno, id); + vlan = ether->vlans[fid]; + if(vlan == nil){ + vlan = mallocz(sizeof(Ether), 1); + if(vlan == nil) + error(Enovmem); + netifinit(vlan, name, Ntypes, ether->limit); + ether->vlans[fid] = vlan; /* id is still zero, can't be matched */ + ether->nvlan++; + }else + memmove(vlan->name, name, KNAMELEN-1); + vlan->attach = nop; + vlan->transmit = nil; + vlan->ctl = vlanctl; + vlan->irq = -1; +// vlan->promiscuous = ether->promiscuous; +// vlan->multicast = ether->multicast; + vlan->arg = vlan; + vlan->mbps = ether->mbps; + vlan->fullduplex = ether->fullduplex; + vlan->encry = ether->encry; + vlan->minmtu = ether->minmtu; + vlan->maxmtu = ether->maxmtu; + vlan->ctlrno = ether->ctlrno; + vlan->vlanid = id; + vlan->alen = Eaddrlen; + memmove(vlan->addr, ether->addr, sizeof(vlan->addr)); + memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast)); + vlan->oq = nil; + vlan->ctlr = ether; + vlan->vlanid = id; + poperror(); + qunlock(ðer->vlq); + return vlan; +} + +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->itype = -1; + + 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->itype, ether->irq, ether->interrupt, ether, 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"); + iprint(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(ether->readers) + 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, + devcreate, + etherclose, + etherread, + etherbread, + etherwrite, + etherbwrite, + devremove, + etherwstat, + etherpower, +}; diff --git a/os/manga/devusb.c b/os/manga/devusb.c new file mode 100644 index 00000000..ead04b9a --- /dev/null +++ b/os/manga/devusb.c @@ -0,0 +1,931 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +static int debug = 0; + +#define Chatty 1 +#define DPRINT if(Chatty)print +#define XPRINT if(debug)iprint + +Usbhost* usbhost[MaxUsb]; + +static char *devstates[] = { + [Disabled] "Disabled", + [Attached] "Attached", + [Enabled] "Enabled", + [Assigned] "Assigned", + [Configured] "Configured", +}; + +static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; + +enum +{ + Qtopdir = 0, + Q2nd, + Qnew, + Qport, + Q3rd, + Qctl, + Qstatus, + Qep0, + /* other endpoint files */ +}; + +/* + * Qid path is: + * 8 bits of file type (qids above) + * 8 bits of slot number; default address 0 used for per-controller files + * 4 bits of controller number + */ +enum { + TYPEBITS = 8, + SLOTBITS = 8, + CTLRBITS = 4, + + SLOTSHIFT = TYPEBITS, + CTLRSHIFT = SLOTSHIFT+SLOTBITS, + + TYPEMASK = (1<<TYPEBITS)-1, + SLOTMASK = (1<<SLOTBITS)-1, + CTLRMASK = (1<<CTLRBITS)-1, +}; + +#define TYPE(q) (((ulong)(q).path)&TYPEMASK) +#define SLOT(q) ((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK) +#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) +#define PATH(t, s, c) ((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT)) + +static Dirtab usbdir2[] = { + "new", {Qnew}, 0, 0666, + "port", {Qport}, 0, 0666, +}; + +static Dirtab usbdir3[]={ + "ctl", {Qctl}, 0, 0666, + "status", {Qstatus}, 0, 0444, + "setup", {Qep0}, 0, 0666, + /* epNdata names are generated on demand */ +}; + +enum +{ + PMdisable, + PMenable, + PMreset, +}; + +enum +{ + CMclass, + CMdata, + CMdebug, + CMep, + CMmaxpkt, + CMadjust, + CMspeed, + CMunstall, +}; + +static Cmdtab usbportmsg[] = +{ + PMdisable, "disable", 2, + PMenable, "enable", 2, + PMreset, "reset", 2, +}; + +static Cmdtab usbctlmsg[] = +{ + CMclass, "class", 0, + CMdata, "data", 3, + CMdebug, "debug", 3, + CMep, "ep", 6, + CMmaxpkt, "maxpkt", 3, + CMadjust, "adjust", 3, + CMspeed, "speed", 2, + CMunstall, "unstall", 2, +}; + +static struct +{ + char* type; + int (*reset)(Usbhost*); +} usbtypes[MaxUsb+1]; + +void +addusbtype(char* t, int (*r)(Usbhost*)) +{ + static int ntype; + + if(ntype == MaxUsb) + panic("too many USB host interface types"); + usbtypes[ntype].type = t; + usbtypes[ntype].reset = r; + ntype++; +} + +static Udev* +usbdeviceofslot(Usbhost *uh, int s) +{ + if(s < 0 || s > nelem(uh->dev)) + return nil; + return uh->dev[s]; +} + +static Udev* +usbdevice(Chan *c) +{ + int bus; + Udev *d; + Usbhost *uh; + + bus = CTLR(c->qid); + if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { + error(Egreg); + return nil; /* for compiler */ + } + d = usbdeviceofslot(uh, SLOT(c->qid)); + if(d == nil || d->id != c->qid.vers || d->state == Disabled) + error(Ehungup); + return d; +} + +static Endpt * +devendpt(Udev *d, int id, int add) +{ + Usbhost *uh; + Endpt *e, **p; + + p = &d->ep[id&0xF]; + lock(d); + e = *p; + if(e != nil){ + incref(e); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); + unlock(d); + return e; + } + unlock(d); + if(!add) + return nil; + + e = mallocz(sizeof(*e), 1); + e->ref = 1; + e->x = id&0xF; + e->id = id; + e->sched = -1; + e->maxpkt = 8; + e->nbuf = 1; + e->dev = d; + e->active = 0; + + uh = d->uh; + uh->epalloc(uh, e); + + lock(d); + if(*p != nil){ + incref(*p); + XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); + unlock(d); + uh->epfree(uh, e); + free(e); + return *p; + } + *p = e; + unlock(d); + e->rq = qopen(8*1024, 0, nil, e); + e->wq = qopen(8*1024, 0, nil, e); + return e; +} + +static void +freept(Endpt *e) +{ + Usbhost *uh; + + if(e != nil && decref(e) == 0){ + XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + uh = e->dev->uh; + uh->epclose(uh, e); + e->dev->ep[e->x] = nil; + uh->epfree(uh, e); + free(e); + } +} + +static Udev* +usbnewdevice(Usbhost *uh) +{ + int i; + Udev *d; + Endpt *e; + + d = nil; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + for(i=0; i<nelem(uh->dev); i++) + if(uh->dev[i] == nil){ + uh->idgen++; + d = mallocz(sizeof(*d), 1); + d->uh = uh; + d->ref = 1; + d->x = i; + d->id = (uh->idgen << 8) | i; + d->state = Enabled; + XPRINT("calling devendpt in usbnewdevice\n"); + e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ + e->mode = ORDWR; + e->iso = 0; + e->sched = -1; + uh->dev[i] = d; + break; + } + poperror(); + qunlock(uh); + return d; +} + +static void +freedev(Udev *d, int ept) +{ + int i; + Endpt *e; + Usbhost *uh; + + uh = d->uh; + if(decref(d) == 0){ + XPRINT("freedev 0x%p, 0\n", d); + for(i=0; i<nelem(d->ep); i++) + freept(d->ep[i]); + if(d->x >= 0) + uh->dev[d->x] = nil; + free(d); + } else { + if(ept >= 0 && ept < nelem(d->ep)){ + e = d->ep[ept]; + XPRINT("freedev, freept 0x%p\n", e); + if(e != nil) + uh->epclose(uh, e); + } + } +} + +static int +usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Udev *d; + Endpt *e; + Dirtab *tab; + Usbhost *uh; + int t, bus, slot, perm; + + /* + * Top level directory contains the controller names. + */ + if(c->qid.path == Qtopdir){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s >= nelem(usbhost) || usbhost[s] == nil) + return -1; + mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", s); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + bus = CTLR(c->qid); + if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) + return -1; + + /* + * Second level contains "new", "port", and a numbered + * directory for each enumerated device on the bus. + */ + t = TYPE(c->qid); + if(t < Q3rd){ + if(s == DEVDOTDOT){ + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#U", 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir2)){ + d = uh->dev[0]; + if(d == nil) + return -1; + tab = &usbdir2[s]; + mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir2); + if(s >= 0 && s < nelem(uh->dev)) { + d = uh->dev[s]; + if(d == nil) + return 0; + sprint(up->genbuf, "%d", s); + mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + return -1; + } + + /* + * Third level. + */ + slot = SLOT(c->qid); + if(s == DEVDOTDOT) { + mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + if(s < nelem(usbdir3)) { + tab = &usbdir3[s]; + mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + s -= nelem(usbdir3); + + /* active endpoints */ + d = usbdeviceofslot(uh, slot); + if(d == nil || s >= nelem(d->ep)) + return -1; + if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ + return 0; + sprint(up->genbuf, "ep%ddata", s); + mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE); + switch(e->mode) { + case OREAD: + perm = 0444; + break; + case OWRITE: + perm = 0222; + break; + default: + perm = 0666; + break; + } + devdir(c, q, up->genbuf, e->buffered, eve, perm, dp); + return 1; +} + +static Usbhost* +usbprobe(int cardno, int ctlrno) +{ + Usbhost *uh; + char buf[128], *ebuf, name[64], *p, *type; + + if(cardno < 0) + return nil; + uh = malloc(sizeof(Usbhost)); + memset(uh, 0, sizeof(Usbhost)); + uh->tbdf = BUSUNKNOWN; + + if(cardno >= MaxUsb || usbtypes[cardno].type == nil){ + free(uh); + return nil; + } + if(usbtypes[cardno].reset(uh) < 0){ + free(uh); + return nil; + } + + snprint(name, sizeof(name), "usb%d", ctlrno); + intrenable(IRQ, uh->irq, uh->interrupt, uh, name); + + ebuf = buf + sizeof buf; + p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %ld", ctlrno, usbtypes[cardno].type, uh->port, uh->irq); + if(uh->mem) + p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem)); + if(uh->size) + seprint(p, ebuf, " size 0x%luX", uh->size); + print("%s\n", buf); + + return uh; +} + +static void +usbreset(void) +{ + int cardno, ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + if((uh = usbprobe(-1, ctlrno)) == nil) + continue; + usbhost[ctlrno] = uh; + } + + if(getconf("*nousbprobe")) + return; + + cardno = ctlrno = 0; + while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){ + if(usbhost[ctlrno] != nil){ + ctlrno++; + continue; + } + if((uh = usbprobe(cardno, ctlrno)) == nil){ + cardno++; + continue; + } + usbhost[ctlrno] = uh; + ctlrno++; + } +} + +void +usbinit(void) +{ + Udev *d; + int ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + uh = usbhost[ctlrno]; + if(uh == nil) + continue; + if(uh->init != 0) + uh->init(uh); + + /* reserve device for configuration */ + d = usbnewdevice(uh); + incref(d); + d->state = Attached; + } +} + +Chan * +usbattach(char *spec) +{ + return devattach('U', spec); +} + +static Walkqid* +usbwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, usbgen); +} + +static int +usbstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, usbgen); +} + +Chan* +usbopen(Chan *c, int omode) +{ + Udev *d; + Endpt *e; + int f, s, type; + Usbhost *uh; + + if(c->qid.type == QTDIR) + return devopen(c, omode, nil, 0, usbgen); + + f = 0; + type = TYPE(c->qid); + if(type == Qnew){ + d = usbdevice(c); + d = usbnewdevice(d->uh); + XPRINT("usbopen, new dev 0x%p\n", d); + if(d == nil) { + XPRINT("usbopen failed (usbnewdevice)\n"); + error(Enodev); + } + type = Qctl; + mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); + f = 1; + } + + if(type < Q3rd){ + XPRINT("usbopen, devopen < Q3rd\n"); + return devopen(c, omode, nil, 0, usbgen); + } + + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + + switch(type){ + case Qctl: + if(0&&d->busy) + error(Einuse); + d->busy = 1; + if(!f) + incref(d); + XPRINT("usbopen, Qctl 0x%p\n", d); + break; + + default: + s = type - Qep0; + XPRINT("usbopen, default 0x%p, %d\n", d, s); + if(s >= 0 && s < nelem(d->ep)){ + if((e = d->ep[s]) == nil) { + XPRINT("usbopen failed (endpoint)\n"); + error(Enodev); + } + XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); + uh->epopen(uh, e); + e->foffset = 0; + e->toffset = 0; + e->poffset = 0; + e->buffered = 0; + } + incref(d); + break; + } + poperror(); + qunlock(uh); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +usbclose(Chan *c) +{ + Udev *d; + int ept, type; + Usbhost *uh; + + type = TYPE(c->qid); + if(c->qid.type == QTDIR || type < Q3rd) + return; + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); + nexterror(); + } + if(type == Qctl) + d->busy = 0; + XPRINT("usbclose: dev 0x%p\n", d); + if(c->flag & COPEN){ + ept = (type != Qctl) ? type - Qep0 : -1; + XPRINT("usbclose: freedev 0x%p\n", d); + freedev(d, ept); + } + poperror(); + qunlock(uh); +} + +static char * +epstatus(char *s, char *se, Endpt *e, int i) +{ + char *p; + + p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); + if(e->iso){ + p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered); + if(e->toffset) + p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); + p = seprint(p, se, "\n"); + } + return p; +} + +long +usbread(Chan *c, void *a, long n, vlong offset) +{ + int t, i; + Udev *d; + Endpt *e; + Usbhost *uh; + char *s, *se, *p; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, usbgen); + + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + + if(t >= Qep0) { + t -= Qep0; + if(t >= nelem(d->ep)) + error(Eio); + e = d->ep[t]; + if(e == nil || e->mode == OWRITE) + error(Egreg); + if(t == 0) { + if(e->iso) + error(Egreg); + e->data01 = 1; + n = uh->read(uh, e, a, n, 0LL); + if(e->setin){ + e->setin = 0; + e->data01 = 1; + uh->write(uh, e, "", 0, 0LL, TokOUT); + } + return n; + } + return uh->read(uh, e, a, n, offset); + } + + s = smalloc(READSTR); + se = s+READSTR; + if(waserror()){ + free(s); + nexterror(); + } + switch(t){ + case Qport: + uh->portinfo(uh, s, se); + break; + + case Qctl: + seprint(s, se, "%11d %11d\n", d->x, d->id); + break; + + case Qstatus: + if (d->did || d->vid) + p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did); + else + p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp); + for(i=0; i<nelem(d->ep); i++) { + e = d->ep[i]; + if(e == nil) + continue; + /* TO DO: freeze e */ + p = epstatus(p, se, e, i); + } + } + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; +} + +long +usbwrite(Chan *c, void *a, long n, vlong offset) +{ + Udev *d; + Endpt *e; + Cmdtab *ct; + Cmdbuf *cb; + Usbhost *uh; + int id, nw, t, i; + char cmd[50]; + + if(c->qid.type == QTDIR) + error(Egreg); + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + switch(t){ + case Qport: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); + id = strtol(cb->f[1], nil, 0); + switch(ct->index){ + case PMdisable: + uh->portenable(uh, id, 0); + break; + case PMenable: + uh->portenable(uh, id, 1); + break; + case PMreset: + uh->portreset(uh, id); + break; + } + + poperror(); + free(cb); + return n; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg)); + switch(ct->index){ + case CMspeed: + d->ls = strtoul(cb->f[1], nil, 0) == 0; + break; + case CMclass: + if (cb->nf != 4 && cb->nf != 6) + cmderror(cb, Ebadusbmsg); + /* class #ifc ept csp ( == class subclass proto) [vendor product] */ + d->npt = strtoul(cb->f[1], nil, 0); /* # of interfaces */ + i = strtoul(cb->f[2], nil, 0); /* endpoint */ + if (i < 0 || i >= nelem(d->ep) + || d->npt > nelem(d->ep) || i >= d->npt) + cmderror(cb, Ebadusbmsg); + if (cb->nf == 6) { + d->vid = strtoul(cb->f[4], nil, 0); + d->did = strtoul(cb->f[5], nil, 0); + } + if (i == 0) + d->csp = strtoul(cb->f[3], nil, 0); + if(d->ep[i] == nil){ + XPRINT("calling devendpt in usbwrite (CMclass)\n"); + d->ep[i] = devendpt(d, i, 1); + } + d->ep[i]->csp = strtoul(cb->f[3], nil, 0); + break; + case CMdata: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->data01 = strtoul(cb->f[2], nil, 0) != 0; + break; + case CMmaxpkt: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->maxpkt = strtoul(cb->f[2], nil, 0); + if(e->maxpkt > 1500) + e->maxpkt = 1500; + break; + case CMadjust: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + if (e->iso == 0) + error(Eperm); + i = strtoul(cb->f[2], nil, 0); + /* speed may not result in change of maxpkt */ + if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms + || i > e->maxpkt/e->samplesz * 1000/e->pollms){ + snprint(cmd, sizeof(cmd), "%d < %d < %d?", + (e->maxpkt-1)/e->samplesz * 1000/e->pollms, + i, + e->maxpkt/e->samplesz * 1000/e->pollms); + error(cmd); + } + e->hz = i; + break; + case CMdebug: + i = strtoul(cb->f[1], nil, 0); + if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + if (i == -1) + debug = 0; + else { + debug = 1; + e = d->ep[i]; + e->debug = strtoul(cb->f[2], nil, 0); + } + break; + case CMunstall: + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) + error(Ebadusbmsg); + e = d->ep[i]; + e->err = nil; + break; + case CMep: + /* ep n `bulk' mode maxpkt nbuf OR + * ep n period mode samplesize Hz + */ + i = strtoul(cb->f[1], nil, 0); + if(i < 0 || i >= nelem(d->ep)) { + XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); + error(Ebadarg); + } + if((e = d->ep[i]) == nil){ + XPRINT("calling devendpt in usbwrite (CMep)\n"); + e = devendpt(d, i, 1); + } + qlock(uh); + if(waserror()){ + freept(e); + qunlock(uh); + nexterror(); + } + if(e->active) + error(Eperm); + if(strcmp(cb->f[2], "bulk") == 0){ + /* ep n `bulk' mode maxpkt nbuf */ + e->iso = 0; + i = strtoul(cb->f[4], nil, 0); + if(i < 8 || i > 1023) + i = 8; + e->maxpkt = i; + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i <= 32) + e->nbuf = i; + } else { + /* ep n period mode samplesize Hz */ + i = strtoul(cb->f[2], nil, 0); + if(i > 0 && i <= 1000){ + e->pollms = i; + }else { + XPRINT("field 4: 0 <= %d <= 1000\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[4], nil, 0); + if(i >= 1 && i <= 8){ + e->samplesz = i; + }else { + XPRINT("field 4: 0 < %d <= 8\n", i); + error(Ebadarg); + } + i = strtoul(cb->f[5], nil, 0); + if(i >= 1 && i*e->samplesz <= 12*1000*1000){ + /* Hz */ + e->hz = i; + e->remain = 0; + }else { + XPRINT("field 5: 1 < %d <= 100000 Hz\n", i); + error(Ebadarg); + } + e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; + e->iso = 1; + } + e->mode = strcmp(cb->f[3],"r") == 0? OREAD : + strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + uh->epmode(uh, e); + poperror(); + qunlock(uh); + } + + poperror(); + free(cb); + return n; + + case Qep0: /* SETUP endpoint 0 */ + /* should canqlock etc */ + e = d->ep[0]; + if(e == nil || e->iso) + error(Egreg); + if(n < 8) + error(Eio); + nw = *(uchar*)a & RD2H; + e->data01 = 0; + n = uh->write(uh, e, a, n, 0LL, TokSETUP); + if(nw == 0) { /* host to device: use IN[DATA1] to ack */ + e->data01 = 1; + nw = uh->read(uh, e, cmd, 0LL, 8); + if(nw != 0) + error(Eio); /* could provide more status */ + }else + e->setin = 1; /* two-phase */ + break; + + default: /* sends DATA[01] */ + t -= Qep0; + if(t < 0 || t >= nelem(d->ep)) + error(Egreg); + e = d->ep[t]; + if(e == nil || e->mode == OREAD) + error(Egreg); + n = uh->write(uh, e, a, n, offset, TokOUT); + break; + } + return n; +} + +Dev usbdevtab = { + 'U', + "usb", + + usbreset, + usbinit, + devshutdown, + usbattach, + usbwalk, + usbstat, + usbopen, + devcreate, + usbclose, + usbread, + devbread, + usbwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/os/manga/eswnotes b/os/manga/eswnotes new file mode 100644 index 00000000..4a4b892a --- /dev/null +++ b/os/manga/eswnotes @@ -0,0 +1,41 @@ +- switch level + - VlanEn vlan enable + - disable tx/rx flow control + - buffer sharing control + - unicast port-VLAN mismatch discard + - fair flow control on/off + - priority buffer reserved + - high prio first, 10:1, 5:1 2:1 + - tag mask enabled + - enable/disable switch? + - TPID mode for direct forwarding from port 5 + - replace null VID with default port VID + - 802.1p base priority + +- port level + - auto negotiation + - spanning tree tx/rx/learn on/off + - priority classification on/off + - diffserve priority classification on/off + - 802.1p classification on/off + - some of those are possibly mutually exclusive + - priority function enabled + - default tag: userprio (3 bits), CFI (mbz), 12-bit VID + - VLAN related + - ingress filter (discard packets from port not in VLAN) + - discard non pvid (discard tagged packets not matching port's default VID) + - receive rate control + - high priority 8-bits, low priority 8-bits + - enable rate and/or flow control, high and low priority, tx/rx + +- FID management + +- static MAC management +- VLAN table (16 entries) + - port membership + - FID + - 802.1Q 12-bit VID + +- stats + +- 8100 or 810x diff --git a/os/manga/ether8139.c b/os/manga/ether8139.c new file mode 100644 index 00000000..d64e0b20 --- /dev/null +++ b/os/manga/ether8139.c @@ -0,0 +1,744 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#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" + +#include "etherif.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timerbit = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */ + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 26, /* Hardware Version ID */ + HwveridMASK = 0x7C000000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct Td Td; +struct Td { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; + Block* bp; +}; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +rtl8139promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +static long +rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + l = snprint(p, READSTR, "rcr %8.8uX\n", ctlr->rcr); + l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth); + l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned); + l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned); + ctlr->dis += csr16r(ctlr, Dis); + l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis); + ctlr->fcsc += csr16r(ctlr, Fcsc); + l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc); + ctlr->rec += csr16r(ctlr, Rec); + l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec); + + l += snprint(p+l, READSTR-l, "Tcr %8.8luX\n", csr32r(ctlr, Tcr)); + l += snprint(p+l, READSTR-l, "Config0 %2.2uX\n", csr8r(ctlr, Config0)); + l += snprint(p+l, READSTR-l, "Config1 %2.2uX\n", csr8r(ctlr, Config1)); + l += snprint(p+l, READSTR-l, "Msr %2.2uX\n", csr8r(ctlr, Msr)); + l += snprint(p+l, READSTR-l, "Config3 %2.2uX\n", csr8r(ctlr, Config3)); + l += snprint(p+l, READSTR-l, "Config4 %2.2uX\n", csr8r(ctlr, Config4)); + + l += snprint(p+l, READSTR-l, "Bmcr %4.4uX\n", csr16r(ctlr, Bmcr)); + l += snprint(p+l, READSTR-l, "Bmsr %4.4uX\n", csr16r(ctlr, Bmsr)); + l += snprint(p+l, READSTR-l, "Anar %4.4uX\n", csr16r(ctlr, Anar)); + l += snprint(p+l, READSTR-l, "Anlpar %4.4uX\n", csr16r(ctlr, Anlpar)); + l += snprint(p+l, READSTR-l, "Aner %4.4uX\n", csr16r(ctlr, Aner)); + l += snprint(p+l, READSTR-l, "Nwaytr %4.4uX\n", csr16r(ctlr, Nwaytr)); + snprint(p+l, READSTR-l, "Cscr %4.4uX\n", csr16r(ctlr, Cscr)); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +rtl8139reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + int i; + + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); + + for(i = 0; i < Ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = mmucacheinhib((char*)ROUNDUP((ulong)ctlr->alloc, CACHELINESZ), ctlr->rblen+16 + Ntd*Tdbsz); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + ctlr->td[i].bp = nil; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + CACHELINESZ, 0); + rtl8139init(edev); + } + qunlock(&ctlr->alock); +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + int size; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + bp = qget(edev->oq); + if(bp == nil) + break; + size = BLEN(bp); + + td = &ctlr->td[ctlr->tdh]; + if(((int)bp->rp) & 0x03){ + memmove(td->data, bp->rp, size); + dcflush(td->data, size); + freeb(bp); + csr32w(ctlr, td->tsad, PCIWADDR(td->data)); + ctlr->tunaligned++; + } + else{ + td->bp = bp; + csr32w(ctlr, td->tsad, PCIWADDR(bp->rp)); + dcflush(bp->rp, size); + ctlr->taligned++; + } + csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size); + + ctlr->ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + if(!(status & Rcok)){ + if(status & (Ise|Fae)) + edev->frames++; + if(status & Crc) + edev->crcs++; + if(status & (Runt|Long)) + edev->buffs++; + + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + if((bp = iallocb(length)) != nil){ + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + memmove(bp->wp, p, l); + bp->wp += l; + length -= l; + p = ctlr->rbstart; + } + if(length > 0){ + memmove(bp->wp, p, length); + bp->wp += length; + } + bp->wp -= 4; + etheriq(edev, bp, 1); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, msr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } + edev->oerrs++; + } + + if(td->bp != nil){ + freeb(td->bp); + td->bp = nil; + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc){ + /* + * Maybe the link changed - do we care very much? + */ + msr = csr8r(ctlr, Msr); + if(!(msr & Linkb)){ + if(!(msr & Speed10) && edev->mbps != 100){ + edev->mbps = 100; + qsetlimit(edev->oq, 256*1024); + } + else if((msr & Speed10) && edev->mbps != 10){ + edev->mbps = 10; + qsetlimit(edev->oq, 65*1024); + } + } + isr &= ~(Clc|PunLc); + } + + /* + * Only Serr|Timer should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if(isr != 0){ + iprint("rtl8139interrupt: imr %4.4uX isr %4.4uX\n", + csr16r(ctlr, Imr), isr); + if(isr & Timerbit) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){ + print("rtl8139: port 0x%uX in use\n", port); + continue; + } + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +static int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; + edev->ifstat = rtl8139ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8139promiscuous; + + /* + * This should be much more dynamic but will do for now. + */ + if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0) + edev->mbps = 100; + + return 0; +} + +void +ether8139link(void) +{ + addethercard("rtl8139", rtl8139pnp); +} diff --git a/os/manga/etherif.h b/os/manga/etherif.h new file mode 100644 index 00000000..61be9ea0 --- /dev/null +++ b/os/manga/etherif.h @@ -0,0 +1,44 @@ +enum { + MaxEther = 4, + MaxFID= 16, + 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 */ + int vlanid; /* non-zero if vlan */ + + Queue* oq; + + QLock vlq; /* array change */ + int nvlan; + Ether* vlans[MaxFID]; + + Netif; +}; + +extern Block* etheriq(Ether*, Block*, int); +extern void addethercard(char*, int(*)(Ether*)); +extern int archether(int, Ether*); diff --git a/os/manga/etherks8695.c b/os/manga/etherks8695.c new file mode 100644 index 00000000..06e7de9e --- /dev/null +++ b/os/manga/etherks8695.c @@ -0,0 +1,1169 @@ +/* + * KS8695P ethernet + * WAN port, LAN port to 4-port switch + */ + +#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" + +#include "etherif.h" +#include "ureg.h" + +#define DBG if(0)iprint +#define MIIDBG if(0)iprint + +enum { + Nrdre = 64, /* receive descriptor ring entries */ + Ntdre = 32, /* transmit descriptor ring entries */ + + Rbsize = ROUNDUP(ETHERMAXTU+4, 4), /* ring buffer size (+4 for CRC), must be multiple of 4 */ + Bufsize = ROUNDUP(Rbsize, CACHELINESZ), /* keep start and end at cache lines */ +}; + +typedef struct DmaReg DmaReg; +struct DmaReg { + ulong dtxc; /* transmit control register */ + ulong drxc; /* receive control register */ + ulong dtsc; /* transmit start command register */ + ulong drsc; /* receive start command register */ + ulong tdlb; /* transmit descriptor list base address */ + ulong rdlb; /* receive descriptor list base address */ + ulong mal; /* mac address low (4 bytes) */ + ulong mah; /* mac address high (2 bytes) */ + ulong pad[0x80-0x20]; + + /* pad to 0x80 for */ + ulong maal[16][2]; /* additional mac addresses */ +}; + +enum { + /* dtxc */ + TxSoftReset= 1<<31, + /* 29:24 is burst size in words; 0, 1, 2, 4, 8, 16, 32; 0=unlimited */ + TxUDPck= 1<<18, /* generate UDP, TCP, IP check sum */ + TxTCPck= 1<<17, + TxIPck= 1<<16, + TxFCE= 1<<9, /* transmit flow control enable */ + TxLB= 1<<8, /* loop back */ + TxEP= 1<<2, /* enable padding */ + TxCrc= 1<<1, /* add CRC */ + TxEnable= 1<<0, /* enable Tx block */ + + /* drxc */ + /* 29:24 is burst size in words */ + RxUDPck= 1<<18, /* check UDP, TCP, IP check sum */ + RxTCPck= 1<<17, + RxIPck= 1<<16, + RxFCE= 1<<9, /* flow control enable */ + RxRB= 1<<6, /* receive broadcast */ + RxRM= 1<<5, /* receive multicast (including broadcast) */ + RxRU= 1<<4, /* receive unicast */ + RxAE= 1<<3, /* receive error frames */ + RxRA= 1<<2, /* receive all */ + RxEnable= 1<<0, /* enable Rx block */ + +}; + +typedef struct WanPhy WanPhy; +struct WanPhy { + ulong did; /* device ID */ + ulong rid; /* revision ID */ + ulong pad0; /* miscellaneous control in plain 8695 (not P or X) */ + ulong wmc; /* WAN miscellaneous control */ + ulong wppm; /* phy power management */ + ulong wpc; /* phys ctl */ + ulong wps; /* phys status */ + ulong pps; /* phy power save */ +}; + +enum { + /* wmc */ + WAnc= 1<<30, /* auto neg complete */ + WAnr= 1<<29, /* auto neg restart */ + WAnaP= 1<<28, /* advertise pause */ + WAna100FD= 1<<27, /* advertise 100BASE-TX FD */ + WAna100HD= 1<<26, /* advertise 100BASE-TX */ + WAna10FD= 1<<25, /* advertise 10BASE-TX FD */ + WAna10HD= 1<<24, /* advertise 10BASE-TX */ + WLs= 1<<23, /* link status */ + WDs= 1<<22, /* duplex status (resolved) */ + WSs= 1<<21, /* speed status (resolved) */ + WLparP= 1<<20, /* link partner pause */ + WLpar100FD= 1<<19, /* link partner 100BASE-TX FD */ + WLpar100HD= 1<<18, + WLpar10FD= 1<<17, + WLpar10HD= 1<<16, + WAnDis= 1<<15, /* auto negotiation disable */ + WForce100= 1<<14, + WForceFD= 1<<13, + /* 6:4 LED1 select */ + /* 2:0 LED0 select */ + + /* LED select */ + LedSpeed= 0, + LedLink, + LedFD, /* full duplex */ + LedColl, /* collision */ + LedTxRx, /* activity */ + LedFDColl, /* FD/collision */ + LedLinkTxRx, /* link and activity */ + + /* ppm */ + WLpbk= 1<<14, /* local (MAC) loopback */ + WRlpblk= 1<<13, /* remote (PHY) loopback */ + WPhyIso= 1<<12, /* isolate PHY from MII and Tx+/Tx- */ + WPhyLink= 1<<10, /* force link in PHY */ + WMdix= 1<<9, /* =1, MDIX, =0, MDX */ + WFef= 1<<8, /* far end fault */ + WAmdixp= 1<<7, /* disable IEEE spec for auto-neg MDIX */ + WTxdis= 1<<6, /* disable port's transmitter */ + WDfef= 1<<5, /* disable far end fault detection */ + Wpd= 1<<4, /* power down */ + WDmdx= 1<<3, /* disable auto MDI/MDIX */ + WFmdx= 1<<2, /* if auto disabled, force MDIX */ + WMlpbk= 1<<1, /* local loopback */ + + /* pps */ + Ppsm= 1<<0, /* enable PHY power save mode */ +}; + +#define DMABURST(n) ((n)<<24) + +typedef struct { + Lock; + int port; + int init; + int active; + int reading; /* device read process is active */ + ulong anap; /* auto negotiate result */ + DmaReg* regs; + WanPhy* wphy; + + Ring; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +} Ctlr; + +static void switchinit(uchar*); +static void switchdump(void); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + if(!ctlr->active){ + /* TO DO: rx/tx enable */ + ctlr->regs->dtxc |= TxEnable; + ctlr->regs->drxc |= RxEnable; + microdelay(10); + ctlr->regs->drsc = 1; /* start read process */ + microdelay(10); + ctlr->reading = (INTRREG->st & (1<<IRQwmrps)) == 0; + 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->dtxc &= ~TxEnable; + ctlr->regs->drxc &= ~RxEnable; + /* TO DO: reset ring? */ + /* TO DO: could wait? */ + ctlr->active = 0; + iunlock(ctlr); + } +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + ulong w; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + /* TO DO: must disable reader */ + w = ctlr->regs->drxc; + if(on != ((w&RxRA)!=0)){ + /* TO DO: must disable reader */ + ctlr->regs->drxc = w ^ RxRA; + /* TO DO: restart reader */ + } + 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); + /* TO DO: must disable reader */ + /* TO DO: use internal multicast tables? (probably needs LRU or some such) */ + if(ether->nmaddr) + ctlr->regs->drxc |= RxRM; + else + ctlr->regs->drxc &= ~RxRM; + 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->ctrl & BdBusy) + 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("etherks8695: txstart"); + ctlr->txb[ctlr->tdrh] = b; + dcflush(b->rp, len); + dre->addr = PADDR(b->rp); + dre->size = TxIC|TxFS|TxLS | len; + dre->ctrl = BdBusy; + ctlr->regs->dtsc = 1; /* go for it */ + 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->ctrl) & BdBusy) == 0){ + if(status & RxES || (status & (RxFS|RxLS)) != (RxFS|RxLS)){ + if(status & (RxRF|RxTL)) + ether->buffs++; + if(status & RxRE) + ether->frames++; + if(status & RxCE) + ether->crcs++; + //if(status & RxOverrun) + // ether->overflows++; + iprint("eth rx: %lux\n", status); + }else{ + /* + * We have a packet. Read it in. + */ + b = clallocb(); + if(b != nil){ + rb = ctlr->rxb[ctlr->rdrx]; + rb->wp += (dre->ctrl & RxFL)-4; + etheriq(ether, rb, 1); + ctlr->rxb[ctlr->rdrx] = b; + dre->addr = PADDR(b->wp); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, + * give it back to the chip, then on to the next... + */ + dre->ctrl = BdBusy; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + } +} + +static void +txring(Ureg*, void *arg) +{ + Ether *ether; + 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]; + if(dre->ctrl & BdBusy) + break; + /* statistics are kept inside the device, but only for LAN */ + /* there seems to be no per-packet error status for transmission */ + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("etherks8695: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre); + } + txstart(ether); + unlock(ctlr); +} + +/* + * receive buffer unavailable (overrun) + */ +static void +rbuintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(ctlr->active) + ctlr->overrun++; + ctlr->reading = 0; +} + +/* + * read process (in device) stopped + */ +static void +rxstopintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(!ctlr->active) + return; + +iprint("rxstopintr\n"); + ctlr->regs->drsc = 1; + /* just restart it? need to fiddle with ring? */ +} + +static void +txstopintr(Ureg*, void *arg) +{ + Ether *ether; + Ctlr *ctlr; + + ether = arg; + ctlr = ether->ctlr; + + ctlr->interrupts++; + if(!ctlr->active) + return; + +iprint("txstopintr\n"); + ctlr->regs->dtsc = 1; + /* just restart it? need to fiddle with ring? */ +} + + +static void +linkchangeintr(Ureg*, void*) +{ + iprint("link change\n"); +} + +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); +{DmaReg *d = ctlr->regs; len += snprint(p+len, READSTR-len, "dtxc=%8.8lux drxc=%8.8lux\n", d->dtxc, d->drxc);} + snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + n = readstr(offset, a, n, p); + free(p); + + if(ctlr->port == 1) + switchdump(); + return n; +} + +static void +physinit(Ether *ether, int force) +{ + Ctlr *ctlr; + WanPhy *p; + ulong anap; + int i; + + ctlr = ether->ctlr; + p = ctlr->wphy; + if(p == nil){ + if(ctlr->port){ + ether->mbps = 100; + ether->fullduplex = 1; + switchinit(nil); + } + return; + } + iprint("phy%d: wmc=%8.8lux wpm=%8.8lux wpc=%8.8lux wps=%8.8lux pps=%8.8lux\n", ctlr->port, p->wmc, p->wppm, p->wpc, p->wps, p->pps); + + p->wppm = 0; /* enable power, other defaults seem fine */ + if(p->rid & 7) + p->wpc = 0x0200b000; /* magic */ + else + p->wpc = 0xb000; + if(p->wppm & WFef) + iprint("ether%d: far end fault\n", ctlr->port); + + if((p->wmc & WLs) == 0){ + iprint("ether%d: no link\n", ctlr->port); + ether->mbps = 100; /* could use 10, but this is 2005 */ + ether->fullduplex = 0; + return; + } + + if((p->wmc & WAnc) == 0 || force){ + p->wmc = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD | (p->wmc & 0x7F); + microdelay(10); + if(p->wmc & WLs){ + for(i=0;; i++){ + if(i > 600){ + iprint("ether%d: auto negotiation failed\n", ctlr->port); + ether->mbps = 10; /* we'll assume it's stupid */ + ether->fullduplex = 0; + return; + } + if(p->wmc & WAnc){ + microdelay(10); + break; + } + delay(1); + } + } + } + anap = p->wmc; + ether->mbps = anap & WSs? 100: 10; + if(anap & (WLpar100FD|WLpar10FD) && anap & WDs) + ether->fullduplex = 1; + else + ether->fullduplex = 0; + ctlr->anap = anap; + + iprint("ks8695%d mii: fd=%d speed=%d wmc=%8.8lux\n", ctlr->port, ether->fullduplex, ether->mbps, anap); +} + +static void +ctlrinit(Ctlr *ctlr, Ether *ether) +{ + int i; + DmaReg *em; + ulong mode; + + em = ctlr->regs; + + /* soft reset */ + em->dtxc = TxSoftReset; + microdelay(10); + for(i=0; em->dtxc & TxSoftReset; i++){ + if(i > 20){ + iprint("etherks8695.%d: soft reset failed\n", ctlr->port); + i=0; + } + microdelay(100); + } +iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr)); + + physinit(ether, 0); + + /* set ether address */ + em->mah = (ether->ea[0]<<8) | ether->ea[1]; + em->mal = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5]; + if(ctlr->port == 0){ + /* clear other addresses for now */ + for(i=0; i<nelem(em->maal); i++){ + em->maal[i][0] = 0; + em->maal[i][1] = 0; + } + } + + /* transmitter, enabled later by attach */ + em->tdlb = PADDR(ctlr->tdr); + em->dtxc = DMABURST(8) | TxFCE | TxCrc; /* don't set TxEP: there is a h/w bug and it's anyway done by higher levels */ + + /* receiver, enabled later by attach */ + em->rdlb = PADDR(ctlr->rdr); + mode = DMABURST(8) | RxRB | RxRU | RxAE; /* RxAE just there for testing */ + if(ether->fullduplex) + mode |= RxFCE; + em->drxc = mode; + + /* tx/rx enable is deferred until attach */ +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + char name[KNAMELEN]; + Ctlr *ctlr; + int i, irqdelta; + + snprint(name, sizeof(name), "ether%d", ether->ctlrno); + + /* + * Insist that the platform-specific code provide the Ethernet address + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("%s (%s %ld): no ether address", name, ether->type, ether->port); + return -1; + } + + ctlr = malloc(sizeof(*ctlr)); + ctlr->port = ether->port; + + switch(ether->port){ + case 0: + ctlr->regs = KADDR(PHYSWANDMA); + ctlr->wphy = KADDR(PHYSMISC); + ctlr->wphy->wmc = (ctlr->wphy->wmc & ~0x7F) | (LedLinkTxRx<<0) | (LedSpeed<<4); + break; + case 1: + ctlr->regs = KADDR(PHYSLANDMA); + ctlr->wphy = nil; + break; + default: + print("%s: %s ether: no port %lud\n", name, ether->type, ether->port); + free(ctlr); + return -1; + } + + ether->ctlr = ctlr; + irqdelta = ether->irq - IRQwmrps; + + physinit(ether, 0); + + if(ioringinit(ctlr, Nrdre, Ntdre) < 0) + panic("etherks8695 initring"); + + for(i = 0; i < ctlr->nrdre; i++){ + if(ctlr->rxb[i] == nil) + ctlr->rxb[i] = clallocb(); + ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp); + ctlr->rdr[i].size = Rbsize; + ctlr->rdr[i].ctrl = BdBusy; + } + + ctlrinit(ctlr, ether); + + ether->attach = attach; + ether->closed = closed; + ether->transmit = transmit; + ether->ifstat = ifstat; + + /* there is more than one interrupt: we must enable some ourselves */ + ether->irq = irqdelta + IRQwmrs; /* set main IRQ to receive status */ + ether->interrupt = rxring; + intrenable(IRQ, irqdelta+IRQwmts, txring, ether, "ethertx"); +// intrenable(IRQ, irqdelta+IRQwmtbu, tbuintr, ether, "ethertbu"); /* don't care? */ + intrenable(IRQ, irqdelta+IRQwmrbu, rbuintr, ether, "etherrbu"); + intrenable(IRQ, irqdelta+IRQwmrps, rxstopintr, ether, "etherrps"); + intrenable(IRQ, irqdelta+IRQwmtps, txstopintr, ether, "ethertps"); + if(ether->port == 0) + intrenable(IRQ, IRQwmlc, linkchangeintr, ether, "etherwanlink"); + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + return 0; +} + +/* + * switch engine registers + * a 10 microsecond delay is required after each (write?) access + */ +typedef struct Switch Switch; +struct Switch { + ulong sec0; /* control register 0 */ + ulong sec1; /* control register 1 */ + ulong sec2; /* control register 2, factory default, do not change */ + ulong cfg[5][3]; /* port configuration registers */ + ulong an[2]; /* ports 1 to 4 auto negotiation [1,2][3,4] */ + ulong seiac; /* indirect access control register */ + ulong seiadh2; /* indirect access data register 2 (4:0 is 68-64 of data) */ + ulong seiadh1; /* indirect access data register 1 (63-32 of data) */ + ulong seiadl; /* indirect access data register low */ + ulong seafc; /* advanced feature control */ + ulong scph; /* services code priority high (ie, TOS priority) */ + ulong scpl; /* services code priority low */ + ulong mah; /* switch MAC address high */ + ulong mal; /* switch MAC address low */ + ulong ppm[2]; /* ports 1 to 4 PHY power management */ +}; + +enum { + /* Sec0 */ + Nbe= 1<<31, /* new backoff (designed for UNH) enable */ + /* 30:28 802.1p base priority */ + /* 27:25 LAN LED1 select */ + /* 24:22 LAN LED0 select */ + Unh= 1<<21, /* =1, drop packets with type 8808 or DA=0180c2000001; =0, drop flow control */ + Lca= 1<<20, /* link change age: faster aging for link->no link transition */ + Paf= 1<<19, /* pass all frames, including bad ones */ + Sfce= 1<<18, /* switch MII full-duplex flow control enable */ + Flfc= 1<<17, /* frame length field check in IEEE (drop invalid ones) */ + Bsm= 1<<16, /* =1, share all buffers; =0, use only 1/5 of pool */ + Age= 1<<15, /* enable age function */ + Agef= 1<<14, /* enable fast ageing */ + Aboe= 1<<13, /* aggressive backoff enable */ + Uvmd= 1<<12, /* unicast port-VLAN mismatch discard */ + Mspd= 1<<11, /* multicast storm protection disable */ + Bpm= 1<<10, /* =1, carrier sense backpressure; =0, collision backpressure */ + Fair= 1<<9, /* fair flow control and back pressure */ + Ncd= 1<<8, /* no excessive collision drop */ + Lmpsd= 1<<7, /* 1=, drop packet sizes over 1536 bytes; =0, 1522 for tagged, 1518 untagged */ + Pbr= 1<<6, /* priority buffer reserved */ + Sbpe= 1<<5, /* switch back pressure enable */ + Shdm= 1<<4, /* switch half duplex mode */ + PrioHi= 0<<2, /* always deliver high priority first */ + Prio10_1= 1<<2, /* high/low at 10:1 */ + Prio5_1= 2<<2, /* high/low at 5:1 */ + Prio2_1= 3<<2, /* high/low at 2:1 */ + Etm= 1<<1, /* enable tag mask */ + Esf= 1<<0, /* enable switch function */ + + /* sec1 */ + /* 31:21 */ /* broadcast storm protection, byte count */ + IEEEneg= 1<<11, /* follow IEEE spec for auto neg */ + Tpid= 1<<10, /* special TPID mode used for direct forwarding from port 5 */ + PhyEn= 1<<8, /* enable PHY MII */ + TfcDis= 1<<7, /* disable IEEE transmit flow control */ + RfcDis= 1<<6, /* disable IEEE receive flow control */ + Hps= 1<<5, /* huge packet support: allow packets up to 1916 bytes */ + VlanEn= 1<<4, /* 802.1Q VLAN enable; recommended when priority queue on */ + Sw10BT= 1<<1, /* switch in 10 Mbps mode not 100 Mbps */ + VIDrep= 1<<0, /* replace null VID with port VID (otherwise no replacement) */ + +}; +#define BASEPRIO(n) (((n)&7)<<28) + + +enum { + /* cfg[n][0] (SEP1C1-SEP4C1) p. 89 */ + /* 31:16 default tag: 31:29=userprio, 28=CFI bit, 27:16=VID[11:0] */ + AnegDis= 1<<15, /* disable auto negotiation */ + Force100= 1<<14, /* force 100BT when auto neg is disabled */ + ForceFD= 1<<13, /* force full duplex when auto neg is disabled */ + /* 12:8 port VLAN membership: bit 8 is port 1, bit 12 is port 5, 1=member */ + STTxEn= 1<<7, /* spanning tree transmit enable */ + STRxEn= 1<<6, /* spanning tree receive enable */ + STLnDis= 1<<5, /* spanning tree learn disnable */ + Bsp= 1<<4, /* enable broadcast storm protection */ + Pce= 1<<3, /* priority classification enable */ + Dpce= 1<<2, /* diffserv priority classification enable */ + IEEEpce= 1<<1, /* IEEE (802.1p) classification enable */ + PrioEn= 1<<0, /* enable priority function on port */ + + /* cfg[n][1] (SEP1C2-SEP4C2) p. 91*/ + IngressFilter= 1<<28, /* discard packets from ingress port not in VLAN */ + DiscardNonPVID= 1<<27, /* discard packets whose VID does not match port default VID */ + ForcePortFC= 1<<26, /* force flow control */ + EnablePortBP= 1<<25, /* enable back pressure */ + /* 23:12 transmit high priority rate control */ + /* 11:0 transmit low priority rate control */ + + /* cfg[n][2] */ + /* 13:20 receive high priority rate control */ + /* 19:8 receive low priority rate control */ + Rdprc= 1<<7, /* receive differential priority rate control */ + Lprrc= 1<<6, /* low priority receive rate control */ + Hprrc= 1<<5, /* high priority receive rate control */ + Lprfce= 1<<4, /* low priority receive flow control enable */ + Hprfce= 1<<3, /* high priority ... */ + Tdprc= 1<<2, /* transmit differential priority rate control */ + Lptrc= 1<<1, /* low priority transmit rate control */ + Hptrc= 1<<0, /* high priority transmit rate control */ + + /* seiac */ + Cread= 1<<12, + Cwrite= 0<<12, + StaticMacs= 0<<10, /* static mac address table used */ + VLANs= 1<<10, /* VLAN table */ + DynMacs= 2<<10, /* dynamic address table */ + MibCounter= 3<<10, /* MIB counter selected */ + /* 0:9, table index */ + + /* seafc */ + /* 26:22 1<<(n+22-1) = removal for port 0 to 4 */ +}; + +/* + * indirect access to + * static MAC address table (3.10.23, p. 107) + * VLAN table (3.10.24, p. 108) + * dynamic MAC address table (3.10.25, p. 109) + * MIB counters (3.10.26, p. 110) + */ +enum { + /* VLAN table */ + VlanValid= 1<<21, /* entry is valid */ + /* 20:16 are bits for VLAN membership */ + /* 15:12 are bits for FID (filter id) for up to 16 active VLANs */ + /* 11:0 has 802.1Q 12 bit VLAN ID */ + + /* Dynamic MAC table (1024 entries) */ + MACempty= 1<<(68-2*32), + /* 67:58 is number of valid entries-1 */ + /* 57:56 ageing time stamp */ + NotReady= 1<<(55-32), + /* 54:52 source port 0 to 5 */ + /* 51:48 FID */ + /* 47:0 MAC */ + + NVlans= 16, + NSMacs= 8, +}; + +/* + * per-port counters, table 3, 3.10.26, p. 110 + * cleared when read + * port counters at n*0x20 [n=0-3] + */ +static char* portmibnames[] = { + "RxLoPriorityByte", + "RxHiPriorityByte", + "RxUndersizePkt", + "RxFragments", + "RxOversize", + "RxJabbers", + "RxSymbolError", + "RxCRCerror", + "RxAlignmentError", + "RxControl8808Pkts", + "RxPausePkts", + "RxBroadcast", + "RxMulticast", + "RxUnicast", + "Rx64Octets", + "Rx65to127Octets", + "Rx128to255Octets", + "Rx256to511Octets", + "Rx512to1023Octets", + "Rx1024to1522Octets", + "TxLoPriorityByte", + "TxHiPriorityByte", + "TxLateCollision", + "TxPausePkts", + "TxBroadcastPkts", + "TxMulticastPkts", + "TxUnicastPkts", + "TxDeferred", + "TxTotalCollision", /* like, totally */ + "TxExcessiveCollision", + "TxSingleCollision", + "TxMultipleCollision", +}; +enum { + /* per-port MIB counter format */ + MibOverflow= 1<<31, + MibValid= 1<<30, + /* 29:0 counter value */ +}; + +/* + * 16 bit `all port' counters, not automatically cleared + * offset 0x100 and up + */ + +static char* allportnames[] = { + "Port1TxDropPackets", + "Port2TxDropPackets", + "Port3TxDropPackets", + "Port4TxDropPackets", + "LanTxDropPackets", /* ie, internal port 5 */ + "Port1RxDropPackets", + "Port2RxDropPackets", + "Port3RxDropPackets", + "Port4RxDropPackets", + "LanRxDropPackets", +}; + +static void +switchinit(uchar *ea) +{ + Switch *sw; + int i; + ulong an; + + /* TO DO: LED gpio setting */ + + GPIOREG->iopm |= 0xF0; /* bits 4-7 are LAN(?) */ +iprint("switch init...\n"); + sw = KADDR(PHYSSWITCH); + if(sw->sec0 & Esf){ + iprint("already inited\n"); + return; + } + sw->seafc = 0; + microdelay(10); + sw->scph = 0; + microdelay(10); + sw->scpl = 0; + microdelay(10); + if(ea != nil){ + sw->mah = (ea[0]<<8) | ea[1]; + microdelay(10); + sw->mal = (ea[2]<<24) | (ea[3]<<16) | (ea[4]<<8) | ea[5]; + microdelay(10); + } + for(i = 0; i < 5; i++){ + sw->cfg[i][0] = (0x1F<<8) | STTxEn | STRxEn | Bsp; /* port is member of all vlans */ + microdelay(10); + sw->cfg[i][1] = 0; + microdelay(10); + sw->cfg[i][2] = 0; + microdelay(10); + } + sw->ppm[0] = 0; /* perhaps soft reset? */ + microdelay(10); + sw->ppm[1] = 0; + microdelay(10); + an = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD; + sw->an[0] = an | (an >> 16); + microdelay(10); + sw->an[1] = an | (an >> 16); + microdelay(10); + sw->sec1 = (0x4A<<21) | PhyEn; + microdelay(10); + sw->sec0 = Nbe | (0<<28) | (LedSpeed<<25) | (LedLinkTxRx<<22) | Sfce | Bsm | Age | Aboe | Bpm | Fair | Sbpe | Shdm | Esf; + microdelay(10); + + /* off we go */ +} + +typedef struct Vidmap Vidmap; +struct Vidmap { + uchar ports; /* bit mask for ports 0 to 4 */ + uchar fid; /* switch's filter id */ + ushort vid; /* 802.1Q vlan id; 0=not valid */ +}; + +static Vidmap +getvidmap(Switch *sw, int i) +{ + ulong w; + Vidmap v; + + v.ports = 0; + v.fid = 0; + v.vid = 0; + if(i < 0 || i >= NVlans) + return v; + sw->seiac = Cread | VLANs | i; + microdelay(10); + w = sw->seiadl; + if((w & VlanValid) == 0) + return v; + v.vid = w & 0xFFFF; + v.fid = (w>>12) & 0xF; + v.ports = (w>>16) & 0x1F; + return v; +} + +static void +putvidmap(Switch *sw, int i, Vidmap v) +{ + ulong w; + + w = ((v.ports & 0x1F)<<16) | ((v.fid & 0xF)<<12) | (v.vid & 0xFFFF); + if(v.vid != 0) + w |= VlanValid; + sw->seiadl = w; + microdelay(10); + sw->seiac = Cwrite | VLANs | i; + microdelay(10); +} + +typedef struct StaticMac StaticMac; +struct StaticMac { + uchar valid; + uchar fid; + uchar usefid; + uchar override; /* override spanning tree tx/rx disable */ + uchar ports; /* forward to this set of ports */ + uchar mac[Eaddrlen]; +}; + +static StaticMac +getstaticmac(Switch *sw, int i) +{ + StaticMac s; + ulong w; + + memset(&s, 0, sizeof(s)); + if(i < 0 || i >= NSMacs) + return s; + sw->seiac = Cread | StaticMacs | i; + microdelay(10); + w = sw->seiadh1; + if((w & (1<<(53-32))) == 0) + return s; /* entry not valid */ + s.valid = 1; + s.fid= (w>>(57-32)) & 0xF; + s.usefid = (w & (1<<(56-32))) != 0; + s.override = (w & (1<<(54-32))) != 0; + s.ports = (w>>(48-32)) & 0x1F; + s.mac[5] = w >> 8; + s.mac[4] = w; + w = sw->seiadl; + s.mac[3] = w>>24; + s.mac[2] = w>>16; + s.mac[1] = w>>8; + s.mac[0] = w; + return s; +} + +static void +putstaticmac(Switch *sw, int i, StaticMac s) +{ + ulong w; + + if(s.valid){ + w = 1<<(53-32); /* entry valid */ + if(s.usefid) + w |= 1<<(55-32); + if(s.override) + w |= 1<<(54-32); + w |= (s.fid & 0xF) << (56-32); + w |= (s.ports & 0x1F) << (48-32); + w |= (s.mac[5] << 8) | s.mac[4]; + sw->seiadh1 = w; + microdelay(10); + w = (s.mac[3]<<24) | (s.mac[2]<<16) | (s.mac[1]<<8) | s.mac[0]; + sw->seiadl = w; + microdelay(10); + }else{ + sw->seiadh1 = 0; /* valid bit is 0; rest doesn't matter */ + microdelay(10); + } + sw->seiac = Cwrite | StaticMacs | i; + microdelay(10); +} + +typedef struct DynMac DynMac; +struct DynMac { + ushort nentry; + uchar valid; + uchar age; + uchar port; /* source port (0 origin) */ + uchar fid; /* filter id */ + uchar mac[Eaddrlen]; +}; + +static DynMac +getdynmac(Switch *sw, int i) +{ + DynMac d; + ulong w; + int n, l; + + memset(&d, 0, sizeof d); + l = 0; + do{ + if(++l > 100) + return d; + sw->seiac = Cread | DynMacs | i; + microdelay(10); + w = sw->seiadh2; + /* peculiar encoding of table size */ + if(w & MACempty) + return d; + n = w & 0xF; + w = sw->seiadh1; + }while(w & NotReady); /* TO DO: how long might it delay? */ + d.nentry = ((n<<6) | (w>>(58-32))) + 1; + if(i < 0 || i >= d.nentry) + return d; + d.valid = 1; + d.age = (w>>(56-32)) & 3; + d.port = (w>>(52-32)) & 7; + d.fid = (w>>(48-32)) & 0xF; + d.mac[5] = w>>8; + d.mac[4] = w; + w = sw->seiadl; + d.mac[3] = w>>24; + d.mac[2] = w>>16; + d.mac[1] = w>>8; + d.mac[0] = w; + return d; +} + +static void +switchdump(void) +{ + Switch *sw; + int i, j; + ulong w; + + sw = KADDR(PHYSSWITCH); + iprint("sec0 %8.8lux\n", sw->sec0); + iprint("sec1 %8.8lux\n", sw->sec1); + for(i = 0; i < 5; i++){ + iprint("cfg%d", i); + for(j = 0; j < 3; j++){ + w = sw->cfg[i][j]; + iprint(" %8.8lux", w); + } + iprint("\n"); + if(i < 2){ + w = sw->an[i]; + iprint(" an=%8.8lux pm=%8.8lux\n", w, sw->ppm[i]); + } + } + for(i = 0; i < 8; i++){ + sw->seiac = Cread | DynMacs | i; + microdelay(10); + w = sw->seiadh2; + microdelay(10); + iprint("dyn%d: %8.8lux", i, w); + w = sw->seiadh1; + microdelay(10); + iprint(" %8.8lux", w); + w = sw->seiadl; + microdelay(10); + iprint(" %8.8lux\n", w); + } + for(i=0; i<0x20; i++){ + sw->seiac = Cread | MibCounter | i; + microdelay(10); + w = sw->seiadl; + microdelay(10); + if(w & (1<<30)) + iprint("%.2ux: %s: %lud\n", i, portmibnames[i], w & ~(3<<30)); + } +} + +static void +switchstatproc(void*) +{ + for(;;){ + tsleep(&up->sleep, return0, nil, 30*1000); + } +} + +void +etherks8695link(void) +{ + addethercard("ks8695", reset); +} + +/* + * notes: + * switch control + * read stats every 30 seconds or so + */ diff --git a/os/manga/flashif.h b/os/manga/flashif.h new file mode 100644 index 00000000..14b6a9e1 --- /dev/null +++ b/os/manga/flashif.h @@ -0,0 +1,82 @@ +typedef struct Flash Flash; + +/* + * structure defining a flash memory card + */ +struct Flash { + QLock; /* interlock on flash operations */ + Flash* next; + + /* the following are filled in by devflash before Flash.reset called */ + char* name; + void* addr; + ulong size; + void * archdata; + int (*reset)(Flash*); + + /* the following are filled in by the reset routine */ + int (*eraseall)(Flash*); + int (*erasezone)(Flash*, int); + int (*read)(Flash*, ulong, void*, long); /* reads of correct width and alignment */ + int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */ + int (*suspend)(Flash*); + int (*resume)(Flash*); + int (*attach)(Flash*); + + uchar id; /* flash manufacturer ID */ + uchar devid; /* flash device ID */ + int width; /* bytes per flash line */ + int erasesize; /* size of erasable unit (accounting for width) */ + void* data; /* flash type routines' private storage, or nil */ + ulong unusable; /* bit mask of unusable sections */ +}; + +/* + * called by link routine of driver for specific flash type: arguments are + * conventional name for card type/model, and card driver's reset routine. + */ +void addflashcard(char*, int (*)(Flash*)); + +/* + * called by devflash.c:/^flashreset; if flash exists, + * sets type, address, and size in bytes of flash + * and returns 0; returns -1 if flash doesn't exist + */ +int archflashreset(int instance, char*, int, void**, long*, void **archdata); + +int archflash12v(int); +void archflashwp(void *archdata, int); + +/* + * Architecture specific routines for managing nand devices + */ + +/* + * do any device spcific initialisation + */ +void archnand_init(void *archdata); + +/* + * if claim is 1, claim device exclusively, and enable it (power it up) + * if claim is 0, release, and disable it (power it down) + * claiming may be as simple as a qlock per device + */ +void archnand_claim(void *archdata, int claim); + +/* + * set command latch enable (CLE) and address latch enable (ALE) + * appropriately + */ +void archnand_setCLEandALE(void *archdata, int cle, int ale); + +/* + * write a sequence of bytes to the device + */ +void archnand_write(void *archdata, void *buf, int len); + +/* + * read a sequence of bytes from the device + * if buf is 0, throw away the data + */ +void archnand_read(void *archdata, void *buf, int len); + diff --git a/os/manga/fns.h b/os/manga/fns.h new file mode 100644 index 00000000..6eb28e26 --- /dev/null +++ b/os/manga/fns.h @@ -0,0 +1,163 @@ +#include "../port/portfns.h" + +ulong aifinit(uchar *aifarr); +int archaudiopower(int); +void archaudiomute(int); +void archaudioamp(int); +int archaudiospeed(int, int); +void archconfinit(void); +void archconsole(void); +int archflash12v(int); +long archkprofmicrosecondspertick(void); +void archkprofenable(int); +void archpowerdown(void); +void archpowerup(void); +void archreboot(void); +void archreset(void); +vlong archrdtsc(void); +ulong archrdtsc32(void); +void archuartpower(int, int); +void blankscreen(int); +void clockcheck(void); +void clockinit(void); +void clockpoll(void); +#define coherence() /* nothing to do for cache coherence for uniprocessor */ +void cursorhide(void); +void cursorunhide(void); +void dcflush(void*, ulong); +void dcflushall(void); +void dcinval(void); +int dmaidle(Dma*); +Dma* dmasetup(int device, void(*)(void*,ulong), void*, ulong); +int dmastart(Dma*, void*, void*, int); +int dmacontinue(Dma*, void*, int); +void dmastop(Dma*); +int dmaerror(Dma*); +void dmafree(Dma*); +void dmareset(void); +void dmawait(Dma*); +void dumplongs(char *, ulong *, int); +void dumpregs(Ureg* ureg); +void dumpstack(void); +int fpiarm(Ureg*); +void fpinit(void); +ulong getcallerpc(void*); +ulong getcclkcfg(void); +char* getconf(char*); +ulong getcpsr(void); +ulong getcpuid(void); +ulong getspsr(void); +void gotopc(ulong); + +void icflush(void*, ulong); +void icflushall(void); +void idle(void); +void idlehands(void); +int inb(ulong); +int ins(ulong); +ulong inl(ulong); +void outb(ulong, int); +void outs(ulong, int); +void outl(ulong, ulong); +void inss(ulong, void*, int); +void outss(ulong, void*, int); +void insb(ulong, void*, int); +void outsb(ulong, void*, int); +void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*); +void intrenable(int, int, void (*)(Ureg*, void*), void*, char*); +void iofree(int); +#define iofree(x) +void ioinit(void); +int iounused(int, int); +int ioalloc(int, int, int, char*); +#define ioalloc(a,b,c,d) 0 +int iprint(char*, ...); +void installprof(void (*)(Ureg *, int)); +int isvalid_va(void*); +void kbdinit(void); +void ledset(int); +void links(void); +void mmuenable(ulong); +void* mmucacheinhib(void*, ulong); +ulong mmugetctl(void); +ulong mmugetdac(void); +ulong mmugetfar(void); +ulong mmugetfsr(void); +void mmuinit(void); +void* mmukaddr(ulong); +void* mmuphysmap(void*, ulong, ulong); +void mmuputctl(ulong); +void mmuputdac(ulong); +void mmuputfsr(ulong); +void mmuputttb(ulong); +void mmureset(void); +void mouseinit(void); +void* pa2va(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 powerenable(void (*)(int)); +void powerdisable(void (*)(int)); +void powerdown(void); +void powerinit(void); +void powersuspend(void); +#define procsave(p) +#define procrestore(p) +void putcclkcfg(ulong); +long rtctime(void); +void screeninit(void); +void (*screenputs)(char*, int); +int segflush(void*, ulong); +void setpanic(void); +void setr13(int, void*); +int splfhi(void); +int splflo(void); +void _suspendcode(void); +void tlbinvalidateall(void); +void tlbinvalidateaddr(void*); +void trapinit(void); +void trapstacks(void); +void trapspecial(int (*)(Ureg *, uint)); +void uartconsole(void); +void uartinstall(void); +int uartprint(char*, ...); +ulong va2pa(void*); +void vectors(void); +void vtable(void); +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +int wasbusy(int); + +#define KADDR(p) mmukaddr((ulong)(p)) +#define PADDR(v) va2pa((void*)(v)) + +ulong timer_start(void); +ulong timer_ticks(ulong); +int timer_devwait(ulong *adr, ulong mask, ulong val, int ost); +void timer_setwatchdog(int ost); +void timer_delay(int ost); +ulong ms2tmr(int ms); +int tmr2ms(ulong t); +void delay(int ms); +ulong us2tmr(int us); +int tmr2us(ulong t); +void microdelay(int us); + +#define archuartclock(p,rate) 14745600 + +/* debugging */ +extern void serialputs(char*, int); +extern void serialputc(int); +extern void xdelay(int); diff --git a/os/manga/fpi.h b/os/manga/fpi.h new file mode 100644 index 00000000..dfb9b1df --- /dev/null +++ b/os/manga/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/manga/fpiarm.c b/os/manga/fpiarm.c new file mode 100644 index 00000000..4acfcd1d --- /dev/null +++ b/os/manga/fpiarm.c @@ -0,0 +1,483 @@ +/* + * this doesn't attempt to implement ARM 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 R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below + +#define REG(x) (*(long*)(((char*)(ur))+roff[(x)])) +#define FPENV (*(ufp)) +#define FR(x) (*(Internal*)(ufp)->regs[(x)&7]) + +/* BUG: check fetch (not worthwhile in Inferno) */ +#define getubyte(a) (*(uchar*)(a)) +#define getuword(a) (*(ushort*)(a)) +#define getulong(a) (*(ulong*)(a)) + +typedef struct FP2 FP2; +typedef struct FP1 FP1; + +struct FP2 { + char* name; + void (*f)(Internal, Internal, Internal*); +}; + +struct FP1 { + char* name; + void (*f)(Internal*, Internal*); +}; + +enum { + N = 1<<31, + Z = 1<<30, + C = 1<<29, + V = 1<<28, + REGPC = 15, +}; + +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), +#ifdef R13OK + OFR(r12), OFR(r13), OFR(r14), OFR(pc), +#else + OFR(r12), OFR(type), OFR(r14), OFR(pc), +#endif +}; + +static Internal fpconst[8] = { /* indexed by op&7 */ + /* s, e, l, h */ + {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ + {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ + {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ + {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ + {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ + {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ + {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ + {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ +}; + +/* + * arm binary operations + */ + +static void +fadd(Internal m, Internal n, Internal *d) +{ + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsub(Internal m, Internal n, Internal *d) +{ + m.s ^= 1; + (m.s == n.s? fpiadd: fpisub)(&m, &n, d); +} + +static void +fsubr(Internal m, Internal n, Internal *d) +{ + n.s ^= 1; + (n.s == m.s? fpiadd: fpisub)(&n, &m, d); +} + +static void +fmul(Internal m, Internal n, Internal *d) +{ + fpimul(&m, &n, d); +} + +static void +fdiv(Internal m, Internal n, Internal *d) +{ + fpidiv(&m, &n, d); +} + +static void +fdivr(Internal m, Internal n, Internal *d) +{ + fpidiv(&n, &m, d); +} + +/* + * arm unary operations + */ + +static void +fmov(Internal *m, Internal *d) +{ + *d = *m; +} + +static void +fmovn(Internal *m, Internal *d) +{ + *d = *m; + d->s ^= 1; +} + +static void +fabsf(Internal *m, Internal *d) +{ + *d = *m; + d->s = 0; +} + +static void +frnd(Internal *m, Internal *d) +{ + short e; + + (m->s? fsub: fadd)(fpconst[6], *m, d); + if(IsWeird(d)) + return; + fpiround(d); + e = (d->e - ExpBias) + 1; + if(e <= 0) + SetZero(d); + else if(e > FractBits){ + if(e < 2*FractBits) + d->l &= ~((1<<(2*FractBits - e))-1); + }else{ + d->l = 0; + if(e < FractBits) + d->h &= ~((1<<(FractBits-e))-1); + } +} + +static FP1 optab1[16] = { /* Fd := OP Fm */ +[0] {"MOVF", fmov}, +[1] {"NEGF", fmovn}, +[2] {"ABSF", fabsf}, +[3] {"RNDF", frnd}, +[4] {"SQTF", /*fsqt*/0}, +/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ +/* URD and NRM aren't implemented */ +}; + +static FP2 optab2[16] = { /* Fd := Fn OP Fm */ +[0] {"ADDF", fadd}, +[1] {"MULF", fmul}, +[2] {"SUBF", fsub}, +[3] {"RSUBF", fsubr}, +[4] {"DIVF", fdiv}, +[5] {"RDIVF", fdivr}, +/* POW, RPW deprecated */ +[8] {"REMF", /*frem*/0}, +[9] {"FMF", fmul}, /* fast multiply */ +[10] {"FDV", fdiv}, /* fast divide */ +[11] {"FRD", fdivr}, /* fast reverse divide */ +/* POL deprecated */ +}; + +static ulong +fcmp(Internal *n, Internal *m) +{ + int i; + + if(IsWeird(m) || IsWeird(n)){ + /* BUG: should trap if not masked */ + return V|C; + } + i = fpicmp(n, m); + if(i > 0) + return C; + else if(i == 0) + return C|Z; + else + return N; +} + +static void +fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp) +{ + void *mem; + + mem = (void*)ea; + (*f)(&FR(d), mem); + if(fpemudebug) + print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); +} + +static void +fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp) +{ + Internal tmp; + void *mem; + + mem = (void*)ea; + tmp = FR(s); + if(fpemudebug) + print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); + (*f)(mem, &tmp); +} + +static int +condok(int cc, int c) +{ + switch(c){ + case 0: /* Z set */ + return cc&Z; + case 1: /* Z clear */ + return (cc&Z) == 0; + case 2: /* C set */ + return cc&C; + case 3: /* C clear */ + return (cc&C) == 0; + case 4: /* N set */ + return cc&N; + case 5: /* N clear */ + return (cc&N) == 0; + case 6: /* V set */ + return cc&V; + case 7: /* V clear */ + return (cc&V) == 0; + case 8: /* C set and Z clear */ + return cc&C && (cc&Z) == 0; + case 9: /* C clear or Z set */ + return (cc&C) == 0 || cc&Z; + case 10: /* N set and V set, or N clear and V clear */ + return (~cc&(N|V))==0 || (cc&(N|V)) == 0; + case 11: /* N set and V clear, or N clear and V set */ + return (cc&(N|V))==N || (cc&(N|V))==V; + case 12: /* Z clear, and either N set and V set or N clear and V clear */ + return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); + case 13: /* Z set, or N set and V clear or N clear and V set */ + return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; + case 14: /* always */ + return 1; + case 15: /* never (reserved) */ + return 0; + } + return 0; /* not reached */ +} + +static void +unimp(ulong pc, ulong op) +{ + char buf[60]; + + snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); + if(fpemudebug) + print("FPE: %s\n", buf); + error(buf); + /* no return */ +} + +static void +fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp) +{ + int rn, rd, tag, o; + long off; + ulong ea; + Internal tmp, *fm, *fn; + + /* note: would update fault status here if we noted numeric exceptions */ + + /* + * LDF, STF; 10.1.1 + */ + if(((op>>25)&7) == 6){ + if(op & (1<<22)) + unimp(pc, op); /* packed or extended */ + rn = (op>>16)&0xF; + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(rn); + if(rn == REGPC) + ea += 8; + if(op & (1<<24)) + ea += off; + rd = (op>>12)&7; + if(op & (1<<20)){ + if(op & (1<<15)) + fld(fpid2i, rd, ea, 8, ufp); + else + fld(fpis2i, rd, ea, 4, ufp); + }else{ + if(op & (1<<15)) + fst(fpii2d, ea, rd, 8, ufp); + else + fst(fpii2s, ea, rd, 4, ufp); + } + if((op & (1<<24)) == 0) + ea += off; + if(op & (1<<21)) + REG(rn) = ea; + return; + } + + /* + * CPRT/transfer, 10.3 + */ + if(op & (1<<4)){ + rd = (op>>12) & 0xF; + + /* + * compare, 10.3.1 + */ + if(rd == 15 && op & (1<<20)){ + rn = (op>>16)&7; + fn = &FR(rn); + if(op & (1<<3)){ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + switch((op>>21)&7){ + default: + unimp(pc, op); + case 4: /* CMF: Fn :: Fm */ + case 6: /* CMFE: Fn :: Fm (with exception) */ + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, fm); + break; + case 5: /* CNF: Fn :: -Fm */ + case 7: /* CNFE: Fn :: -Fm (with exception) */ + tmp = *fm; + tmp.s ^= 1; + ur->psr &= ~(N|C|Z|V); + ur->psr |= fcmp(fn, &tmp); + break; + } + if(fpemudebug) + print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28); + return; + } + + /* + * other transfer, 10.3 + */ + switch((op>>20)&0xF){ + default: + unimp(pc, op); + case 0: /* FLT */ + rn = (op>>16) & 7; + fpiw2i(&FR(rn), ®(rd)); + if(fpemudebug) + print("MOVW[FD] R%d, F%d\n", rd, rn); + break; + case 1: /* FIX */ + if(op & (1<<3)) + unimp(pc, op); + rn = op & 7; + tmp = FR(rn); + fpii2w(®(rd), &tmp); + if(fpemudebug) + print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd)); + break; + case 2: /* FPSR := Rd */ + FPENV.status = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPSR\n", rd); + break; + case 3: /* Rd := FPSR */ + REG(rd) = FPENV.status; + if(fpemudebug) + print("MOVW FPSR, R%d\n", rd); + break; + case 4: /* FPCR := Rd */ + FPENV.control = REG(rd); + if(fpemudebug) + print("MOVW R%d, FPCR\n", rd); + break; + case 5: /* Rd := FPCR */ + REG(rd) = FPENV.control; + if(fpemudebug) + print("MOVW FPCR, R%d\n", rd); + break; + } + return; + } + + /* + * arithmetic + */ + + if(op & (1<<3)){ /* constant */ + fm = &fpconst[op&7]; + tag = 'C'; + }else{ + fm = &FR(op&7); + tag = 'F'; + } + rd = (op>>12)&7; + o = (op>>20)&0xF; + if(op & (1<<15)){ /* monadic */ + FP1 *fp; + fp = &optab1[o]; + if(fp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); + (*fp->f)(fm, &FR(rd)); + } else { + FP2 *fp; + fp = &optab2[o]; + if(fp->f == nil) + unimp(pc, op); + rn = (op>>16)&7; + if(fpemudebug) + print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); + (*fp->f)(*fm, FR(rn), &FR(rd)); + } +} + +/* + * returns the number of FP instructions emulated + */ +int +fpiarm(Ureg *ur) +{ + ulong op, o; + FPenv *ufp; + int n; + +#ifndef R13OK +/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */ + ur->type = (ulong)(ur + 1); +#endif + if (up == nil) + panic("fpiarm not in a process"); + ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ + if(ufp->fpistate != FPACTIVE) { + ufp->fpistate = FPACTIVE; + ufp->control = 0; + ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ + for(n = 0; n < 8; n++) + FR(n) = fpconst[0]; + } + for(n=0;;n++){ + op = getulong(ur->pc); + o = (op>>24) & 0xF; + if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC) + break; + if(condok(ur->psr, op>>28)) + fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; + if(anyhigher()) + sched(); + } + return n; +} diff --git a/os/manga/gpio.c b/os/manga/gpio.c new file mode 100644 index 00000000..c6d0179d --- /dev/null +++ b/os/manga/gpio.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "mem.h" +#include "../port/lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +static ulong gpioreserved; +static Lock gpiolock; + +void +gpioreserve(int n) +{ + ulong mask; + + mask = 1<<n; + ilock(&gpiolock); + if(gpioreserved & mask) + panic("gpioreserve: duplicate use of GPIO %d", n); + gpioreserved |= mask; + iunlock(&gpiolock); +} + +/* + * set direction and alternative function bits in the GPIO control register, + * following the configuration bits in cfg. + */ +void +gpioconfig(int n, ulong cfg) +{ + GpioReg *g; + + ilock(&gpiolock); + g = GPIOREG; + if(cfg & Gpio_out) + g->iopm |= 1<<n; + else + g->iopm &= ~(1<<n); + iunlock(&gpiolock); +} + +ulong +gpioget(int n) +{ + return GPIOREG->iopd & (1<<n); +} + +void +gpioset(int n, int v) +{ + GpioReg *g; + ulong mask; + + mask = 1<<n; + ilock(&gpiolock); + g = GPIOREG; + if(v) + g->iopd |= mask; + else + g->iopd &= ~mask; + iunlock(&gpiolock); +} + +void +gpiorelease(int n) +{ + ulong mask; + + mask = 1<<n; + ilock(&gpiolock); + if((gpioreserved & mask) != mask) + panic("gpiorelease: unexpected release of GPIO %d", n); + gpioreserved &= ~mask; + iunlock(&gpiolock); +} diff --git a/os/manga/inb.c b/os/manga/inb.c new file mode 100644 index 00000000..26c45825 --- /dev/null +++ b/os/manga/inb.c @@ -0,0 +1,85 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#define KIOP(port) KADDR(PHYSPCIIO+(port)) + +int +inb(ulong p) +{ + return *(uchar*)KIOP(p); +} + +int +ins(ulong p) +{ + return *(ushort*)KIOP(p); +} + +ulong +inl(ulong p) +{ + return *(ulong*)KIOP(p); +} + +void +outb(ulong p, int v) +{ + *(uchar*)KIOP(p) = v; +} + +void +outs(ulong p, int v) +{ + *(ushort*)KIOP(p) = v; +} + +void +outl(ulong p, ulong v) +{ + *(ulong*)KIOP(p) = v; +} + +void +inss(ulong p, void* buf, int ns) +{ + ushort *addr; + + addr = (ushort*)buf; + for(;ns > 0; ns--) + *addr++ = *(ushort*)KIOP(p); +} + +void +outss(ulong p, void* buf, int ns) +{ + ushort *addr; + + addr = (ushort*)buf; + for(;ns > 0; ns--) + *(ushort*)KIOP(p) = *addr++; +} + +void +insb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *addr++ = *(uchar*)KIOP(p); +} + +void +outsb(ulong p, void* buf, int ns) +{ + uchar *addr; + + addr = (uchar*)buf; + for(;ns > 0; ns--) + *(uchar*)KIOP(p) = *addr++; +} diff --git a/os/manga/io.h b/os/manga/io.h new file mode 100644 index 00000000..6e53acc1 --- /dev/null +++ b/os/manga/io.h @@ -0,0 +1,320 @@ +typedef struct BD BD; +typedef struct Ring Ring; + +/* + * types of interrupts + */ +enum +{ + /* some flags to change polarity and sensitivity */ + IRQmask= 0xFF, /* actual vector address */ + IRQactivelow= 0<<8, + IRQactivehigh= 1<<8, + IRQrising= 2<<8, + IRQfalling= 4<<8, + IRQmode= IRQactivelow | IRQactivehigh | IRQrising | IRQfalling, + IRQsoft= 1<<11, /* configure ext0 to ext3 as GPIO output */ + IRQ= 0, /* notional bus */ +}; + +enum { + IRQwmlc= 31, /* WAN link changed (edge) */ + IRQwmts= 30, /* WAN MAC transmit status (edge) */ + IRQwmrs= 29, /* WAN MAC receive status (edge) */ + IRQwmtbu= 28, /* WAN MAC transmit buffer unavailable (edge) */ + IRQwmrbu= 27, /* WAN MAC receive buffer unavailable (edge) */ + IRQwmtps= 26, /* WAN MAC transmit process stopped (edge) */ + IRQwmrps= 25, /* WAN MAC receive process stopped (edge) */ + IRQaber= 24, /* AMBA bus error (level) */ + IRQlmts= 17, /* LAN MAC transmit status (edge) */ + IRQlmrs= 16, /* LAN MAC receive status (edge) */ + IRQlmtbu= 15, /* LAN AMC transmit buffer unavailable (edge) */ + IRQlmrbu= 14, /* LAN MAC receive buffer unavailable (edge) */ + IRQlmtps= 13, /* LAN MAC transmit process stopped (edge) */ + IRQlmrps= 12, /* LAN MAC receive process stopped (edge) */ + IRQums= 11, /* UART modem status (level) */ + IRQule= 10, /* UART line status (level) */ + IRQurs= 9, /* UART receive status (level) */ + IRQuts= 8, /* UART transmit status (level) */ + IRQtm1= 7, /* timer 1 (edge) */ + IRQtm0= 6, /* timer 0 (edge) */ + IRQext3= 5, /* external interrupts (gpio control selects edge or level) */ + IRQext2= 4, + IRQext1= 3, + IRQext0= 2, + IRQccts= 1, /* comms channel transmit status (level) */ + IRQccrs= 0, /* comms channel receive status (level) */ +}; + +/* + * 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 { + BusIRQ = IRQ, + BusPCI, + MaxBus +}; + +#define INTRREG ((IntrReg*)PHYSINTR) +typedef struct IntrReg IntrReg; +struct IntrReg { + ulong mc; /* mode control */ + ulong en; /* enable */ + ulong st; /* status */ + ulong pw; /* priority for WAN */ + ulong pad0; + ulong pl; /* priority for LAN */ + ulong pt; /* priority for timer */ + ulong pu; /* priority for UART */ + ulong pe; /* priority for external */ + ulong pc; /* priority for comms channel */ + ulong pbe; /* priority for bus error response */ + ulong ms; /* mask status */ + ulong hpf; /* highest priority for FIQ */ + ulong hpi; /* highest priority for IRQ */ +}; + +#define TIMERREG ((TimerReg*)PHYSTIMER) +typedef struct TimerReg TimerReg; +struct TimerReg { + ulong enable; /* 1<<n to enable timer n */ + ulong count1; + ulong count0; /* 0 becomes watchdog if byte 0 is 0xFF */ + ulong pulse1; + ulong pulse0; +}; + +#define GPIOREG ((GpioReg*)PHYSGPIO) +typedef struct GpioReg GpioReg; +struct GpioReg { + ulong iopm; /* mode (1=output) */ + ulong iopc; /* control */ + ulong iopd; /* data */ +}; + +enum { + /* WLAN and BT values are probably wrong */ + GPIO_WLAN_act_o= 7, + GPIO_WLAN_100_o= 8, + GPIO_BT_act_o= 9, + GPIO_BT_100_o= 10, + GPIO_status_orange_o= 11, + GPIO_status_green_o= 12, + GPIO_button_i= 15, /* reset button, active low */ + GPIO_misc_mask_o= (1<<13)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<6), /* no idea */ +}; + +void gpioreserve(int); +void gpioconfig(int, ulong); +ulong gpioget(int); +void gpioset(int, int); +void gpiorelease(int); + +enum { + /* software configuration bits for gpioconfig */ + Gpio_in= 0<<4, + Gpio_out= 1<<4, +}; + +/* + * Host Communication buffer descriptors + */ + +struct BD { + ulong ctrl; /* BdBusy and rx flags */ + ulong size; /* buffer size, also BdLast and tx flags */ + ulong addr; + ulong next; /* next descriptor address */ +}; + +enum { + /* ctrl */ + BdBusy= 1<<31, /* device owns it */ + + RxFS= 1<<30, /* first buffer of frame */ + RxLS= 1<<29, /* last buffer of frame */ + RxIPE= 1<<28, /* IP checksum error */ + RxTCPE= 1<<27, /* TCP checksum error */ + RxUDPE= 1<<26, /* UDP checksum error */ + RxES= 1<<25, /* error summary */ + RxMF= 1<<24, /* multicast */ + RxRE= 1<<19, /* physical level reported error */ + RxTL= 1<<18, /* frame too long */ + RxRF= 1<<17, /* runt */ + RxCE= 1<<16, /* CRC error */ + RxFT= 1<<15, /* =0, Ether; =1, 802.3 */ + RxFL= 0x7FF, /* frame length */ + + /* size and tx flags */ + BdWrap= 1<<25, /* wrap to base of ring */ + TxIC= 1<<31, /* interrupt on completion */ + TxFS= 1<<30, /* first segment */ + TxLS= 1<<29, /* last segment */ + TxIPG= 1<<28, /* generate IP checksum */ + TxTCPG= 1<<27, /* generate tcp/ip checksum */ + TxUDPG= 1<<26, /* generate udp/ip checksum */ +}; + +BD* bdalloc(ulong); +void bdfree(BD*, int); +void dumpbd(char*, BD*, int); + +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)) + +int ioringinit(Ring*, int, int); + +enum { + /* DMA configuration parameters */ + + /* DMA Direction */ + DmaOut= 0, + DmaIn= 1, +}; + +/* + * 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/manga/ioring.c b/os/manga/ioring.c new file mode 100644 index 00000000..fa3dc209 --- /dev/null +++ b/os/manga/ioring.c @@ -0,0 +1,72 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * Host Communication buffer rings + */ + +/* + * initialise receive and transmit buffer rings + * + * the ring entries must be uncached + */ + +BD* +bdalloc(ulong nd) +{ + BD *b; + + b = xspanalloc(nd*sizeof(*b), CACHELINESZ, 0); + if(b == nil) + panic("bdalloc"); + return mmucacheinhib(b, nd*sizeof(*b)); +} + +int +ioringinit(Ring* r, int nrdre, int ntdre) +{ + int i; + + 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].ctrl = 0; + r->rdr[i].size = 0; + r->rdr[i].addr = 0; + if(i) + r->rdr[i-1].next = PADDR(&r->rdr[i]); + } + r->rdr[i-1].next = PADDR(&r->rdr[0]); + 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].ctrl = 0; + r->tdr[i].size = 0; + r->tdr[i].addr = 0; + if(i) + r->tdr[i-1].next = PADDR(&r->tdr[i]); + } + r->tdr[i-1].next = PADDR(&r->tdr[0]); + r->tdrh = 0; + r->tdri = 0; + r->ntq = 0; + return 0; +} diff --git a/os/manga/l.s b/os/manga/l.s new file mode 100644 index 00000000..c9fa24ec --- /dev/null +++ b/os/manga/l.s @@ -0,0 +1,404 @@ +#include "mem.h" + +#define CPWAIT + +/* + * Entered here from the boot loader with + * supervisor mode, interrupts disabled; + * MMU and caches disabled + */ + +#define LED \ + MOVW $(PHYSGPIO+8), R6;\ + MOVW (R6), R7;\ + EOR $(1<<12), R7;\ + MOVW R7, (R6) + +TEXT _startup(SB), $-4 + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */ + MOVW R1, CPSR + + /* build a temporary translation table at 4MB */ + MOVW $0x400000, R0 + MCR CpMMU, 0, R0, C(CpTTB), C(0), 0 /* set TTB */ + MOVW $4096, R1 + MOVW $0, R3 + ORR $(1<<4), R3 /* must be one */ + ORR $(3<<10), R3 /* supervisor rw */ + ORR $(2<<0), R3 /* section */ +startup0: + BIC $0xFC000000, R3 /* wraps round, at least for 0xC00... */ + MOVW R3, (R0) + ADD $4, R0 + ADD $(1<<20), R3 + SUB $1, R1 + CMP $0, R1 + BNE startup0 + MRC CpMMU, 0, R0, C(CpControl), C(0), 0 + ORR $CpCmmu, R0 + + MOVW $3, R1 + MCR CpMMU, 0, R1, C(CpDAC), C(0) /* set domain 0 to manager */ + BL mmuenable(SB) + + MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */ + BL _relocate(SB) + BL main(SB) +dead: + B dead + BL _div(SB) /* hack to get _div etc loaded */ + +TEXT _relocate(SB), $-4 + ORR $KZERO, R14 + RET + +TEXT getcpuid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCPUID), C(0) + RET + +TEXT getcacheid(SB), $-4 + MRC CpMMU, 0, R0, C(CpCacheID), C(1) + RET + +TEXT mmugetctl(SB), $-4 + MRC CpMMU, 0, R0, C(CpControl), C(0) + RET + +TEXT mmugetdac(SB), $-4 + MRC CpMMU, 0, R0, C(CpDAC), C(0) + RET + +TEXT mmugetfar(SB), $-4 + MRC CpMMU, 0, R0, C(CpFAR), C(0) + RET + +TEXT mmugetfsr(SB), $-4 + MRC CpMMU, 0, R0, C(CpFSR), C(0) + RET + +TEXT mmuputdac(SB), $-4 + MCR CpMMU, 0, R0, C(CpDAC), C(0) + CPWAIT + RET + +TEXT mmuputfsr(SB), $-4 + MCR CpMMU, 0, R0, C(CpFSR), C(0) + CPWAIT + RET + +TEXT mmuputttb(SB), $-4 + MCR CpMMU, 0, R0, C(CpTTB), C(0) + CPWAIT + RET + +TEXT mmuputctl(SB), $-4 + MCR CpMMU, 0, R0, C(CpControl), C(0) + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + RET + +TEXT tlbinvalidateall(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(7) + CPWAIT + RET + +TEXT itlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(5), 1 + CPWAIT + RET + +TEXT dtlbinvalidate(SB), $-4 + MCR CpMMU, 0, R0, C(CpTLBops), C(6), 1 + CPWAIT + RET + +TEXT mmuenable(SB), $-4 + + /* disable and invalidate all caches and TLB's before enabling MMU */ + MCR CpMMU, 0, R1, C(CpControl), C(0) + BIC $(CpCDcache | CpCIcache), R1 + MRC CpMMU, 0, R1, C(CpControl), C(0) + CPWAIT + + MOVW $0, R1 /* disable everything */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* invalidate I&D Caches and BTB */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* invalidate I&D TLB */ + + /* enable desired mmu mode (R0) */ + MCR CpMMU, 0, R0, C(CpControl), C(0) + + /* drain prefetch */ + MOVW R0,R0 + MOVW R0,R0 + RET /* start running in remapped area */ + +TEXT setr13(SB), $-4 + MOVW 4(FP), R1 + + MOVW CPSR, R2 + BIC $PsrMask, R2, R3 + ORR R0, R3 + MOVW R3, CPSR + + MOVW R13, R0 + MOVW R1, R13 + + MOVW R2, CPSR + RET + +TEXT vectors(SB), $-4 + MOVW 0x18(R15), R15 /* reset */ + MOVW 0x18(R15), R15 /* undefined */ + MOVW 0x18(R15), R15 /* SWI */ + MOVW 0x18(R15), R15 /* prefetch abort */ + MOVW 0x18(R15), R15 /* data abort */ + MOVW 0x18(R15), R15 /* reserved */ + MOVW 0x18(R15), R15 /* IRQ */ + MOVW 0x18(R15), R15 /* FIQ */ + +TEXT vtable(SB), $-4 + WORD $_vsvc(SB) /* reset, in svc mode already */ + WORD $_vund(SB) /* undefined, switch to svc mode */ + WORD $_vsvc(SB) /* swi, in svc mode already */ + WORD $_vpab(SB) /* prefetch abort, switch to svc mode */ + WORD $_vdab(SB) /* data abort, switch to svc mode */ + WORD $_vsvc(SB) /* reserved */ + WORD $_virq(SB) /* IRQ, switch to svc mode */ + WORD $_vfiq(SB) /* FIQ, switch to svc mode */ + +TEXT _vund(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMund, R0 + B _vswitch + +TEXT _vsvc(SB), $-4 + MOVW.W R14, -4(R13) + MOVW CPSR, R14 + MOVW.W R14, -4(R13) + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + MOVW $PsrMsvc, R14 + MOVW.W R14, -4(R13) + B _vsaveu + +TEXT _vpab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $PsrMabt, R0 + B _vswitch + +TEXT _vdab(SB), $-4 + MOVM.DB [R0-R3], (R13) + MOVW $(PsrMabt+1), R0 + B _vswitch + +TEXT _vfiq(SB), $-4 /* FIQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMfiq, R0 + B _vswitch + +TEXT _virq(SB), $-4 /* IRQ */ + MOVM.DB [R0-R3], (R13) + MOVW $PsrMirq, R0 + +_vswitch: /* switch to svc mode */ + MOVW SPSR, R1 + MOVW R14, R2 + MOVW R13, R3 + + MOVW CPSR, R14 + BIC $PsrMask, R14 + ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14 + MOVW R14, CPSR + + MOVM.DB.W [R0-R2], (R13) + MOVM.DB (R3), [R0-R3] + +_vsaveu: /* Save Registers */ + MOVW.W R14, -4(R13) /* save link */ + MCR CpMMU, 0, R0, C(0), C(0), 0 + + SUB $8, R13 + MOVM.DB.W [R0-R12], (R13) + + MOVW R0, R0 /* gratuitous noop */ + + MOVW $setR12(SB), R12 /* static base (SB) */ + MOVW R13, R0 /* argument is ureg */ + SUB $8, R13 /* space for arg+lnk*/ + BL trap(SB) + +_vrfe: /* Restore Regs */ + MOVW CPSR, R0 /* splhi on return */ + ORR $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + ADD $(8+4*15), R13 /* [r0-R14]+argument+link */ + MOVW (R13), R14 /* restore link */ + MOVW 8(R13), R0 + MOVW R0, SPSR + MOVM.DB.S (R13), [R0-R14] /* restore user registers */ + MOVW R0, R0 /* gratuitous nop */ + ADD $12, R13 /* skip saved link+type+SPSR*/ + RFE /* MOVM.IA.S.W (R13), [R15] */ + +TEXT splhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDirq), R0, R1 + MOVW R1, CPSR + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + RET + +TEXT spllo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDirq|PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splx(SB), $-4 + MOVW $(MACHADDR), R6 + MOVW R14, (R6) /* m->splpc */ + +TEXT splxpc(SB), $-4 + MOVW R0, R1 + MOVW CPSR, R0 + MOVW R1, CPSR + RET + +TEXT spldone(SB), $-4 + RET + +TEXT islo(SB), $-4 + MOVW CPSR, R0 + AND $(PsrDirq), R0 + EOR $(PsrDirq), R0 + RET + +TEXT splfhi(SB), $-4 + MOVW CPSR, R0 + ORR $(PsrDfiq|PsrDirq), R0, R1 + MOVW R1, CPSR + RET + +TEXT splflo(SB), $-4 + MOVW CPSR, R0 + BIC $(PsrDfiq), R0, R1 + MOVW R1, CPSR + RET + +TEXT getcpsr(SB), $-4 + MOVW CPSR, R0 + RET + +TEXT getspsr(SB), $-4 + MOVW SPSR, R0 + RET + +TEXT getcallerpc(SB), $-4 + MOVW 0(R13), R0 + RET + +TEXT _tas(SB), $-4 + MOVW R0, R1 + MOVW $0xDEADDEAD, R2 + SWPW R2, (R1), R0 + RET + +TEXT setlabel(SB), $-4 + MOVW R13, 0(R0) /* sp */ + MOVW R14, 4(R0) /* pc */ + MOVW $0, R0 + RET + +TEXT gotolabel(SB), $-4 + MOVW 0(R0), R13 /* sp */ + MOVW 4(R0), R14 /* pc */ + MOVW $1, R0 + RET + +/* + * flush (invalidate) the whole icache + */ +TEXT icflushall(SB), $-4 +_icflushall: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 /* invalidate i-cache */ + CPWAIT + RET + +/* + * invalidate part of i-cache + */ +TEXT icflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _icflushall /* might as well do the lot */ + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +icflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 1 /* invalidate entry by address */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO icflush1 + RET + +/* + * write back whole data cache, invalidate, and drain write buffer + */ +TEXT dcflushall(SB), $-4 +_dcflushall: + MOVW $(63<<26), R1 /* index, segment 0 */ +dcflushall0: + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 /* clean and invalidate, using index */ + ADD $(1<<5), R1 /* segment 1 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + ADD $(1<<5), R1 /* segment 2 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + ADD $(1<<5), R1 /* segment 3 */ + MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 + EOR $(3<<5), R1 /* back to 0 */ + SUB.S $(1<<26), R1 + BCS dcflushall0 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +/* + * write back a given region, inavlidate it, and drain write buffer + */ +TEXT dcflush(SB), $-4 + MOVW 4(FP), R1 + CMP $(CACHESIZE/2), R1 + BGE _dcflushall + ADD R0, R1 + BIC $(CACHELINESZ-1), R0 +dcflush1: + MCR CpMMU, 0, R0, C(CpCacheCtl), C(14), 1 /* clean and invalidate entry by address */ + ADD $CACHELINESZ, R0 + CMP R1, R0 + BLO dcflush1 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */ + CPWAIT + RET + +/* + * invalidate data cache + */ +TEXT dcinval(SB), $-4 + MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0 + CPWAIT + RET + +/* for devboot */ +TEXT gotopc(SB), $-4 + MOVW R0, R1 + MOVW $0, R0 + MOVW R1, PC + RET + +TEXT idle(SB), $-4 + MCR CpMMU, 0, R0, C(7), C(0), 4 + RET diff --git a/os/manga/main.c b/os/manga/main.c new file mode 100644 index 00000000..3f1adae0 --- /dev/null +++ b/os/manga/main.c @@ -0,0 +1,317 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" +#include "version.h" + +#define MAXCONF 32 + +Mach *m = (Mach*)MACHADDR; +Proc *up = 0; +Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */ +Conf conf; + +extern ulong kerndate; +extern int cflag; +extern int main_pool_pcnt; +extern int heap_pool_pcnt; +extern int image_pool_pcnt; +ulong cpuidlecount; + +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +void addconf(char *, char *); +void eepromscan(void); +char* getconf(char*); + +void +doc(char *m) +{ + USED(m); + print("%s...\n", m); +} + +void +idoc(char *m) +{ + serialputs(m, strlen(m)); //xdelay(1); +} + +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 char *hello = "Inferno\n"; + +void +main(void) +{ + memset(edata, 0, end-edata); /* clear the BSS */ + memset(m, 0, sizeof(Mach)); /* clear the mach struct */ + conf.nmach = 1; + archreset(); + idoc(hello); + /* TO DO: clock speed */ + quotefmtinstall(); + idoc("confinit...\n"); + confinit(); + idoc("xinit...\n"); + xinit(); + idoc("mmuinit...\n"); + mmuinit(); + poolsizeinit(); + poolinit(); + idoc("trapinit...\n"); + trapinit(); +// dmareset(); + idoc("printinit...\n"); + printinit(); + idoc("uartconsole...\n"); + uartconsole(); + eepromscan(); + pcimapinit(); + doc("clockinit"); + clockinit(); + doc("procinit"); + procinit(); +// cpuidprint(); + doc("links"); + links(); + doc("chandevreset"); + chandevreset(); +iprint("delayloop = %lud\n", m->delayloop); + + eve = strdup("inferno"); + + kbdinit(); + + print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid()); + print("\nInferno %s\n", VERSION); + print("Vita Nuova\n"); + print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag); + + userinit(); + schedinit(); +} + +void +reboot(void) +{ + exit(0); +} + +void +halt(void) +{ + spllo(); + print("cpu halted\n"); + for(;;){ + /* nothing to do */ + } +} + +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) +{ + ulong base; + + archconfinit(); + + base = PGROUND((ulong)end); + conf.base0 = base; + + conf.base1 = 0; + conf.npage1 = 0; + + conf.npage0 = (conf.topofmem - base)/BY2PG; + + conf.npage = conf.npage0 + conf.npage1; + conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG; + + conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5; + conf.nmach = 1; + +} + +void +init0(void) +{ + Osenv *o; + char buf[2*KNAMELEN]; + + up->nerrlab = 0; + + spllo(); + + if(waserror()) + panic("init0 %r"); + /* + * 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", "arm", 0); + snprint(buf, sizeof(buf), "arm %s", conffile); + ksetenv("terminal", buf, 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"); + + p->fpstate = FPINIT; + + /* + * Kernel Stack + * + * N.B. The -12 for the stack pointer is important. + * 4 bytes for gotolabel's return PC + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + ready(p); +} + +void +exit(int inpanic) +{ + up = 0; + + /* Shutdown running devices */ + chandevshutdown(); + + if(inpanic && 0){ + print("Hit the reset button\n"); + for(;;) + clockpoll(); + } + archreboot(); +} + +static void +linkproc(void) +{ + spllo(); + if (waserror()) + print("error() underflow: %r\n"); + else + (*up->kpfun)(up->arg); + pexit("end proc", 1); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK-8; + + p->kpfun = func; + p->arg = arg; +} + +void +idlehands(void) +{ + cpuidlecount++; + idle(); +} + +/* stubs */ +void +setfsr(ulong) +{ +} + +ulong +getfsr() +{ + return 0; +} + +void +setfcr(ulong) +{ +} + +ulong +getfcr() +{ + return 0; +} + +void +fpinit(void) +{ +} + +void +FPsave(void*) +{ +} + +void +FPrestore(void*) +{ +} diff --git a/os/manga/manga b/os/manga/manga new file mode 100644 index 00000000..725e275e --- /dev/null +++ b/os/manga/manga @@ -0,0 +1,137 @@ +dev + root + cons archmanga +# gpio + mnt + pipe + prog + srv + dup + ssl +# cap +# sign + uart + ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux + flash + ether netif netaux + env + pci pci inb + usb pci + +ip + il + tcp + udp +# rudp +# igmp + ipifc + icmp + icmp6 + ipmux + +lib + interp + keyring + sec + mp + math + kern + +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 + +misc + uartks8695 + +link + flashcfi8 + ether8139 + etherks8695 + ethermedium + usbuhci + +code + int main_pool_pcnt = 50; + int heap_pool_pcnt = 50; + int image_pool_pcnt = 0; + int cflag = 0; /* for JIT */ + + int consoleprint = 1; + int panicreset = 0; + +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/manga/mem.h b/os/manga/mem.h new file mode 100644 index 00000000..61296039 --- /dev/null +++ b/os/manga/mem.h @@ -0,0 +1,133 @@ +/* + * 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 BIT(n) (1<<n) +#define BITS(a,b) ((1<<(b+1))-(1<<a)) + +#define MAXMACH 1 /* max # cpus system can run */ + +/* + * 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 */ + +/* + * More accurate time + */ +#define CLOCKFREQ 25000000 +#define MS2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000)) +#define US2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000)) + +/* + * Address spaces + * nearly everything maps 1-1 with physical addresses + * 0 to 1Mb is not mapped + * cache strategy varies as needed (see mmu.c) + */ + +#define KZERO 0xC0000000 +#define MACHADDR (KZERO+0x00001000) +#define KTTB (KZERO+0x00004000) +#define KTZERO (KZERO+0x00008010) +#define KSTACK 8192 /* Size of kernel stack */ +#define FLASHMEM 0x50000000 /* map flash to otherwise unused virtual space */ +#define UCDRAMZERO (KZERO+0x08000000) /* base of memory doubly-mapped as uncached */ +#define AIVECADDR 0xFFFF0000 /* alternative interrupt vector address (other is 0) */ + +/* + * Physical addresses + */ +#define PHYSDRAM0 0x00000000 /* where firmware puts it */ +#define PHYSFLASH0 0x02800000 /* where firmware puts it */ +#define PHYSSCRINIT 0x03FF0000 /* address at reset */ +#define PHYSSCR PHYSSCRINIT /* where it ends up after manga firmware */ +#define PHYSBRIDGE (PHYSSCR+0x2000) /* PCI-AHB bridge configuration */ +#define PHYSMEMCR (PHYSSCR+0x4000) /* memory controller interface */ +#define PHYSWANDMA (PHYSSCR+0x6000) /* WAN DMA registers */ +#define PHYSLANDMA (PHYSSCR+0x8000) /* LAN DMA registers */ +#define PHYSUART (PHYSSCR+0xE000) +#define PHYSINTR (PHYSSCR+0xE200) /* interrupt controller */ +#define PHYSTIMER (PHYSSCR+0xE400) /* timer registers */ +#define PHYSGPIO (PHYSSCR+0xE600) +#define PHYSSWITCH (PHYSSCR+0xE800) /* switch engine configuration */ +#define PHYSMISC (PHYSSCR+0xEA00) /* ``miscellaneous'' registers */ + +#define PHYSPCIBRIDGE 0x80000000 /* physical address that maps to PCI 0 */ +#define PHYSPCIIO 0x10000000 /* physical address that maps to PCI I/O space */ + +#define CACHELINELOG 5 +#define CACHELINESZ (1<<CACHELINELOG) +#define CACHESIZE (8*1024) /* I & D caches are the same size, 4 segment, 64-way associative */ + +/* + * PSR + */ +#define PsrMusr 0x10 /* mode */ +#define PsrMfiq 0x11 +#define PsrMirq 0x12 +#define PsrMsvc 0x13 +#define PsrMabt 0x17 +#define PsrMund 0x1B +#define PsrMsys 0x1F +#define PsrMask 0x1F + +#define PsrDfiq 0x00000040 /* disable FIQ interrupts */ +#define PsrDirq 0x00000080 /* disable IRQ interrupts */ + +#define PsrV 0x10000000 /* overflow */ +#define PsrC 0x20000000 /* carry/borrow/extend */ +#define PsrZ 0x40000000 /* zero */ +#define PsrN 0x80000000 /* negative/less than */ + +/* + * Internal MMU coprocessor registers (ARM 922) + */ +#define CpCPUID 0 /* R: opcode_2 is 0*/ +#define CpCacheID 0 /* R: opcode_2 is 1 */ +#define CpControl 1 /* R/W: control (opcode_2 is 0) */ +#define CpTTB 2 /* R/W: translation table base */ +#define CpDAC 3 /* R/W: domain access control */ +#define CpFSR 5 /* R/W: fault status */ +#define CpFAR 6 /* R/W: fault address */ +#define CpCacheCtl 7 /* W: */ +#define CpTLBops 8 /* W: TLB operations */ +#define CpCacheLk 9 /* W: cache lock down */ +#define CpPID 13 /* R/W: Process ID Virtual Mapping */ +#define CpTest 15 /* R/W: test configuration */ + +/* + * Coprocessors + */ +#define CpMMU 15 + +/* + * CpControl bits + */ +#define CpCmmu (1<<0) /* M: MMU enable */ +#define CpCalign (1<<1) /* A: alignment fault enable */ +#define CpCDcache (1<<2) /* C: data cache on */ +#define CpCwpd (15<<3) /* W, P, D, must be one */ +#define CpCbe (1<<7) /* B: big-endian operation */ +#define CpCsystem (1<<8) /* S: system permission */ +#define CpCrom (1<<9) /* R: ROM permission */ +#define CpCIcache (1<<12) /* I: Instruction Cache on */ +#define CpCaltivec (1<<13) /* X: exception vector relocation */ +#define CpCrrobin (1<<14) /* RR: round robin replacement */ +#define CpCnotFast (1<<30) /* nF: notFastBus select */ +#define CpCasync (1<<31) /* iA: asynchronous clock select */ diff --git a/os/manga/mkfile b/os/manga/mkfile new file mode 100644 index 00000000..a6f7a99a --- /dev/null +++ b/os/manga/mkfile @@ -0,0 +1,89 @@ +<../../mkconfig + +#Configurable parameters + +CONF=manga #default configuration +CONFLIST=manga boot + +SYSTARG=$OSTARG +OBJTYPE=arm +INSTALLDIR=$ROOT/Inferno/$OBJTYPE #path of directory where kernel is installed +#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 + +KTZERO=0xC0008010 + +OBJ=\ + l.$O\ + clock.$O\ + fpi.$O\ + fpiarm.$O\ + fpimem.$O\ + gpio.$O\ + ioring.$O\ + main.$O\ + mmu.$O\ + trap.$O\ + $CONF.root.$O\ + $IP\ + $DEVS\ + $ETHERS\ + $LINKS\ + $PORT\ + $MISC\ + $OTHERS\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBDIRS=$LIBS + +HFILES=\ + mem.h\ + dat.h\ + io.h\ + +CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp +KERNDATE=`{$NDATE} + +default:V: i$CONF i$CONF.p9 k.gz + +install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz + +i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -s -o $target -H5 -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES + +i$CONF.gz: i$CONF + rm -f i$CONF.gz + gzip -9 <i$CONF >i$CONF.gz + +<../port/portmkfile +CLEANEXTRA=k.gz + +../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 +main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h +trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h + +$IP devip.$O: ../ip/ip.h + + +devuart.$O: ../port/devuart.c ../port/uart.h + $CC $CFLAGS ../port/devuart.c + +k.gz: i$CONF.gz pinflate + cat pinflate i$CONF.gz >k.gz + echo burble burble >>k.gz + +# pepinflate: /sys/src/boot/pep/pinflate + +dummy:V: diff --git a/os/manga/mmu.c b/os/manga/mmu.c new file mode 100644 index 00000000..433289d1 --- /dev/null +++ b/os/manga/mmu.c @@ -0,0 +1,244 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +/* + * Small pages: + * L1: 12-bit index -> 4096 descriptors -> 16Kb + * L2: 8-bit index -> 256 descriptors -> 1Kb + * Each L2 descriptor has access permissions for 4 1Kb sub-pages. + * + * TTB + L1Tx gives address of L1 descriptor + * L1 descriptor gives PTBA + * PTBA + L2Tx gives address of L2 descriptor + * L2 descriptor gives PBA + * + * C & B are interpreted as follows: + * C=0 B=0 uncached, unbuffered, stall until data access complete + * C=0 B=1 uncached, buffered + * C=1 B=0 write-through cachable + * C=1 B=1 write-back cachable + * and the i-cache uses only the C bit (cached if non-zero). + */ +#define TTB(pa) ((pa) & ~0x3FFF) /* translation table base */ +#define L1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */ +#define PTBA(pa) ((pa) & ~0x3FF) /* page table base address */ +#define L2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */ +#define PBA(pa) ((pa) & ~0xFFF) /* page base address */ +#define SBA(pa) ((pa) & ~0xFFFFF) /* section base address */ + +enum { + /* sizes */ + Section= 1<<20, + LargePage= 1<<16, + SmallPage= 1<<12, + EsmallPage= 1<<10, + SectionPages = Section/SmallPage, + PtAlign = 1<<10, + + /* L1 descriptor format */ + L1type= 3<<0, /* mask for type */ + L1page= 1<<0, /* descriptor is for L2 pages */ + L1section= 2<<0, /* descriptor is for section */ + L1fpage= 3<<0, /* descriptor is for fine (1k) L2 pages */ + L1buffered= 1<<2, + L1cached= 1<<3, + L1mbo= 1<<4, /* must be one */ + + /* L2 descriptor format for coarse page table */ + L2type= 3<<0, /* mask for type */ + L2invalid= 0<<0, + L2large= 1<<0, /* large page */ + L2small= 2<<0, /* small page */ + L2esmall= 3<<0, /* extended small page */ + L2buffered= 1<<2, + L2cached= 1<<3, + /* then access permissions */ + L2smallX= 1<<6, + L2largeX= 1<<12, + + /* domains */ + Dnone= 0, + Dclient= 1, + Dmanager= 3, + + /* access permissions */ + APsro= 0, /* supervisor ro if S|R */ + APsrw= 1, /* supervisor rw */ + APuro= 2, /* supervisor rw + user ro */ + APurw= 3, /* supervisor rw + user rw */ +}; + +#define L1dom(d) (((d) & 0xF)<<5) /* L1 domain */ +#define AP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */ +#define L1AP(v) AP(3, (v)) +#define L2AP(v) AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v)) + +#define L1krw (L1AP(APsrw) | L1dom(0)) + +/* + * return physical address corresponding to a given virtual address, + * or 0 if there is no such address + */ +ulong +va2pa(void *v) +{ + int idx; + ulong pte, ste, *ttb; + + idx = L1x((ulong)v); + ttb = (ulong*)KTTB; + ste = ttb[idx]; + switch(ste & L1type) { + case L1section: + return SBA(ste)|((ulong)v & 0x000fffff); + case L1page: + pte = ((ulong *)PTBA(ste))[L2x((ulong)v)]; + switch(pte & 3) { + case L2large: + return (pte & 0xffff0000)|((ulong)v & 0x0000ffff); + case L2small: + return (pte & 0xfffff000)|((ulong)v & 0x00000fff); + } + } + return 0; +} + +/* for debugging */ +void +prs(char *s) +{ + serialputs(s, strlen(s)); +} + +void +pr16(ulong n) +{ + int i, c; + + for(i=28; i>=0; i-=4){ + c = (n>>i) & 0xF; + if(c >= 0 && c <= 9) + c += '0'; + else + c += 'A'-10; + serialputc(c); + } +} + +void +xdelay(int n) +{ + int j; + + for(j=0; j<1000000/4; j++) + n++; + USED(n); +} + +void* +mmuphysmap(void *va, ulong pa, ulong nbytes) +{ + ulong *ttb; + ulong p, o; + + if(va == nil) + va = KADDR(pa); + p = (ulong)va; + if((pa|p) & (Section-1)) + panic("kmapphys"); + ttb = (ulong*)KTTB; + nbytes = (nbytes+Section-1)&~(Section-1); + for(o = 0; o < nbytes; o += Section) + ttb[L1x(p+o)] = (pa+o) | (1<<4) | L1krw | L1section; + return va; +} + +void* +mmukaddr(ulong pa) +{ + if(pa >= PHYSDRAM0 && pa < conf.topofmem) + return (void*)(KZERO+(pa-PHYSDRAM0)); + return (void*)pa; +} + +/* + * Set a 1-1 map of virtual to physical memory, except: + * kernel is mapped to KZERO + * doubly-map page0 at the alternative interrupt vector address, + * doubly-map physical memory at KZERO+256*MB as uncached but buffered, + * map flash to virtual space away from 0, + * disable access to 0 (nil pointers). + * + * Other section maps are added later as required by mmuphysmap. + */ +void +mmuinit(void) +{ + int i; + ulong *ttb, *ptable; + + ttb = (ulong*)KTTB; + memset(ttb, 0, 16384); + + /* assume flash is first in special physical space */ + for(i = L1x(PHYSFLASH0); i < 0x1000; i++) + ttb[i] = (i<<20) | L1krw | (1<<4) | L1section; + + /* cached dram at normal kernel addresses */ + for(i = 0; i < 32*MB; i += MB) + ttb[L1x(KZERO+i)] = (PHYSDRAM0+i) | (1<<4) | L1krw | L1section | L1cached | L1buffered; + + /* aliases for uncached dram */ + for(i = 0; i < 64*MB; i += MB) + ttb[L1x(UCDRAMZERO+i)] = (PHYSDRAM0+i) | L1krw | (1<<4) | L1section; + + /* TO DO: make the text read only */ + + /* remap flash */ + for(i=0; i<8*MB; i+=MB) + ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | (1<<4) | L1section; /* we'll make flash uncached for now */ + + /* + * build page table for alternative vector page, mapping trap vectors in *page0 + */ + ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0); + ptable[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small; + ttb[L1x(AIVECADDR)] = PADDR(ptable) | (1<<4) | L1page; + + mmuputttb(KTTB & ~KZERO); + mmuputdac(Dclient); + mmuputctl(mmugetctl() | CpCaltivec | CpCIcache | CpCsystem | CpCwpd | CpCDcache | CpCmmu); + tlbinvalidateall(); +} + +/* + * flush data in a given address range to memory + * and invalidate the region in the instruction cache. + */ +int +segflush(void *a, ulong n) +{ + dcflush(a, n); + icflush(a, n); + return 0; +} + +/* + * return an uncached alias for the memory at a + */ +void* +mmucacheinhib(void *a, ulong nb) +{ + ulong p; + + if(a == nil) + return nil; + p = PADDR(a); + if(p & (CACHELINESZ-1)) + panic("mmucacheinhib"); + dcflush(a, nb); + return (void*)(UCDRAMZERO|PADDR(a)); +} diff --git a/os/manga/pci.c b/os/manga/pci.c new file mode 100644 index 00000000..7bebf05b --- /dev/null +++ b/os/manga/pci.c @@ -0,0 +1,1007 @@ +/* + * 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 +#undef DBG +#define DBG if(1) iprint + +typedef struct Pcicfg Pcicfg; +struct Pcicfg { + ulong addr; + ulong 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 void pcirouting(void); +static void pcirootmap(Pcidev*); +static void pcidumpdev(ulong); + +static char* bustypes[] = { +[BusIRQ] "IRQ", +[BusPCI] "PCI", +}; + +#pragma varargck type "Y" 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': + case 'Y': + 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 %Y 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('Y', 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); + + pcirootmap(pciroot); + + pcirouting(); + + if(1){ + iprint("pci bridge':\n"); + pcidumpdev(pciroot->tbdf); + } + if(1){ + /* see we've left */ + ulong *p; + int i; + + p = KADDR(PHYSBRIDGE+0x200); + iprint("PCI:\n"); + for(i=0; i<10; i++) + iprint("%8.8lux: %8.8lux\n", p+i, p[i]); + } + +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)<<3; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + if(read) + x = (pcicfg->data>>o) & 0xFF; + else + pcicfg->data = (pcicfg->data & ~(0xFF<<o)) | ((data & 0xFF) << o); + 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)<<4; + rno &= ~0x03; + pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno; + if(read) + x = (pcicfg->data>>o) & 0xFFFF; + else + pcicfg->data = (pcicfg->data & ~(0xFFFF<<o)) | ((data&0xFFFF)<<o); + 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; + if(read) + x = pcicfg->data; + else + pcicfg->data = data; + 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); +} + +/* + * KS8695P specific + */ + +typedef struct Pciahb Pciahb; +struct Pciahb { + ulong pbm; /* bridge mode */ + ulong pbcs; /* control and status */ + ulong pmba; /* memory base address */ + ulong pmbac; /* memory base address control */ + ulong pmbam; /* memory base address mask */ + ulong pmbat; /* memory base address translation */ + ulong pioba; /* i/o base address */ + ulong piobac; /* i/o base address control */ + ulong piobam; /* i/o base address mask */ + ulong piobat; /* i/o base address translation */ +}; + +enum { + /* pbm */ + PciHost= 1<<31, /* host bridge mode */ + PciModePCI= 0<<29, + PciModeMini= 1<<29, + PciModeCbus= 2<<29, + + /* pbcs */ + PciReset= 1<<31, + PciPF4= 0<<29, /* prefetch 4, 8 or 16 words */ + PciPF8= 1<<29, + PciPF16= 2<<29, + + /* pmbac, piobac */ + PciTranslate= 1<<31, /* enable downstream address translation */ +}; + +static void +pcidumpdev(ulong tbdf) +{ + int i; + + for(i=0; i<0x40; i+=4) + iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1)); +} + +void +pcimapinit(void) +{ + Pciahb *pm; + int i; + + pm = KADDR(PHYSBRIDGE+0x200); + if(1){ + /* see what the bootstrap left */ + ulong *p; + + p = (ulong*)pm; + iprint("PCI:\n"); + for(i=0; i<10; i++) + iprint("%8.8lux: %8.8lux\n", p+i, p[i]); + } +#ifdef NOT + /* TO DO: soft reset */ + putdcr(Cpc0Srr, Rpci); + delay(1); + putdcr(Cpc0Srr, 0); +#endif + pm->pbcs = 0x30000000; /* prefetch limit 8 words; pci config access disable */ + + pcicfg = KADDR(PHYSBRIDGE+0x100); + + /* + * AHB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb + * are mapped to PCI memory at 0. + */ + pcimem = mmuphysmap(KADDR(PHYSPCIBRIDGE), PHYSPCIBRIDGE, 0x4000000); + pm->pmbac = 0; /* disable during update */ + pm->pmba = PHYSPCIBRIDGE; + pm->pmbam = 0xFC000000; + pm->pmbat = 0; /* TO DO: check */ + pm->pmbac = PciTranslate; /* enable */ + + /* + * AHB addresses between PHYSPCIIO and PHYSPCIIO+64Mb + * are mapped to physical memory. + */ + mmuphysmap(KADDR(PHYSPCIIO), PHYSPCIIO, 64*1024); + pm->piobac = 0; /* disable during update */ + pm->pioba = PHYSPCIIO; + pm->piobam = ~(64*1024-1); + pm->piobat = 0; /* TO DO: check */ + pm->piobac = PciTranslate; /* enable */ + + pcicfgmode = -1; +} + +static void +pcirootmap(Pcidev *bridge) +{ + ulong pcsr; + + /* + * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+64Mb + * are mapped to physical memory. + */ + pcsr = pcicfgr32(bridge, PciPSR); +iprint("pcsr0=%8.8lux\n", pcsr); + pcicfgw32(bridge, PciPSR, pcsr); /* reset error status */ + pcsr = pcicfgr32(bridge, PciPSR); +iprint("pcsr1=%8.8lux\n", pcsr); + pcicfgw32(bridge, PciBAR0, PCIWINDOW); +} + +typedef struct Pciroute Pciroute; +struct Pciroute { + int slot; + int pin; + int irq; +}; + +static Pciroute pciroutes[] = { + {0, 1, IRQext0}, /* bridge */ + {5, 1, IRQext3}, /* USB */ + {5, 2, IRQext1}, + {5, 3, IRQext2}, +// {6, 0, IRQext3}, /* miniPCI0 (or RTL8139 for extra WAN?) */ + {6, 1, IRQext0}, /* RTL8139 for extra WAN */ + {7, 0, IRQext3}, /* miniPCI1 */ + {-1, 0, IRQext0}, +}; + +static void +pcirouting(void) +{ + int i, pin, irq; + Pcidev *pci; + Pciroute *r; + + for(pci = pcilist; pci != nil; pci = pci->list){ + pin = pcicfgr8(pci, PciINTP); + if(pin == 0 || pin == 0xff) + continue; + irq = -1; + for(i=0; i<nelem(pciroutes); i++){ + r = &pciroutes[i]; + if(r->slot < 0 || r->slot == BUSDNO(pci->tbdf) && (r->pin == 0 || r->pin == pin)){ + irq = r->irq; + break; + } + } + if(irq < 0) + continue; + irq |= IRQactivelow; + iprint("pcirouting: %Y at pin %d ", pci->tbdf, pin); + if(pci->intl != 0 && pci->intl != 0xFF && pci->intl != irq) + iprint("irq %d -> %d\n", pci->intl, irq); + else + iprint("irq %d\n", irq); + pcicfgw8(pci, PciINTL, irq); + pci->intl = irq; + } +} diff --git a/os/manga/pinflate b/os/manga/pinflate Binary files differnew file mode 100644 index 00000000..c6e8428e --- /dev/null +++ b/os/manga/pinflate diff --git a/os/manga/trap.c b/os/manga/trap.c new file mode 100644 index 00000000..1ac808ba --- /dev/null +++ b/os/manga/trap.c @@ -0,0 +1,498 @@ +#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" + +#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq))) + +enum +{ + MaxVector= 32, /* determined by bits per word */ + Maxhandler= MaxVector+5 /* max number of interrupt handlers, assuming a few shared */ +}; + +typedef struct Handler Handler; +struct Handler { + void (*r)(Ureg*, void*); + void* a; + 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 = 0xE6BAD010; + +int (*breakhandler)(Ureg*, Proc*); +int (*catchdbg)(Ureg *, uint); + +extern void (*serwrite)(char *, int); + +void +intrenable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name) +{ + int o, x; + ulong f; + GpioReg *g; + Handler *h; + + USED(sort); + f = v; + v &= IRQmask; + if(v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v); + ilock(&veclock); + if(v >= IRQext0 && v <= IRQtm1){ + /* need to switch GPIO pins, set mode */ + g = GPIOREG; + if(v <= IRQext3){ /* choice of interrupt type */ + o = (v-IRQext0)*4; /* b mmm */ + g->iopc = (g->iopc & ~(7<<o)) | (1<<(o+3)) | ((f>>8)&7)<<o; + o = v - IRQext0; + if(f & IRQsoft) + g->iopm |= 1<<o; /* soft interrupt uses GPIO as output */ + else + g->iopm &= ~(1<<o); + }else + g->iopc |= 1<<(16+(v-IRQtm0)); + if(0) + iprint("v=%d iopc=%8.8lux iopm=%8.8lux\n", v, g->iopc, g->iopm); + } + if((h = halloc.freelist) == nil){ + if(halloc.free >= Maxhandler){ + iunlock(&veclock); + panic("out of interrupt handlers"); /* can't happen */ + } + h = &halloc.h[halloc.free++]; + }else + halloc.freelist = h->next; + h->r = r; + h->a = a; + strncpy(h->name, name, KNAMELEN-1); + h->name[KNAMELEN-1] = 0; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + + /* enable the corresponding interrupt in the controller */ + x = splfhi(); + INTRREG->st = 1<<v; + INTRREG->en |= 1<<v; + splx(x); + iunlock(&veclock); +} + +void +intrdisable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name) +{ + int x, o; + GpioReg *g; + Handler *h, **hp; + + USED(sort); + v &= IRQmask; + if(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->a == a && strcmp(h->name, name) == 0){ + *hp = h->next; + h->r = nil; + h->next = halloc.freelist; + halloc.freelist = h; + break; + } + if(halloc.ivec[v] == nil){ + if(v >= IRQext0 && v <= IRQtm1){ + /* need to reset GPIO pins */ + g = GPIOREG; + if(v <= IRQext3){ /* choice of interrupt type */ + o = (v-IRQext0)*4; /* b mmm */ + g->iopc &= ~(0xF<<o); + g->iopm &= ~(v-IRQext0); /* force to input */ + }else + g->iopc &= ~(1<<(16+(v-IRQtm0))); + } + x = splfhi(); + INTRREG->en &= ~(1<<v); + splx(x); + } + iunlock(&veclock); +} + +static void +intrs(Ureg *ur, ulong ibits) +{ + Handler *h; + int i, s; + + for(i=0; i<nelem(halloc.ivec) && ibits; i++) + if(ibits & (1<<i)){ + h = halloc.ivec[i]; + for(; h != nil; h = h->next){ + INTRREG->st = 1<<i; /* reset edge; has no effect on level interrupts */ + h->r(ur, h->a); + ibits &= ~(1<<i); + } + } + if(ibits != 0){ + iprint("spurious irq interrupt: %8.8lux\n", ibits); + s = splfhi(); + INTRREG->en &= ~ibits; + splx(s); + } +} + +/* + * initialise R13 in each trap mode, at the start and after suspend reset. + */ +void +trapstacks(void) +{ + setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack)); + setr13(PsrMirq, m->irqstack+nelem(m->irqstack)); + setr13(PsrMabt, m->abtstack+nelem(m->abtstack)); + setr13(PsrMund, m->undstack+nelem(m->undstack)); +} + +void +trapinit(void) +{ + IntrReg *intr; + + intr = INTRREG; + intr->mc = 0; /* all IRQ not FIQ */ + intr->en = 0; /* disable everything */ + intr->st = intr->st; /* reset edges */ + + trapstacks(); + + memmove(page0->vectors, vectors, sizeof(page0->vectors)); + memmove(page0->vtable, vtable, sizeof(page0->vtable)); + dcflush(page0, sizeof(*page0)); + + icflushall(); +} + +static char *trapnames[PsrMask+1] = { + [ PsrMfiq ] "Fiq interrupt", + [ PsrMirq ] "Mirq interrupt", + [ PsrMsvc ] "SVC/SWI Exception", + [ PsrMabt ] "Prefetch Abort/Data Abort", + [ PsrMabt+1 ] "Data Abort", + [ PsrMund ] "Undefined instruction", + [ PsrMsys ] "Sys trap" +}; + +static char * +trapname(int psr) +{ + char *s; + + s = trapnames[psr & PsrMask]; + if(s == nil) + s = "Undefined trap"; + return s; +} + +static void +sys_trap_error(int type) +{ + char errbuf[ERRMAX]; + sprint(errbuf, "sys: trap: %s\n", trapname(type)); + error(errbuf); +} + +static void +faultarm(Ureg *ureg, ulong far) +{ + char buf[ERRMAX]; + + sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far); + if(1){ + iprint("%s\n", buf); + dumpregs(ureg); + } + if(far == ~0) + disfault(ureg, "dereference of nil"); + disfault(ureg, buf); +} + +/* + * All traps come here. It might be slightly slower to have all traps call trap + * rather than directly vectoring the handler. + * However, this avoids a lot of code duplication and possible bugs. + * trap is called splfhi(). + */ +void +trap(Ureg* ureg) +{ + ulong far, fsr; + int t, itype; + Proc *oup; + + /* + * All interrupts/exceptions should be resumed at ureg->pc-4, + * except for Data Abort which resumes at ureg->pc-8. + */ + itype = ureg->type; + if(itype == PsrMabt+1) + ureg->pc -= 8; + else + ureg->pc -= 4; + ureg->sp = (ulong)(ureg+1); + if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */ + oup = up; + up = nil; + intrs(ureg, INTRREG->ms & INTRREG->mc); /* just FIQ ones */ + up = oup; + return; + } + + /* All other traps */ + + if(up){ + up->pc = ureg->pc; + up->dbgreg = ureg; + } + switch(itype) { + case PsrMirq: + t = m->ticks; /* CPU time per proc */ + up = nil; /* no process at interrupt level */ + splflo(); /* allow fast interrupts */ + intrs(ureg, INTRREG->ms & ~INTRREG->mc); /* just IRQ */ + up = m->proc; + preemption(m->ticks - t); + break; + + case PsrMund: /* Undefined instruction */ + if(*(ulong*)ureg->pc == BREAK && breakhandler) { + int s; + Proc *p; + + p = up; + /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo) + p = 0; */ + s = breakhandler(ureg, p); + if(s == BrkSched) { + p->preempted = 0; + sched(); + } else if(s == BrkNoSched) { + p->preempted = 1; /* stop it being preempted until next instruction */ + if(up) + up->dbgreg = 0; + return; + } + break; + } + if(up == nil) + goto faultpanic; + spllo(); + if(waserror()) { + if(waslo(ureg->psr) && up->type == Interp) + disfault(ureg, up->env->errstr); + setpanic(); + dumpregs(ureg); + panic("%s", up->env->errstr); + } + if(!fpiarm(ureg)) { + dumpregs(ureg); + sys_trap_error(ureg->type); + } + poperror(); + break; + + case PsrMsvc: /* Jump through 0 or SWI */ + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + dumpregs(ureg); + sys_trap_error(ureg->type); + } + setpanic(); + dumpregs(ureg); + panic("SVC/SWI exception"); + break; + + case PsrMabt: /* Prefetch abort */ + if(catchdbg && catchdbg(ureg, 0)) + break; + /* FALL THROUGH */ + case PsrMabt+1: /* Data abort */ + fsr = mmugetfsr(); + far = mmugetfar(); + if(fsr & (1<<9)) { + mmuputfsr(fsr & ~(1<<9)); + if(catchdbg && catchdbg(ureg, fsr)) + break; + print("Debug/"); + } + if(waslo(ureg->psr) && up && up->type == Interp) { + spllo(); + faultarm(ureg, far); + } + iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(500);serialputs("\n", 1); + /* FALL THROUGH */ + + default: /* ??? */ +faultpanic: + setpanic(); + dumpregs(ureg); + panic("exception %uX %s\n", ureg->type, trapname(ureg->type)); + break; + } + + splhi(); + if(up) + up->dbgreg = 0; /* becomes invalid after return from trap */ +} + +void +setpanic(void) +{ + if(breakhandler != 0) /* don't mess up debugger */ + return; +/* + INTRREG->en = 0; + spllo(); +*/ + splhi(); + GPIOREG->iopd &= ~(1<<GPIO_status_orange_o); + consoleprint = 1; + serwrite = serialputs; +} + +int +isvalid_va(void *v) +{ + return (ulong)v >= KZERO && (ulong)v <= (ulong)KADDR(conf.topofmem-1); +} + +void +dumplongs(char *msg, ulong *v, int n) +{ + int i, l; + + l = 0; + iprint("%s at %.8p: ", msg, v); + for(i=0; i<n; i++){ + if(l >= 4){ + iprint("\n %.8p: ", v); + l = 0; + } + if(isvalid_va(v)){ + iprint(" %.8lux", *v++); + l++; + }else{ + iprint(" invalid"); + break; + } + } + iprint("\n"); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong v, *l, *estack; + int i; + + l = (ulong*)(ureg+1); + if((ulong)l & 3){ + iprint("invalid ureg/stack: %.8p\n", l); + return; + } + iprint("dumpstack\n"); + print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14); + if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4)) + estack = (ulong*)(up->kstack+KSTACK); + else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4)) + estack = (ulong*)((ulong)m+BY2PG-4); + else{ + iprint("unknown stack %8.8p\n", l); + return; + } + iprint("estackx %8.8p\n", estack); + i = 0; + for(; l<estack; l++) { + v = *l; + if(KTZERO < v && v < (ulong)etext){ + iprint("%8.8p=%8.8lux ", l, v); + if(i++ == 4){ + iprint("\n"); + i = 0; + } + } + } + if(i) + print("\n"); +} + +void +dumpregs(Ureg* ureg) +{ + print("TRAP: %s", trapname(ureg->type)); + if((ureg->psr & PsrMask) != PsrMsvc) + print(" in %s", trapname(ureg->psr)); + print("\n"); + print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n", + ureg->psr, ureg->type, ureg->pc, ureg->link); + print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n", + ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10); + print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n", + ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5); + print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n", + ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0); + print("Stack is at: %8.8luX\n", ureg); + print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link); + + if(up) + print("Process stack: %8.8lux-%8.8lux\n", + up->kstack, up->kstack+KSTACK-4); + else + print("System stack: %8.8lux-%8.8lux\n", + (ulong)(m+1), (ulong)m+BY2PG-4); + dumplongs("stack", (ulong *)(ureg + 1), 16); + _dumpstack(ureg); +} + +/* + * Fill in enough of Ureg to get a stack trace, and call a function. + * Used by debugging interface rdb. + */ +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + ureg.pc = getcallerpc(&fn); + ureg.sp = (ulong)&fn; + ureg.r14 = 0; + fn(&ureg); +} + +void +dumpstack(void) +{ +return; + callwithureg(_dumpstack); +} + +void +trapspecial(int (*f)(Ureg *, uint)) +{ + catchdbg = f; +} diff --git a/os/manga/uartks8695.c b/os/manga/uartks8695.c new file mode 100644 index 00000000..dd04f02d --- /dev/null +++ b/os/manga/uartks8695.c @@ -0,0 +1,629 @@ +#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/uart.h" + +/* + * KS8695 uart; similar to 8250 etc but registers are slightly different, + * and interrupt control is quite different + */ +enum { + UartFREQ = CLOCKFREQ, +}; + +/* + * similar to i8250/16450/16550 (slight differences) + */ + +enum { /* I/O ports */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 1, /* Transmitter Holding (WO) */ + Fcr = 2, /* FIFO Control */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + Div = 7, /* Divisor */ + Usr = 8, /* Status */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* UART OUT1 asserted */ + Out2 = 0x08, /* UART OUT2 asserted */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Tramsmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ + LsrInput = FIFOerr|Oe|Pe|Fe|Dr|Bi, /* input status only */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Data Set Ready */ +}; + +enum { /* Usr */ + Uti = 0x01, /* INTST[9]=1=> =1, interrupt is timeout; =0, receive FIFO trigger */ +}; + +typedef struct Ctlr { + ulong* regs; + int irq; + int iena; + + Lock; + int fena; +} Ctlr; + +extern PhysUart ks8695physuart; + + +static Ctlr ks8695_ctlr[1] = { +{ .regs = (ulong*)PHYSUART, + .irq = IRQuts, /* base: ts then rs, ls, ms */ +}, +}; + +static Uart ks8695_uart[1] = { +{ .regs = &ks8695_ctlr[0], + .name = "eia0", + .freq = UartFREQ, + .phys = &ks8695physuart, + .special= 0, + .next = nil, }, +}; + +#define csr8r(c, r) ((c)->regs[(r)]) +#define csr8w(c, r, v) ((c)->regs[(r)] = (v)) + +static long +ks8695_status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = malloc(READSTR); + mcr = csr8r(ctlr, Mcr); + msr = csr8r(ctlr, Msr); + ier = INTRREG->en; + lcr = csr8r(ctlr, Lcr); + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n" + "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & (1<<IRQums)) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + ier, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +ks8695_fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +ks8695_dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + int r; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + r = csr8r(ctlr, Mcr); + if(on) + r |= Dtr; + else + r &= ~Dtr; + csr8w(ctlr, Mcr, r); +} + +static void +ks8695_rts(Uart* uart, int on) +{ + Ctlr *ctlr; + int r; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + r = csr8r(ctlr, Mcr); + if(on) + r |= Rts; + else + r &= ~Rts; + csr8w(ctlr, Mcr, r); +} + +static void +ks8695_modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + INTRREG->en |= 1<<IRQums; /* TO DO */ + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + INTRREG->en &= ~(1<<IRQums); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +ks8695_parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr) & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + default: + break; + } + csr8w(ctlr, Lcr, lcr); + + uart->parity = parity; + + return 0; +} + +static int +ks8695_stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr); + switch(stop){ + case 1: + lcr &= ~Stb; + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + csr8w(ctlr, Lcr, lcr); + uart->stop = stop; + return 0; +} + +static int +ks8695_bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr) & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + csr8w(ctlr, Lcr, lcr); + + uart->bits = bits; + + return 0; +} + +static int +ks8695_baud(Uart* uart, int baud) +{ + ulong bgc; + Ctlr *ctlr; + + if(uart->freq == 0 || baud <= 0) + return -1; + ctlr = uart->regs; + bgc = (uart->freq+baud-1)/baud; + csr8w(ctlr, Div, bgc); + uart->baud = baud; + return 0; +} + +static void +ks8695_break(Uart* uart, int ms) +{ + Ctlr *ctlr; + int lcr; + + /* + * Send a break. + */ + if(ms == 0) + ms = 200; + + ctlr = uart->regs; + lcr = csr8r(ctlr, Lcr); + csr8w(ctlr, Lcr, lcr|Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, lcr); +} + +static void +ks8695_kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + return; + + ctlr = uart->regs; + for(i = 0; i < 16; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + csr8w(ctlr, Thr, *uart->op++); + } +} + +static void +ks8695_modemintr(Ureg*, void *arg) +{ + Ctlr *ctlr; + Uart *uart; + int old, r; + + uart = arg; + ctlr = uart->regs; + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } +} + +static void +ks8695_rxintr(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int lsr, r; + + /* handle line error status here as well */ + uart = arg; + ctlr = uart->regs; + while((lsr = csr8r(ctlr, Lsr) & LsrInput) != 0){ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + if(lsr & Dr){ + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + } +} + +static void +ks8695_txintr(Ureg*, void* arg) +{ + uartkick(arg); +} + +static void +ks8695_disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + + if(ctlr->iena != 0){ + intrdisable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name); + intrdisable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name); + ctlr->iena = 0; + } +} + +static void +ks8695_enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ctlr->iena == 0 && ie){ + intrenable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name); + intrenable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name); + ctlr->iena = 1; + } + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); +} + +static Uart* +ks8695_pnp(void) +{ + return ks8695_uart; +} + +static int +ks8695_getc(Uart *uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr)&Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +ks8695_putc(Uart *uart, int c) +{ + serialputc(c); +#ifdef ROT + int i; + Ctlr *ctlr; + + ctlr = uart->regs; + for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 256; i++) + delay(1); + csr8w(ctlr, Thr, c); + if(c == '\n') + while((csr8r(ctlr, Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +#endif +} + +PhysUart ks8695physuart = { + .name = "ks8695", + .pnp = ks8695_pnp, + .enable = ks8695_enable, + .disable = ks8695_disable, + .kick = ks8695_kick, + .dobreak = ks8695_break, + .baud = ks8695_baud, + .bits = ks8695_bits, + .stop = ks8695_stop, + .parity = ks8695_parity, + .modemctl = ks8695_modemctl, + .rts = ks8695_rts, + .dtr = ks8695_dtr, + .status = ks8695_status, + .fifo = ks8695_fifo, + .getc = ks8695_getc, + .putc = ks8695_putc, +}; + +void +uartconsole(void) +{ + Uart *uart; + + uart = &ks8695_uart[0]; + (*uart->phys->enable)(uart, 0); + uartctl(uart, "b38400 l8 pn s1"); + consuart = uart; + uart->console = 1; +} + +#define UR(p,r) ((ulong*)(p))[r] + +void +serialputc(int c) +{ + ulong *p; + + if(c == 0) + return; + p = (ulong*)PHYSUART; + while((UR(p,Lsr) & Thre) == 0){ + /* skip */ + } + UR(p,Thr) = c; + if(c == '\n') + while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +} + +/* + * for iprint, just write it + */ +void +serialputs(char *data, int len) +{ + ulong *p; + + p = (ulong*)PHYSUART; + while(--len >= 0){ + if(*data == '\n') + serialputc('\r'); + serialputc(*data++); + } + while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */ + /* skip */ + } +} +void (*serwrite)(char*, int) = serialputs; diff --git a/os/manga/usb.h b/os/manga/usb.h new file mode 100644 index 00000000..0aad42c5 --- /dev/null +++ b/os/manga/usb.h @@ -0,0 +1,160 @@ +typedef struct Ctlr Ctlr; +typedef struct Endpt Endpt; +typedef struct Udev Udev; +typedef struct Usbhost Usbhost; + +enum +{ + MaxUsb = 4, /* max number of USB Host Controller Interfaces (Usbhost*) */ + MaxUsbDev = 32, /* max number of attached USB devices, including root hub (Udev*) */ + + /* + * USB packet definitions... + */ + TokIN = 0x69, + TokOUT = 0xE1, + TokSETUP = 0x2D, + + /* request type */ + RH2D = 0<<7, + RD2H = 1<<7, + Rstandard = 0<<5, + Rclass = 1<<5, + Rvendor = 2<<5, + Rdevice = 0, + Rinterface = 1, + Rendpt = 2, + Rother = 3, +}; + +#define Class(csp) ((csp)&0xff) +#define Subclass(csp) (((csp)>>8)&0xff) +#define Proto(csp) (((csp)>>16)&0xff) +#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16)) + +/* + * device endpoint + */ +struct Endpt +{ + Ref; + Lock; + int x; /* index in Udev.ep */ + int id; /* hardware endpoint address */ + int maxpkt; /* maximum packet size (from endpoint descriptor) */ + int data01; /* 0=DATA0, 1=DATA1 */ + uchar eof; + ulong csp; + uchar mode; /* OREAD, OWRITE, ORDWR */ + uchar nbuf; /* number of buffers allowed */ + uchar iso; + uchar debug; + uchar active; /* listed for examination by interrupts */ + int setin; + /* ISO related: */ + int hz; + int remain; /* for packet size calculations */ + int samplesz; + int sched; /* schedule index; -1 if undefined or aperiodic */ + int pollms; /* polling interval in msec */ + int psize; /* (remaining) size of this packet */ + int off; /* offset into packet */ + /* Real-time iso stuff */ + ulong foffset; /* file offset (to detect seeks) */ + ulong poffset; /* offset of next packet to be queued */ + ulong toffset; /* offset associated with time */ + vlong time; /* timeassociated with offset */ + int buffered; /* bytes captured but unread, or written but unsent */ + /* end ISO stuff */ + + Udev* dev; /* owning device */ + + ulong nbytes; + ulong nblocks; + + void *private; + + // all the rest could (should?) move to the driver private structure; except perhaps err + QLock rlock; + Rendez rr; + Queue* rq; + QLock wlock; + Rendez wr; + Queue* wq; + + int ntd; + char* err; // needs to be global for unstall; fix? + + Endpt* activef; /* active endpoint list */ +}; + +/* device parameters */ +enum +{ + /* Udev.state */ + Disabled = 0, + Attached, + Enabled, + Assigned, + Configured, + + /* Udev.class */ + Noclass = 0, + Hubclass = 9, +}; + +/* + * active USB device + */ +struct Udev +{ + Ref; + Lock; + Usbhost *uh; + int x; /* index in usbdev[] */ + int busy; + int state; + int id; + uchar port; /* port number on connecting hub */ + ulong csp; + ushort vid; /* vendor id */ + ushort did; /* product id */ + int ls; + int npt; + Endpt* ep[16]; /* active end points */ + Udev* ports; /* active ports, if hub */ + Udev* next; /* next device on this hub */ +}; + +/* + * One of these per active Host Controller Interface (HCI) + */ +struct Usbhost +{ + ISAConf; /* hardware info */ + int tbdf; /* type+busno+devno+funcno */ + + QLock; /* protects namespace state */ + int idgen; /* version number to distinguish new connections */ + Udev* dev[MaxUsbDev]; /* device endpoints managed by this HCI */ + + void (*init)(Usbhost*); + void (*interrupt)(Ureg*, void*); + + void (*portinfo)(Usbhost*, char*, char*); + void (*portreset)(Usbhost*, int); + void (*portenable)(Usbhost*, int, int); + + void (*epalloc)(Usbhost*, Endpt*); + void (*epfree)(Usbhost*, Endpt*); + void (*epopen)(Usbhost*, Endpt*); + void (*epclose)(Usbhost*, Endpt*); + void (*epmode)(Usbhost*, Endpt*); + + long (*read)(Usbhost*, Endpt*, void*, long, vlong); + long (*write)(Usbhost*, Endpt*, void*, long, vlong, int); + + void *ctlr; +}; + +extern void addusbtype(char*, int(*)(Usbhost*)); diff --git a/os/manga/usbuhci.c b/os/manga/usbuhci.c new file mode 100644 index 00000000..12140d04 --- /dev/null +++ b/os/manga/usbuhci.c @@ -0,0 +1,1556 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "usb.h" + +#define todget(x) 0 /* TO DO */ +#define XPRINT if(debug)iprint + +static int Chatty = 0; +static int debug = 0; + +static char Estalled[] = "usb endpoint stalled"; + +/* + * UHCI interface registers and bits + */ +enum +{ + /* i/o space */ + Cmd = 0, + Status = 2, + Usbintr = 4, + Frnum = 6, + Flbaseadd = 8, + SOFMod = 0xC, + Portsc0 = 0x10, + Portsc1 = 0x12, + + /* port status */ + Suspend = 1<<12, + PortReset = 1<<9, + SlowDevice = 1<<8, + ResumeDetect = 1<<6, + PortChange = 1<<3, /* write 1 to clear */ + PortEnable = 1<<2, + StatusChange = 1<<1, /* write 1 to clear */ + DevicePresent = 1<<0, + + NFRAME = 1024, + FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ + + Vf = 1<<2, /* TD only */ + IsQH = 1<<1, + Terminate = 1<<0, + + /* TD.status */ + SPD = 1<<29, + ErrLimit0 = 0<<27, + ErrLimit1 = 1<<27, + ErrLimit2 = 2<<27, + ErrLimit3 = 3<<27, + LowSpeed = 1<<26, + IsoSelect = 1<<25, + IOC = 1<<24, + Active = 1<<23, + Stalled = 1<<22, + DataBufferErr = 1<<21, + Babbling = 1<<20, + NAKed = 1<<19, + CRCorTimeout = 1<<18, + BitstuffErr = 1<<17, + AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), + + /* TD.dev */ + IsDATA1 = 1<<19, + + /* TD.flags (software) */ + CancelTD= 1<<0, + IsoClean= 1<<2, +}; + +static struct +{ + int bit; + char *name; +} +portstatus[] = +{ + { Suspend, "suspend", }, + { PortReset, "reset", }, + { SlowDevice, "lowspeed", }, + { ResumeDetect, "resume", }, + { PortChange, "portchange", }, + { PortEnable, "enable", }, + { StatusChange, "statuschange", }, + { DevicePresent, "present", }, +}; + +typedef struct Ctlr Ctlr; +typedef struct Endptx Endptx; +typedef struct QH QH; +typedef struct TD TD; + +/* + * software structures + */ +struct Ctlr +{ + Lock; /* protects state shared with interrupt (eg, free list) */ + Ctlr* next; + Pcidev* pcidev; + int active; + + int io; + ulong* frames; /* frame list */ + ulong* frameld; /* real time load on each of the frame list entries */ + QLock resetl; /* lock controller during USB reset */ + + TD* tdpool; + TD* freetd; + QH* qhpool; + QH* freeqh; + + QH* ctlq; /* queue for control i/o */ + QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ + QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ + QH* recvq; /* receive queues for bulk i/o */ + + Udev* ports[2]; + + struct { + Lock; + Endpt* f; + } activends; + + long usbints; /* debugging */ + long framenumber; + long frameptr; + long usbbogus; +}; + +#define IN(x) ins(ctlr->io+(x)) +#define OUT(x, v) outs(ctlr->io+(x), (v)) + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +struct Endptx +{ + QH* epq; /* queue of TDs for this endpoint */ + + /* ISO related: */ + void* tdalloc; + void* bpalloc; + uchar* bp0; /* first block in array */ + TD * td0; /* first td in array */ + TD * etd; /* pointer into circular list of TDs for isochronous ept */ + TD * xtd; /* next td to be cleaned */ +}; + +/* + * UHCI hardware structures, aligned on 16-byte boundary + */ +struct TD +{ + ulong link; + ulong status; /* controller r/w */ + ulong dev; + ulong buffer; + + /* software */ + ulong flags; + union{ + Block* bp; /* non-iso */ + ulong offset; /* iso */ + }; + Endpt* ep; + TD* next; +}; +#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +struct QH +{ + ulong head; + ulong entries; /* address of next TD or QH to process (updated by controller) */ + + /* software */ + QH* hlink; + TD* first; + QH* next; /* free list */ + TD* last; + ulong _d1; /* fillers */ + ulong _d2; +}; +#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW))) + +static TD * +alloctd(Ctlr *ctlr) +{ + TD *t; + + ilock(ctlr); + t = ctlr->freetd; + if(t == nil) + panic("alloctd"); /* TO DO */ + ctlr->freetd = t->next; + t->next = nil; + iunlock(ctlr); + t->ep = nil; + t->bp = nil; + t->status = 0; + t->link = Terminate; + t->buffer = 0; + t->flags = 0; + return t; +} + +static void +freetd(Ctlr *ctlr, TD *t) +{ + t->ep = nil; + if(t->bp) + freeb(t->bp); + t->bp = nil; + ilock(ctlr); + t->buffer = 0xdeadbeef; + t->next = ctlr->freetd; + ctlr->freetd = t; + iunlock(ctlr); +} + +static void +dumpdata(Block *b, int n) +{ + int i; + + XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n); + if(n > 16) + n = 16; + for(i=0; i<n; i++) + XPRINT(" %2.2ux", b->rp[i]); + XPRINT("\n"); +} + +static void +dumptd(TD *t, int follow) +{ + int i, n; + char buf[20], *s; + TD *t0; + + t0 = t; + while(t){ + i = t->dev & 0xFF; + if(i == TokOUT || i == TokSETUP) + n = ((t->dev>>21) + 1) & 0x7FF; + else if((t->status & Active) == 0) + n = (t->status + 1) & 0x7FF; + else + n = 0; + s = buf; + if(t->status & Active) + *s++ = 'A'; + if(t->status & Stalled) + *s++ = 'S'; + if(t->status & DataBufferErr) + *s++ = 'D'; + if(t->status & Babbling) + *s++ = 'B'; + if(t->status & NAKed) + *s++ = 'N'; + if(t->status & CRCorTimeout) + *s++ = 'T'; + if(t->status & BitstuffErr) + *s++ = 'b'; + if(t->status & LowSpeed) + *s++ = 'L'; + *s = 0; + XPRINT("td %8.8lux: ", t); + XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", + t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); + XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", + buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); + if(debug && t->bp && (t->flags & CancelTD) == 0) + dumpdata(t->bp, n); + if(!follow || t->link & Terminate || t->link & IsQH) + break; + t = TFOL(t->link); + if(t == t0) + break; /* looped */ + } +} + +static TD * +alloctde(Ctlr *ctlr, Endpt *e, int pid, int n) +{ + TD *t; + int tog, id; + + t = alloctd(ctlr); + id = (e->x<<7)|(e->dev->x&0x7F); + tog = 0; + if(e->data01 && pid != TokSETUP) + tog = IsDATA1; + t->ep = e; + t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ + if(e->dev->ls) + t->status |= LowSpeed; + t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; + return t; +} + +static QH * +allocqh(Ctlr *ctlr) +{ + QH *qh; + + ilock(ctlr); + qh = ctlr->freeqh; + if(qh == nil) + panic("allocqh"); /* TO DO */ + ctlr->freeqh = qh->next; + qh->next = nil; + iunlock(ctlr); + qh->head = Terminate; + qh->entries = Terminate; + qh->hlink = nil; + qh->first = nil; + qh->last = nil; + return qh; +} + +static void +freeqh(Ctlr *ctlr, QH *qh) +{ + ilock(ctlr); + qh->next = ctlr->freeqh; + ctlr->freeqh = qh; + iunlock(ctlr); +} + +static void +dumpqh(QH *q) +{ + int i; + QH *q0; + + q0 = q; + for(i = 0; q != nil && i < 10; i++){ + XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); + if((q->entries & Terminate) == 0) + dumptd(TFOL(q->entries), 1); + if(q->head & Terminate) + break; + if((q->head & IsQH) == 0){ + XPRINT("head:"); + dumptd(TFOL(q->head), 1); + break; + } + q = QFOL(q->head); + if(q == q0) + break; /* looped */ + } +} + +static void +queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why) +{ + TD *lt; + + for(lt = t; lt->next != nil; lt = lt->next) + lt->link = PCIWADDR(lt->next) | vf; + lt->link = Terminate; + ilock(ctlr); + XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", + why, t, lt, q, q->first, q->last, q->entries); + if(q->first != nil){ + q->last->link = PCIWADDR(t) | vf; + q->last->next = t; + }else{ + q->first = t; + q->entries = PCIWADDR(t); + } + q->last = lt; + XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", + t, q, q->first, q->last, q->entries); + dumpqh(q); + iunlock(ctlr); +} + +static void +cleantd(Ctlr *ctlr, TD *t, int discard) +{ + Block *b; + int n, err; + + XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + if(t->ep != nil && t->ep->debug) + dumptd(t, 0); + if(t->status & Active) + panic("cleantd Active"); + err = t->status & (AnyError&~NAKed); + /* TO DO: on t->status&AnyError, q->entries will not have advanced */ + if (err) { + XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); +// print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); + } + switch(t->dev&0xFF){ + case TokIN: + if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){ + if(t->ep != nil){ + if(err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + wakeup(&t->ep->rr); /* in case anyone cares */ + } + break; + } + b = t->bp; + n = (t->status + 1) & 0x7FF; + if(n > b->lim - b->wp) + n = 0; + b->wp += n; + if(Chatty) + dumpdata(b, n); + t->bp = nil; + t->ep->nbytes += n; + t->ep->nblocks++; + qpass(t->ep->rq, b); /* TO DO: flow control */ + wakeup(&t->ep->rr); /* TO DO */ + break; + case TokSETUP: + XPRINT("cleanTD: TokSETUP %lux\n", &t->ep); + /* don't really need to wakeup: subsequent IN or OUT gives status */ + if(t->ep != nil) { + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + case TokOUT: + /* TO DO: mark it done somewhere */ + XPRINT("cleanTD: TokOut %lux\n", &t->ep); + if(t->ep != nil){ + if(t->bp){ + n = BLEN(t->bp); + t->ep->nbytes += n; + t->ep->nblocks++; + } + if(t->ep->x!=0 && err != 0) + t->ep->err = err==Stalled? Estalled: Eio; + if(--t->ep->ntd < 0) + panic("cleantd ntd"); + wakeup(&t->ep->wr); /* TO DO */ + XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); + } + break; + } + freetd(ctlr, t); +} + +static void +cleanq(Ctlr *ctlr, QH *q, int discard, int vf) +{ + TD *t, *tp; + + ilock(ctlr); + tp = nil; + for(t = q->first; t != nil;){ + XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next); + if(t->status & Active){ + if(t->status & NAKed){ + t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + if(t->flags & CancelTD){ + XPRINT("cancelTD: %8.8lux\n", (ulong)t); + t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ + tp = t; + t = t->next; + continue; + } + tp = t; + t = t->next; + continue; + } + t->status &= ~IOC; + if (tp == nil) { + q->first = t->next; + if(q->first != nil) + q->entries = PCIWADDR(q->first); + else + q->entries = Terminate; + } else { + tp->next = t->next; + if (t->next != nil) + tp->link = PCIWADDR(t->next) | vf; + else + tp->link = Terminate; + } + if (q->last == t) + q->last = tp; + iunlock(ctlr); + cleantd(ctlr, t, discard); + ilock(ctlr); + if (tp) + t = tp->next; + else + t = q->first; + XPRINT("t = %8.8lux\n", t); + dumpqh(q); + } + if(q->first && q->entries != PCIWADDR(q->first)){ + ctlr->usbbogus++; + q->entries = PCIWADDR(q->first); + } + iunlock(ctlr); +} + +static void +canceltds(Ctlr *ctlr, QH *q, Endpt *e) +{ + TD *t; + + if(q != nil){ + ilock(ctlr); + for(t = q->first; t != nil; t = t->next) + if(t->ep == e) + t->flags |= CancelTD; + iunlock(ctlr); + XPRINT("cancel:\n"); + dumpqh(q); + } +} + +static void +eptcancel(Ctlr *ctlr, Endpt *e) +{ + Endptx *x; + + if(e == nil) + return; + x = e->private; + canceltds(ctlr, x->epq, e); + canceltds(ctlr, ctlr->ctlq, e); + canceltds(ctlr, ctlr->bulkq, e); +} + +static void +eptactivate(Ctlr *ctlr, Endpt *e) +{ + ilock(&ctlr->activends); + if(e->active == 0){ + XPRINT("activate 0x%p\n", e); + e->active = 1; + e->activef = ctlr->activends.f; + ctlr->activends.f = e; + } + iunlock(&ctlr->activends); +} + +static void +eptdeactivate(Ctlr *ctlr, Endpt *e) +{ + Endpt **l; + + /* could be O(1) but not worth it yet */ + ilock(&ctlr->activends); + if(e->active){ + e->active = 0; + XPRINT("deactivate 0x%p\n", e); + for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef) + if(*l == nil){ + iunlock(&ctlr->activends); + panic("usb eptdeactivate"); + } + *l = e->activef; + } + iunlock(&ctlr->activends); +} + +static void +queueqh(Ctlr *ctlr, QH *qh) +{ + QH *q; + + // See if it's already queued + for (q = ctlr->recvq->next; q; q = q->hlink) + if (q == qh) + return; + if ((qh->hlink = ctlr->recvq->next) == nil) + qh->head = Terminate; + else + qh->head = PCIWADDR(ctlr->recvq->next) | IsQH; + ctlr->recvq->next = qh; + ctlr->recvq->entries = PCIWADDR(qh) | IsQH; +} + +static QH* +qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid) +{ + TD *t; + int n, vf; + QH *qh; + Endptx *x; + + x = e->private; + if(b != nil){ + n = BLEN(b); + dcflush(b->rp, n); + t = alloctde(ctlr, e, pid, n); + t->bp = b; + t->buffer = PCIWADDR(b->rp); + }else + t = alloctde(ctlr, e, pid, 0); + ilock(ctlr); + e->ntd++; + iunlock(ctlr); + if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + vf = 0; + }else if((qh = x->epq) == nil || e->mode != OWRITE){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qxmit"); + return qh; +} + +/* + * allocate receive buffer space on cache-line boundaries + */ +static Block* +clallocb(long n) +{ + Block *b; + + b = allocb(n+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 QH* +qrcv(Ctlr *ctlr, Endpt *e) +{ + TD *t; + Block *b; + QH *qh; + int vf; + Endptx *x; + + x = e->private; + t = alloctde(ctlr, e, TokIN, e->maxpkt); + b = clallocb(e->maxpkt); + t->bp = b; + t->buffer = PCIWADDR(b->wp); + vf = 0; + if(e->x == 0){ + qh = ctlr->ctlq; + }else if((qh = x->epq) == nil || e->mode != OREAD){ + qh = ctlr->bulkq; + vf = Vf; + } + queuetd(ctlr, qh, t, vf, "qrcv"); + return qh; +} + +static int +usbsched(Ctlr *ctlr, int pollms, ulong load) +{ + int i, d, q; + ulong best, worst; + + best = 1000000; + q = -1; + for (d = 0; d < pollms; d++){ + worst = 0; + for (i = d; i < NFRAME; i++){ + if (ctlr->frameld[i] + load > worst) + worst = ctlr->frameld[i] + load; + } + if (worst < best){ + best = worst; + q = d; + } + } + return q; +} + +static int +schedendpt(Ctlr *ctlr, Endpt *e) +{ + TD *td; + Endptx *x; + uchar *bp; + int i, id, ix, size, frnum; + + if(!e->iso || e->sched >= 0) + return 0; + + if (e->active){ + return -1; + } + e->off = 0; + e->sched = usbsched(ctlr, e->pollms, e->maxpkt); + if(e->sched < 0) + return -1; + + x = e->private; + if (x->tdalloc || x->bpalloc) + panic("usb: tdalloc/bpalloc"); + x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD) + CACHELINESZ, 1); + x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms + CACHELINESZ, 1); + x->td0 = mmucacheinhib((TD*)(((ulong)x->tdalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), NFRAME*sizeof(TD) + 0x10); + x->bp0 = mmucacheinhib((uchar *)(((ulong)x->bpalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), e->maxpkt*NFRAME/e->pollms + 0x10); + frnum = (IN(Frnum) + 1) & 0x3ff; + frnum = (frnum & ~(e->pollms - 1)) + e->sched; + x->xtd = &x->td0[(frnum+8)&0x3ff]; /* Next td to finish */ + x->etd = nil; + e->remain = 0; + e->nbytes = 0; + td = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + bp = x->bp0 + e->maxpkt*i/e->pollms; + td->buffer = PCIWADDR(bp); + td->ep = e; + td->next = &td[1]; + ctlr->frameld[i] += e->maxpkt; + td++; + } + td[-1].next = x->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + ix = (frnum+i) & 0x3ff; + td = &x->td0[ix]; + + id = (e->x<<7)|(e->dev->x&0x7F); + if (e->mode == OREAD) + /* enable receive on this entry */ + td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; + else{ + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + td->link = ctlr->frames[ix]; + td->flags |= IsoClean; + ctlr->frames[ix] = PCIWADDR(td); + } + return 0; +} + +static void +unschedendpt(Ctlr *ctlr, Endpt *e) +{ + int q; + TD *td; + Endptx *x; + ulong *addr; + + if(!e->iso || e->sched < 0) + return; + + x = e->private; + if (x->tdalloc == nil) + panic("tdalloc"); + for (q = e->sched; q < NFRAME; q += e->pollms){ + td = x->td0++; + addr = &ctlr->frames[q]; + while(*addr != PADDR(td)) { + if(*addr & IsQH) + panic("usb: TD expected"); + addr = &TFOL(*addr)->link; + } + *addr = td->link; + ctlr->frameld[q] -= e->maxpkt; + } + free(x->tdalloc); + free(x->bpalloc); + x->tdalloc = nil; + x->bpalloc = nil; + x->etd = nil; + x->td0 = nil; + e->sched = -1; +} + +static void +epalloc(Usbhost *uh, Endpt *e) +{ + Endptx *x; + + x = malloc(sizeof(Endptx)); + e->private = x; + x->epq = allocqh(uh->ctlr); + if(x->epq == nil) + panic("devendptx"); +} + +static void +epfree(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(x->epq != nil) + freeqh(ctlr, x->epq); +} + +static void +epopen(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(e->iso && e->active) + error("already open"); + if(schedendpt(ctlr, e) < 0){ + if(e->active) + error("cannot schedule USB endpoint, active"); + else + error("cannot schedule USB endpoint"); + } + eptactivate(ctlr, e); +} + +static void +epclose(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + eptdeactivate(ctlr, e); + unschedendpt(ctlr, e); +} + +static void +epmode(Usbhost *uh, Endpt *e) +{ + Ctlr *ctlr; + Endptx *x; + + ctlr = uh->ctlr; + x = e->private; + if(e->iso) { + if(x->epq != nil) { + freeqh(ctlr, x->epq); + x->epq = nil; + } + } + else { + /* Each bulk device gets a queue head hanging off the + * bulk queue head + */ + if(x->epq == nil) { + x->epq = allocqh(ctlr); + if(x->epq == nil) + panic("epbulk: allocqh"); + } + queueqh(ctlr, x->epq); + } +} + +static int ioport[] = {-1, Portsc0, Portsc1}; + +static void +portreset(Usbhost *uh, int port) +{ + int i, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + XPRINT("r: %x\n", IN(p)); + ilock(ctlr); + OUT(p, PortReset); + delay(12); /* BUG */ + XPRINT("r2: %x\n", IN(p)); + OUT(p, IN(p) & ~PortReset); + XPRINT("r3: %x\n", IN(p)); + OUT(p, IN(p) | PortEnable); + microdelay(64); + for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) + ; + XPRINT("r': %x %d\n", IN(p), i); + OUT(p, (IN(p) & ~PortReset)|PortEnable); + iunlock(ctlr); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portenable(Usbhost *uh, int port, int on) +{ + int w, p; + Ctlr *ctlr; + + ctlr = uh->ctlr; + if(port != 1 && port != 2) + error(Ebadarg); + + /* should check that device not being configured on other port? */ + p = ioport[port]; + qlock(&ctlr->resetl); + if(waserror()){ + qunlock(&ctlr->resetl); + nexterror(); + } + ilock(ctlr); + w = IN(p); + if(on) + w |= PortEnable; + else + w &= ~PortEnable; + OUT(p, w); + microdelay(64); + iunlock(ctlr); + XPRINT("e: %x\n", IN(p)); + poperror(); + qunlock(&ctlr->resetl); +} + +static void +portinfo(Usbhost *uh, char *s, char *se) +{ + int x, i, j; + Ctlr *ctlr; + + ctlr = uh->ctlr; + for(i = 1; i <= 2; i++) { + ilock(ctlr); + x = IN(ioport[i]); + if((x & (PortChange|StatusChange)) != 0) + OUT(ioport[i], x); + iunlock(ctlr); + s = seprint(s, se, "%d %ux", i, x); + for(j = 0; j < nelem(portstatus); j++) { + if((x & portstatus[j].bit) != 0) + s = seprint(s, se, " %s", portstatus[j].name); + } + s = seprint(s, se, "\n"); + } +} + +static void +cleaniso(Endpt *e, int frnum) +{ + TD *td; + int id, n, i; + Endptx *x; + uchar *bp; + + x = e->private; + td = x->xtd; + if (td->status & Active) + return; + id = (e->x<<7)|(e->dev->x&0x7F); + do { + if (td->status & AnyError) + XPRINT("usbisoerror 0x%lux\n", td->status); + n = (td->status + 1) & 0x3ff; + e->nbytes += n; + if ((td->flags & IsoClean) == 0) + e->nblocks++; + if (e->mode == OREAD){ + e->buffered += n; + e->poffset += (td->status + 1) & 0x3ff; + td->offset = e->poffset; + td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; + e->toffset = td->offset; + }else{ + if ((td->flags & IsoClean) == 0){ + e->buffered -= n; + if (e->buffered < 0){ +// print("e->buffered %d?\n", e->buffered); + e->buffered = 0; + } + } + e->toffset = td->offset; + n = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + n *= e->samplesz; + td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT; + td->offset = e->poffset; + e->poffset += n; + } + td = td->next; + if (x->xtd == td){ + XPRINT("@"); + break; + } + } while ((td->status & Active) == 0); + e->time = todget(nil); + x->xtd = td; + for (n = 2; n < 4; n++){ + i = ((frnum + n)&0x3ff); + td = x->td0 + i; + bp = x->bp0 + e->maxpkt*i/e->pollms; + if (td->status & Active) + continue; + + if (e->mode == OWRITE){ + if (td == x->etd) { + XPRINT("*"); + memset(bp+e->off, 0, e->maxpkt-e->off); + if (e->off == 0) + td->flags |= IsoClean; + else + e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off; + x->etd = nil; + }else if ((td->flags & IsoClean) == 0){ + XPRINT("-"); + memset(bp, 0, e->maxpkt); + td->flags |= IsoClean; + } + } else { + /* Unread bytes are now lost */ + e->buffered -= (td->status + 1) & 0x3ff; + } + td->status = ErrLimit1 | Active | IsoSelect | IOC; + } + wakeup(&e->wr); +} + +static void +interrupt(Ureg*, void *a) +{ + QH *q; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + int s, frnum; + Usbhost *uh; + + uh = a; + ctlr = uh->ctlr; + s = IN(Status); + ctlr->frameptr = inl(ctlr->io+Flbaseadd); + ctlr->framenumber = IN(Frnum) & 0x3ff; + OUT(Status, s); + if ((s & 0x1f) == 0) + return; + ctlr->usbints++; + frnum = IN(Frnum) & 0x3ff; + if (s & 0x1a) { + XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); + } + + ilock(&ctlr->activends); + for(e = ctlr->activends.f; e != nil; e = e->activef) { + x = e->private; + if(!e->iso && x->epq != nil) { + XPRINT("cleanq(ctlr, x->epq, 0, 0)\n"); + cleanq(ctlr, x->epq, 0, 0); + } + if(e->iso) { + XPRINT("cleaniso(e)\n"); + cleaniso(e, frnum); + } + } + iunlock(&ctlr->activends); + XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n"); + cleanq(ctlr, ctlr->ctlq, 0, 0); + XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n"); + cleanq(ctlr, ctlr->bulkq, 0, Vf); + XPRINT("clean recvq\n"); + for (q = ctlr->recvq->next; q; q = q->hlink) { + XPRINT("cleanq(ctlr, q, 0, Vf)\n"); + cleanq(ctlr, q, 0, Vf); + } +} + +static int +eptinput(void *arg) +{ + Endpt *e; + + e = arg; + return e->eof || e->err || qcanread(e->rq); +} + +static int +isoreadyx(Endptx *x) +{ + return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0); +} + +static int +isoready(void *arg) +{ + int ret; + Ctlr *ctlr; + Endpt *e; + Endptx *x; + + e = arg; + ctlr = e->dev->uh->ctlr; + x = e->private; + ilock(&ctlr->activends); + ret = isoreadyx(x); + iunlock(&ctlr->activends); + return ret; +} + +static long +isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) +{ + TD *td; + Endptx *x; + int i, frnum; + uchar *p, *q, *bp; + volatile int isolock; + + x = e->private; + qlock(&e->rlock); + isolock = 0; + if(waserror()){ + if (isolock){ + isolock = 0; + iunlock(&ctlr->activends); + } + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + if (offset != 0 && offset != e->foffset){ + iprint("offset %lud, foffset %lud\n", offset, e->foffset); + /* Seek to a specific position */ + frnum = (IN(Frnum) + 8) & 0x3ff; + td = x->td0 +frnum; + if (offset < td->offset) + error("ancient history"); + while (offset > e->toffset){ + tsleep(&e->wr, return0, 0, 500); + } + while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){ + td = td->next; + if (td == x->xtd) + iprint("trouble\n"); + } + ilock(&ctlr->activends); + isolock = 1; + e->off = td->offset - offset; + if (e->off >= e->maxpkt){ + iprint("I can't program: %d\n", e->off); + e->off = 0; + } + x->etd = td; + e->foffset = offset; + } + do { + if (isolock == 0){ + ilock(&ctlr->activends); + isolock = 1; + } + td = x->etd; + if (td == nil || e->off == 0){ + if (td == nil){ + XPRINT("0"); + if (w){ + frnum = (IN(Frnum) + 1) & 0x3ff; + td = x->td0 + frnum; + while(td->status & Active) + td = td->next; + }else{ + frnum = (IN(Frnum) - 4) & 0x3ff; + td = x->td0 + frnum; + while(td->next != x->xtd) + td = td->next; + } + x->etd = td; + e->off = 0; + }else{ + /* New td, make sure it's ready */ + while (isoreadyx(x) == 0){ + isolock = 0; + iunlock(&ctlr->activends); + sleep(&e->wr, isoready, e); + ilock(&ctlr->activends); + isolock = 1; + } + if (x->etd == nil){ + XPRINT("!"); + continue; + } + } + if (w) + e->psize = ((td->dev >> 21) + 1) & 0x7ff; + else + e->psize = (x->etd->status + 1) & 0x7ff; + if(e->psize > e->maxpkt) + panic("packet size > maximum"); + } + if((i = n) >= e->psize) + i = e->psize; + if (w) + e->buffered += i; + else{ + e->buffered -= i; + if (e->buffered < 0) + e->buffered = 0; + } + isolock = 0; + iunlock(&ctlr->activends); + td->flags &= ~IsoClean; + bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms; + q = bp + e->off; + if (w){ + memmove(q, p, i); + }else{ + memmove(p, q, i); + } + p += i; + n -= i; + e->off += i; + e->psize -= i; + if (e->psize){ + if (n != 0) + panic("usb iso: can't happen"); + break; + } + if(w) + td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); + td->status = ErrLimit3 | Active | IsoSelect | IOC; + x->etd = td->next; + e->off = 0; + } while(n > 0); + n = p-(uchar*)a; + e->foffset += n; + poperror(); + if (isolock) + iunlock(&ctlr->activends); + qunlock(&e->rlock); + return n; +} + +static long +read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset) +{ + long l, i; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 0); + + XPRINT("qlock(%p)\n", &e->rlock); + qlock(&e->rlock); + XPRINT("got qlock(%p)\n", &e->rlock); + if(waserror()){ + qunlock(&e->rlock); + eptcancel(ctlr, e); + nexterror(); + } + p = a; + do { + if(e->eof) { + XPRINT("e->eof\n"); + break; + } + if(e->err) + error(e->err); + qrcv(ctlr, e); + if(!e->iso) + e->data01 ^= 1; + sleep(&e->rr, eptinput, e); + if(e->err) + error(e->err); + b = qget(e->rq); /* TO DO */ + if(b == nil) { + XPRINT("b == nil\n"); + break; + } + if(waserror()){ + freeb(b); + nexterror(); + } + l = BLEN(b); + if((i = l) > n) + i = n; + if(i > 0){ + memmove(p, b->rp, i); + p += i; + } + poperror(); + freeb(b); + n -= i; + if (l != e->maxpkt) + break; + } while (n > 0); + poperror(); + qunlock(&e->rlock); + return p-(uchar*)a; +} + +static int +qisempty(void *arg) +{ + return ((QH*)arg)->entries & Terminate; +} + +static long +write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok) +{ + int i, j; + QH *qh; + Block *b; + Ctlr *ctlr; + uchar *p; + + ctlr = uh->ctlr; + if(e->iso) + return isoio(ctlr, e, a, n, (ulong)offset, 1); + + p = a; + qlock(&e->wlock); + if(waserror()){ + qunlock(&e->wlock); + eptcancel(ctlr, e); + nexterror(); + } + do { + if(e->err) + error(e->err); + if((i = n) >= e->maxpkt) + i = e->maxpkt; + b = allocb(i); + if(waserror()){ + freeb(b); + nexterror(); + } + XPRINT("out [%d]", i); + for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); + XPRINT("\n"); + memmove(b->wp, p, i); + b->wp += i; + p += i; + n -= i; + poperror(); + qh = qxmit(ctlr, e, b, tok); + tok = TokOUT; + e->data01 ^= 1; + if(e->ntd >= e->nbuf) { +XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", + "writeusb sleep", qh, qh->first, qh->last, qh->entries); + XPRINT("write: sleep %lux\n", &e->wr); + sleep(&e->wr, qisempty, qh); + XPRINT("write: awake\n"); + } + } while(n > 0); + poperror(); + qunlock(&e->wlock); + return p-(uchar*)a; +} + +static void +init(Usbhost* uh) +{ + Ctlr *ctlr; + + ctlr = uh->ctlr; + ilock(ctlr); + outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames)); + OUT(Frnum, 0); + OUT(Usbintr, 0xF); /* enable all interrupts */ + XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod)); + XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); + if((IN(Cmd)&1)==0) + OUT(Cmd, 1); /* run */ +// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); + iunlock(ctlr); +} + +static void +scanpci(void) +{ + int io; + Ctlr *ctlr; + Pcidev *p; + static int already = 0; + + if(already) + return; + already = 1; + p = nil; + while(p = pcimatch(p, 0, 0)) { + /* + * Find UHCI controllers. Class = 12 (serial controller), + * Sub-class = 3 (USB) and Programming Interface = 0. + */ + if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00) + continue; + io = p->mem[4].bar & ~0x0F; + if(io == 0) { + print("usbuhci: failed to map registers\n"); + continue; + } + if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ + print("usbuhci: port %d in use\n", io); + continue; + } + if(p->intl == 0xFF || p->intl == 0) { + print("usbuhci: no irq assigned for port %d\n", io); + continue; + } + + XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", + p->vid, p->did, io, p->mem[4].size, p->intl); + + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->io = io; + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static int +reset(Usbhost *uh) +{ + int i; + TD *t; + ulong io; + Ctlr *ctlr; + Pcidev *p; + + scanpci(); + + /* + * Any adapter matches if no uh->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(uh->port == 0 || uh->port == ctlr->io){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + io = ctlr->io; + p = ctlr->pcidev; + + uh->ctlr = ctlr; + uh->port = io; + uh->irq = p->intl; + uh->tbdf = p->tbdf; + + XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", + IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum)); + XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", + IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1)); + + OUT(Cmd, 0); /* stop */ + while((IN(Status) & (1<<5)) == 0) /* wait for halt */ + ; + OUT(Status, 0xFF); /* clear pending interrupts */ + pcicfgw16(p, 0xc0, 0x2000); /* legacy support register: turn off lunacy mode */ + + if(0){ + i = inb(io+SOFMod); + OUT(Cmd, 4); /* global reset */ + delay(15); + OUT(Cmd, 0); /* end reset */ + delay(4); + outb(io+SOFMod, i); + } + + ctlr->tdpool = mmucacheinhib(xspanalloc(128*sizeof(TD), /*16*/CACHELINESZ, 0), 128*sizeof(TD)); + for(i=128; --i>=0;){ + ctlr->tdpool[i].next = ctlr->freetd; + ctlr->freetd = &ctlr->tdpool[i]; + } + ctlr->qhpool = mmucacheinhib(xspanalloc(64*sizeof(QH), /*16*/CACHELINESZ, 0), 64*sizeof(QH)); + for(i=64; --i>=0;){ + ctlr->qhpool[i].next = ctlr->freeqh; + ctlr->freeqh = &ctlr->qhpool[i]; + } + + /* + * the last entries of the periodic (interrupt & isochronous) scheduling TD entries + * points to the control queue and the bandwidth sop for bulk traffic. + * this is looped following the instructions in PIIX4 errata 29773804.pdf: + * a QH links to a looped but inactive TD as its sole entry, + * with its head entry leading on to the bulk traffic, the last QH of which + * links back to the empty QH. + */ + ctlr->ctlq = allocqh(ctlr); + ctlr->bwsop = allocqh(ctlr); + ctlr->bulkq = allocqh(ctlr); + ctlr->recvq = allocqh(ctlr); + t = alloctd(ctlr); /* inactive TD, looped */ + t->link = PCIWADDR(t); + ctlr->bwsop->entries = PCIWADDR(t); + + ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH; + ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH; + ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH; + if (1) /* don't use loop back */ + ctlr->bwsop->head = Terminate; + else /* set up loop back */ + ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH; + + ctlr->frames = mmucacheinhib(xspanalloc(FRAMESIZE, FRAMESIZE, 0), FRAMESIZE); + ctlr->frameld = xallocz(FRAMESIZE, 1); + for (i = 0; i < NFRAME; i++) + ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH; + + /* + * Linkage to the generic USB driver. + */ + uh->init = init; + uh->interrupt = interrupt; + + uh->portinfo = portinfo; + uh->portreset = portreset; + uh->portenable = portenable; + + uh->epalloc = epalloc; + uh->epfree = epfree; + uh->epopen = epopen; + uh->epclose = epclose; + uh->epmode = epmode; + + uh->read = read; + uh->write = write; + + return 0; +} + +void +usbuhcilink(void) +{ + addusbtype("uhci", reset); +} |
