diff options
| author | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
| commit | 8a8c2d742b51525f66c2210e3c8a251de10022ff (patch) | |
| tree | 8282ce595e5fbe2e487dc20f54891d9e9e7cbf37 /os/boot/pc/ether82563.c | |
| parent | 31a18a6996a6b5927e39cc553696c167e6c88e3d (diff) | |
20080611-1520
Diffstat (limited to 'os/boot/pc/ether82563.c')
| -rw-r--r-- | os/boot/pc/ether82563.c | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/os/boot/pc/ether82563.c b/os/boot/pc/ether82563.c new file mode 100644 index 00000000..dfe2b4d3 --- /dev/null +++ b/os/boot/pc/ether82563.c @@ -0,0 +1,977 @@ +/* + * bootstrap driver for + * Intel 82563, 82571, 82573 Gigabit Ethernet PCI-Express Controllers + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +/* compatibility with cpu kernels */ +#define iallocb allocb +#ifndef CACHELINESZ +#define CACHELINESZ 32 /* pentium & later */ +#endif + +/* from pci.c */ +enum +{ /* command register pcidev->pcr */ + IOen = 1<<0, + MEMen = 1<<1, + MASen = 1<<2, + MemWrInv = 1<<4, + PErrEn = 1<<6, + SErrEn = 1<<8, +}; + +/* + * these are in the order they appear in the manual, not numeric order. + * It was too hard to find them in the book. Ref 21489, rev 2.6 + */ + +enum { + /* General */ + + Ctrl = 0x00000000, /* Device Control */ + Status = 0x00000008, /* Device Status */ + Eec = 0x00000010, /* EEPROM/Flash Control/Data */ + Eerd = 0x00000014, /* EEPROM Read */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Fla = 0x0000001c, /* Flash Access */ + Mdic = 0x00000020, /* MDI Control */ + Seresctl = 0x00000024, /* Serdes ana */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Kumctrlsta = 0x00000034, /* Kumeran Controll and Status Register */ + Vet = 0x00000038, /* VLAN EtherType */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Rxcw = 0x00000180, /* Receive Configuration Word */ + Ledctl = 0x00000E00, /* LED control */ + Pba = 0x00001000, /* Packet Buffer Allocation */ + + /* Interrupt */ + + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Iam = 0x000000E0, /* Interrupt acknowledge Auto Mask */ + + /* Receive */ + + Rctl = 0x00000100, /* Receive Control */ + Ert = 0x00002008, /* Early Receive Threshold (573[EVL] only) */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Psrctl = 0x00002170, /* Packet Split Receive Control */ + Rdbal = 0x00002800, /* Rdesc Base Address Low Queue 0 */ + Rdbah = 0x00002804, /* Rdesc Base Address High Queue 0 */ + Rdlen = 0x00002808, /* Receive Descriptor Length Queue 0 */ + Rdh = 0x00002810, /* Receive Descriptor Head Queue 0 */ + Rdt = 0x00002818, /* Receive Descriptor Tail Queue 0 */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ + Rdbal1 = 0x00002900, /* Rdesc Base Address Low Queue 1 */ + Rdbah1 = 0x00002804, /* Rdesc Base Address High Queue 1 */ + Rdlen1 = 0x00002908, /* Receive Descriptor Length Queue 1 */ + Rdh1 = 0x00002910, /* Receive Descriptor Head Queue 1 */ + Rdt1 = 0x00002918, /* Receive Descriptor Tail Queue 1 */ + Rxdctl1 = 0x00002928, /* Receive Descriptor Control Queue 1 */ + Rsrpd = 0x00002c00, /* Receive Small Packet Detect */ + Raid = 0x00002c08, /* Receive ACK interrupt delay */ + Cpuvec = 0x00002c10, /* CPU Vector */ + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Rfctl = 0x00005008, /* Receive Filter Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ + Vfta = 0x00005600, /* VLAN Filter Table Array */ + Mrqc = 0x00005818, /* Multiple Receive Queues Command */ + Rssim = 0x00005864, /* RSS Interrupt Mask */ + Rssir = 0x00005868, /* RSS Interrupt Request */ + Reta = 0x00005c00, /* Redirection Table */ + Rssrk = 0x00005c80, /* RSS Random Key */ + + /* Transmit */ + + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tdbal = 0x00003800, /* Tdesc Base Address Low */ + Tdbah = 0x00003804, /* Tdesc Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ + Tarc0 = 0x00003840, /* Transmit Arbitration Counter Queue 0 */ + Tdbal1 = 0x00003900, /* Transmit Descriptor Base Low Queue 1 */ + Tdbah1 = 0x00003904, /* Transmit Descriptor Base High Queue 1 */ + Tdlen1 = 0x00003908, /* Transmit Descriptor Length Queue 1 */ + Tdh1 = 0x00003910, /* Transmit Descriptor Head Queue 1 */ + Tdt1 = 0x00003918, /* Transmit Descriptor Tail Queue 1 */ + Txdctl1 = 0x00003928, /* Transmit Descriptor Control 1 */ + Tarc1 = 0x00003940, /* Transmit Arbitration Counter Queue 1 */ + + /* Statistics */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + +}; + +enum { /* Ctrl */ + GIOmd = 1<<2, /* BIO master disable */ + Lrst = 1<<3, /* link reset */ + Slu = 1<<6, /* Set Link Up */ + SspeedMASK = 3<<8, /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = 1<<11, /* Force Speed */ + Frcdplx = 1<<12, /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = 1<<26, /* Device Reset */ + Rfce = 1<<27, /* Receive Flow Control Enable */ + Tfce = 1<<28, /* Transmit Flow Control Enable */ + Vme = 1<<30, /* VLAN Mode Enable */ + Phy_rst = 1<<31, /* Phy Reset */ +}; + +enum { /* Status */ + Lu = 1<<1, /* Link Up */ + Lanid = 3<<2, /* mask for Lan ID. */ + Txoff = 1<<4, /* Transmission Paused */ + Tbimode = 1<<5, /* TBI Mode Indication */ + SpeedMASK = 0x000000C0, + Speed10 = 0x00000000, /* 10Mb/s */ + Speed100 = 0x00000040, /* 100Mb/s */ + Speed1000 = 0x00000080, /* 1000Mb/s */ + Phyra = 1<<10, /* PHY Reset Asserted */ + GIOme = 1<<19, /* GIO Master Enable Status */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eec */ + Sk = 1<<0, /* Clock input to the EEPROM */ + Cs = 1<<1, /* Chip Select */ + Di = 1<<2, /* Data Input to the EEPROM */ + Do = 1<<3, /* Data Output from the EEPROM */ + Areq = 1<<6, /* EEPROM Access Request */ + Agnt = 1<<7, /* EEPROM Access Grant */ +}; + +enum { /* Eerd */ + ee_start = 1<<0, /* Start Read */ + ee_done = 1<<1, /* Read done */ + ee_addr = 0xfff8<<2, /* Read address [15:2] */ + ee_data = 0xffff<<16, /* Read Data; Data returned from eeprom/nvm */ +}; + +enum { /* Ctrlext */ + Asdchk = 1<<12, /* ASD Check */ + Eerst = 1<<13, /* EEPROM Reset */ + Spdbyps = 1<<15, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, + Ack = 0x00020000, /* receive ACK frame */ +}; + +enum { /* Txcw */ + TxcwFd = 0x00000020, /* Full Duplex */ + TxcwHd = 0x00000040, /* Half Duplex */ + TxcwPauseMASK = 0x00000180, /* Pause */ + TxcwPauseSHIFT = 7, + TxcwPs = 1<<TxcwPauseSHIFT, /* Pause Supported */ + TxcwAs = 2<<TxcwPauseSHIFT, /* Asymmetric FC desired */ + TxcwRfiMASK = 0x00003000, /* Remote Fault Indication */ + TxcwRfiSHIFT = 12, + TxcwNpr = 0x00008000, /* Next Page Request */ + TxcwConfig = 0x40000000, /* Transmit COnfig Control */ + TxcwAne = 0x80000000, /* Auto-Negotiation Enable */ +}; + +enum { /* Rctl */ + Rrst = 0x00000001, /* Receiver Software Reset */ + Ren = 0x00000002, /* Receiver Enable */ + Sbp = 0x00000004, /* Store Bad Packets */ + Upe = 0x00000008, /* Unicast Promiscuous Enable */ + Mpe = 0x00000010, /* Multicast Promiscuous Enable */ + Lpe = 0x00000020, /* Long Packet Reception Enable */ + LbmMASK = 0x000000C0, /* Loopback Mode */ + LbmOFF = 0x00000000, /* No Loopback */ + LbmTBI = 0x00000040, /* TBI Loopback */ + LbmMII = 0x00000080, /* GMII/MII Loopback */ + LbmXCVR = 0x000000C0, /* Transceiver Loopback */ + RdtmsMASK = 0x00000300, /* Rdesc Minimum Threshold Size */ + RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */ + RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */ + RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */ + MoMASK = 0x00003000, /* Multicast Offset */ + Bam = 0x00008000, /* Broadcast Accept Mode */ + BsizeMASK = 0x00030000, /* Receive Buffer Size */ + Bsize2048 = 0x00000000, + Bsize1024 = 0x00010000, + Bsize512 = 0x00020000, + Bsize256 = 0x00030000, + Vfe = 0x00040000, /* VLAN Filter Enable */ + Cfien = 0x00080000, /* Canonical Form Indicator Enable */ + Cfi = 0x00100000, /* Canonical Form Indicator value */ + Dpf = 0x00400000, /* Discard Pause Frames */ + Pmcf = 0x00800000, /* Pass MAC Control Frames */ + Bsex = 0x02000000, /* Buffer Size Extension */ + Secrc = 0x04000000, /* Strip CRC from incoming packet */ +}; + +enum { /* Tctl */ + Trst = 0x00000001, /* Transmitter Software Reset */ + Ten = 0x00000002, /* Transmit Enable */ + Psp = 0x00000008, /* Pad Short Packets */ + Mulr = 0x10000000, /* Allow multiple concurrent requests */ + CtMASK = 0x00000FF0, /* Collision Threshold */ + CtSHIFT = 4, + ColdMASK = 0x003FF000, /* Collision Distance */ + ColdSHIFT = 12, + Swxoff = 0x00400000, /* Sofware XOFF Transmission */ + Pbe = 0x00800000, /* Packet Burst Enable */ + Rtlc = 0x01000000, /* Re-transmit on Late Collision */ + Nrtu = 0x02000000, /* No Re-transmit on Underrrun */ +}; + +enum { /* [RT]xdctl */ + PthreshMASK = 0x0000003F, /* Prefetch Threshold */ + PthreshSHIFT = 0, + HthreshMASK = 0x00003F00, /* Host Threshold */ + HthreshSHIFT = 8, + WthreshMASK = 0x003F0000, /* Writebacj Threshold */ + WthreshSHIFT = 16, + Gran = 0x01000000, /* Granularity */ +}; + +enum { /* Rxcsum */ + PcssMASK = 0x000000FF, /* Packet Checksum Start */ + PcssSHIFT = 0, + Ipofl = 0x00000100, /* IP Checksum Off-load Enable */ + Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */ +}; + +typedef struct Rdesc { /* Receive Descriptor */ + uint addr[2]; + ushort length; + ushort checksum; + uchar status; + uchar errors; + ushort special; +} Rdesc; + +enum { /* Rdesc status */ + Rdd = 0x01, /* Descriptor Done */ + Reop = 0x02, /* End of Packet */ + Ixsm = 0x04, /* Ignore Checksum Indication */ + Vp = 0x08, /* Packet is 802.1Q (matched VET) */ + Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */ + Ipcs = 0x40, /* IP Checksum Calculated on Packet */ + Pif = 0x80, /* Passed in-exact filter */ +}; + +enum { /* Rdesc errors */ + Ce = 0x01, /* CRC Error or Alignment Error */ + Se = 0x02, /* Symbol Error */ + Seq = 0x04, /* Sequence Error */ + Cxe = 0x10, /* Carrier Extension Error */ + Tcpe = 0x20, /* TCP/UDP Checksum Error */ + Ipe = 0x40, /* IP Checksum Error */ + Rxe = 0x80, /* RX Data Error */ +}; + +typedef struct Tdesc { /* Legacy+Normal Transmit Descriptor */ + uint addr[2]; + uint control; /* varies with descriptor type */ + uint status; /* varies with descriptor type */ +} Tdesc; + +enum { /* Tdesc control */ + LenMASK = 0x000FFFFF, /* Data/Packet Length Field */ + LenSHIFT = 0, + DtypeCD = 0x00000000, /* Data Type 'Context Descriptor' */ + DtypeDD = 0x00100000, /* Data Type 'Data Descriptor' */ + PtypeTCP = 0x01000000, /* TCP/UDP Packet Type (CD) */ + Teop = 0x01000000, /* End of Packet (DD) */ + PtypeIP = 0x02000000, /* IP Packet Type (CD) */ + Ifcs = 0x02000000, /* Insert FCS (DD) */ + Tse = 0x04000000, /* TCP Segmentation Enable */ + Rs = 0x08000000, /* Report Status */ + Rps = 0x10000000, /* Report Status Sent */ + Dext = 0x20000000, /* Descriptor Extension */ + Vle = 0x40000000, /* VLAN Packet Enable */ + Ide = 0x80000000, /* Interrupt Delay Enable */ +}; + +enum { /* Tdesc status */ + Tdd = 0x00000001, /* Descriptor Done */ + Ec = 0x00000002, /* Excess Collisions */ + Lc = 0x00000004, /* Late Collision */ + Tu = 0x00000008, /* Transmit Underrun */ + CssMASK = 0x0000FF00, /* Checksum Start Field */ + CssSHIFT = 8, +}; + +enum { + Nrdesc = 128, /* multiple of 8 */ + Ntdesc = 128, /* multiple of 8 */ +}; + +enum { + i82563, + i82571, + i82573, +}; + +static char *tname[] = { + "i82563", + "i82571", + "i82573", +}; + +#define Type tname[ctlr->type] + +typedef struct Ctlr Ctlr; +struct Ctlr { + int port; + Pcidev *pcidev; + Ctlr *next; + int active; + int cls; + ushort eeprom[0x40]; + uchar ra[Eaddrlen]; /* receive address */ + int type; + + int* nic; + Lock imlock; + int im; /* interrupt mask */ + + Lock slock; + uint statistics[Nstatistics]; + + Rdesc *rdba; /* receive descriptor base address */ + Block **rb; /* receive buffers */ + int rdh; /* receive descriptor head */ + int rdt; /* receive descriptor tail */ + + Tdesc *tdba; /* transmit descriptor base address */ + Lock tdlock; + Block **tb; /* transmit buffers */ + int tdh; /* transmit descriptor head */ + int tdt; /* transmit descriptor tail */ + + int txcw; + int fcrtl; + int fcrth; + + /* bootstrap goo */ + Block *bqhead; /* transmission queue */ + Block *bqtail; +}; + +static Ctlr *ctlrhead; +static Ctlr *ctlrtail; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void +i82563im(Ctlr* ctlr, int im) +{ + ilock(&ctlr->imlock); + ctlr->im |= im; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); +} + +static void +i82563attach(Ether* edev) +{ + int ctl; + Ctlr *ctlr; + + ctlr = edev->ctlr; + i82563im(ctlr, 0); + ctl = csr32r(ctlr, Rctl)|Ren; + csr32w(ctlr, Rctl, ctl); + ctl = csr32r(ctlr, Tctl)|Ten; + csr32w(ctlr, Tctl, ctl); +} + + +static void +txstart(Ether *edev) +{ + int tdh, tdt; + Ctlr *ctlr = edev->ctlr; + Block *bp; + Tdesc *tdesc; + + /* + * Try to fill the ring back up, moving buffers from the transmit q. + */ + tdh = PREV(ctlr->tdh, Ntdesc); + for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){ + /* pull off the head of the transmission queue */ + if((bp = ctlr->bqhead) == nil) /* was qget(edev->oq) */ + break; + ctlr->bqhead = bp->next; + if (ctlr->bqtail == bp) + ctlr->bqtail = nil; + + /* set up a descriptor for it */ + tdesc = &ctlr->tdba[tdt]; + tdesc->addr[0] = PCIWADDR(bp->rp); + tdesc->addr[1] = 0; + tdesc->control = /* Ide | */ Rs | Ifcs | Teop | BLEN(bp); + + ctlr->tb[tdt] = bp; + } + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + i82563im(ctlr, Txdw); +} + +static Block * +fromringbuf(Ether *ether) +{ + RingBuf *tb = ðer->tb[ether->ti]; + Block *bp = allocb(tb->len); + + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + return bp; +} + +static void +i82563transmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Tdesc *tdesc; + RingBuf *tb; + int tdh; + + ctlr = edev->ctlr; + ilock(&ctlr->tdlock); + + /* + * Free any completed packets + * - try to get the soft tdh to catch the tdt; + * - if the packet had an underrun bump the threshold + * - the Tu bit doesn't seem to ever be set, perhaps + * because Rs mode is used? + */ + tdh = ctlr->tdh; + for(;;){ + tdesc = &ctlr->tdba[tdh]; + if(!(tdesc->status & Tdd)) + break; + if(ctlr->tb[tdh] != nil){ + freeb(ctlr->tb[tdh]); + ctlr->tb[tdh] = nil; + } + tdesc->status = 0; + tdh = NEXT(tdh, Ntdesc); + } + ctlr->tdh = tdh; + + /* copy packets from the software RingBuf to the transmission q */ + while((tb = &edev->tb[edev->ti])->owner == Interface){ + bp = fromringbuf(edev); +// print("#l%d: tx %d %E %E\n", edev->ctlrno, edev->ti, bp->rp, +// bp->rp+6); + + if(ctlr->bqhead) + ctlr->bqtail->next = bp; + else + ctlr->bqhead = bp; + ctlr->bqtail = bp; + + txstart(edev); /* kick transmitter */ + tb->owner = Host; /* give descriptor back */ + edev->ti = NEXT(edev->ti, edev->ntb); + } + iunlock(&ctlr->tdlock); +} + +static void +i82563replenish(Ctlr* ctlr) +{ + int rdt; + Block *bp; + Rdesc *rdesc; + + rdt = ctlr->rdt; + while(NEXT(rdt, Nrdesc) != ctlr->rdh){ + rdesc = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] != nil){ + /* nothing to do */ + } + else if((bp = iallocb(2048)) != nil){ + ctlr->rb[rdt] = bp; + rdesc->addr[0] = PCIWADDR(bp->rp); + rdesc->addr[1] = 0; + } + else + break; + rdesc->status = 0; + + rdt = NEXT(rdt, Nrdesc); + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); +} + +static void +toringbuf(Ether *ether, Block *bp) +{ + RingBuf *rb = ðer->rb[ether->ri]; + + if (rb->owner == Interface) { + rb->len = BLEN(bp); + memmove(rb->pkt, bp->rp, rb->len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } else if (debug) + print("#l%d: toringbuf: dropping packets @ ri %d\n", + ether->ctlrno, ether->ri); +} + +static void +i82563interrupt(Ureg*, void* arg) +{ + int icr, im, rdh, txdw = 0; + Block *bp; + Ctlr *ctlr; + Ether *edev; + Rdesc *rdesc; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + im = ctlr->im; + + for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){ + if(icr & (Rxseq|Lsc)){ + /* should be more here */ + } + + rdh = ctlr->rdh; + for (;;) { + rdesc = &ctlr->rdba[rdh]; + if(!(rdesc->status & Rdd)) + break; + if ((rdesc->status & Reop) && rdesc->errors == 0) { + bp = ctlr->rb[rdh]; + if(0 && memcmp(bp->rp, broadcast, 6) != 0) + print("#l%d: rx %d %E %E %d\n", + edev->ctlrno, rdh, bp->rp, + bp->rp+6, rdesc->length); + ctlr->rb[rdh] = nil; + bp->wp += rdesc->length; + toringbuf(edev, bp); + freeb(bp); + } else if (rdesc->status & Reop && rdesc->errors) + print("%s: input packet error 0x%ux\n", + Type, rdesc->errors); + rdesc->status = 0; + rdh = NEXT(rdh, Nrdesc); + } + ctlr->rdh = rdh; + if(icr & Rxdmt0) + i82563replenish(ctlr); + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + } + } + ctlr->im = im; + csr32w(ctlr, Ims, im); + iunlock(&ctlr->imlock); + if(txdw) + i82563transmit(edev); +} + +static void +i82563init(Ether* edev) +{ + int csr, i, r; + Ctlr *ctlr; + + ctlr = edev->ctlr; + csr = edev->ea[3]<<24 | edev->ea[2]<<16 | edev->ea[1]<<8 | edev->ea[0]; + csr32w(ctlr, Ral, csr); + csr = 0x80000000 | edev->ea[5]<<8 | edev->ea[4]; + csr32w(ctlr, Rah, csr); + for (i = 1; i < 16; i++) { + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + csr32w(ctlr, Rctl, 0); + ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 256, 0); + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); + ctlr->rdh = 0; + csr32w(ctlr, Rdh, ctlr->rdh); + ctlr->rdt = 0; + csr32w(ctlr, Rdt, ctlr->rdt); + ctlr->rb = malloc(sizeof(Block*)*Nrdesc); + i82563replenish(ctlr); + csr32w(ctlr, Rdtr, 0); + csr32w(ctlr, Rctl, Dpf | Bsize2048 | Bam | RdtmsHALF); + i82563im(ctlr, Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack); + + csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp | 0x3f<<ColdSHIFT | Mulr); + csr32w(ctlr, Tipg, 6<<20 | 8<<10 | 8); + csr32w(ctlr, Tidv, 1); + + ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 256, 0); + memset(ctlr->tdba, 0, Ntdesc*sizeof(Tdesc)); + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); + ctlr->tdh = 0; + csr32w(ctlr, Tdh, ctlr->tdh); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, ctlr->tdt); + ctlr->tb = malloc(sizeof(Block*)*Ntdesc); + +// r = 4<<WthreshSHIFT | 4<<HthreshSHIFT | 8<<PthreshSHIFT; +// csr32w(ctlr, Txdctl, r); + csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE<<PcssSHIFT); + r = csr32r(ctlr, Tctl); + r |= Ten; + csr32w(ctlr, Tctl, r); +} + + +static ushort +eeread(Ctlr* ctlr, int adr) +{ + csr32w(ctlr, Eerd, ee_start | adr << 2); + while ((csr32r(ctlr, Eerd) & ee_done) == 0) + ; + return csr32r(ctlr, Eerd) >> 16; +} + +static int +eeload(Ctlr* ctlr) +{ + ushort sum; + int data, adr; + + sum = 0; + for (adr = 0; adr < 0x40; adr++) { + data = eeread(ctlr, adr); + ctlr->eeprom[adr] = data; + sum += data; + } + return sum; +} + + +static void +detach(Ctlr *ctlr) +{ + int r; + + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); + + r = csr32r(ctlr, Ctrl); + csr32w(ctlr, Ctrl, Devrst | r); + /* apparently needed on multi-GHz processors to avoid infinite loops */ + delay(1); + while(csr32r(ctlr, Ctrl) & Devrst) + ; + + if(1 || ctlr->type != i82563){ + r = csr32r(ctlr, Ctrl); + csr32w(ctlr, Ctrl, Slu | r); + } + + csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext)); + delay(1); + while(csr32r(ctlr, Ctrlext) & Eerst) + ; + + csr32w(ctlr, Imc, ~0); + delay(1); + while(csr32r(ctlr, Icr)) + ; +} + +static void +i82563detach(Ether *edev) +{ + detach(edev->ctlr); +} + +static void +i82563shutdown(Ether* ether) +{ + i82563detach(ether); +} + +static int +i82563reset(Ctlr* ctlr) +{ + int i, r; + + detach(ctlr); + + r = eeload(ctlr); + if (r != 0 && r != 0xBABA){ + print("%s: bad EEPROM checksum - 0x%4.4ux\n", Type, r); + return -1; + } + + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + r = (csr32r(ctlr, Status) & Lanid) >> 2; + ctlr->ra[5] += r; /* ea ctlr[1] = ea ctlr[0]+1 */ + + r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0]; + csr32w(ctlr, Ral, r); + r = 0x80000000 | ctlr->ra[5]<<8 | ctlr->ra[4]; + csr32w(ctlr, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + ctlr->im = 0; /* was = Lsc, which hangs some controllers */ + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); + + return 0; +} + +static void +i82563pci(void) +{ + int port, type, cls; + Pcidev *p; + Ctlr *ctlr; + static int first = 1; + + if (first) + first = 0; + else + return; + + p = nil; + while(p = pcimatch(p, 0x8086, 0)){ + switch(p->did){ + case 0x1096: + case 0x10ba: + type = i82563; + break; + case 0x108b: /* e */ + case 0x108c: /* e (iamt) */ + case 0x109a: /* l */ + type = i82573; + break; + default: + continue; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("%s: can't map %d @ 0x%8.8lux\n", tname[type], + p->mem[0].size, p->mem[0].bar); + continue; + } + + if(p->pcr & MemWrInv){ + cls = pcicfgr8(p, PciCLS) * 4; + if(cls != CACHELINESZ) + pcicfgw8(p, PciCLS, CACHELINESZ/4); + } + + cls = pcicfgr8(p, PciCLS); + switch(cls){ + default: + print("%s: unexpected CLS - %d bytes\n", + tname[type], cls*sizeof(long)); + break; + case 0x00: + case 0xFF: + /* alphapc 164lx returns 0 */ + print("%s: unusable PciCLS: %d, using %d longs\n", + tname[type], cls, CACHELINESZ/sizeof(long)); + cls = CACHELINESZ/sizeof(long); + pcicfgw8(p, PciCLS, cls); + break; + case 0x08: + case 0x10: + break; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->cls = cls*4; + ctlr->type = type; + ctlr->nic = KADDR(ctlr->port); + if(i82563reset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +static uchar nilea[Eaddrlen]; + +int +i82563pnp(Ether* edev) +{ + Ctlr *ctlr; + + if(ctlrhead == nil) + i82563pci(); + + /* + * 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; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; +// edev->mbps = 1000; + + if(memcmp(edev->ea, nilea, Eaddrlen) == 0) + memmove(edev->ea, ctlr->ra, Eaddrlen); + i82563init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = i82563attach; + edev->transmit = i82563transmit; + edev->interrupt = i82563interrupt; + edev->detach = i82563detach; + + /* + * with the current structure, there is no right place for this. + * ideally, we recognize the interface, note it's down and move on. + * currently either we can skip the interface or note it is down, + * but not both. + */ + if((csr32r(ctlr, Status)&Lu) == 0){ + print("ether#%d: 82563 (%s): link down\n", edev->ctlrno, Type); + return -1; + } + + return 0; +} |
