19b12e100SDavid du Colombier /*
29b12e100SDavid du Colombier * VIA VT6102 Fast Ethernet Controller (Rhine II).
39b12e100SDavid du Colombier * To do:
49b12e100SDavid du Colombier * cache-line size alignments - done
59b12e100SDavid du Colombier * reduce tx interrupts
69b12e100SDavid du Colombier * use 2 descriptors on tx for alignment - done
79b12e100SDavid du Colombier * reorganise initialisation/shutdown/reset
89b12e100SDavid du Colombier * adjust Tx FIFO threshold on underflow - untested
99b12e100SDavid du Colombier * why does the link status never cause an interrupt?
109b12e100SDavid du Colombier * use the lproc as a periodic timer for stalls, etc.
119b12e100SDavid du Colombier */
129b12e100SDavid du Colombier #include "u.h"
139b12e100SDavid du Colombier #include "../port/lib.h"
149b12e100SDavid du Colombier #include "mem.h"
159b12e100SDavid du Colombier #include "dat.h"
169b12e100SDavid du Colombier #include "fns.h"
179b12e100SDavid du Colombier #include "io.h"
189b12e100SDavid du Colombier #include "../port/error.h"
199b12e100SDavid du Colombier #include "../port/netif.h"
209b12e100SDavid du Colombier
219b12e100SDavid du Colombier #include "etherif.h"
229b12e100SDavid du Colombier #include "ethermii.h"
239b12e100SDavid du Colombier
249b12e100SDavid du Colombier enum {
259b12e100SDavid du Colombier Par0 = 0x00, /* Ethernet Address */
269b12e100SDavid du Colombier Rcr = 0x06, /* Receive Configuration */
279b12e100SDavid du Colombier Tcr = 0x07, /* Transmit Configuration */
289b12e100SDavid du Colombier Cr = 0x08, /* Control */
299b12e100SDavid du Colombier Isr = 0x0C, /* Interrupt Status */
309b12e100SDavid du Colombier Imr = 0x0E, /* Interrupt Mask */
317e254d1cSDavid du Colombier Mcfilt0 = 0x10, /* Multicast Filter 0 */
327e254d1cSDavid du Colombier Mcfilt1 = 0x14, /* Multicast Filter 1 */
339b12e100SDavid du Colombier Rxdaddr = 0x18, /* Current Rx Descriptor Address */
349b12e100SDavid du Colombier Txdaddr = 0x1C, /* Current Tx Descriptor Address */
359b12e100SDavid du Colombier Phyadr = 0x6C, /* Phy Address */
369b12e100SDavid du Colombier Miisr = 0x6D, /* MII Status */
379b12e100SDavid du Colombier Bcr0 = 0x6E, /* Bus Control */
389b12e100SDavid du Colombier Bcr1 = 0x6F,
399b12e100SDavid du Colombier Miicr = 0x70, /* MII Control */
409b12e100SDavid du Colombier Miiadr = 0x71, /* MII Address */
419b12e100SDavid du Colombier Miidata = 0x72, /* MII Data */
429b12e100SDavid du Colombier Eecsr = 0x74, /* EEPROM Control and Status */
4376c9a8f8SDavid du Colombier Stickhw = 0x83, /* Sticky Hardware Control */
4476c9a8f8SDavid du Colombier Wolcrclr = 0xA4,
4576c9a8f8SDavid du Colombier Wolcgclr = 0xA7,
4676c9a8f8SDavid du Colombier Pwrcsrclr = 0xAC,
479b12e100SDavid du Colombier };
489b12e100SDavid du Colombier
499b12e100SDavid du Colombier enum { /* Rcr */
509b12e100SDavid du Colombier Sep = 0x01, /* Accept Error Packets */
519b12e100SDavid du Colombier Ar = 0x02, /* Accept Small Packets */
529b12e100SDavid du Colombier Am = 0x04, /* Accept Multicast */
539b12e100SDavid du Colombier Ab = 0x08, /* Accept Broadcast */
549b12e100SDavid du Colombier Prom = 0x10, /* Accept Physical Address Packets */
559b12e100SDavid du Colombier RrftMASK = 0xE0, /* Receive FIFO Threshold */
569b12e100SDavid du Colombier RrftSHIFT = 5,
579b12e100SDavid du Colombier Rrft64 = 0<<RrftSHIFT,
589b12e100SDavid du Colombier Rrft32 = 1<<RrftSHIFT,
599b12e100SDavid du Colombier Rrft128 = 2<<RrftSHIFT,
609b12e100SDavid du Colombier Rrft256 = 3<<RrftSHIFT,
619b12e100SDavid du Colombier Rrft512 = 4<<RrftSHIFT,
629b12e100SDavid du Colombier Rrft768 = 5<<RrftSHIFT,
639b12e100SDavid du Colombier Rrft1024 = 6<<RrftSHIFT,
649b12e100SDavid du Colombier RrftSAF = 7<<RrftSHIFT,
659b12e100SDavid du Colombier };
669b12e100SDavid du Colombier
679b12e100SDavid du Colombier enum { /* Tcr */
689b12e100SDavid du Colombier Lb0 = 0x02, /* Loopback Mode */
699b12e100SDavid du Colombier Lb1 = 0x04,
709b12e100SDavid du Colombier Ofset = 0x08, /* Back-off Priority Selection */
719b12e100SDavid du Colombier RtsfMASK = 0xE0, /* Transmit FIFO Threshold */
729b12e100SDavid du Colombier RtsfSHIFT = 5,
739b12e100SDavid du Colombier Rtsf128 = 0<<RtsfSHIFT,
749b12e100SDavid du Colombier Rtsf256 = 1<<RtsfSHIFT,
759b12e100SDavid du Colombier Rtsf512 = 2<<RtsfSHIFT,
769b12e100SDavid du Colombier Rtsf1024 = 3<<RtsfSHIFT,
779b12e100SDavid du Colombier RtsfSAF = 7<<RtsfSHIFT,
789b12e100SDavid du Colombier };
799b12e100SDavid du Colombier
809b12e100SDavid du Colombier enum { /* Cr */
819b12e100SDavid du Colombier Init = 0x0001, /* INIT Process Begin */
829b12e100SDavid du Colombier Strt = 0x0002, /* Start NIC */
839b12e100SDavid du Colombier Stop = 0x0004, /* Stop NIC */
849b12e100SDavid du Colombier Rxon = 0x0008, /* Turn on Receive Process */
859b12e100SDavid du Colombier Txon = 0x0010, /* Turn on Transmit Process */
869b12e100SDavid du Colombier Tdmd = 0x0020, /* Transmit Poll Demand */
879b12e100SDavid du Colombier Rdmd = 0x0040, /* Receive Poll Demand */
889b12e100SDavid du Colombier Eren = 0x0100, /* Early Receive Enable */
899b12e100SDavid du Colombier Fdx = 0x0400, /* Set MAC to Full Duplex Mode */
909b12e100SDavid du Colombier Dpoll = 0x0800, /* Disable Td/Rd Auto Polling */
919b12e100SDavid du Colombier Tdmd1 = 0x2000, /* Transmit Poll Demand 1 */
929b12e100SDavid du Colombier Rdmd1 = 0x4000, /* Receive Poll Demand 1 */
939b12e100SDavid du Colombier Sfrst = 0x8000, /* Software Reset */
949b12e100SDavid du Colombier };
959b12e100SDavid du Colombier
969b12e100SDavid du Colombier enum { /* Isr/Imr */
979b12e100SDavid du Colombier Prx = 0x0001, /* Received Packet Successfully */
989b12e100SDavid du Colombier Ptx = 0x0002, /* Transmitted Packet Successfully */
999b12e100SDavid du Colombier Rxe = 0x0004, /* Receive Error */
1009b12e100SDavid du Colombier Txe = 0x0008, /* Transmit Error */
1019b12e100SDavid du Colombier Tu = 0x0010, /* Transmit Buffer Underflow */
1029b12e100SDavid du Colombier Ru = 0x0020, /* Receive Buffer Link Error */
1039b12e100SDavid du Colombier Be = 0x0040, /* PCI Bus Error */
1049b12e100SDavid du Colombier Cnt = 0x0080, /* Counter Overflow */
1059b12e100SDavid du Colombier Eri = 0x0100, /* Early Receive Interrupt */
1069b12e100SDavid du Colombier Udfi = 0x0200, /* Tx FIFO Underflow */
1079b12e100SDavid du Colombier Ovfi = 0x0400, /* Receive FIFO Overflow */
1089b12e100SDavid du Colombier Pktrace = 0x0800, /* Hmmm... */
1099b12e100SDavid du Colombier Norbf = 0x1000, /* No Receive Buffers */
1109b12e100SDavid du Colombier Abti = 0x2000, /* Transmission Abort */
1119b12e100SDavid du Colombier Srci = 0x4000, /* Port State Change */
1129b12e100SDavid du Colombier Geni = 0x8000, /* General Purpose Interrupt */
1139b12e100SDavid du Colombier };
1149b12e100SDavid du Colombier
1159b12e100SDavid du Colombier enum { /* Phyadr */
1169b12e100SDavid du Colombier PhyadMASK = 0x1F, /* PHY Address */
1179b12e100SDavid du Colombier PhyadSHIFT = 0,
1189b12e100SDavid du Colombier Mfdc = 0x20, /* Accelerate MDC Speed */
1199b12e100SDavid du Colombier Mpo0 = 0x40, /* MII Polling Timer Interval */
1209b12e100SDavid du Colombier Mpo1 = 0x80,
1219b12e100SDavid du Colombier };
1229b12e100SDavid du Colombier
1239b12e100SDavid du Colombier enum { /* Bcr0 */
1249b12e100SDavid du Colombier DmaMASK = 0x07, /* DMA Length */
1259b12e100SDavid du Colombier DmaSHIFT = 0,
1269b12e100SDavid du Colombier Dma32 = 0<<DmaSHIFT,
1279b12e100SDavid du Colombier Dma64 = 1<<DmaSHIFT,
1289b12e100SDavid du Colombier Dma128 = 2<<DmaSHIFT,
1299b12e100SDavid du Colombier Dma256 = 3<<DmaSHIFT,
1309b12e100SDavid du Colombier Dma512 = 4<<DmaSHIFT,
1319b12e100SDavid du Colombier Dma1024 = 5<<DmaSHIFT,
1329b12e100SDavid du Colombier DmaSAF = 7<<DmaSHIFT,
1339b12e100SDavid du Colombier CrftMASK = 0x38, /* Rx FIFO Threshold */
1349b12e100SDavid du Colombier CrftSHIFT = 3,
1359b12e100SDavid du Colombier Crft64 = 1<<CrftSHIFT,
1369b12e100SDavid du Colombier Crft128 = 2<<CrftSHIFT,
1379b12e100SDavid du Colombier Crft256 = 3<<CrftSHIFT,
1389b12e100SDavid du Colombier Crft512 = 4<<CrftSHIFT,
1399b12e100SDavid du Colombier Crft1024 = 5<<CrftSHIFT,
1409b12e100SDavid du Colombier CrftSAF = 7<<CrftSHIFT,
1419b12e100SDavid du Colombier Extled = 0x40, /* Extra LED Support Control */
1429b12e100SDavid du Colombier Med2 = 0x80, /* Medium Select Control */
1439b12e100SDavid du Colombier };
1449b12e100SDavid du Colombier
1459b12e100SDavid du Colombier enum { /* Bcr1 */
1469b12e100SDavid du Colombier PotMASK = 0x07, /* Polling Timer Interval */
1479b12e100SDavid du Colombier PotSHIFT = 0,
1489b12e100SDavid du Colombier CtftMASK = 0x38, /* Tx FIFO Threshold */
1499b12e100SDavid du Colombier CtftSHIFT = 3,
1509b12e100SDavid du Colombier Ctft64 = 1<<CtftSHIFT,
1519b12e100SDavid du Colombier Ctft128 = 2<<CtftSHIFT,
1529b12e100SDavid du Colombier Ctft256 = 3<<CtftSHIFT,
1539b12e100SDavid du Colombier Ctft512 = 4<<CtftSHIFT,
1549b12e100SDavid du Colombier Ctft1024 = 5<<CtftSHIFT,
1559b12e100SDavid du Colombier CtftSAF = 7<<CtftSHIFT,
1569b12e100SDavid du Colombier };
1579b12e100SDavid du Colombier
1589b12e100SDavid du Colombier enum { /* Miicr */
1599b12e100SDavid du Colombier Mdc = 0x01, /* Clock */
1609b12e100SDavid du Colombier Mdi = 0x02, /* Data In */
1619b12e100SDavid du Colombier Mdo = 0x04, /* Data Out */
1629b12e100SDavid du Colombier Mout = 0x08, /* Output Enable */
1639b12e100SDavid du Colombier Mdpm = 0x10, /* Direct Program Mode Enable */
1649b12e100SDavid du Colombier Wcmd = 0x20, /* Write Enable */
1659b12e100SDavid du Colombier Rcmd = 0x40, /* Read Enable */
1669b12e100SDavid du Colombier Mauto = 0x80, /* Auto Polling Enable */
1679b12e100SDavid du Colombier };
1689b12e100SDavid du Colombier
1699b12e100SDavid du Colombier enum { /* Miiadr */
1709b12e100SDavid du Colombier MadMASK = 0x1F, /* MII Port Address */
1719b12e100SDavid du Colombier MadSHIFT = 0,
1729b12e100SDavid du Colombier Mdone = 0x20, /* Accelerate MDC Speed */
1739b12e100SDavid du Colombier Msrcen = 0x40, /* MII Polling Timer Interval */
1749b12e100SDavid du Colombier Midle = 0x80,
1759b12e100SDavid du Colombier };
1769b12e100SDavid du Colombier
1779b12e100SDavid du Colombier enum { /* Eecsr */
1789b12e100SDavid du Colombier Edo = 0x01, /* Data Out */
1799b12e100SDavid du Colombier Edi = 0x02, /* Data In */
1809b12e100SDavid du Colombier Eck = 0x04, /* Clock */
1819b12e100SDavid du Colombier Ecs = 0x08, /* Chip Select */
1829b12e100SDavid du Colombier Dpm = 0x10, /* Direct Program Mode Enable */
1839b12e100SDavid du Colombier Autold = 0x20, /* Dynamic Reload */
1849b12e100SDavid du Colombier Embp = 0x40, /* Embedded Program Enable */
1859b12e100SDavid du Colombier Eepr = 0x80, /* Programmed */
1869b12e100SDavid du Colombier };
1879b12e100SDavid du Colombier
1889b12e100SDavid du Colombier /*
1899b12e100SDavid du Colombier * Ring descriptor. The space allocated for each
1909b12e100SDavid du Colombier * of these will be rounded up to a cache-line boundary.
1919b12e100SDavid du Colombier * The first 4 elements are known to the hardware.
1929b12e100SDavid du Colombier */
1939b12e100SDavid du Colombier typedef struct Ds Ds;
1949b12e100SDavid du Colombier typedef struct Ds {
1959b12e100SDavid du Colombier uint status;
1969b12e100SDavid du Colombier uint control;
1979b12e100SDavid du Colombier uint addr;
1989b12e100SDavid du Colombier uint branch;
1999b12e100SDavid du Colombier
2009b12e100SDavid du Colombier Block* bp;
2019b12e100SDavid du Colombier void* bounce;
2029b12e100SDavid du Colombier Ds* next;
2039b12e100SDavid du Colombier Ds* prev;
2049b12e100SDavid du Colombier } Ds;
2059b12e100SDavid du Colombier
2069b12e100SDavid du Colombier enum { /* Rx Ds status */
2079b12e100SDavid du Colombier Rerr = 0x00000001, /* Receiver Error */
2089b12e100SDavid du Colombier Crc = 0x00000002, /* CRC Error */
2099b12e100SDavid du Colombier Fae = 0x00000004, /* Frame Alignment Error */
2109b12e100SDavid du Colombier Fov = 0x00000008, /* FIFO Overflow */
2119b12e100SDavid du Colombier Long = 0x00000010, /* A Long Packet */
2129b12e100SDavid du Colombier Runt = 0x00000020, /* A Runt Packet */
2139b12e100SDavid du Colombier Rxserr = 0x00000040, /* System Error */
2149b12e100SDavid du Colombier Buff = 0x00000080, /* Buffer Underflow Error */
2159b12e100SDavid du Colombier Rxedp = 0x00000100, /* End of Packet Buffer */
2169b12e100SDavid du Colombier Rxstp = 0x00000200, /* Packet Start */
2179b12e100SDavid du Colombier Chn = 0x00000400, /* Chain Buffer */
2189b12e100SDavid du Colombier Phy = 0x00000800, /* Physical Address Packet */
2199b12e100SDavid du Colombier Bar = 0x00001000, /* Broadcast Packet */
2209b12e100SDavid du Colombier Mar = 0x00002000, /* Multicast Packet */
2219b12e100SDavid du Colombier Rxok = 0x00008000, /* Packet Received Successfully */
2229b12e100SDavid du Colombier LengthMASK = 0x07FF0000, /* Received Packet Length */
2239b12e100SDavid du Colombier LengthSHIFT = 16,
2249b12e100SDavid du Colombier
2259b12e100SDavid du Colombier Own = 0x80000000, /* Descriptor Owned by NIC */
2269b12e100SDavid du Colombier };
2279b12e100SDavid du Colombier
2289b12e100SDavid du Colombier enum { /* Tx Ds status */
2299b12e100SDavid du Colombier NcrMASK = 0x0000000F, /* Collision Retry Count */
2309b12e100SDavid du Colombier NcrSHIFT = 0,
2319b12e100SDavid du Colombier Cols = 0x00000010, /* Experienced Collisions */
2329b12e100SDavid du Colombier Cdh = 0x00000080, /* CD Heartbeat */
2339b12e100SDavid du Colombier Abt = 0x00000100, /* Aborted after Excessive Collisions */
2349b12e100SDavid du Colombier Owc = 0x00000200, /* Out of Window Collision Seen */
2359b12e100SDavid du Colombier Crs = 0x00000400, /* Carrier Sense Lost */
2369b12e100SDavid du Colombier Udf = 0x00000800, /* FIFO Underflow */
2379b12e100SDavid du Colombier Tbuff = 0x00001000, /* Invalid Td */
2389b12e100SDavid du Colombier Txserr = 0x00002000, /* System Error */
2399b12e100SDavid du Colombier Terr = 0x00008000, /* Excessive Collisions */
2409b12e100SDavid du Colombier };
2419b12e100SDavid du Colombier
2429b12e100SDavid du Colombier enum { /* Tx Ds control */
2439b12e100SDavid du Colombier TbsMASK = 0x000007FF, /* Tx Buffer Size */
2449b12e100SDavid du Colombier TbsSHIFT = 0,
2459b12e100SDavid du Colombier Chain = 0x00008000, /* Chain Buffer */
2469b12e100SDavid du Colombier Crcdisable = 0x00010000, /* Disable CRC generation */
2479b12e100SDavid du Colombier Stp = 0x00200000, /* Start of Packet */
2489b12e100SDavid du Colombier Edp = 0x00400000, /* End of Packet */
2499b12e100SDavid du Colombier Ic = 0x00800000, /* Assert Interrupt Immediately */
2509b12e100SDavid du Colombier };
2519b12e100SDavid du Colombier
2529b12e100SDavid du Colombier enum {
2539b12e100SDavid du Colombier Nrd = 64,
2549b12e100SDavid du Colombier Ntd = 64,
2559b12e100SDavid du Colombier Rdbsz = ROUNDUP(ETHERMAXTU+4, 4),
2569b12e100SDavid du Colombier
2579b12e100SDavid du Colombier Nrxstats = 8,
2589b12e100SDavid du Colombier Ntxstats = 9,
2599b12e100SDavid du Colombier
2609b12e100SDavid du Colombier Txcopy = 128,
2619b12e100SDavid du Colombier };
2629b12e100SDavid du Colombier
2639b12e100SDavid du Colombier typedef struct Ctlr Ctlr;
2649b12e100SDavid du Colombier typedef struct Ctlr {
2659b12e100SDavid du Colombier int port;
2669b12e100SDavid du Colombier Pcidev* pcidev;
2679b12e100SDavid du Colombier Ctlr* next;
2689b12e100SDavid du Colombier int active;
2699b12e100SDavid du Colombier int id;
2709b12e100SDavid du Colombier uchar par[Eaddrlen];
2719b12e100SDavid du Colombier
2729b12e100SDavid du Colombier QLock alock; /* attach */
2739b12e100SDavid du Colombier void* alloc; /* receive/transmit descriptors */
2749b12e100SDavid du Colombier int cls; /* alignment */
2759b12e100SDavid du Colombier int nrd;
2769b12e100SDavid du Colombier int ntd;
2779b12e100SDavid du Colombier
2789b12e100SDavid du Colombier Ds* rd;
2799b12e100SDavid du Colombier Ds* rdh;
2809b12e100SDavid du Colombier
2819b12e100SDavid du Colombier Lock tlock;
2829b12e100SDavid du Colombier Ds* td;
2839b12e100SDavid du Colombier Ds* tdh;
2849b12e100SDavid du Colombier Ds* tdt;
2859b12e100SDavid du Colombier int tdused;
2869b12e100SDavid du Colombier
2879b12e100SDavid du Colombier Lock clock; /* */
2889b12e100SDavid du Colombier int cr;
2899b12e100SDavid du Colombier int imr;
2909b12e100SDavid du Colombier int tft; /* Tx threshold */
2919b12e100SDavid du Colombier
2929b12e100SDavid du Colombier Mii* mii;
2939b12e100SDavid du Colombier Rendez lrendez;
2949b12e100SDavid du Colombier int lwakeup;
2959b12e100SDavid du Colombier
2969b12e100SDavid du Colombier uint rxstats[Nrxstats]; /* statistics */
2979b12e100SDavid du Colombier uint txstats[Ntxstats];
2989b12e100SDavid du Colombier uint intr;
2999b12e100SDavid du Colombier uint lintr;
3009b12e100SDavid du Colombier uint lsleep;
3019b12e100SDavid du Colombier uint rintr;
3029b12e100SDavid du Colombier uint tintr;
3039b12e100SDavid du Colombier uint taligned;
3049b12e100SDavid du Colombier uint tsplit;
3059b12e100SDavid du Colombier uint tcopied;
3069b12e100SDavid du Colombier uint txdw;
3079b12e100SDavid du Colombier } Ctlr;
3089b12e100SDavid du Colombier
3099b12e100SDavid du Colombier static Ctlr* vt6102ctlrhead;
3109b12e100SDavid du Colombier static Ctlr* vt6102ctlrtail;
3119b12e100SDavid du Colombier
3129b12e100SDavid du Colombier #define csr8r(c, r) (inb((c)->port+(r)))
3139b12e100SDavid du Colombier #define csr16r(c, r) (ins((c)->port+(r)))
3149b12e100SDavid du Colombier #define csr32r(c, r) (inl((c)->port+(r)))
3159b12e100SDavid du Colombier #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
3169b12e100SDavid du Colombier #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
3179b12e100SDavid du Colombier #define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
3189b12e100SDavid du Colombier
3199b12e100SDavid du Colombier static char* rxstats[Nrxstats] = {
3209b12e100SDavid du Colombier "Receiver Error",
3219b12e100SDavid du Colombier "CRC Error",
3229b12e100SDavid du Colombier "Frame Alignment Error",
3239b12e100SDavid du Colombier "FIFO Overflow",
3249b12e100SDavid du Colombier "Long Packet",
3259b12e100SDavid du Colombier "Runt Packet",
3269b12e100SDavid du Colombier "System Error",
3279b12e100SDavid du Colombier "Buffer Underflow Error",
3289b12e100SDavid du Colombier };
3299b12e100SDavid du Colombier static char* txstats[Ntxstats] = {
3309b12e100SDavid du Colombier "Aborted after Excessive Collisions",
3319b12e100SDavid du Colombier "Out of Window Collision Seen",
3329b12e100SDavid du Colombier "Carrier Sense Lost",
3339b12e100SDavid du Colombier "FIFO Underflow",
3349b12e100SDavid du Colombier "Invalid Td",
3359b12e100SDavid du Colombier "System Error",
3369b12e100SDavid du Colombier nil,
3379b12e100SDavid du Colombier "Excessive Collisions",
3389b12e100SDavid du Colombier };
3399b12e100SDavid du Colombier
3409b12e100SDavid du Colombier static long
vt6102ifstat(Ether * edev,void * a,long n,ulong offset)3419b12e100SDavid du Colombier vt6102ifstat(Ether* edev, void* a, long n, ulong offset)
3429b12e100SDavid du Colombier {
3439b12e100SDavid du Colombier char *p;
3449b12e100SDavid du Colombier Ctlr *ctlr;
3459b12e100SDavid du Colombier int i, l, r;
3469b12e100SDavid du Colombier
3479b12e100SDavid du Colombier ctlr = edev->ctlr;
3489b12e100SDavid du Colombier
34946136019SDavid du Colombier p = malloc(READSTR);
350aa72973aSDavid du Colombier if(p == nil)
351aa72973aSDavid du Colombier error(Enomem);
3529b12e100SDavid du Colombier l = 0;
3539b12e100SDavid du Colombier for(i = 0; i < Nrxstats; i++){
35446136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %ud\n",
3559b12e100SDavid du Colombier rxstats[i], ctlr->rxstats[i]);
3569b12e100SDavid du Colombier }
3579b12e100SDavid du Colombier for(i = 0; i < Ntxstats; i++){
3589b12e100SDavid du Colombier if(txstats[i] == nil)
3599b12e100SDavid du Colombier continue;
36046136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %ud\n",
3619b12e100SDavid du Colombier txstats[i], ctlr->txstats[i]);
3629b12e100SDavid du Colombier }
36346136019SDavid du Colombier l += snprint(p+l, READSTR-l, "cls: %ud\n", ctlr->cls);
36446136019SDavid du Colombier l += snprint(p+l, READSTR-l, "intr: %ud\n", ctlr->intr);
36546136019SDavid du Colombier l += snprint(p+l, READSTR-l, "lintr: %ud\n", ctlr->lintr);
36646136019SDavid du Colombier l += snprint(p+l, READSTR-l, "lsleep: %ud\n", ctlr->lsleep);
36746136019SDavid du Colombier l += snprint(p+l, READSTR-l, "rintr: %ud\n", ctlr->rintr);
36846136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tintr: %ud\n", ctlr->tintr);
36946136019SDavid du Colombier l += snprint(p+l, READSTR-l, "taligned: %ud\n", ctlr->taligned);
37046136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tsplit: %ud\n", ctlr->tsplit);
37146136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tcopied: %ud\n", ctlr->tcopied);
37246136019SDavid du Colombier l += snprint(p+l, READSTR-l, "txdw: %ud\n", ctlr->txdw);
37346136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tft: %ud\n", ctlr->tft);
3749b12e100SDavid du Colombier
3759b12e100SDavid du Colombier if(ctlr->mii != nil && ctlr->mii->curphy != nil){
37646136019SDavid du Colombier l += snprint(p+l, READSTR, "phy: ");
3779b12e100SDavid du Colombier for(i = 0; i < NMiiPhyr; i++){
3789b12e100SDavid du Colombier if(i && ((i & 0x07) == 0))
37946136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
3809b12e100SDavid du Colombier r = miimir(ctlr->mii, i);
38146136019SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", r);
3829b12e100SDavid du Colombier }
38346136019SDavid du Colombier snprint(p+l, READSTR-l, "\n");
3849b12e100SDavid du Colombier }
38546136019SDavid du Colombier snprint(p+l, READSTR-l, "\n");
3869b12e100SDavid du Colombier
3879b12e100SDavid du Colombier n = readstr(offset, a, n, p);
3889b12e100SDavid du Colombier free(p);
3899b12e100SDavid du Colombier
3909b12e100SDavid du Colombier return n;
3919b12e100SDavid du Colombier }
3929b12e100SDavid du Colombier
3939b12e100SDavid du Colombier static void
vt6102promiscuous(void * arg,int on)3949b12e100SDavid du Colombier vt6102promiscuous(void* arg, int on)
3959b12e100SDavid du Colombier {
3969b12e100SDavid du Colombier int rcr;
3979b12e100SDavid du Colombier Ctlr *ctlr;
3989b12e100SDavid du Colombier Ether *edev;
3999b12e100SDavid du Colombier
4009b12e100SDavid du Colombier edev = arg;
4019b12e100SDavid du Colombier ctlr = edev->ctlr;
4029b12e100SDavid du Colombier rcr = csr8r(ctlr, Rcr);
4039b12e100SDavid du Colombier if(on)
4049b12e100SDavid du Colombier rcr |= Prom;
4059b12e100SDavid du Colombier else
4069b12e100SDavid du Colombier rcr &= ~Prom;
4079b12e100SDavid du Colombier csr8w(ctlr, Rcr, rcr);
4089b12e100SDavid du Colombier }
4099b12e100SDavid du Colombier
4109b12e100SDavid du Colombier static void
vt6102multicast(void * arg,uchar * addr,int on)4119b12e100SDavid du Colombier vt6102multicast(void* arg, uchar* addr, int on)
4129b12e100SDavid du Colombier {
4139b12e100SDavid du Colombier /*
4149b12e100SDavid du Colombier * For now Am is set in Rcr.
4159b12e100SDavid du Colombier * Will need to interlock with promiscuous
4169b12e100SDavid du Colombier * when this gets filled in.
4179b12e100SDavid du Colombier */
4189b12e100SDavid du Colombier USED(arg, addr, on);
4199b12e100SDavid du Colombier }
4209b12e100SDavid du Colombier
4219b12e100SDavid du Colombier static int
vt6102wakeup(void * v)4229b12e100SDavid du Colombier vt6102wakeup(void* v)
4239b12e100SDavid du Colombier {
4249b12e100SDavid du Colombier return *((int*)v) != 0;
4259b12e100SDavid du Colombier }
4269b12e100SDavid du Colombier
4279b12e100SDavid du Colombier static void
vt6102imr(Ctlr * ctlr,int imr)4289b12e100SDavid du Colombier vt6102imr(Ctlr* ctlr, int imr)
4299b12e100SDavid du Colombier {
4309b12e100SDavid du Colombier ilock(&ctlr->clock);
4319b12e100SDavid du Colombier ctlr->imr |= imr;
4329b12e100SDavid du Colombier csr16w(ctlr, Imr, ctlr->imr);
4339b12e100SDavid du Colombier iunlock(&ctlr->clock);
4349b12e100SDavid du Colombier }
4359b12e100SDavid du Colombier
4369b12e100SDavid du Colombier static void
vt6102lproc(void * arg)4379b12e100SDavid du Colombier vt6102lproc(void* arg)
4389b12e100SDavid du Colombier {
4399b12e100SDavid du Colombier Ctlr *ctlr;
4409b12e100SDavid du Colombier Ether *edev;
4419b12e100SDavid du Colombier MiiPhy *phy;
4429b12e100SDavid du Colombier
4439b12e100SDavid du Colombier edev = arg;
4449b12e100SDavid du Colombier ctlr = edev->ctlr;
4459b12e100SDavid du Colombier for(;;){
4469b12e100SDavid du Colombier if(ctlr->mii == nil || ctlr->mii->curphy == nil)
4479b12e100SDavid du Colombier break;
4489b12e100SDavid du Colombier if(miistatus(ctlr->mii) < 0)
4499b12e100SDavid du Colombier goto enable;
4509b12e100SDavid du Colombier
4519b12e100SDavid du Colombier phy = ctlr->mii->curphy;
4529b12e100SDavid du Colombier ilock(&ctlr->clock);
4539b12e100SDavid du Colombier if(phy->fd)
4549b12e100SDavid du Colombier ctlr->cr |= Fdx;
4559b12e100SDavid du Colombier else
4569b12e100SDavid du Colombier ctlr->cr &= ~Fdx;
4579b12e100SDavid du Colombier csr16w(ctlr, Cr, ctlr->cr);
4589b12e100SDavid du Colombier iunlock(&ctlr->clock);
4599b12e100SDavid du Colombier enable:
4609b12e100SDavid du Colombier ctlr->lwakeup = 0;
4619b12e100SDavid du Colombier vt6102imr(ctlr, Srci);
4629b12e100SDavid du Colombier
4639b12e100SDavid du Colombier ctlr->lsleep++;
4649b12e100SDavid du Colombier sleep(&ctlr->lrendez, vt6102wakeup, &ctlr->lwakeup);
4659b12e100SDavid du Colombier
4669b12e100SDavid du Colombier }
4679b12e100SDavid du Colombier pexit("vt6102lproc: done", 1);
4689b12e100SDavid du Colombier }
4699b12e100SDavid du Colombier
4709b12e100SDavid du Colombier static void
vt6102attach(Ether * edev)4719b12e100SDavid du Colombier vt6102attach(Ether* edev)
4729b12e100SDavid du Colombier {
4739b12e100SDavid du Colombier int i;
4749b12e100SDavid du Colombier Ctlr *ctlr;
4759b12e100SDavid du Colombier Ds *ds, *prev;
4769b12e100SDavid du Colombier uchar *alloc, *bounce;
4779b12e100SDavid du Colombier char name[KNAMELEN];
4789b12e100SDavid du Colombier
4799b12e100SDavid du Colombier ctlr = edev->ctlr;
4809b12e100SDavid du Colombier qlock(&ctlr->alock);
4819b12e100SDavid du Colombier if(ctlr->alloc != nil){
4829b12e100SDavid du Colombier qunlock(&ctlr->alock);
4839b12e100SDavid du Colombier return;
4849b12e100SDavid du Colombier }
4859b12e100SDavid du Colombier
4869b12e100SDavid du Colombier /*
4879b12e100SDavid du Colombier * Descriptor and bounce-buffer space.
4889b12e100SDavid du Colombier * Must all be aligned on a 4-byte boundary,
4899b12e100SDavid du Colombier * but try to align on cache-lines.
4909b12e100SDavid du Colombier */
4919b12e100SDavid du Colombier ctlr->nrd = Nrd;
4929b12e100SDavid du Colombier ctlr->ntd = Ntd;
4939b12e100SDavid du Colombier alloc = malloc((ctlr->nrd+ctlr->ntd)*ctlr->cls + ctlr->ntd*Txcopy + ctlr->cls-1);
4949b12e100SDavid du Colombier if(alloc == nil){
4959b12e100SDavid du Colombier qunlock(&ctlr->alock);
496*6fbfa2f3SDavid du Colombier error(Enomem);
4979b12e100SDavid du Colombier }
4989b12e100SDavid du Colombier ctlr->alloc = alloc;
4999b12e100SDavid du Colombier alloc = (uchar*)ROUNDUP((ulong)alloc, ctlr->cls);
5009b12e100SDavid du Colombier
5019b12e100SDavid du Colombier ctlr->rd = (Ds*)alloc;
5029b12e100SDavid du Colombier
5039b12e100SDavid du Colombier if(waserror()){
5049b12e100SDavid du Colombier ds = ctlr->rd;
5059b12e100SDavid du Colombier for(i = 0; i < ctlr->nrd; i++){
5069b12e100SDavid du Colombier if(ds->bp != nil){
5079b12e100SDavid du Colombier freeb(ds->bp);
5089b12e100SDavid du Colombier ds->bp = nil;
5099b12e100SDavid du Colombier }
5109b12e100SDavid du Colombier if((ds = ds->next) == nil)
5119b12e100SDavid du Colombier break;
5129b12e100SDavid du Colombier }
5139b12e100SDavid du Colombier free(ctlr->alloc);
5149b12e100SDavid du Colombier ctlr->alloc = nil;
5159b12e100SDavid du Colombier qunlock(&ctlr->alock);
5169b12e100SDavid du Colombier nexterror();
5179b12e100SDavid du Colombier }
5189b12e100SDavid du Colombier
5199b12e100SDavid du Colombier prev = ctlr->rd + ctlr->nrd-1;
5209b12e100SDavid du Colombier for(i = 0; i < ctlr->nrd; i++){
5219b12e100SDavid du Colombier ds = (Ds*)alloc;
5229b12e100SDavid du Colombier alloc += ctlr->cls;
5239b12e100SDavid du Colombier
5249b12e100SDavid du Colombier ds->control = Rdbsz;
5259b12e100SDavid du Colombier ds->branch = PCIWADDR(alloc);
5269b12e100SDavid du Colombier
5279b12e100SDavid du Colombier ds->bp = iallocb(Rdbsz+3);
5289b12e100SDavid du Colombier if(ds->bp == nil)
5299b12e100SDavid du Colombier error("vt6102: can't allocate receive ring\n");
5309b12e100SDavid du Colombier ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4);
5319b12e100SDavid du Colombier ds->addr = PCIWADDR(ds->bp->rp);
5329b12e100SDavid du Colombier
5339b12e100SDavid du Colombier ds->next = (Ds*)alloc;
5349b12e100SDavid du Colombier ds->prev = prev;
5359b12e100SDavid du Colombier prev = ds;
5369b12e100SDavid du Colombier
5379b12e100SDavid du Colombier ds->status = Own;
5389b12e100SDavid du Colombier }
5399b12e100SDavid du Colombier prev->branch = 0;
5409b12e100SDavid du Colombier prev->next = ctlr->rd;
5419b12e100SDavid du Colombier prev->status = 0;
5429b12e100SDavid du Colombier ctlr->rdh = ctlr->rd;
5439b12e100SDavid du Colombier
5449b12e100SDavid du Colombier ctlr->td = (Ds*)alloc;
5459b12e100SDavid du Colombier prev = ctlr->td + ctlr->ntd-1;
5469b12e100SDavid du Colombier bounce = alloc + ctlr->ntd*ctlr->cls;
5479b12e100SDavid du Colombier for(i = 0; i < ctlr->ntd; i++){
5489b12e100SDavid du Colombier ds = (Ds*)alloc;
5499b12e100SDavid du Colombier alloc += ctlr->cls;
5509b12e100SDavid du Colombier
5519b12e100SDavid du Colombier ds->bounce = bounce;
5529b12e100SDavid du Colombier bounce += Txcopy;
5539b12e100SDavid du Colombier ds->next = (Ds*)alloc;
5549b12e100SDavid du Colombier ds->prev = prev;
5559b12e100SDavid du Colombier prev = ds;
5569b12e100SDavid du Colombier }
5579b12e100SDavid du Colombier prev->next = ctlr->td;
5589b12e100SDavid du Colombier ctlr->tdh = ctlr->tdt = ctlr->td;
5599b12e100SDavid du Colombier ctlr->tdused = 0;
5609b12e100SDavid du Colombier
5619b12e100SDavid du Colombier ctlr->cr = Dpoll|Rdmd|Txon|Rxon|Strt;
5629b12e100SDavid du Colombier /*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/
5639b12e100SDavid du Colombier ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx;
5649b12e100SDavid du Colombier
5659b12e100SDavid du Colombier ilock(&ctlr->clock);
5669b12e100SDavid du Colombier csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd));
5679b12e100SDavid du Colombier csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td));
5689b12e100SDavid du Colombier csr16w(ctlr, Isr, ~0);
5699b12e100SDavid du Colombier csr16w(ctlr, Imr, ctlr->imr);
5709b12e100SDavid du Colombier csr16w(ctlr, Cr, ctlr->cr);
5719b12e100SDavid du Colombier iunlock(&ctlr->clock);
5729b12e100SDavid du Colombier
5739b12e100SDavid du Colombier snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
5749b12e100SDavid du Colombier kproc(name, vt6102lproc, edev);
5759b12e100SDavid du Colombier
5769b12e100SDavid du Colombier qunlock(&ctlr->alock);
5779b12e100SDavid du Colombier poperror();
5789b12e100SDavid du Colombier }
5799b12e100SDavid du Colombier
5809b12e100SDavid du Colombier static void
vt6102transmit(Ether * edev)5819b12e100SDavid du Colombier vt6102transmit(Ether* edev)
5829b12e100SDavid du Colombier {
5839b12e100SDavid du Colombier Block *bp;
5849b12e100SDavid du Colombier Ctlr *ctlr;
5859b12e100SDavid du Colombier Ds *ds, *next;
5869b12e100SDavid du Colombier int control, i, o, prefix, size, tdused, timeo;
5879b12e100SDavid du Colombier
5889b12e100SDavid du Colombier ctlr = edev->ctlr;
5899b12e100SDavid du Colombier
5909b12e100SDavid du Colombier ilock(&ctlr->tlock);
5919b12e100SDavid du Colombier
5929b12e100SDavid du Colombier /*
5939b12e100SDavid du Colombier * Free any completed packets
5949b12e100SDavid du Colombier */
5959b12e100SDavid du Colombier ds = ctlr->tdh;
5969b12e100SDavid du Colombier for(tdused = ctlr->tdused; tdused > 0; tdused--){
5979b12e100SDavid du Colombier /*
5989b12e100SDavid du Colombier * For some errors the chip will turn the Tx engine
5999b12e100SDavid du Colombier * off. Wait for that to happen.
6009b12e100SDavid du Colombier * Could reset and re-init the chip here if it doesn't
6019b12e100SDavid du Colombier * play fair.
6029b12e100SDavid du Colombier * To do: adjust Tx FIFO threshold on underflow.
6039b12e100SDavid du Colombier */
6049b12e100SDavid du Colombier if(ds->status & (Abt|Tbuff|Udf)){
6059b12e100SDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
6069b12e100SDavid du Colombier if(!(csr16r(ctlr, Cr) & Txon))
6079b12e100SDavid du Colombier break;
6089b12e100SDavid du Colombier microdelay(1);
6099b12e100SDavid du Colombier }
6109b12e100SDavid du Colombier ds->status = Own;
6119b12e100SDavid du Colombier csr32w(ctlr, Txdaddr, PCIWADDR(ds));
6129b12e100SDavid du Colombier }
6139b12e100SDavid du Colombier
6149b12e100SDavid du Colombier if(ds->status & Own)
6159b12e100SDavid du Colombier break;
6169b12e100SDavid du Colombier ds->addr = 0;
6179b12e100SDavid du Colombier ds->branch = 0;
6189b12e100SDavid du Colombier
6199b12e100SDavid du Colombier if(ds->bp != nil){
6209b12e100SDavid du Colombier freeb(ds->bp);
6219b12e100SDavid du Colombier ds->bp = nil;
6229b12e100SDavid du Colombier }
6239b12e100SDavid du Colombier for(i = 0; i < Ntxstats-1; i++){
6249b12e100SDavid du Colombier if(ds->status & (1<<i))
6259b12e100SDavid du Colombier ctlr->txstats[i]++;
6269b12e100SDavid du Colombier }
6279b12e100SDavid du Colombier ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT;
6289b12e100SDavid du Colombier
6299b12e100SDavid du Colombier ds = ds->next;
6309b12e100SDavid du Colombier }
6319b12e100SDavid du Colombier ctlr->tdh = ds;
6329b12e100SDavid du Colombier
6339b12e100SDavid du Colombier /*
6349b12e100SDavid du Colombier * Try to fill the ring back up.
6359b12e100SDavid du Colombier */
6369b12e100SDavid du Colombier ds = ctlr->tdt;
6379b12e100SDavid du Colombier while(tdused < ctlr->ntd-2){
6389b12e100SDavid du Colombier if((bp = qget(edev->oq)) == nil)
6399b12e100SDavid du Colombier break;
6409b12e100SDavid du Colombier tdused++;
6419b12e100SDavid du Colombier
6429b12e100SDavid du Colombier size = BLEN(bp);
6439b12e100SDavid du Colombier prefix = 0;
6449b12e100SDavid du Colombier
6459b12e100SDavid du Colombier if(o = (((int)bp->rp) & 0x03)){
6469b12e100SDavid du Colombier prefix = Txcopy-o;
6479b12e100SDavid du Colombier if(prefix > size)
6489b12e100SDavid du Colombier prefix = size;
6499b12e100SDavid du Colombier memmove(ds->bounce, bp->rp, prefix);
6509b12e100SDavid du Colombier ds->addr = PCIWADDR(ds->bounce);
6519b12e100SDavid du Colombier bp->rp += prefix;
6529b12e100SDavid du Colombier size -= prefix;
6539b12e100SDavid du Colombier }
6549b12e100SDavid du Colombier
6559b12e100SDavid du Colombier next = ds->next;
6569b12e100SDavid du Colombier ds->branch = PCIWADDR(ds->next);
6579b12e100SDavid du Colombier
6589b12e100SDavid du Colombier if(size){
6599b12e100SDavid du Colombier if(prefix){
6609b12e100SDavid du Colombier next->bp = bp;
6619b12e100SDavid du Colombier next->addr = PCIWADDR(bp->rp);
6629b12e100SDavid du Colombier next->branch = PCIWADDR(next->next);
6639b12e100SDavid du Colombier next->control = Edp|Chain|((size<<TbsSHIFT) & TbsMASK);
6649b12e100SDavid du Colombier
6659b12e100SDavid du Colombier control = Stp|Chain|((prefix<<TbsSHIFT) & TbsMASK);
6669b12e100SDavid du Colombier
6679b12e100SDavid du Colombier next = next->next;
6689b12e100SDavid du Colombier tdused++;
6699b12e100SDavid du Colombier ctlr->tsplit++;
6709b12e100SDavid du Colombier }
6719b12e100SDavid du Colombier else{
6729b12e100SDavid du Colombier ds->bp = bp;
6739b12e100SDavid du Colombier ds->addr = PCIWADDR(bp->rp);
6749b12e100SDavid du Colombier control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK);
6759b12e100SDavid du Colombier ctlr->taligned++;
6769b12e100SDavid du Colombier }
6779b12e100SDavid du Colombier }
6789b12e100SDavid du Colombier else{
6799b12e100SDavid du Colombier freeb(bp);
6809b12e100SDavid du Colombier control = Edp|Stp|((prefix<<TbsSHIFT) & TbsMASK);
6819b12e100SDavid du Colombier ctlr->tcopied++;
6829b12e100SDavid du Colombier }
6839b12e100SDavid du Colombier
6849b12e100SDavid du Colombier ds->control = control;
6859b12e100SDavid du Colombier if(tdused >= ctlr->ntd-2){
6869b12e100SDavid du Colombier ds->control |= Ic;
6879b12e100SDavid du Colombier ctlr->txdw++;
6889b12e100SDavid du Colombier }
6899b12e100SDavid du Colombier coherence();
6909b12e100SDavid du Colombier ds->status = Own;
6919b12e100SDavid du Colombier
6929b12e100SDavid du Colombier ds = next;
6939b12e100SDavid du Colombier }
6949b12e100SDavid du Colombier ctlr->tdt = ds;
6959b12e100SDavid du Colombier ctlr->tdused = tdused;
6969b12e100SDavid du Colombier if(ctlr->tdused)
6979b12e100SDavid du Colombier csr16w(ctlr, Cr, Tdmd|ctlr->cr);
6989b12e100SDavid du Colombier
6999b12e100SDavid du Colombier iunlock(&ctlr->tlock);
7009b12e100SDavid du Colombier }
7019b12e100SDavid du Colombier
7029b12e100SDavid du Colombier static void
vt6102receive(Ether * edev)7039b12e100SDavid du Colombier vt6102receive(Ether* edev)
7049b12e100SDavid du Colombier {
7059b12e100SDavid du Colombier Ds *ds;
7069b12e100SDavid du Colombier Block *bp;
7079b12e100SDavid du Colombier Ctlr *ctlr;
7089b12e100SDavid du Colombier int i, len;
7099b12e100SDavid du Colombier
7109b12e100SDavid du Colombier ctlr = edev->ctlr;
7119b12e100SDavid du Colombier
7129b12e100SDavid du Colombier ds = ctlr->rdh;
7139b12e100SDavid du Colombier while(!(ds->status & Own) && ds->status != 0){
7149b12e100SDavid du Colombier if(ds->status & Rerr){
7159b12e100SDavid du Colombier for(i = 0; i < Nrxstats; i++){
7169b12e100SDavid du Colombier if(ds->status & (1<<i))
7179b12e100SDavid du Colombier ctlr->rxstats[i]++;
7189b12e100SDavid du Colombier }
7199b12e100SDavid du Colombier }
7209b12e100SDavid du Colombier else if(bp = iallocb(Rdbsz+3)){
7219b12e100SDavid du Colombier len = ((ds->status & LengthMASK)>>LengthSHIFT)-4;
7229b12e100SDavid du Colombier ds->bp->wp = ds->bp->rp+len;
7239b12e100SDavid du Colombier etheriq(edev, ds->bp, 1);
7249b12e100SDavid du Colombier bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4);
7259b12e100SDavid du Colombier ds->addr = PCIWADDR(bp->rp);
7269b12e100SDavid du Colombier ds->bp = bp;
7279b12e100SDavid du Colombier }
7289b12e100SDavid du Colombier ds->control = Rdbsz;
7299b12e100SDavid du Colombier ds->branch = 0;
7309b12e100SDavid du Colombier ds->status = 0;
7319b12e100SDavid du Colombier
7329b12e100SDavid du Colombier ds->prev->branch = PCIWADDR(ds);
7339b12e100SDavid du Colombier coherence();
7349b12e100SDavid du Colombier ds->prev->status = Own;
7359b12e100SDavid du Colombier
7369b12e100SDavid du Colombier ds = ds->next;
7379b12e100SDavid du Colombier }
7389b12e100SDavid du Colombier ctlr->rdh = ds;
7399b12e100SDavid du Colombier
7409b12e100SDavid du Colombier csr16w(ctlr, Cr, ctlr->cr);
7419b12e100SDavid du Colombier }
7429b12e100SDavid du Colombier
7439b12e100SDavid du Colombier static void
vt6102interrupt(Ureg *,void * arg)7449b12e100SDavid du Colombier vt6102interrupt(Ureg*, void* arg)
7459b12e100SDavid du Colombier {
7469b12e100SDavid du Colombier Ctlr *ctlr;
7479b12e100SDavid du Colombier Ether *edev;
7489b12e100SDavid du Colombier int imr, isr, r, timeo;
7499b12e100SDavid du Colombier
7509b12e100SDavid du Colombier edev = arg;
7519b12e100SDavid du Colombier ctlr = edev->ctlr;
7529b12e100SDavid du Colombier
7539b12e100SDavid du Colombier ilock(&ctlr->clock);
7549b12e100SDavid du Colombier csr16w(ctlr, Imr, 0);
7559b12e100SDavid du Colombier imr = ctlr->imr;
7569b12e100SDavid du Colombier ctlr->intr++;
7579b12e100SDavid du Colombier for(;;){
7589b12e100SDavid du Colombier if((isr = csr16r(ctlr, Isr)) != 0)
7599b12e100SDavid du Colombier csr16w(ctlr, Isr, isr);
7609b12e100SDavid du Colombier if((isr & ctlr->imr) == 0)
7619b12e100SDavid du Colombier break;
7629b12e100SDavid du Colombier
7639b12e100SDavid du Colombier if(isr & Srci){
7649b12e100SDavid du Colombier imr &= ~Srci;
7659b12e100SDavid du Colombier ctlr->lwakeup = isr & Srci;
7669b12e100SDavid du Colombier wakeup(&ctlr->lrendez);
7679b12e100SDavid du Colombier isr &= ~Srci;
7689b12e100SDavid du Colombier ctlr->lintr++;
7699b12e100SDavid du Colombier }
7709b12e100SDavid du Colombier if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){
7719b12e100SDavid du Colombier vt6102receive(edev);
7729b12e100SDavid du Colombier isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx);
7739b12e100SDavid du Colombier ctlr->rintr++;
7749b12e100SDavid du Colombier }
7759b12e100SDavid du Colombier if(isr & (Abti|Udfi|Tu|Txe|Ptx)){
7769b12e100SDavid du Colombier if(isr & (Abti|Udfi|Tu)){
7779b12e100SDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
7789b12e100SDavid du Colombier if(!(csr16r(ctlr, Cr) & Txon))
7799b12e100SDavid du Colombier break;
7809b12e100SDavid du Colombier microdelay(1);
7819b12e100SDavid du Colombier }
7829b12e100SDavid du Colombier
7839b12e100SDavid du Colombier if((isr & Udfi) && ctlr->tft < CtftSAF){
7849b12e100SDavid du Colombier ctlr->tft += 1<<CtftSHIFT;
7859b12e100SDavid du Colombier r = csr8r(ctlr, Bcr1) & ~CtftMASK;
7869b12e100SDavid du Colombier csr8w(ctlr, Bcr1, r|ctlr->tft);
7879b12e100SDavid du Colombier }
7889b12e100SDavid du Colombier }
7899b12e100SDavid du Colombier vt6102transmit(edev);
7909b12e100SDavid du Colombier isr &= ~(Abti|Udfi|Tu|Txe|Ptx);
7919b12e100SDavid du Colombier ctlr->tintr++;
7929b12e100SDavid du Colombier }
79326ad7229SDavid du Colombier if(isr)
7949b12e100SDavid du Colombier panic("vt6102: isr %4.4uX\n", isr);
7959b12e100SDavid du Colombier }
7969b12e100SDavid du Colombier ctlr->imr = imr;
7979b12e100SDavid du Colombier csr16w(ctlr, Imr, ctlr->imr);
7989b12e100SDavid du Colombier iunlock(&ctlr->clock);
7999b12e100SDavid du Colombier }
8009b12e100SDavid du Colombier
8019b12e100SDavid du Colombier static int
vt6102miimicmd(Mii * mii,int pa,int ra,int cmd,int data)8029b12e100SDavid du Colombier vt6102miimicmd(Mii* mii, int pa, int ra, int cmd, int data)
8039b12e100SDavid du Colombier {
8049b12e100SDavid du Colombier Ctlr *ctlr;
8059b12e100SDavid du Colombier int r, timeo;
8069b12e100SDavid du Colombier
8079b12e100SDavid du Colombier ctlr = mii->ctlr;
8089b12e100SDavid du Colombier
8099b12e100SDavid du Colombier csr8w(ctlr, Miicr, 0);
8109b12e100SDavid du Colombier r = csr8r(ctlr, Phyadr);
8119b12e100SDavid du Colombier csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa);
8129b12e100SDavid du Colombier csr8w(ctlr, Phyadr, pa);
8139b12e100SDavid du Colombier csr8w(ctlr, Miiadr, ra);
8149b12e100SDavid du Colombier if(cmd == Wcmd)
8159b12e100SDavid du Colombier csr16w(ctlr, Miidata, data);
8169b12e100SDavid du Colombier csr8w(ctlr, Miicr, cmd);
8179b12e100SDavid du Colombier
8189b12e100SDavid du Colombier for(timeo = 0; timeo < 10000; timeo++){
8199b12e100SDavid du Colombier if(!(csr8r(ctlr, Miicr) & cmd))
8209b12e100SDavid du Colombier break;
8219b12e100SDavid du Colombier microdelay(1);
8229b12e100SDavid du Colombier }
8239b12e100SDavid du Colombier if(timeo >= 10000)
8249b12e100SDavid du Colombier return -1;
8259b12e100SDavid du Colombier
8269b12e100SDavid du Colombier if(cmd == Wcmd)
8279b12e100SDavid du Colombier return 0;
8289b12e100SDavid du Colombier return csr16r(ctlr, Miidata);
8299b12e100SDavid du Colombier }
8309b12e100SDavid du Colombier
8319b12e100SDavid du Colombier static int
vt6102miimir(Mii * mii,int pa,int ra)8329b12e100SDavid du Colombier vt6102miimir(Mii* mii, int pa, int ra)
8339b12e100SDavid du Colombier {
8349b12e100SDavid du Colombier return vt6102miimicmd(mii, pa, ra, Rcmd, 0);
8359b12e100SDavid du Colombier }
8369b12e100SDavid du Colombier
8379b12e100SDavid du Colombier static int
vt6102miimiw(Mii * mii,int pa,int ra,int data)8389b12e100SDavid du Colombier vt6102miimiw(Mii* mii, int pa, int ra, int data)
8399b12e100SDavid du Colombier {
8409b12e100SDavid du Colombier return vt6102miimicmd(mii, pa, ra, Wcmd, data);
8419b12e100SDavid du Colombier }
8429b12e100SDavid du Colombier
8439b12e100SDavid du Colombier static int
vt6102detach(Ctlr * ctlr)8449b12e100SDavid du Colombier vt6102detach(Ctlr* ctlr)
8459b12e100SDavid du Colombier {
84676c9a8f8SDavid du Colombier int revid, timeo;
84776c9a8f8SDavid du Colombier
84876c9a8f8SDavid du Colombier /*
84976c9a8f8SDavid du Colombier * Reset power management registers.
85076c9a8f8SDavid du Colombier */
85176c9a8f8SDavid du Colombier revid = pcicfgr8(ctlr->pcidev, PciRID);
85276c9a8f8SDavid du Colombier if(revid >= 0x40){
85376c9a8f8SDavid du Colombier /* Set power state D0. */
85476c9a8f8SDavid du Colombier csr8w(ctlr, Stickhw, csr8r(ctlr, Stickhw) & 0xFC);
85576c9a8f8SDavid du Colombier
85676c9a8f8SDavid du Colombier /* Disable force PME-enable. */
85776c9a8f8SDavid du Colombier csr8w(ctlr, Wolcgclr, 0x80);
85876c9a8f8SDavid du Colombier
85976c9a8f8SDavid du Colombier /* Clear WOL config and status bits. */
86076c9a8f8SDavid du Colombier csr8w(ctlr, Wolcrclr, 0xFF);
86176c9a8f8SDavid du Colombier csr8w(ctlr, Pwrcsrclr, 0xFF);
86276c9a8f8SDavid du Colombier }
8639b12e100SDavid du Colombier
8649b12e100SDavid du Colombier /*
8659b12e100SDavid du Colombier * Soft reset the controller.
8669b12e100SDavid du Colombier */
8677e254d1cSDavid du Colombier csr16w(ctlr, Cr, Stop);
8687e254d1cSDavid du Colombier csr16w(ctlr, Cr, Stop|Sfrst);
8699b12e100SDavid du Colombier for(timeo = 0; timeo < 10000; timeo++){
8709b12e100SDavid du Colombier if(!(csr16r(ctlr, Cr) & Sfrst))
8719b12e100SDavid du Colombier break;
8729b12e100SDavid du Colombier microdelay(1);
8739b12e100SDavid du Colombier }
8749b12e100SDavid du Colombier if(timeo >= 1000)
8759b12e100SDavid du Colombier return -1;
8769b12e100SDavid du Colombier
8779b12e100SDavid du Colombier return 0;
8789b12e100SDavid du Colombier }
8799b12e100SDavid du Colombier
8800a2a9dafSDavid du Colombier static void
vt6102shutdown(Ether * ether)8810a2a9dafSDavid du Colombier vt6102shutdown(Ether *ether)
8820a2a9dafSDavid du Colombier {
8830a2a9dafSDavid du Colombier Ctlr *ctlr = ether->ctlr;
8840a2a9dafSDavid du Colombier
8850a2a9dafSDavid du Colombier vt6102detach(ctlr);
8860a2a9dafSDavid du Colombier }
8870a2a9dafSDavid du Colombier
8889b12e100SDavid du Colombier static int
vt6102reset(Ctlr * ctlr)8899b12e100SDavid du Colombier vt6102reset(Ctlr* ctlr)
8909b12e100SDavid du Colombier {
8919b12e100SDavid du Colombier MiiPhy *phy;
8929b12e100SDavid du Colombier int i, r, timeo;
8939b12e100SDavid du Colombier
8949b12e100SDavid du Colombier if(vt6102detach(ctlr) < 0)
8959b12e100SDavid du Colombier return -1;
8969b12e100SDavid du Colombier
8979b12e100SDavid du Colombier /*
8989b12e100SDavid du Colombier * Load the MAC address into the PAR[01]
8999b12e100SDavid du Colombier * registers.
9009b12e100SDavid du Colombier */
9019b12e100SDavid du Colombier r = csr8r(ctlr, Eecsr);
9029b12e100SDavid du Colombier csr8w(ctlr, Eecsr, Autold|r);
9039b12e100SDavid du Colombier for(timeo = 0; timeo < 100; timeo++){
9049b12e100SDavid du Colombier if(!(csr8r(ctlr, Cr) & Autold))
9059b12e100SDavid du Colombier break;
9069b12e100SDavid du Colombier microdelay(1);
9079b12e100SDavid du Colombier }
9089b12e100SDavid du Colombier if(timeo >= 100)
9099b12e100SDavid du Colombier return -1;
9109b12e100SDavid du Colombier
9119b12e100SDavid du Colombier for(i = 0; i < Eaddrlen; i++)
9129b12e100SDavid du Colombier ctlr->par[i] = csr8r(ctlr, Par0+i);
9139b12e100SDavid du Colombier
9149b12e100SDavid du Colombier /*
9159b12e100SDavid du Colombier * Configure DMA and Rx/Tx thresholds.
9169b12e100SDavid du Colombier * If the Rx/Tx threshold bits in Bcr[01] are 0 then
9179b12e100SDavid du Colombier * the thresholds are determined by Rcr/Tcr.
9189b12e100SDavid du Colombier */
9199b12e100SDavid du Colombier r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
9209b12e100SDavid du Colombier csr8w(ctlr, Bcr0, r|Crft64|Dma64);
9219b12e100SDavid du Colombier r = csr8r(ctlr, Bcr1) & ~CtftMASK;
9229b12e100SDavid du Colombier csr8w(ctlr, Bcr1, r|ctlr->tft);
9239b12e100SDavid du Colombier
9249b12e100SDavid du Colombier r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
9259b12e100SDavid du Colombier csr8w(ctlr, Rcr, r|Ab|Am);
9267e254d1cSDavid du Colombier csr32w(ctlr, Mcfilt0, ~0UL); /* accept all multicast */
9277e254d1cSDavid du Colombier csr32w(ctlr, Mcfilt1, ~0UL);
9289b12e100SDavid du Colombier
9299b12e100SDavid du Colombier r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
9309b12e100SDavid du Colombier csr8w(ctlr, Tcr, r);
9319b12e100SDavid du Colombier
9329b12e100SDavid du Colombier /*
9339b12e100SDavid du Colombier * Link management.
9349b12e100SDavid du Colombier */
9359b12e100SDavid du Colombier if((ctlr->mii = malloc(sizeof(Mii))) == nil)
9369b12e100SDavid du Colombier return -1;
9379b12e100SDavid du Colombier ctlr->mii->mir = vt6102miimir;
9389b12e100SDavid du Colombier ctlr->mii->miw = vt6102miimiw;
9399b12e100SDavid du Colombier ctlr->mii->ctlr = ctlr;
9409b12e100SDavid du Colombier
9419b12e100SDavid du Colombier if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
9429b12e100SDavid du Colombier free(ctlr->mii);
9439b12e100SDavid du Colombier ctlr->mii = nil;
9449b12e100SDavid du Colombier return -1;
9459b12e100SDavid du Colombier }
9464de34a7eSDavid du Colombier // print("oui %X phyno %d\n", phy->oui, phy->phyno);
94726ad7229SDavid du Colombier USED(phy);
9489b12e100SDavid du Colombier
9499b12e100SDavid du Colombier //miiane(ctlr->mii, ~0, ~0, ~0);
9509b12e100SDavid du Colombier
9519b12e100SDavid du Colombier return 0;
9529b12e100SDavid du Colombier }
9539b12e100SDavid du Colombier
9549b12e100SDavid du Colombier static void
vt6102pci(void)9559b12e100SDavid du Colombier vt6102pci(void)
9569b12e100SDavid du Colombier {
9579b12e100SDavid du Colombier Pcidev *p;
9589b12e100SDavid du Colombier Ctlr *ctlr;
9599b12e100SDavid du Colombier int cls, port;
9609b12e100SDavid du Colombier
9619b12e100SDavid du Colombier p = nil;
9629b12e100SDavid du Colombier while(p = pcimatch(p, 0, 0)){
963cbc16779SDavid du Colombier if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
9649b12e100SDavid du Colombier continue;
9659b12e100SDavid du Colombier
9669b12e100SDavid du Colombier switch((p->did<<16)|p->vid){
9679b12e100SDavid du Colombier default:
9689b12e100SDavid du Colombier continue;
9699b12e100SDavid du Colombier case (0x3065<<16)|0x1106: /* Rhine II */
970ea15f0ccSDavid du Colombier case (0x3106<<16)|0x1106: /* Rhine III */
9719b12e100SDavid du Colombier break;
9729b12e100SDavid du Colombier }
9739b12e100SDavid du Colombier
9749b12e100SDavid du Colombier port = p->mem[0].bar & ~0x01;
9759b12e100SDavid du Colombier if(ioalloc(port, p->mem[0].size, 0, "vt6102") < 0){
9769b12e100SDavid du Colombier print("vt6102: port 0x%uX in use\n", port);
9779b12e100SDavid du Colombier continue;
9789b12e100SDavid du Colombier }
9799b12e100SDavid du Colombier ctlr = malloc(sizeof(Ctlr));
980aa72973aSDavid du Colombier if(ctlr == nil) {
981aa72973aSDavid du Colombier iofree(port);
982aa72973aSDavid du Colombier error(Enomem);
983aa72973aSDavid du Colombier }
9849b12e100SDavid du Colombier ctlr->port = port;
9859b12e100SDavid du Colombier ctlr->pcidev = p;
9869b12e100SDavid du Colombier ctlr->id = (p->did<<16)|p->vid;
9879b12e100SDavid du Colombier if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF)
9889b12e100SDavid du Colombier cls = 0x10;
9899b12e100SDavid du Colombier ctlr->cls = cls*4;
9909b12e100SDavid du Colombier if(ctlr->cls < sizeof(Ds)){
9919b12e100SDavid du Colombier print("vt6102: cls %d < sizeof(Ds)\n", ctlr->cls);
992cbc16779SDavid du Colombier iofree(port);
9939b12e100SDavid du Colombier free(ctlr);
9949b12e100SDavid du Colombier continue;
9959b12e100SDavid du Colombier }
9969b12e100SDavid du Colombier ctlr->tft = Ctft64;
9979b12e100SDavid du Colombier
9989b12e100SDavid du Colombier if(vt6102reset(ctlr)){
999cbc16779SDavid du Colombier iofree(port);
10009b12e100SDavid du Colombier free(ctlr);
10019b12e100SDavid du Colombier continue;
10029b12e100SDavid du Colombier }
10039b12e100SDavid du Colombier pcisetbme(p);
10049b12e100SDavid du Colombier
10059b12e100SDavid du Colombier if(vt6102ctlrhead != nil)
10069b12e100SDavid du Colombier vt6102ctlrtail->next = ctlr;
10079b12e100SDavid du Colombier else
10089b12e100SDavid du Colombier vt6102ctlrhead = ctlr;
10099b12e100SDavid du Colombier vt6102ctlrtail = ctlr;
10109b12e100SDavid du Colombier }
10119b12e100SDavid du Colombier }
10129b12e100SDavid du Colombier
10139b12e100SDavid du Colombier static int
vt6102pnp(Ether * edev)10149b12e100SDavid du Colombier vt6102pnp(Ether* edev)
10159b12e100SDavid du Colombier {
10169b12e100SDavid du Colombier Ctlr *ctlr;
10179b12e100SDavid du Colombier
10189b12e100SDavid du Colombier if(vt6102ctlrhead == nil)
10199b12e100SDavid du Colombier vt6102pci();
10209b12e100SDavid du Colombier
10219b12e100SDavid du Colombier /*
10229b12e100SDavid du Colombier * Any adapter matches if no edev->port is supplied,
10239b12e100SDavid du Colombier * otherwise the ports must match.
10249b12e100SDavid du Colombier */
10259b12e100SDavid du Colombier for(ctlr = vt6102ctlrhead; ctlr != nil; ctlr = ctlr->next){
10269b12e100SDavid du Colombier if(ctlr->active)
10279b12e100SDavid du Colombier continue;
10289b12e100SDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
10299b12e100SDavid du Colombier ctlr->active = 1;
10309b12e100SDavid du Colombier break;
10319b12e100SDavid du Colombier }
10329b12e100SDavid du Colombier }
10339b12e100SDavid du Colombier if(ctlr == nil)
10349b12e100SDavid du Colombier return -1;
10359b12e100SDavid du Colombier
10369b12e100SDavid du Colombier edev->ctlr = ctlr;
10379b12e100SDavid du Colombier edev->port = ctlr->port;
10389b12e100SDavid du Colombier edev->irq = ctlr->pcidev->intl;
10399b12e100SDavid du Colombier edev->tbdf = ctlr->pcidev->tbdf;
10409b12e100SDavid du Colombier edev->mbps = 100;
10419b12e100SDavid du Colombier memmove(edev->ea, ctlr->par, Eaddrlen);
10429b12e100SDavid du Colombier
10439b12e100SDavid du Colombier /*
10449b12e100SDavid du Colombier * Linkage to the generic ethernet driver.
10459b12e100SDavid du Colombier */
10469b12e100SDavid du Colombier edev->attach = vt6102attach;
10479b12e100SDavid du Colombier edev->transmit = vt6102transmit;
10489b12e100SDavid du Colombier edev->interrupt = vt6102interrupt;
10499b12e100SDavid du Colombier edev->ifstat = vt6102ifstat;
10500a2a9dafSDavid du Colombier edev->shutdown = vt6102shutdown;
10519b12e100SDavid du Colombier edev->ctl = nil;
10529b12e100SDavid du Colombier
10539b12e100SDavid du Colombier edev->arg = edev;
10549b12e100SDavid du Colombier edev->promiscuous = vt6102promiscuous;
10559b12e100SDavid du Colombier edev->multicast = vt6102multicast;
10569b12e100SDavid du Colombier
10579b12e100SDavid du Colombier return 0;
10589b12e100SDavid du Colombier }
10599b12e100SDavid du Colombier
10609b12e100SDavid du Colombier void
ethervt6102link(void)10619b12e100SDavid du Colombier ethervt6102link(void)
10629b12e100SDavid du Colombier {
10639b12e100SDavid du Colombier addethercard("vt6102", vt6102pnp);
10649b12e100SDavid du Colombier addethercard("rhine", vt6102pnp);
10659b12e100SDavid du Colombier }
1066