19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
39a747e4fSDavid du Colombier * To do:
49a747e4fSDavid du Colombier * thresholds;
59a747e4fSDavid du Colombier * ring sizing;
69a747e4fSDavid du Colombier * handle more error conditions;
79a747e4fSDavid du Colombier * tidy setup packet mess;
89a747e4fSDavid du Colombier * push initialisation back to attach;
99a747e4fSDavid du Colombier * full SROM decoding.
109a747e4fSDavid du Colombier */
119a747e4fSDavid du Colombier #include "u.h"
129a747e4fSDavid du Colombier #include "../port/lib.h"
139a747e4fSDavid du Colombier #include "mem.h"
149a747e4fSDavid du Colombier #include "dat.h"
159a747e4fSDavid du Colombier #include "fns.h"
169a747e4fSDavid du Colombier #include "io.h"
179a747e4fSDavid du Colombier #include "../port/error.h"
189a747e4fSDavid du Colombier #include "../port/netif.h"
199a747e4fSDavid du Colombier
209a747e4fSDavid du Colombier #include "etherif.h"
219a747e4fSDavid du Colombier
229a747e4fSDavid du Colombier #define DEBUG (0)
239a747e4fSDavid du Colombier #define debug if(DEBUG)print
249a747e4fSDavid du Colombier
259a747e4fSDavid du Colombier enum {
269a747e4fSDavid du Colombier Nrde = 64,
279a747e4fSDavid du Colombier Ntde = 64,
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier
309a747e4fSDavid du Colombier #define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4)
319a747e4fSDavid du Colombier
329a747e4fSDavid du Colombier enum { /* CRS0 - Bus Mode */
339a747e4fSDavid du Colombier Swr = 0x00000001, /* Software Reset */
349a747e4fSDavid du Colombier Bar = 0x00000002, /* Bus Arbitration */
359a747e4fSDavid du Colombier Dsl = 0x0000007C, /* Descriptor Skip Length (field) */
369a747e4fSDavid du Colombier Ble = 0x00000080, /* Big/Little Endian */
379a747e4fSDavid du Colombier Pbl = 0x00003F00, /* Programmable Burst Length (field) */
389a747e4fSDavid du Colombier Cal = 0x0000C000, /* Cache Alignment (field) */
399a747e4fSDavid du Colombier Cal8 = 0x00004000, /* 8 longword boundary alignment */
409a747e4fSDavid du Colombier Cal16 = 0x00008000, /* 16 longword boundary alignment */
419a747e4fSDavid du Colombier Cal32 = 0x0000C000, /* 32 longword boundary alignment */
429a747e4fSDavid du Colombier Tap = 0x000E0000, /* Transmit Automatic Polling (field) */
439a747e4fSDavid du Colombier Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */
449a747e4fSDavid du Colombier Rml = 0x00200000, /* Read Multiple */
459a747e4fSDavid du Colombier };
469a747e4fSDavid du Colombier
479a747e4fSDavid du Colombier enum { /* CSR[57] - Status and Interrupt Enable */
489a747e4fSDavid du Colombier Ti = 0x00000001, /* Transmit Interrupt */
499a747e4fSDavid du Colombier Tps = 0x00000002, /* Transmit Process Stopped */
509a747e4fSDavid du Colombier Tu = 0x00000004, /* Transmit buffer Unavailable */
519a747e4fSDavid du Colombier Tjt = 0x00000008, /* Transmit Jabber Timeout */
529a747e4fSDavid du Colombier Unf = 0x00000020, /* transmit UNderFlow */
539a747e4fSDavid du Colombier Ri = 0x00000040, /* Receive Interrupt */
549a747e4fSDavid du Colombier Ru = 0x00000080, /* Receive buffer Unavailable */
559a747e4fSDavid du Colombier Rps = 0x00000100, /* Receive Process Stopped */
569a747e4fSDavid du Colombier Rwt = 0x00000200, /* Receive Watchdog Timeout */
579a747e4fSDavid du Colombier Eti = 0x00000400, /* Early Transmit Interrupt */
589a747e4fSDavid du Colombier Gte = 0x00000800, /* General purpose Timer Expired */
599a747e4fSDavid du Colombier Fbe = 0x00002000, /* Fatal Bus Error */
609a747e4fSDavid du Colombier Ais = 0x00008000, /* Abnormal Interrupt Summary */
619a747e4fSDavid du Colombier Nis = 0x00010000, /* Normal Interrupt Summary */
629a747e4fSDavid du Colombier Rs = 0x000E0000, /* Receive process State (field) */
639a747e4fSDavid du Colombier Ts = 0x00700000, /* Transmit process State (field) */
649a747e4fSDavid du Colombier Eb = 0x03800000, /* Error bits */
659a747e4fSDavid du Colombier };
669a747e4fSDavid du Colombier
679a747e4fSDavid du Colombier enum { /* CSR6 - Operating Mode */
689a747e4fSDavid du Colombier Hp = 0x00000001, /* Hash/Perfect receive filtering mode */
699a747e4fSDavid du Colombier Sr = 0x00000002, /* Start/stop Receive */
709a747e4fSDavid du Colombier Ho = 0x00000004, /* Hash-Only filtering mode */
719a747e4fSDavid du Colombier Pb = 0x00000008, /* Pass Bad frames */
729a747e4fSDavid du Colombier If = 0x00000010, /* Inverse Filtering */
739a747e4fSDavid du Colombier Sb = 0x00000020, /* Start/stop Backoff counter */
749a747e4fSDavid du Colombier Pr = 0x00000040, /* Promiscuous Mode */
759a747e4fSDavid du Colombier Pm = 0x00000080, /* Pass all Multicast */
769a747e4fSDavid du Colombier Fd = 0x00000200, /* Full Duplex mode */
779a747e4fSDavid du Colombier Om = 0x00000C00, /* Operating Mode (field) */
789a747e4fSDavid du Colombier Fc = 0x00001000, /* Force Collision */
799a747e4fSDavid du Colombier St = 0x00002000, /* Start/stop Transmission Command */
809a747e4fSDavid du Colombier Tr = 0x0000C000, /* ThReshold control bits (field) */
819a747e4fSDavid du Colombier Tr128 = 0x00000000,
829a747e4fSDavid du Colombier Tr256 = 0x00004000,
839a747e4fSDavid du Colombier Tr512 = 0x00008000,
849a747e4fSDavid du Colombier Tr1024 = 0x0000C000,
859a747e4fSDavid du Colombier Ca = 0x00020000, /* CApture effect enable */
869a747e4fSDavid du Colombier Ps = 0x00040000, /* Port Select */
879a747e4fSDavid du Colombier Hbd = 0x00080000, /* HeartBeat Disable */
889a747e4fSDavid du Colombier Imm = 0x00100000, /* IMMediate mode */
899a747e4fSDavid du Colombier Sf = 0x00200000, /* Store and Forward */
909a747e4fSDavid du Colombier Ttm = 0x00400000, /* Transmit Threshold Mode */
919a747e4fSDavid du Colombier Pcs = 0x00800000, /* PCS function */
929a747e4fSDavid du Colombier Scr = 0x01000000, /* SCRambler mode */
939a747e4fSDavid du Colombier Mbo = 0x02000000, /* Must Be One */
949a747e4fSDavid du Colombier Ra = 0x40000000, /* Receive All */
959a747e4fSDavid du Colombier Sc = 0x80000000, /* Special Capture effect enable */
969a747e4fSDavid du Colombier
979a747e4fSDavid du Colombier TrMODE = Tr512, /* default transmission threshold */
989a747e4fSDavid du Colombier };
999a747e4fSDavid du Colombier
1009a747e4fSDavid du Colombier enum { /* CSR9 - ROM and MII Management */
1019a747e4fSDavid du Colombier Scs = 0x00000001, /* serial ROM chip select */
1029a747e4fSDavid du Colombier Sclk = 0x00000002, /* serial ROM clock */
1039a747e4fSDavid du Colombier Sdi = 0x00000004, /* serial ROM data in */
1049a747e4fSDavid du Colombier Sdo = 0x00000008, /* serial ROM data out */
1059a747e4fSDavid du Colombier Ss = 0x00000800, /* serial ROM select */
1069a747e4fSDavid du Colombier Wr = 0x00002000, /* write */
1079a747e4fSDavid du Colombier Rd = 0x00004000, /* read */
1089a747e4fSDavid du Colombier
1099a747e4fSDavid du Colombier Mdc = 0x00010000, /* MII management clock */
1109a747e4fSDavid du Colombier Mdo = 0x00020000, /* MII management write data */
1119a747e4fSDavid du Colombier Mii = 0x00040000, /* MII management operation mode (W) */
1129a747e4fSDavid du Colombier Mdi = 0x00080000, /* MII management data in */
1139a747e4fSDavid du Colombier };
1149a747e4fSDavid du Colombier
1159a747e4fSDavid du Colombier enum { /* CSR12 - General-Purpose Port */
1169a747e4fSDavid du Colombier Gpc = 0x00000100, /* General Purpose Control */
1179a747e4fSDavid du Colombier };
1189a747e4fSDavid du Colombier
1199a747e4fSDavid du Colombier typedef struct Des {
1209a747e4fSDavid du Colombier int status;
1219a747e4fSDavid du Colombier int control;
1229a747e4fSDavid du Colombier ulong addr;
1239a747e4fSDavid du Colombier Block* bp;
1249a747e4fSDavid du Colombier } Des;
1259a747e4fSDavid du Colombier
1269a747e4fSDavid du Colombier enum { /* status */
1279a747e4fSDavid du Colombier Of = 0x00000001, /* Rx: OverFlow */
1289a747e4fSDavid du Colombier Ce = 0x00000002, /* Rx: CRC Error */
1299a747e4fSDavid du Colombier Db = 0x00000004, /* Rx: Dribbling Bit */
1309a747e4fSDavid du Colombier Re = 0x00000008, /* Rx: Report on MII Error */
1319a747e4fSDavid du Colombier Rw = 0x00000010, /* Rx: Receive Watchdog */
1329a747e4fSDavid du Colombier Ft = 0x00000020, /* Rx: Frame Type */
1339a747e4fSDavid du Colombier Cs = 0x00000040, /* Rx: Collision Seen */
1349a747e4fSDavid du Colombier Tl = 0x00000080, /* Rx: Frame too Long */
1359a747e4fSDavid du Colombier Ls = 0x00000100, /* Rx: Last deScriptor */
1369a747e4fSDavid du Colombier Fs = 0x00000200, /* Rx: First deScriptor */
1379a747e4fSDavid du Colombier Mf = 0x00000400, /* Rx: Multicast Frame */
1389a747e4fSDavid du Colombier Rf = 0x00000800, /* Rx: Runt Frame */
1399a747e4fSDavid du Colombier Dt = 0x00003000, /* Rx: Data Type (field) */
1409a747e4fSDavid du Colombier De = 0x00004000, /* Rx: Descriptor Error */
1419a747e4fSDavid du Colombier Fl = 0x3FFF0000, /* Rx: Frame Length (field) */
1429a747e4fSDavid du Colombier Ff = 0x40000000, /* Rx: Filtering Fail */
1439a747e4fSDavid du Colombier
1449a747e4fSDavid du Colombier Def = 0x00000001, /* Tx: DEFerred */
1459a747e4fSDavid du Colombier Uf = 0x00000002, /* Tx: UnderFlow error */
1469a747e4fSDavid du Colombier Lf = 0x00000004, /* Tx: Link Fail report */
1479a747e4fSDavid du Colombier Cc = 0x00000078, /* Tx: Collision Count (field) */
1489a747e4fSDavid du Colombier Hf = 0x00000080, /* Tx: Heartbeat Fail */
1499a747e4fSDavid du Colombier Ec = 0x00000100, /* Tx: Excessive Collisions */
1509a747e4fSDavid du Colombier Lc = 0x00000200, /* Tx: Late Collision */
1519a747e4fSDavid du Colombier Nc = 0x00000400, /* Tx: No Carrier */
1529a747e4fSDavid du Colombier Lo = 0x00000800, /* Tx: LOss of carrier */
1539a747e4fSDavid du Colombier To = 0x00004000, /* Tx: Transmission jabber timeOut */
1549a747e4fSDavid du Colombier
1559a747e4fSDavid du Colombier Es = 0x00008000, /* [RT]x: Error Summary */
1569a747e4fSDavid du Colombier Own = 0x80000000, /* [RT]x: OWN bit */
1579a747e4fSDavid du Colombier };
1589a747e4fSDavid du Colombier
1599a747e4fSDavid du Colombier enum { /* control */
1609a747e4fSDavid du Colombier Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */
1619a747e4fSDavid du Colombier Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */
1629a747e4fSDavid du Colombier
1639a747e4fSDavid du Colombier Ch = 0x01000000, /* [RT]x: second address CHained */
1649a747e4fSDavid du Colombier Er = 0x02000000, /* [RT]x: End of Ring */
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier Ft0 = 0x00400000, /* Tx: Filtering Type 0 */
1679a747e4fSDavid du Colombier Dpd = 0x00800000, /* Tx: Disabled PaDding */
1689a747e4fSDavid du Colombier Ac = 0x04000000, /* Tx: Add CRC disable */
1699a747e4fSDavid du Colombier Set = 0x08000000, /* Tx: SETup packet */
1709a747e4fSDavid du Colombier Ft1 = 0x10000000, /* Tx: Filtering Type 1 */
1719a747e4fSDavid du Colombier Fseg = 0x20000000, /* Tx: First SEGment */
1729a747e4fSDavid du Colombier Lseg = 0x40000000, /* Tx: Last SEGment */
1739a747e4fSDavid du Colombier Ic = 0x80000000, /* Tx: Interrupt on Completion */
1749a747e4fSDavid du Colombier };
1759a747e4fSDavid du Colombier
1769a747e4fSDavid du Colombier enum { /* PHY registers */
1779a747e4fSDavid du Colombier Bmcr = 0, /* Basic Mode Control */
1789a747e4fSDavid du Colombier Bmsr = 1, /* Basic Mode Status */
1799a747e4fSDavid du Colombier Phyidr1 = 2, /* PHY Identifier #1 */
1809a747e4fSDavid du Colombier Phyidr2 = 3, /* PHY Identifier #2 */
1819a747e4fSDavid du Colombier Anar = 4, /* Auto-Negotiation Advertisment */
1829a747e4fSDavid du Colombier Anlpar = 5, /* Auto-Negotiation Link Partner Ability */
1839a747e4fSDavid du Colombier Aner = 6, /* Auto-Negotiation Expansion */
1849a747e4fSDavid du Colombier };
1859a747e4fSDavid du Colombier
1869a747e4fSDavid du Colombier enum { /* Variants */
1879a747e4fSDavid du Colombier Tulip0 = (0x0009<<16)|0x1011,
1889a747e4fSDavid du Colombier Tulip3 = (0x0019<<16)|0x1011,
1899a747e4fSDavid du Colombier Pnic = (0x0002<<16)|0x11AD,
1909a747e4fSDavid du Colombier Pnic2 = (0xC115<<16)|0x11AD,
1919a747e4fSDavid du Colombier };
1929a747e4fSDavid du Colombier
1939a747e4fSDavid du Colombier typedef struct Ctlr Ctlr;
1949a747e4fSDavid du Colombier typedef struct Ctlr {
1959a747e4fSDavid du Colombier int port;
1969a747e4fSDavid du Colombier Pcidev* pcidev;
1979a747e4fSDavid du Colombier Ctlr* next;
1989a747e4fSDavid du Colombier int active;
1999a747e4fSDavid du Colombier int id; /* (pcidev->did<<16)|pcidev->vid */
2009a747e4fSDavid du Colombier
2019a747e4fSDavid du Colombier uchar* srom;
2029a747e4fSDavid du Colombier int sromsz; /* address size in bits */
2039a747e4fSDavid du Colombier uchar* sromea; /* MAC address */
2049a747e4fSDavid du Colombier uchar* leaf;
2059a747e4fSDavid du Colombier int sct; /* selected connection type */
2069a747e4fSDavid du Colombier int k; /* info block count */
2079a747e4fSDavid du Colombier uchar* infoblock[16];
2089a747e4fSDavid du Colombier int sctk; /* sct block index */
2099a747e4fSDavid du Colombier int curk; /* current block index */
2109a747e4fSDavid du Colombier uchar* type5block;
2119a747e4fSDavid du Colombier
2129a747e4fSDavid du Colombier int phy[32]; /* logical to physical map */
2139a747e4fSDavid du Colombier int phyreset; /* reset bitmap */
2149a747e4fSDavid du Colombier int curphyad;
2159a747e4fSDavid du Colombier int fdx;
2169a747e4fSDavid du Colombier int ttm;
2179a747e4fSDavid du Colombier
2189a747e4fSDavid du Colombier uchar fd; /* option */
2199a747e4fSDavid du Colombier int medium; /* option */
2209a747e4fSDavid du Colombier
2219a747e4fSDavid du Colombier int csr6; /* CSR6 - operating mode */
2229a747e4fSDavid du Colombier int mask; /* CSR[57] - interrupt mask */
2239a747e4fSDavid du Colombier int mbps;
2249a747e4fSDavid du Colombier
2259a747e4fSDavid du Colombier Lock lock;
2269a747e4fSDavid du Colombier
2279a747e4fSDavid du Colombier Des* rdr; /* receive descriptor ring */
2289a747e4fSDavid du Colombier int nrdr; /* size of rdr */
2299a747e4fSDavid du Colombier int rdrx; /* index into rdr */
2309a747e4fSDavid du Colombier
2319a747e4fSDavid du Colombier Lock tlock;
2329a747e4fSDavid du Colombier Des* tdr; /* transmit descriptor ring */
2339a747e4fSDavid du Colombier int ntdr; /* size of tdr */
2349a747e4fSDavid du Colombier int tdrh; /* host index into tdr */
2359a747e4fSDavid du Colombier int tdri; /* interface index into tdr */
2369a747e4fSDavid du Colombier int ntq; /* descriptors active */
2379a747e4fSDavid du Colombier int ntqmax;
2389a747e4fSDavid du Colombier Block* setupbp;
2399a747e4fSDavid du Colombier
2409a747e4fSDavid du Colombier ulong of; /* receive statistics */
2419a747e4fSDavid du Colombier ulong ce;
2429a747e4fSDavid du Colombier ulong cs;
2439a747e4fSDavid du Colombier ulong tl;
2449a747e4fSDavid du Colombier ulong rf;
2459a747e4fSDavid du Colombier ulong de;
2469a747e4fSDavid du Colombier
2479a747e4fSDavid du Colombier ulong ru;
2489a747e4fSDavid du Colombier ulong rps;
2499a747e4fSDavid du Colombier ulong rwt;
2509a747e4fSDavid du Colombier
2519a747e4fSDavid du Colombier ulong uf; /* transmit statistics */
2529a747e4fSDavid du Colombier ulong ec;
2539a747e4fSDavid du Colombier ulong lc;
2549a747e4fSDavid du Colombier ulong nc;
2559a747e4fSDavid du Colombier ulong lo;
2569a747e4fSDavid du Colombier ulong to;
2579a747e4fSDavid du Colombier
2589a747e4fSDavid du Colombier ulong tps;
2599a747e4fSDavid du Colombier ulong tu;
2609a747e4fSDavid du Colombier ulong tjt;
2619a747e4fSDavid du Colombier ulong unf;
2629a747e4fSDavid du Colombier } Ctlr;
2639a747e4fSDavid du Colombier
2649a747e4fSDavid du Colombier static Ctlr* ctlrhead;
2659a747e4fSDavid du Colombier static Ctlr* ctlrtail;
2669a747e4fSDavid du Colombier
2679a747e4fSDavid du Colombier #define csr32r(c, r) (inl((c)->port+((r)*8)))
2689a747e4fSDavid du Colombier #define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l)))
2699a747e4fSDavid du Colombier
2709a747e4fSDavid du Colombier static void
promiscuous(void * arg,int on)2719a747e4fSDavid du Colombier promiscuous(void* arg, int on)
2729a747e4fSDavid du Colombier {
2739a747e4fSDavid du Colombier Ctlr *ctlr;
2749a747e4fSDavid du Colombier
2759a747e4fSDavid du Colombier ctlr = ((Ether*)arg)->ctlr;
2769a747e4fSDavid du Colombier ilock(&ctlr->lock);
2779a747e4fSDavid du Colombier if(on)
2789a747e4fSDavid du Colombier ctlr->csr6 |= Pr;
2799a747e4fSDavid du Colombier else
2809a747e4fSDavid du Colombier ctlr->csr6 &= ~Pr;
2819a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
2829a747e4fSDavid du Colombier iunlock(&ctlr->lock);
2839a747e4fSDavid du Colombier }
2849a747e4fSDavid du Colombier
2859a747e4fSDavid du Colombier static void
attach(Ether * ether)2869a747e4fSDavid du Colombier attach(Ether* ether)
2879a747e4fSDavid du Colombier {
2889a747e4fSDavid du Colombier Ctlr *ctlr;
2899a747e4fSDavid du Colombier
2909a747e4fSDavid du Colombier ctlr = ether->ctlr;
2919a747e4fSDavid du Colombier ilock(&ctlr->lock);
2929a747e4fSDavid du Colombier if(!(ctlr->csr6 & Sr)){
2939a747e4fSDavid du Colombier ctlr->csr6 |= Sr;
2949a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
2959a747e4fSDavid du Colombier }
2969a747e4fSDavid du Colombier iunlock(&ctlr->lock);
2979a747e4fSDavid du Colombier }
2989a747e4fSDavid du Colombier
2999a747e4fSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)3009a747e4fSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
3019a747e4fSDavid du Colombier {
3029a747e4fSDavid du Colombier Ctlr *ctlr;
3039a747e4fSDavid du Colombier char *buf, *p;
3049a747e4fSDavid du Colombier int i, l, len;
3059a747e4fSDavid du Colombier
3069a747e4fSDavid du Colombier ctlr = ether->ctlr;
3079a747e4fSDavid du Colombier
3089a747e4fSDavid du Colombier ether->crcs = ctlr->ce;
3099a747e4fSDavid du Colombier ether->frames = ctlr->rf+ctlr->cs;
3109a747e4fSDavid du Colombier ether->buffs = ctlr->de+ctlr->tl;
3119a747e4fSDavid du Colombier ether->overflows = ctlr->of;
3129a747e4fSDavid du Colombier
3139a747e4fSDavid du Colombier if(n == 0)
3149a747e4fSDavid du Colombier return 0;
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier p = malloc(READSTR);
3179a747e4fSDavid du Colombier l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
3189a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
3199a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
3209a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
3219a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
3229a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
3239a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
3249a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
3259a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
3269a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
3279a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
3289a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
3299a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
3309a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
3319a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
3329a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
3339a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
3349a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
3359a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
3369a747e4fSDavid du Colombier ctlr->to);
3379a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
3389a747e4fSDavid du Colombier ctlr->csr6);
3399a747e4fSDavid du Colombier snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
3409a747e4fSDavid du Colombier ctlr->ntqmax = 0;
3419a747e4fSDavid du Colombier buf = a;
3429a747e4fSDavid du Colombier len = readstr(offset, buf, n, p);
3439a747e4fSDavid du Colombier if(offset > l)
3449a747e4fSDavid du Colombier offset -= l;
3459a747e4fSDavid du Colombier else
3469a747e4fSDavid du Colombier offset = 0;
3479a747e4fSDavid du Colombier buf += len;
3489a747e4fSDavid du Colombier n -= len;
3499a747e4fSDavid du Colombier
3509a747e4fSDavid du Colombier l = snprint(p, READSTR, "srom:");
3519a747e4fSDavid du Colombier for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
3529a747e4fSDavid du Colombier if(i && ((i & 0x0F) == 0))
3539a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
3549a747e4fSDavid du Colombier l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
3559a747e4fSDavid du Colombier }
3569a747e4fSDavid du Colombier
3579a747e4fSDavid du Colombier snprint(p+l, READSTR-l, "\n");
3589a747e4fSDavid du Colombier len += readstr(offset, buf, n, p);
3599a747e4fSDavid du Colombier free(p);
3609a747e4fSDavid du Colombier
3619a747e4fSDavid du Colombier return len;
3629a747e4fSDavid du Colombier }
3639a747e4fSDavid du Colombier
3649a747e4fSDavid du Colombier static void
txstart(Ether * ether)3659a747e4fSDavid du Colombier txstart(Ether* ether)
3669a747e4fSDavid du Colombier {
3679a747e4fSDavid du Colombier Ctlr *ctlr;
3689a747e4fSDavid du Colombier Block *bp;
3699a747e4fSDavid du Colombier Des *des;
3709a747e4fSDavid du Colombier int control;
3719a747e4fSDavid du Colombier
3729a747e4fSDavid du Colombier ctlr = ether->ctlr;
3739a747e4fSDavid du Colombier while(ctlr->ntq < (ctlr->ntdr-1)){
3749a747e4fSDavid du Colombier if(ctlr->setupbp){
3759a747e4fSDavid du Colombier bp = ctlr->setupbp;
3769a747e4fSDavid du Colombier ctlr->setupbp = 0;
3779a747e4fSDavid du Colombier control = Ic|Set|BLEN(bp);
3789a747e4fSDavid du Colombier }
3799a747e4fSDavid du Colombier else{
3809a747e4fSDavid du Colombier bp = qget(ether->oq);
3819a747e4fSDavid du Colombier if(bp == nil)
3829a747e4fSDavid du Colombier break;
3839a747e4fSDavid du Colombier control = Ic|Lseg|Fseg|BLEN(bp);
3849a747e4fSDavid du Colombier }
3859a747e4fSDavid du Colombier
3869a747e4fSDavid du Colombier ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
3879a747e4fSDavid du Colombier des = &ctlr->tdr[ctlr->tdrh];
3889a747e4fSDavid du Colombier des->bp = bp;
3899a747e4fSDavid du Colombier des->addr = PCIWADDR(bp->rp);
3909a747e4fSDavid du Colombier des->control |= control;
3919a747e4fSDavid du Colombier ctlr->ntq++;
3929a747e4fSDavid du Colombier coherence();
3939a747e4fSDavid du Colombier des->status = Own;
3949a747e4fSDavid du Colombier csr32w(ctlr, 1, 0);
3959a747e4fSDavid du Colombier ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
3969a747e4fSDavid du Colombier }
3979a747e4fSDavid du Colombier
3989a747e4fSDavid du Colombier if(ctlr->ntq > ctlr->ntqmax)
3999a747e4fSDavid du Colombier ctlr->ntqmax = ctlr->ntq;
4009a747e4fSDavid du Colombier }
4019a747e4fSDavid du Colombier
4029a747e4fSDavid du Colombier static void
transmit(Ether * ether)4039a747e4fSDavid du Colombier transmit(Ether* ether)
4049a747e4fSDavid du Colombier {
4059a747e4fSDavid du Colombier Ctlr *ctlr;
4069a747e4fSDavid du Colombier
4079a747e4fSDavid du Colombier ctlr = ether->ctlr;
4089a747e4fSDavid du Colombier ilock(&ctlr->tlock);
4099a747e4fSDavid du Colombier txstart(ether);
4109a747e4fSDavid du Colombier iunlock(&ctlr->tlock);
4119a747e4fSDavid du Colombier }
4129a747e4fSDavid du Colombier
4139a747e4fSDavid du Colombier static void
interrupt(Ureg *,void * arg)4149a747e4fSDavid du Colombier interrupt(Ureg*, void* arg)
4159a747e4fSDavid du Colombier {
4169a747e4fSDavid du Colombier Ctlr *ctlr;
4179a747e4fSDavid du Colombier Ether *ether;
4189a747e4fSDavid du Colombier int len, status;
4199a747e4fSDavid du Colombier Des *des;
4209a747e4fSDavid du Colombier Block *bp;
4219a747e4fSDavid du Colombier
4229a747e4fSDavid du Colombier ether = arg;
4239a747e4fSDavid du Colombier ctlr = ether->ctlr;
4249a747e4fSDavid du Colombier
4259a747e4fSDavid du Colombier while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
4269a747e4fSDavid du Colombier /*
4279a747e4fSDavid du Colombier * Acknowledge the interrupts and mask-out
4289a747e4fSDavid du Colombier * the ones that are implicitly handled.
4299a747e4fSDavid du Colombier */
4309a747e4fSDavid du Colombier csr32w(ctlr, 5, status);
4319a747e4fSDavid du Colombier status &= (ctlr->mask & ~(Nis|Ti));
4329a747e4fSDavid du Colombier
4339a747e4fSDavid du Colombier if(status & Ais){
4349a747e4fSDavid du Colombier if(status & Tps)
4359a747e4fSDavid du Colombier ctlr->tps++;
4369a747e4fSDavid du Colombier if(status & Tu)
4379a747e4fSDavid du Colombier ctlr->tu++;
4389a747e4fSDavid du Colombier if(status & Tjt)
4399a747e4fSDavid du Colombier ctlr->tjt++;
4409a747e4fSDavid du Colombier if(status & Ru)
4419a747e4fSDavid du Colombier ctlr->ru++;
4429a747e4fSDavid du Colombier if(status & Rps)
4439a747e4fSDavid du Colombier ctlr->rps++;
4449a747e4fSDavid du Colombier if(status & Rwt)
4459a747e4fSDavid du Colombier ctlr->rwt++;
4469a747e4fSDavid du Colombier status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
4479a747e4fSDavid du Colombier }
4489a747e4fSDavid du Colombier
4499a747e4fSDavid du Colombier /*
4509a747e4fSDavid du Colombier * Received packets.
4519a747e4fSDavid du Colombier */
4529a747e4fSDavid du Colombier if(status & Ri){
4539a747e4fSDavid du Colombier des = &ctlr->rdr[ctlr->rdrx];
4549a747e4fSDavid du Colombier while(!(des->status & Own)){
4559a747e4fSDavid du Colombier if(des->status & Es){
4569a747e4fSDavid du Colombier if(des->status & Of)
4579a747e4fSDavid du Colombier ctlr->of++;
4589a747e4fSDavid du Colombier if(des->status & Ce)
4599a747e4fSDavid du Colombier ctlr->ce++;
4609a747e4fSDavid du Colombier if(des->status & Cs)
4619a747e4fSDavid du Colombier ctlr->cs++;
4629a747e4fSDavid du Colombier if(des->status & Tl)
4639a747e4fSDavid du Colombier ctlr->tl++;
4649a747e4fSDavid du Colombier if(des->status & Rf)
4659a747e4fSDavid du Colombier ctlr->rf++;
4669a747e4fSDavid du Colombier if(des->status & De)
4679a747e4fSDavid du Colombier ctlr->de++;
4689a747e4fSDavid du Colombier }
4699a747e4fSDavid du Colombier else if(bp = iallocb(Rbsz)){
4709a747e4fSDavid du Colombier len = ((des->status & Fl)>>16)-4;
4719a747e4fSDavid du Colombier des->bp->wp = des->bp->rp+len;
4729a747e4fSDavid du Colombier etheriq(ether, des->bp, 1);
4739a747e4fSDavid du Colombier des->bp = bp;
4749a747e4fSDavid du Colombier des->addr = PCIWADDR(bp->rp);
4759a747e4fSDavid du Colombier }
4769a747e4fSDavid du Colombier
4779a747e4fSDavid du Colombier des->control &= Er;
4789a747e4fSDavid du Colombier des->control |= Rbsz;
4799a747e4fSDavid du Colombier coherence();
4809a747e4fSDavid du Colombier des->status = Own;
4819a747e4fSDavid du Colombier
4829a747e4fSDavid du Colombier ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
4839a747e4fSDavid du Colombier des = &ctlr->rdr[ctlr->rdrx];
4849a747e4fSDavid du Colombier }
4859a747e4fSDavid du Colombier status &= ~Ri;
4869a747e4fSDavid du Colombier }
4879a747e4fSDavid du Colombier
4889a747e4fSDavid du Colombier /*
4899a747e4fSDavid du Colombier * Check the transmit side:
4909a747e4fSDavid du Colombier * check for Transmit Underflow and Adjust
4919a747e4fSDavid du Colombier * the threshold upwards;
4929a747e4fSDavid du Colombier * free any transmitted buffers and try to
4939a747e4fSDavid du Colombier * top-up the ring.
4949a747e4fSDavid du Colombier */
4959a747e4fSDavid du Colombier if(status & Unf){
4969a747e4fSDavid du Colombier ctlr->unf++;
4979a747e4fSDavid du Colombier ilock(&ctlr->lock);
4989a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6 & ~St);
4999a747e4fSDavid du Colombier switch(ctlr->csr6 & Tr){
5009a747e4fSDavid du Colombier case Tr128:
5019a747e4fSDavid du Colombier len = Tr256;
5029a747e4fSDavid du Colombier break;
5039a747e4fSDavid du Colombier case Tr256:
5049a747e4fSDavid du Colombier len = Tr512;
5059a747e4fSDavid du Colombier break;
5069a747e4fSDavid du Colombier case Tr512:
5079a747e4fSDavid du Colombier len = Tr1024;
5089a747e4fSDavid du Colombier break;
5099a747e4fSDavid du Colombier default:
5109a747e4fSDavid du Colombier case Tr1024:
5119a747e4fSDavid du Colombier len = Sf;
5129a747e4fSDavid du Colombier break;
5139a747e4fSDavid du Colombier }
5149a747e4fSDavid du Colombier ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
5159a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
5169a747e4fSDavid du Colombier iunlock(&ctlr->lock);
5179a747e4fSDavid du Colombier csr32w(ctlr, 5, Tps);
5189a747e4fSDavid du Colombier status &= ~(Unf|Tps);
5199a747e4fSDavid du Colombier }
5209a747e4fSDavid du Colombier
5219a747e4fSDavid du Colombier ilock(&ctlr->tlock);
5229a747e4fSDavid du Colombier while(ctlr->ntq){
5239a747e4fSDavid du Colombier des = &ctlr->tdr[ctlr->tdri];
5249a747e4fSDavid du Colombier if(des->status & Own)
5259a747e4fSDavid du Colombier break;
5269a747e4fSDavid du Colombier
5279a747e4fSDavid du Colombier if(des->status & Es){
5289a747e4fSDavid du Colombier if(des->status & Uf)
5299a747e4fSDavid du Colombier ctlr->uf++;
5309a747e4fSDavid du Colombier if(des->status & Ec)
5319a747e4fSDavid du Colombier ctlr->ec++;
5329a747e4fSDavid du Colombier if(des->status & Lc)
5339a747e4fSDavid du Colombier ctlr->lc++;
5349a747e4fSDavid du Colombier if(des->status & Nc)
5359a747e4fSDavid du Colombier ctlr->nc++;
5369a747e4fSDavid du Colombier if(des->status & Lo)
5379a747e4fSDavid du Colombier ctlr->lo++;
5389a747e4fSDavid du Colombier if(des->status & To)
5399a747e4fSDavid du Colombier ctlr->to++;
5409a747e4fSDavid du Colombier ether->oerrs++;
5419a747e4fSDavid du Colombier }
5429a747e4fSDavid du Colombier
5439a747e4fSDavid du Colombier freeb(des->bp);
5449a747e4fSDavid du Colombier des->control &= Er;
5459a747e4fSDavid du Colombier
5469a747e4fSDavid du Colombier ctlr->ntq--;
5479a747e4fSDavid du Colombier ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
5489a747e4fSDavid du Colombier }
5499a747e4fSDavid du Colombier txstart(ether);
5509a747e4fSDavid du Colombier iunlock(&ctlr->tlock);
5519a747e4fSDavid du Colombier
5529a747e4fSDavid du Colombier /*
5539a747e4fSDavid du Colombier * Anything left not catered for?
5549a747e4fSDavid du Colombier */
5559a747e4fSDavid du Colombier if(status)
5569a747e4fSDavid du Colombier panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
5579a747e4fSDavid du Colombier }
5589a747e4fSDavid du Colombier }
5599a747e4fSDavid du Colombier
5609a747e4fSDavid du Colombier static void
ctlrinit(Ether * ether)5619a747e4fSDavid du Colombier ctlrinit(Ether* ether)
5629a747e4fSDavid du Colombier {
5639a747e4fSDavid du Colombier Ctlr *ctlr;
5649a747e4fSDavid du Colombier Des *des;
5659a747e4fSDavid du Colombier Block *bp;
5669a747e4fSDavid du Colombier int i;
5679a747e4fSDavid du Colombier uchar bi[Eaddrlen*2];
5689a747e4fSDavid du Colombier
5699a747e4fSDavid du Colombier ctlr = ether->ctlr;
5709a747e4fSDavid du Colombier
5719a747e4fSDavid du Colombier /*
5729a747e4fSDavid du Colombier * Allocate and initialise the receive ring;
5739a747e4fSDavid du Colombier * allocate and initialise the transmit ring;
5749a747e4fSDavid du Colombier * unmask interrupts and start the transmit side;
5759a747e4fSDavid du Colombier * create and post a setup packet to initialise
5769a747e4fSDavid du Colombier * the physical ethernet address.
5779a747e4fSDavid du Colombier */
5789a747e4fSDavid du Colombier ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
5799a747e4fSDavid du Colombier for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
5809a747e4fSDavid du Colombier des->bp = iallocb(Rbsz);
5819a747e4fSDavid du Colombier if(des->bp == nil)
5829a747e4fSDavid du Colombier panic("can't allocate ethernet receive ring\n");
5839a747e4fSDavid du Colombier des->status = Own;
5849a747e4fSDavid du Colombier des->control = Rbsz;
5859a747e4fSDavid du Colombier des->addr = PCIWADDR(des->bp->rp);
5869a747e4fSDavid du Colombier }
5879a747e4fSDavid du Colombier ctlr->rdr[ctlr->nrdr-1].control |= Er;
5889a747e4fSDavid du Colombier ctlr->rdrx = 0;
5899a747e4fSDavid du Colombier csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));
5909a747e4fSDavid du Colombier
5919a747e4fSDavid du Colombier ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
5929a747e4fSDavid du Colombier ctlr->tdr[ctlr->ntdr-1].control |= Er;
5939a747e4fSDavid du Colombier ctlr->tdrh = 0;
5949a747e4fSDavid du Colombier ctlr->tdri = 0;
5959a747e4fSDavid du Colombier csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));
5969a747e4fSDavid du Colombier
5979a747e4fSDavid du Colombier /*
5989a747e4fSDavid du Colombier * Clear any bits in the Status Register (CSR5) as
5999a747e4fSDavid du Colombier * the PNIC has a different reset value from a true 2114x.
6009a747e4fSDavid du Colombier */
6019a747e4fSDavid du Colombier ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
6029a747e4fSDavid du Colombier csr32w(ctlr, 5, ctlr->mask);
6039a747e4fSDavid du Colombier csr32w(ctlr, 7, ctlr->mask);
6049a747e4fSDavid du Colombier ctlr->csr6 |= St;
6059a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
6069a747e4fSDavid du Colombier
6079a747e4fSDavid du Colombier for(i = 0; i < Eaddrlen/2; i++){
6089a747e4fSDavid du Colombier bi[i*4] = ether->ea[i*2];
6099a747e4fSDavid du Colombier bi[i*4+1] = ether->ea[i*2+1];
6109a747e4fSDavid du Colombier bi[i*4+2] = ether->ea[i*2+1];
6119a747e4fSDavid du Colombier bi[i*4+3] = ether->ea[i*2];
6129a747e4fSDavid du Colombier }
6139a747e4fSDavid du Colombier bp = iallocb(Eaddrlen*2*16);
6149a747e4fSDavid du Colombier if(bp == nil)
6159a747e4fSDavid du Colombier panic("can't allocate ethernet setup buffer\n");
6169a747e4fSDavid du Colombier memset(bp->rp, 0xFF, sizeof(bi));
6179a747e4fSDavid du Colombier for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
6189a747e4fSDavid du Colombier memmove(bp->rp+i, bi, sizeof(bi));
6199a747e4fSDavid du Colombier bp->wp += sizeof(bi)*16;
6209a747e4fSDavid du Colombier
6219a747e4fSDavid du Colombier ctlr->setupbp = bp;
622*3ff48bf5SDavid du Colombier ether->oq = qopen(256*1024, Qmsg, 0, 0);
6239a747e4fSDavid du Colombier transmit(ether);
6249a747e4fSDavid du Colombier }
6259a747e4fSDavid du Colombier
6269a747e4fSDavid du Colombier static void
csr9w(Ctlr * ctlr,int data)6279a747e4fSDavid du Colombier csr9w(Ctlr* ctlr, int data)
6289a747e4fSDavid du Colombier {
6299a747e4fSDavid du Colombier csr32w(ctlr, 9, data);
6309a747e4fSDavid du Colombier microdelay(1);
6319a747e4fSDavid du Colombier }
6329a747e4fSDavid du Colombier
6339a747e4fSDavid du Colombier static int
miimdi(Ctlr * ctlr,int n)6349a747e4fSDavid du Colombier miimdi(Ctlr* ctlr, int n)
6359a747e4fSDavid du Colombier {
6369a747e4fSDavid du Colombier int data, i;
6379a747e4fSDavid du Colombier
6389a747e4fSDavid du Colombier /*
6399a747e4fSDavid du Colombier * Read n bits from the MII Management Register.
6409a747e4fSDavid du Colombier */
6419a747e4fSDavid du Colombier data = 0;
6429a747e4fSDavid du Colombier for(i = n-1; i >= 0; i--){
6439a747e4fSDavid du Colombier if(csr32r(ctlr, 9) & Mdi)
6449a747e4fSDavid du Colombier data |= (1<<i);
6459a747e4fSDavid du Colombier csr9w(ctlr, Mii|Mdc);
6469a747e4fSDavid du Colombier csr9w(ctlr, Mii);
6479a747e4fSDavid du Colombier }
6489a747e4fSDavid du Colombier csr9w(ctlr, 0);
6499a747e4fSDavid du Colombier
6509a747e4fSDavid du Colombier return data;
6519a747e4fSDavid du Colombier }
6529a747e4fSDavid du Colombier
6539a747e4fSDavid du Colombier static void
miimdo(Ctlr * ctlr,int bits,int n)6549a747e4fSDavid du Colombier miimdo(Ctlr* ctlr, int bits, int n)
6559a747e4fSDavid du Colombier {
6569a747e4fSDavid du Colombier int i, mdo;
6579a747e4fSDavid du Colombier
6589a747e4fSDavid du Colombier /*
6599a747e4fSDavid du Colombier * Write n bits to the MII Management Register.
6609a747e4fSDavid du Colombier */
6619a747e4fSDavid du Colombier for(i = n-1; i >= 0; i--){
6629a747e4fSDavid du Colombier if(bits & (1<<i))
6639a747e4fSDavid du Colombier mdo = Mdo;
6649a747e4fSDavid du Colombier else
6659a747e4fSDavid du Colombier mdo = 0;
6669a747e4fSDavid du Colombier csr9w(ctlr, mdo);
6679a747e4fSDavid du Colombier csr9w(ctlr, mdo|Mdc);
6689a747e4fSDavid du Colombier csr9w(ctlr, mdo);
6699a747e4fSDavid du Colombier }
6709a747e4fSDavid du Colombier }
6719a747e4fSDavid du Colombier
6729a747e4fSDavid du Colombier static int
miir(Ctlr * ctlr,int phyad,int regad)6739a747e4fSDavid du Colombier miir(Ctlr* ctlr, int phyad, int regad)
6749a747e4fSDavid du Colombier {
6759a747e4fSDavid du Colombier int data, i;
6769a747e4fSDavid du Colombier
6779a747e4fSDavid du Colombier if(ctlr->id == Pnic){
6789a747e4fSDavid du Colombier i = 1000;
6799a747e4fSDavid du Colombier csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
6809a747e4fSDavid du Colombier do{
6819a747e4fSDavid du Colombier microdelay(1);
6829a747e4fSDavid du Colombier data = csr32r(ctlr, 20);
6839a747e4fSDavid du Colombier }while((data & 0x80000000) && --i);
6849a747e4fSDavid du Colombier
6859a747e4fSDavid du Colombier if(i == 0)
6869a747e4fSDavid du Colombier return -1;
6879a747e4fSDavid du Colombier return data & 0xFFFF;
6889a747e4fSDavid du Colombier }
6899a747e4fSDavid du Colombier
6909a747e4fSDavid du Colombier /*
6919a747e4fSDavid du Colombier * Preamble;
6929a747e4fSDavid du Colombier * ST+OP+PHYAD+REGAD;
6939a747e4fSDavid du Colombier * TA + 16 data bits.
6949a747e4fSDavid du Colombier */
6959a747e4fSDavid du Colombier miimdo(ctlr, 0xFFFFFFFF, 32);
6969a747e4fSDavid du Colombier miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
6979a747e4fSDavid du Colombier data = miimdi(ctlr, 18);
6989a747e4fSDavid du Colombier
6999a747e4fSDavid du Colombier if(data & 0x10000)
7009a747e4fSDavid du Colombier return -1;
7019a747e4fSDavid du Colombier
7029a747e4fSDavid du Colombier return data & 0xFFFF;
7039a747e4fSDavid du Colombier }
7049a747e4fSDavid du Colombier
7059a747e4fSDavid du Colombier static void
miiw(Ctlr * ctlr,int phyad,int regad,int data)7069a747e4fSDavid du Colombier miiw(Ctlr* ctlr, int phyad, int regad, int data)
7079a747e4fSDavid du Colombier {
7089a747e4fSDavid du Colombier /*
7099a747e4fSDavid du Colombier * Preamble;
7109a747e4fSDavid du Colombier * ST+OP+PHYAD+REGAD+TA + 16 data bits;
7119a747e4fSDavid du Colombier * Z.
7129a747e4fSDavid du Colombier */
7139a747e4fSDavid du Colombier miimdo(ctlr, 0xFFFFFFFF, 32);
7149a747e4fSDavid du Colombier data &= 0xFFFF;
7159a747e4fSDavid du Colombier data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
7169a747e4fSDavid du Colombier miimdo(ctlr, data, 32);
7179a747e4fSDavid du Colombier csr9w(ctlr, Mdc);
7189a747e4fSDavid du Colombier csr9w(ctlr, 0);
7199a747e4fSDavid du Colombier }
7209a747e4fSDavid du Colombier
7219a747e4fSDavid du Colombier static int
sromr(Ctlr * ctlr,int r)7229a747e4fSDavid du Colombier sromr(Ctlr* ctlr, int r)
7239a747e4fSDavid du Colombier {
7249a747e4fSDavid du Colombier int i, op, data, size;
7259a747e4fSDavid du Colombier
7269a747e4fSDavid du Colombier if(ctlr->id == Pnic){
7279a747e4fSDavid du Colombier i = 1000;
7289a747e4fSDavid du Colombier csr32w(ctlr, 19, 0x600|r);
7299a747e4fSDavid du Colombier do{
7309a747e4fSDavid du Colombier microdelay(1);
7319a747e4fSDavid du Colombier data = csr32r(ctlr, 19);
7329a747e4fSDavid du Colombier }while((data & 0x80000000) && --i);
7339a747e4fSDavid du Colombier
7349a747e4fSDavid du Colombier if(ctlr->sromsz == 0)
7359a747e4fSDavid du Colombier ctlr->sromsz = 6;
7369a747e4fSDavid du Colombier
7379a747e4fSDavid du Colombier return csr32r(ctlr, 9) & 0xFFFF;
7389a747e4fSDavid du Colombier }
7399a747e4fSDavid du Colombier
7409a747e4fSDavid du Colombier /*
7419a747e4fSDavid du Colombier * This sequence for reading a 16-bit register 'r'
7429a747e4fSDavid du Colombier * in the EEPROM is taken straight from Section
7439a747e4fSDavid du Colombier * 7.4 of the 21140 Hardware Reference Manual.
7449a747e4fSDavid du Colombier */
7459a747e4fSDavid du Colombier reread:
7469a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss);
7479a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss|Scs);
7489a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss|Sclk|Scs);
7499a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss);
7509a747e4fSDavid du Colombier
7519a747e4fSDavid du Colombier op = 0x06;
7529a747e4fSDavid du Colombier for(i = 3-1; i >= 0; i--){
7539a747e4fSDavid du Colombier data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
7549a747e4fSDavid du Colombier csr9w(ctlr, data);
7559a747e4fSDavid du Colombier csr9w(ctlr, data|Sclk);
7569a747e4fSDavid du Colombier csr9w(ctlr, data);
7579a747e4fSDavid du Colombier }
7589a747e4fSDavid du Colombier
7599a747e4fSDavid du Colombier /*
7609a747e4fSDavid du Colombier * First time through must work out the EEPROM size.
7619a747e4fSDavid du Colombier */
7629a747e4fSDavid du Colombier if((size = ctlr->sromsz) == 0)
7639a747e4fSDavid du Colombier size = 8;
7649a747e4fSDavid du Colombier
7659a747e4fSDavid du Colombier for(size = size-1; size >= 0; size--){
7669a747e4fSDavid du Colombier data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
7679a747e4fSDavid du Colombier csr9w(ctlr, data);
7689a747e4fSDavid du Colombier csr9w(ctlr, data|Sclk);
7699a747e4fSDavid du Colombier csr9w(ctlr, data);
7709a747e4fSDavid du Colombier microdelay(1);
7719a747e4fSDavid du Colombier if(!(csr32r(ctlr, 9) & Sdo))
7729a747e4fSDavid du Colombier break;
7739a747e4fSDavid du Colombier }
7749a747e4fSDavid du Colombier
7759a747e4fSDavid du Colombier data = 0;
7769a747e4fSDavid du Colombier for(i = 16-1; i >= 0; i--){
7779a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss|Sclk|Scs);
7789a747e4fSDavid du Colombier if(csr32r(ctlr, 9) & Sdo)
7799a747e4fSDavid du Colombier data |= (1<<i);
7809a747e4fSDavid du Colombier csr9w(ctlr, Rd|Ss|Scs);
7819a747e4fSDavid du Colombier }
7829a747e4fSDavid du Colombier
7839a747e4fSDavid du Colombier csr9w(ctlr, 0);
7849a747e4fSDavid du Colombier
7859a747e4fSDavid du Colombier if(ctlr->sromsz == 0){
7869a747e4fSDavid du Colombier ctlr->sromsz = 8-size;
7879a747e4fSDavid du Colombier goto reread;
7889a747e4fSDavid du Colombier }
7899a747e4fSDavid du Colombier
7909a747e4fSDavid du Colombier return data & 0xFFFF;
7919a747e4fSDavid du Colombier }
7929a747e4fSDavid du Colombier
7939a747e4fSDavid du Colombier static void
softreset(Ctlr * ctlr)7949a747e4fSDavid du Colombier softreset(Ctlr* ctlr)
7959a747e4fSDavid du Colombier {
7969a747e4fSDavid du Colombier /*
7979a747e4fSDavid du Colombier * Soft-reset the controller and initialise bus mode.
7989a747e4fSDavid du Colombier * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
7999a747e4fSDavid du Colombier */
8009a747e4fSDavid du Colombier csr32w(ctlr, 0, Swr);
8019a747e4fSDavid du Colombier microdelay(10);
8029a747e4fSDavid du Colombier csr32w(ctlr, 0, Rml|Cal16|Dbo);
8039a747e4fSDavid du Colombier delay(1);
8049a747e4fSDavid du Colombier }
8059a747e4fSDavid du Colombier
8069a747e4fSDavid du Colombier static int
type5block(Ctlr * ctlr,uchar * block)8079a747e4fSDavid du Colombier type5block(Ctlr* ctlr, uchar* block)
8089a747e4fSDavid du Colombier {
8099a747e4fSDavid du Colombier int csr15, i, len;
8109a747e4fSDavid du Colombier
8119a747e4fSDavid du Colombier /*
8129a747e4fSDavid du Colombier * Reset or GPR sequence. Reset should be once only,
8139a747e4fSDavid du Colombier * before the GPR sequence.
8149a747e4fSDavid du Colombier * Note 'block' is not a pointer to the block head but
8159a747e4fSDavid du Colombier * a pointer to the data in the block starting at the
8169a747e4fSDavid du Colombier * reset length value so type5block can be used for the
8179a747e4fSDavid du Colombier * sequences contained in type 1 and type 3 blocks.
8189a747e4fSDavid du Colombier * The SROM docs state the 21140 type 5 block is the
8199a747e4fSDavid du Colombier * same as that for the 21143, but the two controllers
8209a747e4fSDavid du Colombier * use different registers and sequence-element lengths
8219a747e4fSDavid du Colombier * so the 21140 code here is a guess for a real type 5
8229a747e4fSDavid du Colombier * sequence.
8239a747e4fSDavid du Colombier */
8249a747e4fSDavid du Colombier len = *block++;
8259a747e4fSDavid du Colombier if(ctlr->id != Tulip3){
8269a747e4fSDavid du Colombier for(i = 0; i < len; i++){
8279a747e4fSDavid du Colombier csr32w(ctlr, 12, *block);
8289a747e4fSDavid du Colombier block++;
8299a747e4fSDavid du Colombier }
8309a747e4fSDavid du Colombier return len;
8319a747e4fSDavid du Colombier }
8329a747e4fSDavid du Colombier
8339a747e4fSDavid du Colombier for(i = 0; i < len; i++){
8349a747e4fSDavid du Colombier csr15 = *block++<<16;
8359a747e4fSDavid du Colombier csr15 |= *block++<<24;
8369a747e4fSDavid du Colombier csr32w(ctlr, 15, csr15);
8379a747e4fSDavid du Colombier debug("%8.8uX ", csr15);
8389a747e4fSDavid du Colombier }
8399a747e4fSDavid du Colombier return 2*len;
8409a747e4fSDavid du Colombier }
8419a747e4fSDavid du Colombier
8429a747e4fSDavid du Colombier static int
typephylink(Ctlr * ctlr,uchar *)8439a747e4fSDavid du Colombier typephylink(Ctlr* ctlr, uchar*)
8449a747e4fSDavid du Colombier {
8459a747e4fSDavid du Colombier int an, bmcr, bmsr, csr6, x;
8469a747e4fSDavid du Colombier
8479a747e4fSDavid du Colombier /*
8489a747e4fSDavid du Colombier * Fail if
8499a747e4fSDavid du Colombier * auto-negotiataion enabled but not complete;
8509a747e4fSDavid du Colombier * no valid link established.
8519a747e4fSDavid du Colombier */
8529a747e4fSDavid du Colombier bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
8539a747e4fSDavid du Colombier miir(ctlr, ctlr->curphyad, Bmsr);
8549a747e4fSDavid du Colombier bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
8559a747e4fSDavid du Colombier debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
8569a747e4fSDavid du Colombier if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
8579a747e4fSDavid du Colombier return 0;
8589a747e4fSDavid du Colombier
8599a747e4fSDavid du Colombier if(bmcr & 0x1000){
8609a747e4fSDavid du Colombier an = miir(ctlr, ctlr->curphyad, Anar);
8619a747e4fSDavid du Colombier an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
8629a747e4fSDavid du Colombier debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
8639a747e4fSDavid du Colombier miir(ctlr, ctlr->curphyad, Anar),
8649a747e4fSDavid du Colombier miir(ctlr, ctlr->curphyad, Anlpar),
8659a747e4fSDavid du Colombier an);
8669a747e4fSDavid du Colombier
8679a747e4fSDavid du Colombier if(an & 0x0100)
8689a747e4fSDavid du Colombier x = 0x4000;
8699a747e4fSDavid du Colombier else if(an & 0x0080)
8709a747e4fSDavid du Colombier x = 0x2000;
8719a747e4fSDavid du Colombier else if(an & 0x0040)
8729a747e4fSDavid du Colombier x = 0x1000;
8739a747e4fSDavid du Colombier else if(an & 0x0020)
8749a747e4fSDavid du Colombier x = 0x0800;
8759a747e4fSDavid du Colombier else
8769a747e4fSDavid du Colombier x = 0;
8779a747e4fSDavid du Colombier }
8789a747e4fSDavid du Colombier else if((bmcr & 0x2100) == 0x2100)
8799a747e4fSDavid du Colombier x = 0x4000;
8809a747e4fSDavid du Colombier else if(bmcr & 0x2000){
8819a747e4fSDavid du Colombier /*
8829a747e4fSDavid du Colombier * If FD capable, force it if necessary.
8839a747e4fSDavid du Colombier */
8849a747e4fSDavid du Colombier if((bmsr & 0x4000) && ctlr->fd){
8859a747e4fSDavid du Colombier miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
8869a747e4fSDavid du Colombier x = 0x4000;
8879a747e4fSDavid du Colombier }
8889a747e4fSDavid du Colombier else
8899a747e4fSDavid du Colombier x = 0x2000;
8909a747e4fSDavid du Colombier }
8919a747e4fSDavid du Colombier else if(bmcr & 0x0100)
8929a747e4fSDavid du Colombier x = 0x1000;
8939a747e4fSDavid du Colombier else
8949a747e4fSDavid du Colombier x = 0x0800;
8959a747e4fSDavid du Colombier
8969a747e4fSDavid du Colombier csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
8979a747e4fSDavid du Colombier if(ctlr->fdx & x)
8989a747e4fSDavid du Colombier csr6 |= Fd;
8999a747e4fSDavid du Colombier if(ctlr->ttm & x)
9009a747e4fSDavid du Colombier csr6 |= Ttm;
9019a747e4fSDavid du Colombier debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
9029a747e4fSDavid du Colombier csr6, ctlr->csr6, csr32r(ctlr, 6));
9039a747e4fSDavid du Colombier if(csr6 != ctlr->csr6){
9049a747e4fSDavid du Colombier ctlr->csr6 = csr6;
9059a747e4fSDavid du Colombier csr32w(ctlr, 6, csr6);
9069a747e4fSDavid du Colombier }
9079a747e4fSDavid du Colombier
9089a747e4fSDavid du Colombier return 1;
9099a747e4fSDavid du Colombier }
9109a747e4fSDavid du Colombier
9119a747e4fSDavid du Colombier static int
typephymode(Ctlr * ctlr,uchar * block,int wait)9129a747e4fSDavid du Colombier typephymode(Ctlr* ctlr, uchar* block, int wait)
9139a747e4fSDavid du Colombier {
9149a747e4fSDavid du Colombier uchar *p;
9159a747e4fSDavid du Colombier int len, mc, nway, phyx, timeo;
9169a747e4fSDavid du Colombier
9179a747e4fSDavid du Colombier if(DEBUG){
9189a747e4fSDavid du Colombier int i;
9199a747e4fSDavid du Colombier
9209a747e4fSDavid du Colombier len = (block[0] & ~0x80)+1;
9219a747e4fSDavid du Colombier for(i = 0; i < len; i++)
9229a747e4fSDavid du Colombier debug("%2.2uX ", block[i]);
9239a747e4fSDavid du Colombier debug("\n");
9249a747e4fSDavid du Colombier }
9259a747e4fSDavid du Colombier
9269a747e4fSDavid du Colombier if(block[1] == 1)
9279a747e4fSDavid du Colombier len = 1;
9289a747e4fSDavid du Colombier else if(block[1] == 3)
9299a747e4fSDavid du Colombier len = 2;
9309a747e4fSDavid du Colombier else
9319a747e4fSDavid du Colombier return -1;
9329a747e4fSDavid du Colombier
9339a747e4fSDavid du Colombier /*
9349a747e4fSDavid du Colombier * Snarf the media capabilities, nway advertisment,
9359a747e4fSDavid du Colombier * FDX and TTM bitmaps.
9369a747e4fSDavid du Colombier */
9379a747e4fSDavid du Colombier p = &block[5+len*block[3]+len*block[4+len*block[3]]];
9389a747e4fSDavid du Colombier mc = *p++;
9399a747e4fSDavid du Colombier mc |= *p++<<8;
9409a747e4fSDavid du Colombier nway = *p++;
9419a747e4fSDavid du Colombier nway |= *p++<<8;
9429a747e4fSDavid du Colombier ctlr->fdx = *p++;
9439a747e4fSDavid du Colombier ctlr->fdx |= *p++<<8;
9449a747e4fSDavid du Colombier ctlr->ttm = *p++;
9459a747e4fSDavid du Colombier ctlr->ttm |= *p<<8;
9469a747e4fSDavid du Colombier debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
9479a747e4fSDavid du Colombier mc, nway, ctlr->fdx, ctlr->ttm);
9489a747e4fSDavid du Colombier USED(mc);
9499a747e4fSDavid du Colombier
9509a747e4fSDavid du Colombier phyx = block[2];
9519a747e4fSDavid du Colombier ctlr->curphyad = ctlr->phy[phyx];
9529a747e4fSDavid du Colombier
9539a747e4fSDavid du Colombier ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
9549a747e4fSDavid du Colombier //csr32w(ctlr, 6, ctlr->csr6);
9559a747e4fSDavid du Colombier if(typephylink(ctlr, block))
9569a747e4fSDavid du Colombier return 0;
9579a747e4fSDavid du Colombier
9589a747e4fSDavid du Colombier if(!(ctlr->phyreset & (1<<phyx))){
9599a747e4fSDavid du Colombier debug("reset seq: len %d: ", block[3]);
9609a747e4fSDavid du Colombier if(ctlr->type5block)
9619a747e4fSDavid du Colombier type5block(ctlr, &ctlr->type5block[2]);
9629a747e4fSDavid du Colombier else
9639a747e4fSDavid du Colombier type5block(ctlr, &block[4+len*block[3]]);
9649a747e4fSDavid du Colombier debug("\n");
9659a747e4fSDavid du Colombier ctlr->phyreset |= (1<<phyx);
9669a747e4fSDavid du Colombier }
9679a747e4fSDavid du Colombier
9689a747e4fSDavid du Colombier /*
9699a747e4fSDavid du Colombier * GPR sequence.
9709a747e4fSDavid du Colombier */
9719a747e4fSDavid du Colombier debug("gpr seq: len %d: ", block[3]);
9729a747e4fSDavid du Colombier type5block(ctlr, &block[3]);
9739a747e4fSDavid du Colombier debug("\n");
9749a747e4fSDavid du Colombier
9759a747e4fSDavid du Colombier ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
9769a747e4fSDavid du Colombier //csr32w(ctlr, 6, ctlr->csr6);
9779a747e4fSDavid du Colombier if(typephylink(ctlr, block))
9789a747e4fSDavid du Colombier return 0;
9799a747e4fSDavid du Colombier
9809a747e4fSDavid du Colombier /*
9819a747e4fSDavid du Colombier * Turn off auto-negotiation, set the auto-negotiation
9829a747e4fSDavid du Colombier * advertisment register then start the auto-negotiation
9839a747e4fSDavid du Colombier * process again.
9849a747e4fSDavid du Colombier */
9859a747e4fSDavid du Colombier miiw(ctlr, ctlr->curphyad, Bmcr, 0);
9869a747e4fSDavid du Colombier miiw(ctlr, ctlr->curphyad, Anar, nway|1);
9879a747e4fSDavid du Colombier miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
9889a747e4fSDavid du Colombier
9899a747e4fSDavid du Colombier if(!wait)
9909a747e4fSDavid du Colombier return 0;
9919a747e4fSDavid du Colombier
9929a747e4fSDavid du Colombier for(timeo = 0; timeo < 30; timeo++){
9939a747e4fSDavid du Colombier if(typephylink(ctlr, block))
9949a747e4fSDavid du Colombier return 0;
9959a747e4fSDavid du Colombier delay(100);
9969a747e4fSDavid du Colombier }
9979a747e4fSDavid du Colombier
9989a747e4fSDavid du Colombier return -1;
9999a747e4fSDavid du Colombier }
10009a747e4fSDavid du Colombier
10019a747e4fSDavid du Colombier static int
typesymmode(Ctlr * ctlr,uchar * block,int wait)10029a747e4fSDavid du Colombier typesymmode(Ctlr *ctlr, uchar *block, int wait)
10039a747e4fSDavid du Colombier {
10049a747e4fSDavid du Colombier uint gpmode, gpdata, command;
10059a747e4fSDavid du Colombier
10069a747e4fSDavid du Colombier USED(wait);
10079a747e4fSDavid du Colombier gpmode = block[3] | ((uint) block[4] << 8);
10089a747e4fSDavid du Colombier gpdata = block[5] | ((uint) block[6] << 8);
10099a747e4fSDavid du Colombier command = (block[7] | ((uint) block[8] << 8)) & 0x71;
10109a747e4fSDavid du Colombier if (command & 0x8000) {
10119a747e4fSDavid du Colombier print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
10129a747e4fSDavid du Colombier return -1;
10139a747e4fSDavid du Colombier }
10149a747e4fSDavid du Colombier csr32w(ctlr, 15, gpmode);
10159a747e4fSDavid du Colombier csr32w(ctlr, 15, gpdata);
10169a747e4fSDavid du Colombier ctlr->csr6 = (command & 0x71) << 18;
10179a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
10189a747e4fSDavid du Colombier return 0;
10199a747e4fSDavid du Colombier }
10209a747e4fSDavid du Colombier
10219a747e4fSDavid du Colombier static int
type2mode(Ctlr * ctlr,uchar * block,int)10229a747e4fSDavid du Colombier type2mode(Ctlr* ctlr, uchar* block, int)
10239a747e4fSDavid du Colombier {
10249a747e4fSDavid du Colombier uchar *p;
10259a747e4fSDavid du Colombier int csr6, csr13, csr14, csr15, gpc, gpd;
10269a747e4fSDavid du Colombier
10279a747e4fSDavid du Colombier csr6 = Sc|Mbo|Ca|Sb|TrMODE;
10289a747e4fSDavid du Colombier debug("type2mode: medium 0x%2.2uX\n", block[2]);
10299a747e4fSDavid du Colombier
10309a747e4fSDavid du Colombier /*
10319a747e4fSDavid du Colombier * Don't attempt full-duplex
10329a747e4fSDavid du Colombier * unless explicitly requested.
10339a747e4fSDavid du Colombier */
10349a747e4fSDavid du Colombier if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */
10359a747e4fSDavid du Colombier if(!ctlr->fd)
10369a747e4fSDavid du Colombier return -1;
10379a747e4fSDavid du Colombier csr6 |= Fd;
10389a747e4fSDavid du Colombier }
10399a747e4fSDavid du Colombier
10409a747e4fSDavid du Colombier /*
10419a747e4fSDavid du Colombier * Operating mode programming values from the datasheet
10429a747e4fSDavid du Colombier * unless media specific data is explicitly given.
10439a747e4fSDavid du Colombier */
10449a747e4fSDavid du Colombier p = &block[3];
10459a747e4fSDavid du Colombier if(block[2] & 0x40){
10469a747e4fSDavid du Colombier csr13 = (block[4]<<8)|block[3];
10479a747e4fSDavid du Colombier csr14 = (block[6]<<8)|block[5];
10489a747e4fSDavid du Colombier csr15 = (block[8]<<8)|block[7];
10499a747e4fSDavid du Colombier p += 6;
10509a747e4fSDavid du Colombier }
10519a747e4fSDavid du Colombier else switch(block[2] & 0x3F){
10529a747e4fSDavid du Colombier default:
10539a747e4fSDavid du Colombier return -1;
10549a747e4fSDavid du Colombier case 0x00: /* 10BASE-T */
10559a747e4fSDavid du Colombier csr13 = 0x00000001;
10569a747e4fSDavid du Colombier csr14 = 0x00007F3F;
10579a747e4fSDavid du Colombier csr15 = 0x00000008;
10589a747e4fSDavid du Colombier break;
10599a747e4fSDavid du Colombier case 0x01: /* 10BASE-2 */
10609a747e4fSDavid du Colombier csr13 = 0x00000009;
10619a747e4fSDavid du Colombier csr14 = 0x00000705;
10629a747e4fSDavid du Colombier csr15 = 0x00000006;
10639a747e4fSDavid du Colombier break;
10649a747e4fSDavid du Colombier case 0x02: /* 10BASE-5 (AUI) */
10659a747e4fSDavid du Colombier csr13 = 0x00000009;
10669a747e4fSDavid du Colombier csr14 = 0x00000705;
10679a747e4fSDavid du Colombier csr15 = 0x0000000E;
10689a747e4fSDavid du Colombier break;
10699a747e4fSDavid du Colombier case 0x04: /* 10BASE-TFD */
10709a747e4fSDavid du Colombier csr13 = 0x00000001;
10719a747e4fSDavid du Colombier csr14 = 0x00007F3D;
10729a747e4fSDavid du Colombier csr15 = 0x00000008;
10739a747e4fSDavid du Colombier break;
10749a747e4fSDavid du Colombier }
10759a747e4fSDavid du Colombier gpc = *p++<<16;
10769a747e4fSDavid du Colombier gpc |= *p++<<24;
10779a747e4fSDavid du Colombier gpd = *p++<<16;
10789a747e4fSDavid du Colombier gpd |= *p<<24;
10799a747e4fSDavid du Colombier
10809a747e4fSDavid du Colombier csr32w(ctlr, 13, 0);
10819a747e4fSDavid du Colombier csr32w(ctlr, 14, csr14);
10829a747e4fSDavid du Colombier csr32w(ctlr, 15, gpc|csr15);
10839a747e4fSDavid du Colombier delay(10);
10849a747e4fSDavid du Colombier csr32w(ctlr, 15, gpd|csr15);
10859a747e4fSDavid du Colombier csr32w(ctlr, 13, csr13);
10869a747e4fSDavid du Colombier
10879a747e4fSDavid du Colombier ctlr->csr6 = csr6;
10889a747e4fSDavid du Colombier csr32w(ctlr, 6, ctlr->csr6);
10899a747e4fSDavid du Colombier
10909a747e4fSDavid du Colombier debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
10919a747e4fSDavid du Colombier csr13, csr14, csr15);
10929a747e4fSDavid du Colombier debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
10939a747e4fSDavid du Colombier gpc, gpd, csr6);
10949a747e4fSDavid du Colombier
10959a747e4fSDavid du Colombier return 0;
10969a747e4fSDavid du Colombier }
10979a747e4fSDavid du Colombier
10989a747e4fSDavid du Colombier static int
type0link(Ctlr * ctlr,uchar * block)10999a747e4fSDavid du Colombier type0link(Ctlr* ctlr, uchar* block)
11009a747e4fSDavid du Colombier {
11019a747e4fSDavid du Colombier int m, polarity, sense;
11029a747e4fSDavid du Colombier
11039a747e4fSDavid du Colombier m = (block[3]<<8)|block[2];
11049a747e4fSDavid du Colombier sense = 1<<((m & 0x000E)>>1);
11059a747e4fSDavid du Colombier if(m & 0x0080)
11069a747e4fSDavid du Colombier polarity = sense;
11079a747e4fSDavid du Colombier else
11089a747e4fSDavid du Colombier polarity = 0;
11099a747e4fSDavid du Colombier
11109a747e4fSDavid du Colombier return (csr32r(ctlr, 12) & sense)^polarity;
11119a747e4fSDavid du Colombier }
11129a747e4fSDavid du Colombier
11139a747e4fSDavid du Colombier static int
type0mode(Ctlr * ctlr,uchar * block,int wait)11149a747e4fSDavid du Colombier type0mode(Ctlr* ctlr, uchar* block, int wait)
11159a747e4fSDavid du Colombier {
11169a747e4fSDavid du Colombier int csr6, m, timeo;
11179a747e4fSDavid du Colombier
11189a747e4fSDavid du Colombier csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
11199a747e4fSDavid du Colombier debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
11209a747e4fSDavid du Colombier ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);
11219a747e4fSDavid du Colombier switch(block[0]){
11229a747e4fSDavid du Colombier default:
11239a747e4fSDavid du Colombier break;
11249a747e4fSDavid du Colombier
11259a747e4fSDavid du Colombier case 0x04: /* 10BASE-TFD */
11269a747e4fSDavid du Colombier case 0x05: /* 100BASE-TXFD */
11279a747e4fSDavid du Colombier case 0x08: /* 100BASE-FXFD */
11289a747e4fSDavid du Colombier /*
11299a747e4fSDavid du Colombier * Don't attempt full-duplex
11309a747e4fSDavid du Colombier * unless explicitly requested.
11319a747e4fSDavid du Colombier */
11329a747e4fSDavid du Colombier if(!ctlr->fd)
11339a747e4fSDavid du Colombier return -1;
11349a747e4fSDavid du Colombier csr6 |= Fd;
11359a747e4fSDavid du Colombier break;
11369a747e4fSDavid du Colombier }
11379a747e4fSDavid du Colombier
11389a747e4fSDavid du Colombier m = (block[3]<<8)|block[2];
11399a747e4fSDavid du Colombier if(m & 0x0001)
11409a747e4fSDavid du Colombier csr6 |= Ps;
11419a747e4fSDavid du Colombier if(m & 0x0010)
11429a747e4fSDavid du Colombier csr6 |= Ttm;
11439a747e4fSDavid du Colombier if(m & 0x0020)
11449a747e4fSDavid du Colombier csr6 |= Pcs;
11459a747e4fSDavid du Colombier if(m & 0x0040)
11469a747e4fSDavid du Colombier csr6 |= Scr;
11479a747e4fSDavid du Colombier
11489a747e4fSDavid du Colombier csr32w(ctlr, 12, block[1]);
11499a747e4fSDavid du Colombier microdelay(10);
11509a747e4fSDavid du Colombier csr32w(ctlr, 6, csr6);
11519a747e4fSDavid du Colombier ctlr->csr6 = csr6;
11529a747e4fSDavid du Colombier
11539a747e4fSDavid du Colombier if(!wait)
11549a747e4fSDavid du Colombier return 0;
11559a747e4fSDavid du Colombier
11569a747e4fSDavid du Colombier for(timeo = 0; timeo < 30; timeo++){
11579a747e4fSDavid du Colombier if(type0link(ctlr, block))
11589a747e4fSDavid du Colombier return 0;
11599a747e4fSDavid du Colombier delay(100);
11609a747e4fSDavid du Colombier }
11619a747e4fSDavid du Colombier
11629a747e4fSDavid du Colombier return -1;
11639a747e4fSDavid du Colombier }
11649a747e4fSDavid du Colombier
11659a747e4fSDavid du Colombier static int
mediaxx(Ether * ether,int wait)11669a747e4fSDavid du Colombier mediaxx(Ether* ether, int wait)
11679a747e4fSDavid du Colombier {
11689a747e4fSDavid du Colombier Ctlr* ctlr;
11699a747e4fSDavid du Colombier uchar *block;
11709a747e4fSDavid du Colombier
11719a747e4fSDavid du Colombier ctlr = ether->ctlr;
11729a747e4fSDavid du Colombier block = ctlr->infoblock[ctlr->curk];
11739a747e4fSDavid du Colombier if(block[0] & 0x80){
11749a747e4fSDavid du Colombier switch(block[1]){
11759a747e4fSDavid du Colombier default:
11769a747e4fSDavid du Colombier return -1;
11779a747e4fSDavid du Colombier case 0:
11789a747e4fSDavid du Colombier if(ctlr->medium >= 0 && block[2] != ctlr->medium)
11799a747e4fSDavid du Colombier return 0;
11809a747e4fSDavid du Colombier /* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
11819a747e4fSDavid du Colombier return 0;
11829a747e4fSDavid du Colombier if(type0mode(ctlr, block+2, wait))
11839a747e4fSDavid du Colombier return 0;
11849a747e4fSDavid du Colombier break;
11859a747e4fSDavid du Colombier case 1:
11869a747e4fSDavid du Colombier if(typephymode(ctlr, block, wait))
11879a747e4fSDavid du Colombier return 0;
11889a747e4fSDavid du Colombier break;
11899a747e4fSDavid du Colombier case 2:
11909a747e4fSDavid du Colombier debug("type2: medium %d block[2] %d\n",
11919a747e4fSDavid du Colombier ctlr->medium, block[2]);
11929a747e4fSDavid du Colombier if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
11939a747e4fSDavid du Colombier return 0;
11949a747e4fSDavid du Colombier if(type2mode(ctlr, block, wait))
11959a747e4fSDavid du Colombier return 0;
11969a747e4fSDavid du Colombier break;
11979a747e4fSDavid du Colombier case 3:
11989a747e4fSDavid du Colombier if(typephymode(ctlr, block, wait))
11999a747e4fSDavid du Colombier return 0;
12009a747e4fSDavid du Colombier break;
12019a747e4fSDavid du Colombier case 4:
12029a747e4fSDavid du Colombier debug("type4: medium %d block[2] %d\n",
12039a747e4fSDavid du Colombier ctlr->medium, block[2]);
12049a747e4fSDavid du Colombier if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
12059a747e4fSDavid du Colombier return 0;
12069a747e4fSDavid du Colombier if(typesymmode(ctlr, block, wait))
12079a747e4fSDavid du Colombier return 0;
12089a747e4fSDavid du Colombier break;
12099a747e4fSDavid du Colombier }
12109a747e4fSDavid du Colombier }
12119a747e4fSDavid du Colombier else{
12129a747e4fSDavid du Colombier if(ctlr->medium >= 0 && block[0] != ctlr->medium)
12139a747e4fSDavid du Colombier return 0;
12149a747e4fSDavid du Colombier /* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
12159a747e4fSDavid du Colombier return 0;
12169a747e4fSDavid du Colombier if(type0mode(ctlr, block, wait))
12179a747e4fSDavid du Colombier return 0;
12189a747e4fSDavid du Colombier }
12199a747e4fSDavid du Colombier
12209a747e4fSDavid du Colombier if(ctlr->csr6){
12219a747e4fSDavid du Colombier if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
12229a747e4fSDavid du Colombier return 10;
12239a747e4fSDavid du Colombier return 100;
12249a747e4fSDavid du Colombier }
12259a747e4fSDavid du Colombier
12269a747e4fSDavid du Colombier return 0;
12279a747e4fSDavid du Colombier }
12289a747e4fSDavid du Colombier
12299a747e4fSDavid du Colombier static int
media(Ether * ether,int wait)12309a747e4fSDavid du Colombier media(Ether* ether, int wait)
12319a747e4fSDavid du Colombier {
12329a747e4fSDavid du Colombier Ctlr* ctlr;
12339a747e4fSDavid du Colombier int k, mbps;
12349a747e4fSDavid du Colombier
12359a747e4fSDavid du Colombier ctlr = ether->ctlr;
12369a747e4fSDavid du Colombier for(k = 0; k < ctlr->k; k++){
12379a747e4fSDavid du Colombier mbps = mediaxx(ether, wait);
12389a747e4fSDavid du Colombier if(mbps > 0)
12399a747e4fSDavid du Colombier return mbps;
12409a747e4fSDavid du Colombier if(ctlr->curk == 0)
12419a747e4fSDavid du Colombier ctlr->curk = ctlr->k-1;
12429a747e4fSDavid du Colombier else
12439a747e4fSDavid du Colombier ctlr->curk--;
12449a747e4fSDavid du Colombier }
12459a747e4fSDavid du Colombier
12469a747e4fSDavid du Colombier return 0;
12479a747e4fSDavid du Colombier }
12489a747e4fSDavid du Colombier
12499a747e4fSDavid du Colombier static char* mediatable[9] = {
12509a747e4fSDavid du Colombier "10BASE-T", /* TP */
12519a747e4fSDavid du Colombier "10BASE-2", /* BNC */
12529a747e4fSDavid du Colombier "10BASE-5", /* AUI */
12539a747e4fSDavid du Colombier "100BASE-TX",
12549a747e4fSDavid du Colombier "10BASE-TFD",
12559a747e4fSDavid du Colombier "100BASE-TXFD",
12569a747e4fSDavid du Colombier "100BASE-T4",
12579a747e4fSDavid du Colombier "100BASE-FX",
12589a747e4fSDavid du Colombier "100BASE-FXFD",
12599a747e4fSDavid du Colombier };
12609a747e4fSDavid du Colombier
12619a747e4fSDavid du Colombier static uchar en1207[] = { /* Accton EN1207-COMBO */
12629a747e4fSDavid du Colombier 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */
12639a747e4fSDavid du Colombier 0x00, /* [3] spare */
12649a747e4fSDavid du Colombier
12659a747e4fSDavid du Colombier 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
12669a747e4fSDavid du Colombier 0x1F, /* [6] general purpose control */
12679a747e4fSDavid du Colombier 2, /* [7] block count */
12689a747e4fSDavid du Colombier
12699a747e4fSDavid du Colombier 0x00, /* [8] media code (10BASE-TX) */
12709a747e4fSDavid du Colombier 0x0B, /* [9] general purpose port data */
12719a747e4fSDavid du Colombier 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
12729a747e4fSDavid du Colombier
12739a747e4fSDavid du Colombier 0x03, /* [8] media code (100BASE-TX) */
12749a747e4fSDavid du Colombier 0x1B, /* [9] general purpose port data */
12759a747e4fSDavid du Colombier 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
12769a747e4fSDavid du Colombier
12779a747e4fSDavid du Colombier /* There is 10BASE-2 as well, but... */
12789a747e4fSDavid du Colombier };
12799a747e4fSDavid du Colombier
12809a747e4fSDavid du Colombier static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */
12819a747e4fSDavid du Colombier 0x00, 0x00, 0x92, /* [0] vendor ethernet code */
12829a747e4fSDavid du Colombier 0x00, /* [3] spare */
12839a747e4fSDavid du Colombier
12849a747e4fSDavid du Colombier 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
12859a747e4fSDavid du Colombier 0x3F, /* [6] general purpose control */
12869a747e4fSDavid du Colombier 1, /* [7] block count */
12879a747e4fSDavid du Colombier
12889a747e4fSDavid du Colombier 0x07, /* [8] media code (100BASE-FX) */
12899a747e4fSDavid du Colombier 0x03, /* [9] general purpose port data */
12909a747e4fSDavid du Colombier 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */
12919a747e4fSDavid du Colombier };
12929a747e4fSDavid du Colombier
12939a747e4fSDavid du Colombier static uchar smc9332[] = { /* SMC 9332 */
12949a747e4fSDavid du Colombier 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */
12959a747e4fSDavid du Colombier 0x00, /* [3] spare */
12969a747e4fSDavid du Colombier
12979a747e4fSDavid du Colombier 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
12989a747e4fSDavid du Colombier 0x1F, /* [6] general purpose control */
12999a747e4fSDavid du Colombier 2, /* [7] block count */
13009a747e4fSDavid du Colombier
13019a747e4fSDavid du Colombier 0x00, /* [8] media code (10BASE-TX) */
13029a747e4fSDavid du Colombier 0x00, /* [9] general purpose port data */
13039a747e4fSDavid du Colombier 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
13049a747e4fSDavid du Colombier
13059a747e4fSDavid du Colombier 0x03, /* [8] media code (100BASE-TX) */
13069a747e4fSDavid du Colombier 0x09, /* [9] general purpose port data */
13079a747e4fSDavid du Colombier 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
13089a747e4fSDavid du Colombier };
13099a747e4fSDavid du Colombier
13109a747e4fSDavid du Colombier static uchar* leaf21140[] = {
13119a747e4fSDavid du Colombier en1207, /* Accton EN1207-COMBO */
13129a747e4fSDavid du Colombier ana6910fx, /* Adaptec (Cogent) ANA-6910FX */
13139a747e4fSDavid du Colombier smc9332, /* SMC 9332 */
13149a747e4fSDavid du Colombier nil,
13159a747e4fSDavid du Colombier };
13169a747e4fSDavid du Colombier
13179a747e4fSDavid du Colombier /*
13189a747e4fSDavid du Colombier * Copied to ctlr->srom at offset 20.
13199a747e4fSDavid du Colombier */
13209a747e4fSDavid du Colombier static uchar leafpnic[] = {
13219a747e4fSDavid du Colombier 0x00, 0x00, 0x00, 0x00, /* MAC address */
13229a747e4fSDavid du Colombier 0x00, 0x00,
13239a747e4fSDavid du Colombier 0x00, /* controller 0 device number */
13249a747e4fSDavid du Colombier 0x1E, 0x00, /* controller 0 info leaf offset */
13259a747e4fSDavid du Colombier 0x00, /* reserved */
13269a747e4fSDavid du Colombier 0x00, 0x08, /* selected connection type */
13279a747e4fSDavid du Colombier 0x00, /* general purpose control */
13289a747e4fSDavid du Colombier 0x01, /* block count */
13299a747e4fSDavid du Colombier
13309a747e4fSDavid du Colombier 0x8C, /* format indicator and count */
13319a747e4fSDavid du Colombier 0x01, /* block type */
13329a747e4fSDavid du Colombier 0x00, /* PHY number */
13339a747e4fSDavid du Colombier 0x00, /* GPR sequence length */
13349a747e4fSDavid du Colombier 0x00, /* reset sequence length */
13359a747e4fSDavid du Colombier 0x00, 0x78, /* media capabilities */
13369a747e4fSDavid du Colombier 0xE0, 0x01, /* Nway advertisment */
13379a747e4fSDavid du Colombier 0x00, 0x50, /* FDX bitmap */
13389a747e4fSDavid du Colombier 0x00, 0x18, /* TTM bitmap */
13399a747e4fSDavid du Colombier };
13409a747e4fSDavid du Colombier
13419a747e4fSDavid du Colombier static int
srom(Ctlr * ctlr)13429a747e4fSDavid du Colombier srom(Ctlr* ctlr)
13439a747e4fSDavid du Colombier {
13449a747e4fSDavid du Colombier int i, k, oui, phy, x;
13459a747e4fSDavid du Colombier uchar *p;
13469a747e4fSDavid du Colombier
13479a747e4fSDavid du Colombier /*
13489a747e4fSDavid du Colombier * This is a partial decoding of the SROM format described in
13499a747e4fSDavid du Colombier * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
13509a747e4fSDavid du Colombier * 2-Mar-98'. Only the 2114[03] are handled, support for other
13519a747e4fSDavid du Colombier * controllers can be added as needed.
13529a747e4fSDavid du Colombier * Do a dummy read first to get the size and allocate ctlr->srom.
13539a747e4fSDavid du Colombier */
13549a747e4fSDavid du Colombier sromr(ctlr, 0);
13559a747e4fSDavid du Colombier if(ctlr->srom == nil)
13569a747e4fSDavid du Colombier ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
13579a747e4fSDavid du Colombier for(i = 0; i < (1<<ctlr->sromsz); i++){
13589a747e4fSDavid du Colombier x = sromr(ctlr, i);
13599a747e4fSDavid du Colombier ctlr->srom[2*i] = x;
13609a747e4fSDavid du Colombier ctlr->srom[2*i+1] = x>>8;
13619a747e4fSDavid du Colombier }
13629a747e4fSDavid du Colombier
13639a747e4fSDavid du Colombier /*
13649a747e4fSDavid du Colombier * There are 2 SROM layouts:
13659a747e4fSDavid du Colombier * e.g. Digital EtherWORKS station address at offset 20;
13669a747e4fSDavid du Colombier * this complies with the 21140A SROM
13679a747e4fSDavid du Colombier * application note from Digital;
13689a747e4fSDavid du Colombier * e.g. SMC9332 station address at offset 0 followed by
13699a747e4fSDavid du Colombier * 2 additional bytes, repeated at offset
13709a747e4fSDavid du Colombier * 6; the 8 bytes are also repeated in
13719a747e4fSDavid du Colombier * reverse order at offset 8.
13729a747e4fSDavid du Colombier * To check which it is, read the SROM and check for the repeating
13739a747e4fSDavid du Colombier * patterns of the non-compliant cards; if that fails use the one at
13749a747e4fSDavid du Colombier * offset 20.
13759a747e4fSDavid du Colombier */
13769a747e4fSDavid du Colombier ctlr->sromea = ctlr->srom;
13779a747e4fSDavid du Colombier for(i = 0; i < 8; i++){
13789a747e4fSDavid du Colombier x = ctlr->srom[i];
13799a747e4fSDavid du Colombier if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
13809a747e4fSDavid du Colombier ctlr->sromea = &ctlr->srom[20];
13819a747e4fSDavid du Colombier break;
13829a747e4fSDavid du Colombier }
13839a747e4fSDavid du Colombier }
13849a747e4fSDavid du Colombier
13859a747e4fSDavid du Colombier /*
13869a747e4fSDavid du Colombier * Fake up the SROM for the PNIC.
13879a747e4fSDavid du Colombier * It looks like a 21140 with a PHY.
13889a747e4fSDavid du Colombier * The MAC address is byte-swapped in the orginal SROM data.
13899a747e4fSDavid du Colombier */
13909a747e4fSDavid du Colombier if(ctlr->id == Pnic){
13919a747e4fSDavid du Colombier memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
13929a747e4fSDavid du Colombier for(i = 0; i < Eaddrlen; i += 2){
13939a747e4fSDavid du Colombier ctlr->srom[20+i] = ctlr->srom[i+1];
13949a747e4fSDavid du Colombier ctlr->srom[20+i+1] = ctlr->srom[i];
13959a747e4fSDavid du Colombier }
13969a747e4fSDavid du Colombier }
13979a747e4fSDavid du Colombier
13989a747e4fSDavid du Colombier /*
13999a747e4fSDavid du Colombier * Next, try to find the info leaf in the SROM for media detection.
14009a747e4fSDavid du Colombier * If it's a non-conforming card try to match the vendor ethernet code
14019a747e4fSDavid du Colombier * and point p at a fake info leaf with compact 21140 entries.
14029a747e4fSDavid du Colombier */
14039a747e4fSDavid du Colombier if(ctlr->sromea == ctlr->srom){
14049a747e4fSDavid du Colombier p = nil;
14059a747e4fSDavid du Colombier for(i = 0; leaf21140[i] != nil; i++){
14069a747e4fSDavid du Colombier if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
14079a747e4fSDavid du Colombier p = &leaf21140[i][4];
14089a747e4fSDavid du Colombier break;
14099a747e4fSDavid du Colombier }
14109a747e4fSDavid du Colombier }
14119a747e4fSDavid du Colombier if(p == nil)
14129a747e4fSDavid du Colombier return -1;
14139a747e4fSDavid du Colombier }
14149a747e4fSDavid du Colombier else
14159a747e4fSDavid du Colombier p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
14169a747e4fSDavid du Colombier
14179a747e4fSDavid du Colombier /*
14189a747e4fSDavid du Colombier * Set up the info needed for later media detection.
14199a747e4fSDavid du Colombier * For the 21140, set the general-purpose mask in CSR12.
14209a747e4fSDavid du Colombier * The info block entries are stored in order of increasing
14219a747e4fSDavid du Colombier * precedence, so detection will work backwards through the
14229a747e4fSDavid du Colombier * stored indexes into ctlr->srom.
14239a747e4fSDavid du Colombier * If an entry is found which matches the selected connection
14249a747e4fSDavid du Colombier * type, save the index. Otherwise, start at the last entry.
14259a747e4fSDavid du Colombier * If any MII entries are found (type 1 and 3 blocks), scan
14269a747e4fSDavid du Colombier * for PHYs.
14279a747e4fSDavid du Colombier */
14289a747e4fSDavid du Colombier ctlr->leaf = p;
14299a747e4fSDavid du Colombier ctlr->sct = *p++;
14309a747e4fSDavid du Colombier ctlr->sct |= *p++<<8;
14319a747e4fSDavid du Colombier if(ctlr->id != Tulip3){
14329a747e4fSDavid du Colombier csr32w(ctlr, 12, Gpc|*p++);
14339a747e4fSDavid du Colombier delay(200);
14349a747e4fSDavid du Colombier }
14359a747e4fSDavid du Colombier ctlr->k = *p++;
14369a747e4fSDavid du Colombier if(ctlr->k >= nelem(ctlr->infoblock))
14379a747e4fSDavid du Colombier ctlr->k = nelem(ctlr->infoblock)-1;
14389a747e4fSDavid du Colombier ctlr->sctk = ctlr->k-1;
14399a747e4fSDavid du Colombier phy = 0;
14409a747e4fSDavid du Colombier for(k = 0; k < ctlr->k; k++){
14419a747e4fSDavid du Colombier ctlr->infoblock[k] = p;
14429a747e4fSDavid du Colombier /*
14439a747e4fSDavid du Colombier * The RAMIX PMC665 has a badly-coded SROM,
14449a747e4fSDavid du Colombier * hence the test for 21143 and type 3.
14459a747e4fSDavid du Colombier */
14469a747e4fSDavid du Colombier if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
14479a747e4fSDavid du Colombier *p |= 0x80;
14489a747e4fSDavid du Colombier if(*(p+1) == 1 || *(p+1) == 3)
14499a747e4fSDavid du Colombier phy = 1;
14509a747e4fSDavid du Colombier if(*(p+1) == 5)
14519a747e4fSDavid du Colombier ctlr->type5block = p;
14529a747e4fSDavid du Colombier p += (*p & ~0x80)+1;
14539a747e4fSDavid du Colombier }
14549a747e4fSDavid du Colombier else{
14559a747e4fSDavid du Colombier debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
14569a747e4fSDavid du Colombier p[0], p[1], p[2], p[3]);
14579a747e4fSDavid du Colombier if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
14589a747e4fSDavid du Colombier ctlr->sctk = k;
14599a747e4fSDavid du Colombier p += 4;
14609a747e4fSDavid du Colombier }
14619a747e4fSDavid du Colombier }
14629a747e4fSDavid du Colombier ctlr->curk = ctlr->sctk;
14639a747e4fSDavid du Colombier debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
14649a747e4fSDavid du Colombier ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
14659a747e4fSDavid du Colombier
14669a747e4fSDavid du Colombier if(phy){
14679a747e4fSDavid du Colombier x = 0;
14689a747e4fSDavid du Colombier for(k = 0; k < nelem(ctlr->phy); k++){
14699a747e4fSDavid du Colombier if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
14709a747e4fSDavid du Colombier continue;
14719a747e4fSDavid du Colombier if(DEBUG){
14729a747e4fSDavid du Colombier oui = (oui & 0x3FF)<<6;
14739a747e4fSDavid du Colombier oui |= miir(ctlr, k, 3)>>10;
14749a747e4fSDavid du Colombier miir(ctlr, k, 1);
14759a747e4fSDavid du Colombier debug("phy%d: index %d oui %uX reg1 %uX\n",
14769a747e4fSDavid du Colombier x, k, oui, miir(ctlr, k, 1));
14779a747e4fSDavid du Colombier USED(oui);
14789a747e4fSDavid du Colombier }
14799a747e4fSDavid du Colombier ctlr->phy[x] = k;
14809a747e4fSDavid du Colombier }
14819a747e4fSDavid du Colombier }
14829a747e4fSDavid du Colombier
14839a747e4fSDavid du Colombier ctlr->fd = 0;
14849a747e4fSDavid du Colombier ctlr->medium = -1;
14859a747e4fSDavid du Colombier
14869a747e4fSDavid du Colombier return 0;
14879a747e4fSDavid du Colombier }
14889a747e4fSDavid du Colombier
14899a747e4fSDavid du Colombier static void
dec2114xpci(void)14909a747e4fSDavid du Colombier dec2114xpci(void)
14919a747e4fSDavid du Colombier {
14929a747e4fSDavid du Colombier Ctlr *ctlr;
14939a747e4fSDavid du Colombier Pcidev *p;
14949a747e4fSDavid du Colombier int x;
14959a747e4fSDavid du Colombier
14969a747e4fSDavid du Colombier p = nil;
14979a747e4fSDavid du Colombier while(p = pcimatch(p, 0, 0)){
14989a747e4fSDavid du Colombier if(p->ccrb != 0x02 || p->ccru != 0)
14999a747e4fSDavid du Colombier continue;
15009a747e4fSDavid du Colombier switch((p->did<<16)|p->vid){
15019a747e4fSDavid du Colombier default:
15029a747e4fSDavid du Colombier continue;
15039a747e4fSDavid du Colombier
15049a747e4fSDavid du Colombier case Tulip3: /* 21143 */
15059a747e4fSDavid du Colombier /*
15069a747e4fSDavid du Colombier * Exit sleep mode.
15079a747e4fSDavid du Colombier */
15089a747e4fSDavid du Colombier x = pcicfgr32(p, 0x40);
15099a747e4fSDavid du Colombier x &= ~0xc0000000;
15109a747e4fSDavid du Colombier pcicfgw32(p, 0x40, x);
15119a747e4fSDavid du Colombier /*FALLTHROUGH*/
15129a747e4fSDavid du Colombier
15139a747e4fSDavid du Colombier case Pnic: /* PNIC */
15149a747e4fSDavid du Colombier case Pnic2: /* PNIC-II */
15159a747e4fSDavid du Colombier case Tulip0: /* 21140 */
15169a747e4fSDavid du Colombier break;
15179a747e4fSDavid du Colombier }
15189a747e4fSDavid du Colombier
15199a747e4fSDavid du Colombier /*
15209a747e4fSDavid du Colombier * bar[0] is the I/O port register address and
15219a747e4fSDavid du Colombier * bar[1] is the memory-mapped register address.
15229a747e4fSDavid du Colombier */
15239a747e4fSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
15249a747e4fSDavid du Colombier ctlr->port = p->mem[0].bar & ~0x01;
15259a747e4fSDavid du Colombier ctlr->pcidev = p;
15269a747e4fSDavid du Colombier ctlr->id = (p->did<<16)|p->vid;
15279a747e4fSDavid du Colombier
15289a747e4fSDavid du Colombier if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
15299a747e4fSDavid du Colombier print("dec2114x: port 0x%uX in use\n", ctlr->port);
15309a747e4fSDavid du Colombier free(ctlr);
15319a747e4fSDavid du Colombier continue;
15329a747e4fSDavid du Colombier }
15339a747e4fSDavid du Colombier
15349a747e4fSDavid du Colombier /*
15359a747e4fSDavid du Colombier * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
15369a747e4fSDavid du Colombier * set or they don't always work right after a hardware
15379a747e4fSDavid du Colombier * reset.
15389a747e4fSDavid du Colombier */
15399a747e4fSDavid du Colombier csr32w(ctlr, 6, Mbo|Ps);
15409a747e4fSDavid du Colombier softreset(ctlr);
15419a747e4fSDavid du Colombier
15429a747e4fSDavid du Colombier if(srom(ctlr)){
15439a747e4fSDavid du Colombier iofree(ctlr->port);
15449a747e4fSDavid du Colombier free(ctlr);
15459a747e4fSDavid du Colombier continue;
15469a747e4fSDavid du Colombier }
15479a747e4fSDavid du Colombier
15489a747e4fSDavid du Colombier switch(ctlr->id){
15499a747e4fSDavid du Colombier default:
15509a747e4fSDavid du Colombier break;
15519a747e4fSDavid du Colombier
15529a747e4fSDavid du Colombier case Pnic: /* PNIC */
15539a747e4fSDavid du Colombier /*
15549a747e4fSDavid du Colombier * Turn off the jabber timer.
15559a747e4fSDavid du Colombier */
15569a747e4fSDavid du Colombier csr32w(ctlr, 15, 0x00000001);
15579a747e4fSDavid du Colombier break;
15589a747e4fSDavid du Colombier }
15599a747e4fSDavid du Colombier
15609a747e4fSDavid du Colombier if(ctlrhead != nil)
15619a747e4fSDavid du Colombier ctlrtail->next = ctlr;
15629a747e4fSDavid du Colombier else
15639a747e4fSDavid du Colombier ctlrhead = ctlr;
15649a747e4fSDavid du Colombier ctlrtail = ctlr;
15659a747e4fSDavid du Colombier }
15669a747e4fSDavid du Colombier }
15679a747e4fSDavid du Colombier
15689a747e4fSDavid du Colombier static int
reset(Ether * ether)15699a747e4fSDavid du Colombier reset(Ether* ether)
15709a747e4fSDavid du Colombier {
15719a747e4fSDavid du Colombier Ctlr *ctlr;
15729a747e4fSDavid du Colombier int i, x;
15739a747e4fSDavid du Colombier uchar ea[Eaddrlen];
15749a747e4fSDavid du Colombier static int scandone;
15759a747e4fSDavid du Colombier
15769a747e4fSDavid du Colombier if(scandone == 0){
15779a747e4fSDavid du Colombier dec2114xpci();
15789a747e4fSDavid du Colombier scandone = 1;
15799a747e4fSDavid du Colombier }
15809a747e4fSDavid du Colombier
15819a747e4fSDavid du Colombier /*
15829a747e4fSDavid du Colombier * Any adapter matches if no ether->port is supplied,
15839a747e4fSDavid du Colombier * otherwise the ports must match.
15849a747e4fSDavid du Colombier */
15859a747e4fSDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
15869a747e4fSDavid du Colombier if(ctlr->active)
15879a747e4fSDavid du Colombier continue;
15889a747e4fSDavid du Colombier if(ether->port == 0 || ether->port == ctlr->port){
15899a747e4fSDavid du Colombier ctlr->active = 1;
15909a747e4fSDavid du Colombier break;
15919a747e4fSDavid du Colombier }
15929a747e4fSDavid du Colombier }
15939a747e4fSDavid du Colombier if(ctlr == nil)
15949a747e4fSDavid du Colombier return -1;
15959a747e4fSDavid du Colombier
15969a747e4fSDavid du Colombier ether->ctlr = ctlr;
15979a747e4fSDavid du Colombier ether->port = ctlr->port;
15989a747e4fSDavid du Colombier // ether->irq = ctlr->pcidev->intl;
15999a747e4fSDavid du Colombier ether->irq = 2; /* arrrrrgh */
16009a747e4fSDavid du Colombier ether->tbdf = ctlr->pcidev->tbdf;
16019a747e4fSDavid du Colombier
16029a747e4fSDavid du Colombier /*
16039a747e4fSDavid du Colombier * Check if the adapter's station address is to be overridden.
16049a747e4fSDavid du Colombier * If not, read it from the EEPROM and set in ether->ea prior to
16059a747e4fSDavid du Colombier * loading the station address in the hardware.
16069a747e4fSDavid du Colombier */
16079a747e4fSDavid du Colombier memset(ea, 0, Eaddrlen);
16089a747e4fSDavid du Colombier if(memcmp(ea, ether->ea, Eaddrlen) == 0)
16099a747e4fSDavid du Colombier memmove(ether->ea, ctlr->sromea, Eaddrlen);
16109a747e4fSDavid du Colombier
16119a747e4fSDavid du Colombier /*
16129a747e4fSDavid du Colombier * Look for a medium override in case there's no autonegotiation
16139a747e4fSDavid du Colombier * (no MII) or the autonegotiation fails.
16149a747e4fSDavid du Colombier */
16159a747e4fSDavid du Colombier for(i = 0; i < ether->nopt; i++){
16169a747e4fSDavid du Colombier if(cistrcmp(ether->opt[i], "FD") == 0){
16179a747e4fSDavid du Colombier ctlr->fd = 1;
16189a747e4fSDavid du Colombier continue;
16199a747e4fSDavid du Colombier }
16209a747e4fSDavid du Colombier for(x = 0; x < nelem(mediatable); x++){
16219a747e4fSDavid du Colombier debug("compare <%s> <%s>\n", mediatable[x],
16229a747e4fSDavid du Colombier ether->opt[i]);
16239a747e4fSDavid du Colombier if(cistrcmp(mediatable[x], ether->opt[i]))
16249a747e4fSDavid du Colombier continue;
16259a747e4fSDavid du Colombier ctlr->medium = x;
16269a747e4fSDavid du Colombier
16279a747e4fSDavid du Colombier switch(ctlr->medium){
16289a747e4fSDavid du Colombier default:
16299a747e4fSDavid du Colombier ctlr->fd = 0;
16309a747e4fSDavid du Colombier break;
16319a747e4fSDavid du Colombier
16329a747e4fSDavid du Colombier case 0x04: /* 10BASE-TFD */
16339a747e4fSDavid du Colombier case 0x05: /* 100BASE-TXFD */
16349a747e4fSDavid du Colombier case 0x08: /* 100BASE-FXFD */
16359a747e4fSDavid du Colombier ctlr->fd = 1;
16369a747e4fSDavid du Colombier break;
16379a747e4fSDavid du Colombier }
16389a747e4fSDavid du Colombier break;
16399a747e4fSDavid du Colombier }
16409a747e4fSDavid du Colombier }
16419a747e4fSDavid du Colombier
16429a747e4fSDavid du Colombier ether->mbps = media(ether, 1);
16439a747e4fSDavid du Colombier
16449a747e4fSDavid du Colombier /*
16459a747e4fSDavid du Colombier * Initialise descriptor rings, ethernet address.
16469a747e4fSDavid du Colombier */
16479a747e4fSDavid du Colombier ctlr->nrdr = Nrde;
16489a747e4fSDavid du Colombier ctlr->ntdr = Ntde;
16499a747e4fSDavid du Colombier pcisetbme(ctlr->pcidev);
16509a747e4fSDavid du Colombier ctlrinit(ether);
16519a747e4fSDavid du Colombier
16529a747e4fSDavid du Colombier /*
16539a747e4fSDavid du Colombier * Linkage to the generic ethernet driver.
16549a747e4fSDavid du Colombier */
16559a747e4fSDavid du Colombier ether->attach = attach;
16569a747e4fSDavid du Colombier ether->transmit = transmit;
16579a747e4fSDavid du Colombier ether->interrupt = interrupt;
16589a747e4fSDavid du Colombier ether->ifstat = ifstat;
16599a747e4fSDavid du Colombier
16609a747e4fSDavid du Colombier ether->arg = ether;
16619a747e4fSDavid du Colombier ether->promiscuous = promiscuous;
16629a747e4fSDavid du Colombier
16639a747e4fSDavid du Colombier return 0;
16649a747e4fSDavid du Colombier }
16659a747e4fSDavid du Colombier
16669a747e4fSDavid du Colombier void
ether2114xlink(void)16679a747e4fSDavid du Colombier ether2114xlink(void)
16689a747e4fSDavid du Colombier {
16699a747e4fSDavid du Colombier addethercard("21140", reset);
16709a747e4fSDavid du Colombier addethercard("2114x", reset);
16719a747e4fSDavid du Colombier }
1672