From 6c30c2deef36a3d8a8651b06a9389c02a560ae36 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 18 Apr 1996 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1996-04-18 --- pc/devvga.c | 48 ++-- pc/ether82557.c | 708 ++++++++++++++++++++++++++++++++++++++++++++++++ pc/vga.h | 1 + 3 files changed, 734 insertions(+), 23 deletions(-) create mode 100644 pc/ether82557.c diff --git a/pc/devvga.c b/pc/devvga.c index 5aa22e3df4dcec560e70feb16f0b5ab993bede50..8574a825d0ea7f2d4fcb486f7ad451005d0db39c 100644 --- a/pc/devvga.c +++ b/pc/devvga.c @@ -158,9 +158,8 @@ vgacreate(Chan*, char*, int, ulong) } void -vgaclose(Chan *c) +vgaclose(Chan*) { - USED(c); } static int @@ -241,6 +240,7 @@ vgactl(char *arg) char *cp, *field[4]; Hwgc *hwgcp; Vgac *vgacp; + ulong mem, size, align; n = parsefields(arg, field, 4, " "); if(n < 2) @@ -322,20 +322,20 @@ vgactl(char *arg) else if(strcmp(field[0], "linear") == 0){ if(n < 2) error(Ebadarg); - x = strtoul(field[1], 0, 0); + size = strtoul(field[1], 0, 0); if(n < 3) - y = 0; + align = 0; else - y = strtoul(field[2], 0, 0); + align = strtoul(field[2], 0, 0); /* see if it fits in the usual place */ - if(x <= Footprint){ + if(size <= Footprint){ screenmem = SCREENMEM; - if(x == 0){ + if(size == 0){ footprint = Footprint; footshift = Footshift; } else { - footprint = x; + footprint = size; for(n = 0; n < 31; n++) if((1<= footprint) break; @@ -346,24 +346,29 @@ vgactl(char *arg) } /* grab new space */ - if(y == 0){ - if(footprint >= x) + if(align == 0){ + if(footprint >= size) return; } else { int s, e; s = screenmem & ~KZERO; e = s + footprint; - s = ROUND(s, y); - if(e >= s + x) + s = ROUND(s, align); + if(e >= s + size) return; } - y = getspace(x, y); - if(y == 0) + + mem = 0; + if(vgac->linear) + (*vgac->linear)(&mem, &size, &align); + else + mem = getspace(size, align); + if(mem == 0) error("not enough free address space"); - screenmem = y; - gscreen.base = (void*)y; - footprint = x; + screenmem = mem; + gscreen.base = (void*)screenmem; + footprint = size; for(n = 0; n < 31; n++) if((1<= footprint) break; @@ -431,16 +436,14 @@ vgabwrite(Chan *c, Block *bp, ulong offset) } void -vgaremove(Chan *c) +vgaremove(Chan*) { - USED(c); error(Eperm); } void -vgawstat(Chan *c, char *dp) +vgawstat(Chan*, char*) { - USED(c, dp); error(Eperm); } @@ -1011,9 +1014,8 @@ screenputs(char *s, int n) } static void -nopage(int page) +nopage(int) { - USED(page); } static int pos; diff --git a/pc/ether82557.c b/pc/ether82557.c new file mode 100644 index 0000000000000000000000000000000000000000..f9d7b1c7ee21616ffb581438fb75a147e3b7581e --- /dev/null +++ b/pc/ether82557.c @@ -0,0 +1,708 @@ +/* + * Intel 82557 Fast Ethernet PCI Bus LAN Controller + * as found on the Intel EtherExpress PRO/100B. This chip is full + * of smarts, unfortunately none of them are in the right place. + * To do: + * the PCI scanning code could be made common to other adapters; + * PCI code needs rewritten to handle byte, word, dword accesses + * and using the devno as a bus+dev+function triplet; + * stats. + */ +#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 { + Nrfd = 64, /* receive frame area */ + + NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ +}; + +enum { /* CSR */ + Status = 0x00, /* byte or word (word includes Ack) */ + Ack = 0x01, /* byte */ + Command = 0x02, /* byte or word (word includes Interrupt) */ + Interrupt = 0x03, /* byte */ + Pointer = 0x04, /* dword */ + Port = 0x08, /* dword */ + Fcr = 0x0C, /* Flash control register */ + Ecr = 0x0E, /* EEPROM control register */ + Mcr = 0x10, /* MDI control register */ +}; + +enum { /* Status */ + RUidle = 0x0000, + RUsuspended = 0x0004, + RUnoresources = 0x0008, + RUready = 0x0010, + RUrbd = 0x0020, /* bit */ + RUstatus = 0x003F, /* mask */ + + CUidle = 0x0000, + CUsuspended = 0x0040, + CUactive = 0x0080, + CUstatus = 0x00C0, /* mask */ + + StatSWI = 0x0400, /* SoftWare generated Interrupt */ + StatMDI = 0x0800, /* MDI r/w done */ + StatRNR = 0x1000, /* Receive unit Not Ready */ + StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ + StatFR = 0x4000, /* Finished Receiving */ + StatCX = 0x8000, /* Command eXecuted */ + StatTNO = 0x8000, /* Transmit NOT OK */ +}; + +enum { /* Command (byte) */ + CUnop = 0x00, + CUstart = 0x10, + CUresume = 0x20, + LoadDCA = 0x40, /* Load Dump Counters Address */ + DumpSC = 0x50, /* Dump Statistical Counters */ + LoadCUB = 0x60, /* Load CU Base */ + ResetSA = 0x70, /* Dump and Reset Statistical Counters */ + + RUstart = 0x01, + RUresume = 0x02, + RUabort = 0x04, + LoadHDS = 0x05, /* Load Header Data Size */ + LoadRUB = 0x06, /* Load RU Base */ + RBDresume = 0x07, /* Resume frame reception */ +}; + +enum { /* Interrupt (byte) */ + InterruptM = 0x01, /* interrupt Mask */ + InterruptSI = 0x02, /* Software generated Interrupt */ +}; + +enum { /* Ecr */ + EEsk = 0x01, /* serial clock */ + EEcs = 0x02, /* chip select */ + EEdi = 0x04, /* serial data in */ + EEdo = 0x08, /* serial data out */ + + EEstart = 0x04, /* start bit */ + EEread = 0x02, /* read opcode */ + + EEaddrsz = 6, /* bits of address */ +}; + +enum { /* Mcr */ + MDIread = 0x08000000, /* read opcode */ + MDIwrite = 0x04000000, /* write opcode */ + MDIready = 0x10000000, /* ready bit */ + MDIie = 0x20000000, /* interrupt enable */ +}; + +typedef struct Rfd { + int field; + ulong link; + ulong rbd; + ushort count; + ushort size; + + Etherpkt; + + Block* bp; +} Rfd; + +enum { /* field */ + RfdCollision = 0x00000001, + RfdIA = 0x00000002, /* IA match */ + RfdRxerr = 0x00000010, /* PHY character error */ + RfdType = 0x00000020, /* Type frame */ + RfdRunt = 0x00000080, + RfdOverrun = 0x00000100, + RfdBuffer = 0x00000200, + RfdAlignment = 0x00000400, + RfdCRC = 0x00000800, + + RfdOK = 0x00002000, /* frame received OK */ + RfdC = 0x00008000, /* reception Complete */ + RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ + RfdH = 0x00100000, /* Header RFD */ + + RfdI = 0x20000000, /* Interrupt after completion */ + RfdS = 0x40000000, /* Suspend after completion */ + RfdEL = 0x80000000, /* End of List */ +}; + +enum { /* count */ + RfdF = 0x00004000, + RfdEOF = 0x00008000, +}; + +typedef struct Cb { + int command; + ulong link; + uchar data[24]; /* CbIAS + CbConfigure */ +} Cb; + +typedef struct TxCB { + int command; + ulong link; + ulong tbd; + ushort count; + uchar threshold; + uchar number; +} TxCB; + +enum { /* action command */ + CbOK = 0x00002000, /* DMA completed OK */ + CbC = 0x00008000, /* execution Complete */ + + CbNOP = 0x00000000, + CbIAS = 0x00010000, /* Indvidual Address Setup */ + CbConfigure = 0x00020000, + CbMAS = 0x00030000, /* Multicast Address Setup */ + CbTransmit = 0x00040000, + CbDump = 0x00060000, + CbDiagnose = 0x00070000, + CbCommand = 0x00070000, /* mask */ + + CbSF = 0x00080000, /* CbTransmit */ + + CbI = 0x20000000, /* Interrupt after completion */ + CbS = 0x40000000, /* Suspend after completion */ + CbEL = 0x80000000, /* End of List */ +}; + +enum { /* CbTransmit count */ + CbEOF = 0x00008000, +}; + +typedef struct Ctlr { + int port; + + int ctlrno; + char* type; + + uchar configdata[24]; + + Lock rlock; + + Block* rfd[Nrfd]; + int rfdl; + int rfdx; + + Lock cbqlock; + Block* cbqhead; + Block* cbqtail; + int cbqbusy; +} Ctlr; + +static uchar configdata[24] = { + 0x16, /* byte count */ + 0x44, /* Rx/Tx FIFO limit */ + 0x00, /* adaptive IFS */ + 0x00, + 0x04, /* Rx DMA maximum byte count */ + 0x84, /* Tx DMA maximum byte count */ + 0x33, /* late SCB, CNA interrupts */ + 0x01, /* discard short Rx frames */ + 0x00, /* 503/MII */ + + 0x00, + 0x2E, /* normal operation, NSAI */ + 0x00, /* linear priority */ + 0x60, /* inter-frame spacing */ + 0x00, + 0xF2, + 0x48, /* promiscuous mode off */ + 0x00, + 0x40, + 0xF2, /* transmit padding enable */ + 0x80, /* full duplex pin enable */ + 0x3F, /* no Multi IA */ + 0x05, /* no Multi Cast ALL */ +}; + +#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 +custart(Ctlr* ctlr) +{ + if(ctlr->cbqhead == 0){ + ctlr->cbqbusy = 0; + return; + } + ctlr->cbqbusy = 1; + + csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp)); + while(csr8r(ctlr, Command)) + ; + csr8w(ctlr, Command, CUstart); +} + +static void +action(Ctlr* ctlr, Block* bp) +{ + Cb *cb; + + ilock(&ctlr->cbqlock); + cb = (Cb*)bp->rp; + cb->command |= CbEL; + + if(ctlr->cbqhead){ + ctlr->cbqtail->next = bp; + cb = (Cb*)ctlr->cbqtail->rp; + cb->link = PADDR(bp->rp); + cb->command &= ~CbEL; + } + else + ctlr->cbqhead = bp; + ctlr->cbqtail = bp; + + if(ctlr->cbqbusy == 0) + custart(ctlr); + + iunlock(&ctlr->cbqlock); +} + +static void +attach(Ether* ether) +{ + int status; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->rlock); + status = csr16r(ctlr, Status); + if((status & RUstatus) == RUidle){ + csr32w(ctlr, Pointer, PADDR(ctlr->rfd[ctlr->rfdx]->rp)); + while(csr8r(ctlr, Command)) + ; + csr8w(ctlr, Command, RUstart); + } + iunlock(&ctlr->rlock); +} + +static void +configure(void* arg, int promiscuous) +{ + Ctlr *ctlr; + Block *bp; + Cb *cb; + + ctlr = ((Ether*)arg)->ctlr; + + bp = allocb(sizeof(Cb)); + cb = (Cb*)bp->rp; + bp->wp += sizeof(Cb); + + cb->command = CbConfigure; + cb->link = NullPointer; + memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); + if(promiscuous) + cb->data[15] |= 0x01; + action(ctlr, bp); +} + +static long +write(Ether* ether, void* buf, long n) +{ + Block *bp; + TxCB *txcb; + + bp = allocb(n+sizeof(TxCB)); + txcb = (TxCB*)bp->wp; + bp->wp += sizeof(TxCB); + + txcb->command = CbTransmit; + txcb->link = NullPointer; + txcb->tbd = NullPointer; + txcb->count = CbEOF|n; + txcb->threshold = 2; + txcb->number = 0; + + memmove(bp->wp, buf, n); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + + action(ether->ctlr, bp); + + ether->outpackets++; + + return n; +} + +static void +interrupt(Ureg*, void* arg) +{ + Rfd *rfd; + Block *bp; + Ctlr *ctlr; + Ether *ether; + int status; + + ether = arg; + ctlr = ether->ctlr; + + for(;;){ + status = csr16r(ctlr, Status); + csr8w(ctlr, Ack, (status>>8) & 0xFF); + + if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0) + return; + + if(status & StatFR){ + bp = ctlr->rfd[ctlr->rfdx]; + rfd = (Rfd*)bp->rp; + while(rfd->field & RfdC){ + etherrloop(ether, rfd, rfd->count & 0x3FFF); + ether->inpackets++; + + /* + * Reinitialise the frame for reception and bump + * the receive frame processing index; + * bump the sentinel index, mark the new sentinel + * and clear the old sentinel suspend bit; + * set bp and rfd for the next receive frame to + * process. + */ + rfd->field = 0; + rfd->count = 0; + ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd); + + rfd = (Rfd*)ctlr->rfd[ctlr->rfdl]->rp; + ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd); + ((Rfd*)ctlr->rfd[ctlr->rfdl]->rp)->field |= RfdS; + rfd->field &= ~RfdS; + + bp = ctlr->rfd[ctlr->rfdx]; + rfd = (Rfd*)bp->rp; + } + status &= ~StatFR; + } + + if(status & StatRNR){ + while(csr8r(ctlr, Command)) + ; + csr8w(ctlr, Command, RUresume); + + status &= ~StatRNR; + } + + if(status & StatCNA){ + lock(&ctlr->cbqlock); + while(bp = ctlr->cbqhead){ + if((((Cb*)bp->rp)->command & CbC) == 0) + break; + ctlr->cbqhead = bp->next; + freeb(bp); + } + custart(ctlr); + unlock(&ctlr->cbqlock); + + status &= ~StatCNA; + } + + if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) + panic("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status); + } +} + +static void +ctlrinit(Ctlr* ctlr) +{ + int i; + Block *bp; + Rfd *rfd; + ulong link; + + link = NullPointer; + for(i = Nrfd-1; i >= 0; i--){ + if(ctlr->rfd[i] == 0){ + bp = allocb(sizeof(Rfd)); + ctlr->rfd[i] = bp; + } + else + bp = ctlr->rfd[i]; + rfd = (Rfd*)bp->rp; + + rfd->field = 0; + rfd->link = link; + link = PADDR(rfd); + rfd->rbd = NullPointer; + rfd->count = 0; + rfd->size = sizeof(Etherpkt); + + rfd->bp = bp; + } + ((Rfd*)ctlr->rfd[Nrfd-1]->rp)->link = PADDR(ctlr->rfd[0]->rp); + + ctlr->rfdl = 0; + ((Rfd*)ctlr->rfd[0]->rp)->field |= RfdS; + ctlr->rfdx = 2; + + memmove(ctlr->configdata, configdata, sizeof(configdata)); +} + +static int +dp83840r(Ctlr* ctlr, int phyadd, int regadd) +{ + int mcr, timo; + + /* + * DP83840 + * 10/100Mb/s Ethernet Physical Layer. + */ + csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); + mcr = 0; + for(timo = 10; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + delay(1); + } + + if(mcr & MDIready) + return mcr & 0xFFFF; + + return -1; +} + +static int +hy93c46r(Ctlr* ctlr, int r) +{ + int i, op, data; + + /* + * Hyundai HY93C46 or equivalent serial EEPROM. + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 2.3.4.2 of the Intel 82557 User's Guide. + */ + csr16w(ctlr, Ecr, EEcs); + op = EEstart|EEread; + for(i = 2; i >= 0; i--){ + data = (((op>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + delay(1); + } + + for(i = EEaddrsz-1; i >= 0; i--){ + data = (((r>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + delay(1); + if((csr16r(ctlr, Ecr) & EEdo) == 0) + break; + } + + data = 0; + for(i = 15; i >= 0; i--){ + csr16w(ctlr, Ecr, EEcs|EEsk); + delay(1); + if(csr16r(ctlr, Ecr) & EEdo) + data |= (1<port == 0 || ether->port == port){ + ether->irq = irq; + *pcidevno = devno-1; + return port; + } + } + if(port == 0) + continue; + + ap = malloc(sizeof(Adapter)); + ap->port = port; + ap->irq = irq; + ap->pcidevno = devno-1; + ap->next = adapter; + adapter = ap; + } + + return 0; +} + +static int +reset(Ether* ether) +{ + int i, pcidevno, port, x; + Adapter *ap, **app; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + Block *bp; + Cb *cb; + + /* + * Any adapter matches if no ether->port is supplied, otherwise the + * ports must match. First see if an adapter that fits the bill has + * already been found. If not, scan for another. + */ + port = 0; + pcidevno = -1; + for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){ + if(ether->port == 0 || ether->port == ap->port){ + ether->irq = ap->irq; + pcidevno = ap->pcidevno; + *app = ap->next; + free(ap); + break; + } + } + if(port == 0 && (port = i82557(ether, &pcidevno)) == 0) + return -1; + + /* + * Allocate a controller structure and start to initialise it. + * Perform a software reset after which need to ensure busmastering + * is still enabled. The EtherExpress PRO/100B appears to leave + * the PCI configuration alone (see the 'To do' list above) so punt + * for now. + * Load the RUB and CUB registers for linear addressing (0). + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + ctlr->ctlrno = ether->ctlrno; + ctlr->type = ether->type; + ctlr->port = port; + + csr32w(ctlr, Port, 0); + delay(1); + + pcicfgr(0, pcidevno, 0, 0x04, &x, 4); + if((x & 0x05) != 0x05) + print("PCI command = %uX\n", x); + + csr32w(ctlr, Pointer, 0); + csr8w(ctlr, Command, LoadRUB); + while(csr8r(ctlr, Command)) + ; + csr8w(ctlr, Command, LoadCUB); + while(csr8r(ctlr, Command)) + ; + + /* + * Initialise the action and receive frame areas. + */ + ctlrinit(ctlr); + + /* + * Possibly need to configure the physical-layer chip here, but the + * EtherExpress PRO/100B appears to bring it up with a sensible default + * configuration. However, should check for the existence of the PHY + * and, if found, check whether to use 82503 (serial) or MII (nibble) + * mode. Verify the PHY is a National Semiconductor DP83840 by looking + * at the Organizationally Unique Identifier (OUI) in registers 2 and 3 + * which should be 0x80017. + */ + for(i = 1; i < 32; i++){ + if((x = dp83840r(ctlr, i, 2)) == 0xFFFF) + continue; + x <<= 6; + x |= dp83840r(ctlr, i, 3)>>10; + if(x != 0x80017) + continue; + + x = dp83840r(ctlr, i, 0x1B); + if((x & 0x0200) == 0){ + ctlr->configdata[8] = 1; + ctlr->configdata[15] |= 0x80; + } + break; + } + + /* + * Load the chip configuration + */ + configure(ether, 0); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading + * the station address with the Individual Address Setup command. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = hy93c46r(ctlr, i); + ether->ea[2*i] = x & 0xFF; + ether->ea[2*i+1] = (x>>8) & 0xFF; + } + } + + bp = allocb(sizeof(Cb)); + cb = (Cb*)bp->rp; + bp->wp += sizeof(Cb); + + cb->command = CbIAS; + cb->link = NullPointer; + memmove(cb->data, ether->ea, Eaddrlen); + action(ctlr, bp); + + /* + * Linkage to the generic ethernet driver. + */ + ether->port = port; + ether->attach = attach; + ether->write = write; + ether->interrupt = interrupt; + + ether->promiscuous = configure; + ether->arg = ether; + + return 0; +} + +void +ether82557link(void) +{ + addethercard("i82557", reset); +} diff --git a/pc/vga.h b/pc/vga.h index 3ea97089bed61b8d909435308e309798b84ebcab..53edbfba069aa3044e26e0568253a49d5d1666f1 100644 --- a/pc/vga.h +++ b/pc/vga.h @@ -42,6 +42,7 @@ typedef struct Vgac Vgac; struct Vgac { char *name; void (*page)(int); + int (*linear)(ulong*, ulong*, ulong*); Vgac* link; };