18e32b400SDavid du Colombier /*
28e32b400SDavid du Colombier * SMSC 9221 Ethernet driver
38e32b400SDavid du Colombier * specifically for the ISEE IGEPv2 board,
48e32b400SDavid du Colombier * where it is assigned to Chip Select 5,
58e32b400SDavid du Colombier * its registers are at 0x2c000000 (inherited from u-boot),
68e32b400SDavid du Colombier * and irq is 34 from gpio pin 176, thus gpio module 6.
78e32b400SDavid du Colombier *
88e32b400SDavid du Colombier * it's slow due to the use of fifos instead of buffer rings.
98e32b400SDavid du Colombier * the slow system dma just makes it worse.
108e32b400SDavid du Colombier *
118e32b400SDavid du Colombier * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221.
128e32b400SDavid du Colombier */
138e32b400SDavid du Colombier #include "u.h"
148e32b400SDavid du Colombier #include "../port/lib.h"
158e32b400SDavid du Colombier #include "mem.h"
168e32b400SDavid du Colombier #include "dat.h"
178e32b400SDavid du Colombier #include "fns.h"
188e32b400SDavid du Colombier #include "io.h"
198e32b400SDavid du Colombier #include "../port/error.h"
208e32b400SDavid du Colombier #include "../port/netif.h"
218e32b400SDavid du Colombier
228e32b400SDavid du Colombier #include "etherif.h"
238e32b400SDavid du Colombier
248e32b400SDavid du Colombier /* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */
258e32b400SDavid du Colombier #undef USE_KPROCS
268e32b400SDavid du Colombier
278e32b400SDavid du Colombier enum {
288e32b400SDavid du Colombier Vid9221 = 0x9221,
298e32b400SDavid du Colombier Slop = 4, /* beyond ETHERMAXTU */
308e32b400SDavid du Colombier };
318e32b400SDavid du Colombier
328e32b400SDavid du Colombier typedef struct Regs Regs;
338e32b400SDavid du Colombier struct Regs {
348e32b400SDavid du Colombier /* fifo ports */
358e32b400SDavid du Colombier ulong rxdata;
368e32b400SDavid du Colombier uchar _pad0[0x20 - 4];
378e32b400SDavid du Colombier ulong txdata;
388e32b400SDavid du Colombier uchar _pad1[0x40 - 0x24];
398e32b400SDavid du Colombier ulong rxsts;
408e32b400SDavid du Colombier ulong rxstspeek;
418e32b400SDavid du Colombier ulong txsts;
428e32b400SDavid du Colombier ulong txstspeek;
438e32b400SDavid du Colombier
448e32b400SDavid du Colombier /* control & status */
458e32b400SDavid du Colombier ushort rev; /* chip revision */
468e32b400SDavid du Colombier ushort id; /* chip id, 0x9221 */
478e32b400SDavid du Colombier ulong irqcfg;
488e32b400SDavid du Colombier ulong intsts;
498e32b400SDavid du Colombier ulong inten;
508e32b400SDavid du Colombier ulong _pad2;
518e32b400SDavid du Colombier ulong bytetest;
528e32b400SDavid du Colombier ulong fifoint; /* fifo level interrupts */
538e32b400SDavid du Colombier ulong rxcfg;
548e32b400SDavid du Colombier ulong txcfg;
558e32b400SDavid du Colombier ulong hwcfg;
568e32b400SDavid du Colombier ulong rxdpctl; /* rx data path control */
578e32b400SDavid du Colombier ulong rxfifoinf;
588e32b400SDavid du Colombier ulong txfifoinf;
598e32b400SDavid du Colombier ulong pmtctl; /* power mgmt. control */
608e32b400SDavid du Colombier ulong gpiocfg;
618e32b400SDavid du Colombier ulong gptcfg; /* timer */
628e32b400SDavid du Colombier ulong gptcnt;
638e32b400SDavid du Colombier ulong _pad3;
648e32b400SDavid du Colombier ulong wordswap;
658e32b400SDavid du Colombier ulong freerun; /* counters */
668e32b400SDavid du Colombier ulong rxdrop;
678e32b400SDavid du Colombier
688e32b400SDavid du Colombier /*
698e32b400SDavid du Colombier * mac registers are accessed indirectly via the mac csr registers.
708e32b400SDavid du Colombier * phy registers are doubly indirect, via the mac csr mii_acc &
718e32b400SDavid du Colombier * mii_data mac csr registers.
728e32b400SDavid du Colombier */
738e32b400SDavid du Colombier ulong maccsrcmd; /* mac csr synchronizer */
748e32b400SDavid du Colombier ulong maccsrdata;
758e32b400SDavid du Colombier ulong afccfg; /* automatic flow control cfg. */
768e32b400SDavid du Colombier ulong eepcmd; /* eeprom */
778e32b400SDavid du Colombier ulong eepdata;
788e32b400SDavid du Colombier /* 0xb8 */
798e32b400SDavid du Colombier };
808e32b400SDavid du Colombier
818e32b400SDavid du Colombier enum {
828e32b400SDavid du Colombier Nstatistics = 128,
838e32b400SDavid du Colombier };
848e32b400SDavid du Colombier
858e32b400SDavid du Colombier enum {
868e32b400SDavid du Colombier /* txcmda bits */
878e32b400SDavid du Colombier Intcompl = 1<<31,
888e32b400SDavid du Colombier Bufendalign = 3<<24, /* mask */
898e32b400SDavid du Colombier Datastoff = 037<<16, /* mask */
908e32b400SDavid du Colombier Firstseg = 1<<13,
918e32b400SDavid du Colombier Lastseg = 1<<12,
928e32b400SDavid du Colombier Bufsize = MASK(11),
938e32b400SDavid du Colombier
948e32b400SDavid du Colombier /* txcmdb bits */
958e32b400SDavid du Colombier Pkttag = MASK(16) << 16,
968e32b400SDavid du Colombier Txcksumen = 1<<14,
978e32b400SDavid du Colombier Addcrcdis = 1<<13,
988e32b400SDavid du Colombier Framepaddis = 1<<12,
998e32b400SDavid du Colombier Pktlen = (1<<1) - 1, /* mask */
1008e32b400SDavid du Colombier
1018e32b400SDavid du Colombier /* txcfg bits */
1028e32b400SDavid du Colombier Txsdump = 1<<15, /* flush tx status fifo */
1038e32b400SDavid du Colombier Txddump = 1<<14, /* flush tx data fifo */
1048e32b400SDavid du Colombier Txon = 1<<1,
1058e32b400SDavid du Colombier Stoptx = 1<<0,
1068e32b400SDavid du Colombier
1078e32b400SDavid du Colombier /* hwcfg bits */
1088e32b400SDavid du Colombier Mbo = 1<<20, /* must be one */
1098e32b400SDavid du Colombier Srstto = 1<<1, /* soft reset time-out */
1108e32b400SDavid du Colombier Srst = 1<<0,
1118e32b400SDavid du Colombier
1128e32b400SDavid du Colombier /* rxcfg bits */
1138e32b400SDavid du Colombier Rxdmacntshift = 16, /* ulong count, 12 bits wide */
1148e32b400SDavid du Colombier Rxdmacntmask = MASK(12) << Rxdmacntshift,
1158e32b400SDavid du Colombier Rxdump = 1<<15, /* flush rx fifos */
1168e32b400SDavid du Colombier
1178e32b400SDavid du Colombier /* rxsts bits */
1188e32b400SDavid du Colombier Rxpktlenshift = 16, /* byte count */
1198e32b400SDavid du Colombier Rxpktlenmask = MASK(14) << Rxpktlenshift,
1208e32b400SDavid du Colombier Rxerr = 1<<15,
1218e32b400SDavid du Colombier
1228e32b400SDavid du Colombier /* rxfifoinf bits */
1238e32b400SDavid du Colombier Rxstsusedshift = 16, /* ulong count */
1248e32b400SDavid du Colombier Rxstsusedmask = MASK(8) << Rxstsusedshift,
1258e32b400SDavid du Colombier Rxdatausedmask = MASK(16), /* byte count */
1268e32b400SDavid du Colombier
1278e32b400SDavid du Colombier /* txfifoinf bits */
1288e32b400SDavid du Colombier Txstsusedshift = 16, /* ulong count */
1298e32b400SDavid du Colombier Txstsusedmask = MASK(8) << Txstsusedshift,
1308e32b400SDavid du Colombier Txdatafreemask = MASK(16), /* byte count */
1318e32b400SDavid du Colombier
1328e32b400SDavid du Colombier /* pmtctl bits */
1338e32b400SDavid du Colombier Dready = 1<<0,
1348e32b400SDavid du Colombier
1358e32b400SDavid du Colombier /* maccsrcmd bits */
1368e32b400SDavid du Colombier Csrbusy = 1<<31,
1378e32b400SDavid du Colombier Csrread = 1<<30, /* not write */
1388e32b400SDavid du Colombier Csraddrshift = 0,
1398e32b400SDavid du Colombier Csraddrmask = MASK(8) - 1,
1408e32b400SDavid du Colombier
1418e32b400SDavid du Colombier /* mac registers' indices */
1428e32b400SDavid du Colombier Maccr = 1,
1438e32b400SDavid du Colombier Macaddrh,
1448e32b400SDavid du Colombier Macaddrl,
1458e32b400SDavid du Colombier Machashh,
1468e32b400SDavid du Colombier Machashl,
1478e32b400SDavid du Colombier Macmiiacc, /* for doubly-indirect phy access */
1488e32b400SDavid du Colombier Macmiidata,
1498e32b400SDavid du Colombier Macflow,
1508e32b400SDavid du Colombier Macvlan1,
1518e32b400SDavid du Colombier Macvlan2,
1528e32b400SDavid du Colombier Macwuff,
1538e32b400SDavid du Colombier Macwucsr,
1548e32b400SDavid du Colombier Maccoe,
1558e32b400SDavid du Colombier
1568e32b400SDavid du Colombier /* Maccr bits */
1578e32b400SDavid du Colombier Rxall = 1<<31,
1588e32b400SDavid du Colombier Rcvown = 1<<23, /* don't receive own transmissions */
1598e32b400SDavid du Colombier Fdpx = 1<<20, /* full duplex */
1608e32b400SDavid du Colombier Mcpas = 1<<19, /* pass all multicast */
1618e32b400SDavid du Colombier Prms = 1<<18, /* promiscuous */
1628e32b400SDavid du Colombier Ho = 1<<15, /* hash-only filtering */
1638e32b400SDavid du Colombier Hpfilt = 1<<13, /* hash/perfect filtering */
1648e32b400SDavid du Colombier Padstr = 1<<8, /* strip padding & fcs (crc) */
1658e32b400SDavid du Colombier Txen = 1<<3,
1668e32b400SDavid du Colombier Rxen = 1<<2,
1678e32b400SDavid du Colombier
1688e32b400SDavid du Colombier /* irqcfg bits */
1698e32b400SDavid du Colombier Irqdeasclr = 1<<14, /* deassertion intv'l clear */
1708e32b400SDavid du Colombier Irqdeassts = 1<<13, /* deassertion intv'l status */
1718e32b400SDavid du Colombier Irqint = 1<<12, /* intr being asserted? (ro) */
1728e32b400SDavid du Colombier Irqen = 1<<8,
1738e32b400SDavid du Colombier Irqpol = 1<<4, /* irq output is active high */
1748e32b400SDavid du Colombier Irqpushpull = 1<<0, /* irq output is push/pull driver */
1758e32b400SDavid du Colombier
1768e32b400SDavid du Colombier /* intsts/inten bits */
1778e32b400SDavid du Colombier Swint = 1<<31, /* generate an interrupt */
1788e32b400SDavid du Colombier Txstop = 1<<25,
1798e32b400SDavid du Colombier Rxstop = 1<<24,
1808e32b400SDavid du Colombier Txioc = 1<<21,
1818e32b400SDavid du Colombier Rxdma = 1<<20,
1828e32b400SDavid du Colombier Gptimer = 1<<19,
1838e32b400SDavid du Colombier Phy = 1<<18,
1848e32b400SDavid du Colombier Rxe = 1<<14, /* errors */
1858e32b400SDavid du Colombier Txe = 1<<13,
1868e32b400SDavid du Colombier Tdfo = 1<<10, /* tx data fifo overrun */
1878e32b400SDavid du Colombier Tdfa = 1<<9, /* tx data fifo available */
1888e32b400SDavid du Colombier Tsff = 1<<8, /* tx status fifo full */
1898e32b400SDavid du Colombier Tsfl = 1<<7, /* tx status fifo level */
1908e32b400SDavid du Colombier Rsff = 1<<4, /* rx status fifo full */
1918e32b400SDavid du Colombier Rsfl = 1<<3, /* rx status fifo level */
1928e32b400SDavid du Colombier
1938e32b400SDavid du Colombier /* eepcmd bits */
1948e32b400SDavid du Colombier Epcbusy = 1<<31,
1958e32b400SDavid du Colombier Epccmdshift = 28, /* interesting one is Reload (7) */
1968e32b400SDavid du Colombier Epctimeout = 1<<9,
1978e32b400SDavid du Colombier Epcmacloaded = 1<<8,
1988e32b400SDavid du Colombier Epcaddrshift = 0,
1998e32b400SDavid du Colombier };
2008e32b400SDavid du Colombier
2018e32b400SDavid du Colombier enum {
2028e32b400SDavid du Colombier Rxintrs = Rsff | Rsfl | Rxe,
2038e32b400SDavid du Colombier Txintrs = Tsff | Tsfl | Txe | Txioc,
2048e32b400SDavid du Colombier };
2058e32b400SDavid du Colombier
2068e32b400SDavid du Colombier /* wake-up frame filter */
2078e32b400SDavid du Colombier struct Wakeup {
2088e32b400SDavid du Colombier ulong bytemask[4]; /* index is filter # */
2098e32b400SDavid du Colombier uchar filt0cmd; /* filter 0 command */
2108e32b400SDavid du Colombier uchar _pad0;
2118e32b400SDavid du Colombier uchar filt1cmd;
2128e32b400SDavid du Colombier uchar _pad1;
2138e32b400SDavid du Colombier uchar filt2cmd;
2148e32b400SDavid du Colombier uchar _pad2;
2158e32b400SDavid du Colombier uchar filt3cmd;
2168e32b400SDavid du Colombier uchar _pad3;
2178e32b400SDavid du Colombier uchar offset[4]; /* index is filter # */
2188e32b400SDavid du Colombier ushort crc16[4]; /* " */
2198e32b400SDavid du Colombier };
2208e32b400SDavid du Colombier
2218e32b400SDavid du Colombier typedef struct Ctlr Ctlr;
2228e32b400SDavid du Colombier struct Ctlr {
2238e32b400SDavid du Colombier int port;
2248e32b400SDavid du Colombier Ctlr* next;
2258e32b400SDavid du Colombier Ether* edev;
2268e32b400SDavid du Colombier Regs* regs;
2278e32b400SDavid du Colombier int active;
2288e32b400SDavid du Colombier int started;
2298e32b400SDavid du Colombier int inited;
2308e32b400SDavid du Colombier int id;
2318e32b400SDavid du Colombier int cls;
2328e32b400SDavid du Colombier ushort eeprom[0x40];
2338e32b400SDavid du Colombier
2348e32b400SDavid du Colombier QLock alock; /* attach */
2358e32b400SDavid du Colombier int nrb; /* how many this Ctlr has in the pool */
2368e32b400SDavid du Colombier
2378e32b400SDavid du Colombier int* nic;
2388e32b400SDavid du Colombier Lock imlock;
2398e32b400SDavid du Colombier int im; /* interrupt mask */
2408e32b400SDavid du Colombier
2418e32b400SDavid du Colombier // Mii* mii;
2428e32b400SDavid du Colombier // Rendez lrendez;
2438e32b400SDavid du Colombier int lim;
2448e32b400SDavid du Colombier
2458e32b400SDavid du Colombier int link;
2468e32b400SDavid du Colombier
2478e32b400SDavid du Colombier QLock slock;
2488e32b400SDavid du Colombier uint statistics[Nstatistics];
2498e32b400SDavid du Colombier uint lsleep;
2508e32b400SDavid du Colombier uint lintr;
2518e32b400SDavid du Colombier uint rsleep;
2528e32b400SDavid du Colombier uint rintr;
2538e32b400SDavid du Colombier int tsleep;
2548e32b400SDavid du Colombier uint tintr;
2558e32b400SDavid du Colombier
2568e32b400SDavid du Colombier uchar ra[Eaddrlen]; /* receive address */
2578e32b400SDavid du Colombier ulong mta[128]; /* multicast table array */
2588e32b400SDavid du Colombier
2598e32b400SDavid du Colombier Rendez rrendez;
2608e32b400SDavid du Colombier int gotinput;
2618e32b400SDavid du Colombier int rdcpydone;
2628e32b400SDavid du Colombier
2638e32b400SDavid du Colombier Rendez trendez;
2648e32b400SDavid du Colombier int gotoutput;
2658e32b400SDavid du Colombier int wrcpydone;
2668e32b400SDavid du Colombier
2678e32b400SDavid du Colombier Lock tlock;
2688e32b400SDavid du Colombier };
2698e32b400SDavid du Colombier
2708e32b400SDavid du Colombier #define csr32r(c, r) (*((c)->nic+((r)/4)))
2718e32b400SDavid du Colombier #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
2728e32b400SDavid du Colombier
2738e32b400SDavid du Colombier static Ctlr *smcctlrhead, *smcctlrtail;
2748e32b400SDavid du Colombier
2758e32b400SDavid du Colombier static char* statistics[Nstatistics] = { "dummy", };
2768e32b400SDavid du Colombier
2778e32b400SDavid du Colombier static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, };
2788e32b400SDavid du Colombier
2798e32b400SDavid du Colombier static void etherclock(void);
2808e32b400SDavid du Colombier static void smcreceive(Ether *edev);
2818e32b400SDavid du Colombier static void smcinterrupt(Ureg*, void* arg);
2828e32b400SDavid du Colombier
2838e32b400SDavid du Colombier static Ether *thisether;
2848e32b400SDavid du Colombier static int attached;
2858e32b400SDavid du Colombier
2868e32b400SDavid du Colombier static void
smconce(Ether * edev)2878e32b400SDavid du Colombier smconce(Ether *edev)
2888e32b400SDavid du Colombier {
2898e32b400SDavid du Colombier static int beenhere;
2908e32b400SDavid du Colombier static Lock l;
2918e32b400SDavid du Colombier
2928e32b400SDavid du Colombier ilock(&l);
2938e32b400SDavid du Colombier if (!beenhere && edev != nil) {
2948e32b400SDavid du Colombier beenhere = 1;
2958e32b400SDavid du Colombier /* simulate interrupts if we don't know the irq */
2968e32b400SDavid du Colombier if (edev->irq < 0) { /* poll as backup */
2978e32b400SDavid du Colombier thisether = edev;
2988e32b400SDavid du Colombier addclock0link(etherclock, 1000/HZ);
2998e32b400SDavid du Colombier iprint(" polling");
3008e32b400SDavid du Colombier }
3018e32b400SDavid du Colombier }
3028e32b400SDavid du Colombier iunlock(&l);
3038e32b400SDavid du Colombier }
3048e32b400SDavid du Colombier
3058e32b400SDavid du Colombier /*
3068e32b400SDavid du Colombier * indirect (mac) register access
3078e32b400SDavid du Colombier */
3088e32b400SDavid du Colombier
3098e32b400SDavid du Colombier static void
macwait(Regs * regs)3108e32b400SDavid du Colombier macwait(Regs *regs)
3118e32b400SDavid du Colombier {
3128e32b400SDavid du Colombier long bound;
3138e32b400SDavid du Colombier
314*df2dbabfSDavid du Colombier for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--)
3158e32b400SDavid du Colombier ;
3168e32b400SDavid du Colombier if (bound <= 0)
3178e32b400SDavid du Colombier iprint("smc: mac registers didn't come ready\n");
3188e32b400SDavid du Colombier }
3198e32b400SDavid du Colombier
3208e32b400SDavid du Colombier static ulong
macrd(Regs * regs,uchar index)3218e32b400SDavid du Colombier macrd(Regs *regs, uchar index)
3228e32b400SDavid du Colombier {
3238e32b400SDavid du Colombier macwait(regs);
3248e32b400SDavid du Colombier regs->maccsrcmd = Csrbusy | Csrread | index;
3258e32b400SDavid du Colombier coherence(); /* back-to-back write/read delay per §6.2.1 */
3268e32b400SDavid du Colombier macwait(regs);
3278e32b400SDavid du Colombier return regs->maccsrdata;
3288e32b400SDavid du Colombier }
3298e32b400SDavid du Colombier
3308e32b400SDavid du Colombier static void
macwr(Regs * regs,uchar index,ulong val)3318e32b400SDavid du Colombier macwr(Regs *regs, uchar index, ulong val)
3328e32b400SDavid du Colombier {
3338e32b400SDavid du Colombier macwait(regs);
3348e32b400SDavid du Colombier regs->maccsrdata = val;
3358e32b400SDavid du Colombier regs->maccsrcmd = Csrbusy | index; /* fire */
3368e32b400SDavid du Colombier macwait(regs);
3378e32b400SDavid du Colombier }
3388e32b400SDavid du Colombier
3398e32b400SDavid du Colombier
3408e32b400SDavid du Colombier static long
smcifstat(Ether * edev,void * a,long n,ulong offset)3418e32b400SDavid du Colombier smcifstat(Ether* edev, void* a, long n, ulong offset)
3428e32b400SDavid du Colombier {
3438e32b400SDavid du Colombier Ctlr *ctlr;
3448e32b400SDavid du Colombier char *p, *s;
3458e32b400SDavid du Colombier int i, l, r;
3468e32b400SDavid du Colombier
3478e32b400SDavid du Colombier ctlr = edev->ctlr;
3488e32b400SDavid du Colombier qlock(&ctlr->slock);
3498e32b400SDavid du Colombier p = malloc(READSTR);
3508e32b400SDavid du Colombier l = 0;
3518e32b400SDavid du Colombier for(i = 0; i < Nstatistics; i++){
3528e32b400SDavid du Colombier // read regs->rxdrop TODO
3538e32b400SDavid du Colombier r = 0;
3548e32b400SDavid du Colombier if((s = statistics[i]) == nil)
3558e32b400SDavid du Colombier continue;
3568e32b400SDavid du Colombier switch(i){
3578e32b400SDavid du Colombier default:
3588e32b400SDavid du Colombier ctlr->statistics[i] += r;
3598e32b400SDavid du Colombier if(ctlr->statistics[i] == 0)
3608e32b400SDavid du Colombier continue;
3618e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
3628e32b400SDavid du Colombier s, ctlr->statistics[i], r);
3638e32b400SDavid du Colombier break;
3648e32b400SDavid du Colombier }
3658e32b400SDavid du Colombier }
3668e32b400SDavid du Colombier
3678e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
3688e32b400SDavid du Colombier ctlr->lintr, ctlr->lsleep);
3698e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
3708e32b400SDavid du Colombier ctlr->rintr, ctlr->rsleep);
3718e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
3728e32b400SDavid du Colombier ctlr->tintr, ctlr->tsleep);
3738e32b400SDavid du Colombier
3748e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "eeprom:");
3758e32b400SDavid du Colombier for(i = 0; i < 0x40; i++){
3768e32b400SDavid du Colombier if(i && ((i & 0x07) == 0))
3778e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
3788e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
3798e32b400SDavid du Colombier }
3808e32b400SDavid du Colombier l += snprint(p+l, READSTR-l, "\n");
3818e32b400SDavid du Colombier USED(l);
3828e32b400SDavid du Colombier
3838e32b400SDavid du Colombier n = readstr(offset, a, n, p);
3848e32b400SDavid du Colombier free(p);
3858e32b400SDavid du Colombier qunlock(&ctlr->slock);
3868e32b400SDavid du Colombier
3878e32b400SDavid du Colombier return n;
3888e32b400SDavid du Colombier }
3898e32b400SDavid du Colombier
3908e32b400SDavid du Colombier static void
smcpromiscuous(void * arg,int on)3918e32b400SDavid du Colombier smcpromiscuous(void* arg, int on)
3928e32b400SDavid du Colombier {
3938e32b400SDavid du Colombier int rctl;
3948e32b400SDavid du Colombier Ctlr *ctlr;
3958e32b400SDavid du Colombier Ether *edev;
3968e32b400SDavid du Colombier Regs *regs;
3978e32b400SDavid du Colombier
3988e32b400SDavid du Colombier edev = arg;
3998e32b400SDavid du Colombier ctlr = edev->ctlr;
4008e32b400SDavid du Colombier regs = ctlr->regs;
4018e32b400SDavid du Colombier rctl = macrd(regs, Maccr);
4028e32b400SDavid du Colombier if(on)
4038e32b400SDavid du Colombier rctl |= Prms;
4048e32b400SDavid du Colombier else
4058e32b400SDavid du Colombier rctl &= ~Prms;
4068e32b400SDavid du Colombier macwr(regs, Maccr, rctl);
4078e32b400SDavid du Colombier }
4088e32b400SDavid du Colombier
4098e32b400SDavid du Colombier static void
smcmulticast(void *,uchar *,int)4108e32b400SDavid du Colombier smcmulticast(void*, uchar*, int)
4118e32b400SDavid du Colombier {
4128e32b400SDavid du Colombier /* nothing to do, we allow all multicast packets in */
4138e32b400SDavid du Colombier }
4148e32b400SDavid du Colombier
4158e32b400SDavid du Colombier static int
iswrcpydone(void * arg)4168e32b400SDavid du Colombier iswrcpydone(void *arg)
4178e32b400SDavid du Colombier {
4188e32b400SDavid du Colombier return ((Ctlr *)arg)->wrcpydone;
4198e32b400SDavid du Colombier }
4208e32b400SDavid du Colombier
4218e32b400SDavid du Colombier static int
smctxstart(Ctlr * ctlr,uchar * ubuf,uint len)4228e32b400SDavid du Colombier smctxstart(Ctlr *ctlr, uchar *ubuf, uint len)
4238e32b400SDavid du Colombier {
4248e32b400SDavid du Colombier uint wds, ruplen;
4258e32b400SDavid du Colombier ulong *wdp, *txdp;
4268e32b400SDavid du Colombier Regs *regs;
4278e32b400SDavid du Colombier static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)];
4288e32b400SDavid du Colombier
4298e32b400SDavid du Colombier if (!ctlr->inited) {
4308e32b400SDavid du Colombier iprint("smctxstart: too soon to send\n");
4318e32b400SDavid du Colombier return -1; /* toss it */
4328e32b400SDavid du Colombier }
4338e32b400SDavid du Colombier regs = ctlr->regs;
4348e32b400SDavid du Colombier
4358e32b400SDavid du Colombier /* is there room for a packet in the tx data fifo? */
4368e32b400SDavid du Colombier if (len < ETHERMINTU)
4378e32b400SDavid du Colombier iprint("sending too-short (%d) pkt\n", len);
4388e32b400SDavid du Colombier else if (len > ETHERMAXTU)
4398e32b400SDavid du Colombier iprint("sending jumbo (%d) pkt\n", len);
4408e32b400SDavid du Colombier
4418e32b400SDavid du Colombier ruplen = ROUNDUP(len, sizeof(ulong));
4428e32b400SDavid du Colombier coherence(); /* back-to-back read/read delay per §6.2.2 */
4438e32b400SDavid du Colombier if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong))
4448e32b400SDavid du Colombier return -1; /* not enough room for data + command words */
4458e32b400SDavid du Colombier
4468e32b400SDavid du Colombier if ((uintptr)ubuf & MASK(2)) { /* ensure word alignment */
4478e32b400SDavid du Colombier memmove(buf, ubuf, len);
4488e32b400SDavid du Colombier ubuf = (uchar *)buf;
4498e32b400SDavid du Colombier }
4508e32b400SDavid du Colombier
4518e32b400SDavid du Colombier /* tx cmd a: length is bytes in this buffer */
4528e32b400SDavid du Colombier txdp = ®s->txdata;
4538e32b400SDavid du Colombier *txdp = Intcompl | Firstseg | Lastseg | len;
4548e32b400SDavid du Colombier /* tx cmd b: length is bytes in this packet (could be multiple buf.s) */
4558e32b400SDavid du Colombier *txdp = len;
4568e32b400SDavid du Colombier
4578e32b400SDavid du Colombier /* shovel pkt into tx fifo, which triggers transmission due to Txon */
4588e32b400SDavid du Colombier wdp = (ulong *)ubuf;
4598e32b400SDavid du Colombier for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; )
4608e32b400SDavid du Colombier *txdp = *wdp++;
4618e32b400SDavid du Colombier
4628e32b400SDavid du Colombier regs->intsts = Txintrs; /* dismiss intr */
4638e32b400SDavid du Colombier coherence();
4648e32b400SDavid du Colombier regs->inten |= Txintrs;
4658e32b400SDavid du Colombier coherence(); /* back-to-back write/read delay per §6.2.1 */
4668e32b400SDavid du Colombier return 0;
4678e32b400SDavid du Colombier }
4688e32b400SDavid du Colombier
4698e32b400SDavid du Colombier static void
smctransmit(Ether * edev)4708e32b400SDavid du Colombier smctransmit(Ether* edev)
4718e32b400SDavid du Colombier {
4728e32b400SDavid du Colombier Block *bp;
4738e32b400SDavid du Colombier Ctlr *ctlr;
4748e32b400SDavid du Colombier
4758e32b400SDavid du Colombier ctlr = edev->ctlr;
4768e32b400SDavid du Colombier if (ctlr == nil)
4778e32b400SDavid du Colombier panic("smctransmit: nil ctlr");
4788e32b400SDavid du Colombier ilock(&ctlr->tlock);
4798e32b400SDavid du Colombier /*
4808e32b400SDavid du Colombier * Try to fill the chip's buffers back up, via the tx fifo.
4818e32b400SDavid du Colombier */
4828e32b400SDavid du Colombier while ((bp = qget(edev->oq)) != nil)
4838e32b400SDavid du Colombier if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) {
4848e32b400SDavid du Colombier qputback(edev->oq, bp); /* retry the block later */
4858e32b400SDavid du Colombier iprint("smctransmit: tx data fifo full\n");
4868e32b400SDavid du Colombier break;
4878e32b400SDavid du Colombier } else
4888e32b400SDavid du Colombier freeb(bp);
4898e32b400SDavid du Colombier iunlock(&ctlr->tlock);
4908e32b400SDavid du Colombier }
4918e32b400SDavid du Colombier
4928e32b400SDavid du Colombier static void
smctransmitcall(Ether * edev)4938e32b400SDavid du Colombier smctransmitcall(Ether *edev) /* called from devether.c */
4948e32b400SDavid du Colombier {
4958e32b400SDavid du Colombier Ctlr *ctlr;
4968e32b400SDavid du Colombier
4978e32b400SDavid du Colombier ctlr = edev->ctlr;
4988e32b400SDavid du Colombier ctlr->gotoutput = 1;
4998e32b400SDavid du Colombier #ifdef USE_KPROCS
5008e32b400SDavid du Colombier wakeup(&ctlr->trendez);
5018e32b400SDavid du Colombier #else
5028e32b400SDavid du Colombier smctransmit(edev);
5038e32b400SDavid du Colombier #endif
5048e32b400SDavid du Colombier }
5058e32b400SDavid du Colombier
5068e32b400SDavid du Colombier static int
smcrim(void * ctlr)5078e32b400SDavid du Colombier smcrim(void* ctlr)
5088e32b400SDavid du Colombier {
5098e32b400SDavid du Colombier return ((Ctlr*)ctlr)->gotinput;
5108e32b400SDavid du Colombier }
5118e32b400SDavid du Colombier
5128e32b400SDavid du Colombier static void
smcrproc(void * arg)5138e32b400SDavid du Colombier smcrproc(void* arg)
5148e32b400SDavid du Colombier {
5158e32b400SDavid du Colombier Ctlr *ctlr;
5168e32b400SDavid du Colombier Ether *edev;
5178e32b400SDavid du Colombier
5188e32b400SDavid du Colombier edev = arg;
5198e32b400SDavid du Colombier ctlr = edev->ctlr;
5208e32b400SDavid du Colombier for(;;){
5218e32b400SDavid du Colombier ctlr->rsleep++;
5228e32b400SDavid du Colombier sleep(&ctlr->rrendez, smcrim, ctlr);
5238e32b400SDavid du Colombier
5248e32b400SDavid du Colombier /* process any newly-arrived packets and pass to etheriq */
5258e32b400SDavid du Colombier ctlr->gotinput = 0;
5268e32b400SDavid du Colombier smcreceive(edev);
5278e32b400SDavid du Colombier }
5288e32b400SDavid du Colombier }
5298e32b400SDavid du Colombier
5308e32b400SDavid du Colombier static int
smcgotout(void * ctlr)5318e32b400SDavid du Colombier smcgotout(void* ctlr)
5328e32b400SDavid du Colombier {
5338e32b400SDavid du Colombier return ((Ctlr*)ctlr)->gotoutput;
5348e32b400SDavid du Colombier }
5358e32b400SDavid du Colombier
5368e32b400SDavid du Colombier static void
smctproc(void * arg)5378e32b400SDavid du Colombier smctproc(void* arg)
5388e32b400SDavid du Colombier {
5398e32b400SDavid du Colombier Ctlr *ctlr;
5408e32b400SDavid du Colombier Ether *edev;
5418e32b400SDavid du Colombier
5428e32b400SDavid du Colombier edev = arg;
5438e32b400SDavid du Colombier ctlr = edev->ctlr;
5448e32b400SDavid du Colombier for(;;){
5458e32b400SDavid du Colombier ctlr->tsleep++;
5468e32b400SDavid du Colombier sleep(&ctlr->trendez, smcgotout, ctlr);
5478e32b400SDavid du Colombier
5488e32b400SDavid du Colombier /* process any newly-arrived packets and pass to etheriq */
5498e32b400SDavid du Colombier ctlr->gotoutput = 0;
5508e32b400SDavid du Colombier smctransmit(edev);
5518e32b400SDavid du Colombier }
5528e32b400SDavid du Colombier }
5538e32b400SDavid du Colombier
5548e32b400SDavid du Colombier void gpioirqclr(void);
5558e32b400SDavid du Colombier
5568e32b400SDavid du Colombier static void
smcattach(Ether * edev)5578e32b400SDavid du Colombier smcattach(Ether* edev)
5588e32b400SDavid du Colombier {
5598e32b400SDavid du Colombier #ifdef USE_KPROCS
5608e32b400SDavid du Colombier char name[KNAMELEN];
5618e32b400SDavid du Colombier #endif
5628e32b400SDavid du Colombier Ctlr *ctlr;
5638e32b400SDavid du Colombier
5648e32b400SDavid du Colombier ctlr = edev->ctlr;
5658e32b400SDavid du Colombier qlock(&ctlr->alock);
5668e32b400SDavid du Colombier if(waserror()){
5678e32b400SDavid du Colombier qunlock(&ctlr->alock);
5688e32b400SDavid du Colombier nexterror();
5698e32b400SDavid du Colombier }
5708e32b400SDavid du Colombier if (!ctlr->inited) {
5718e32b400SDavid du Colombier ctlr->inited = 1;
5728e32b400SDavid du Colombier #ifdef USE_KPROCS
5738e32b400SDavid du Colombier snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
5748e32b400SDavid du Colombier kproc(name, smcrproc, edev);
5758e32b400SDavid du Colombier
5768e32b400SDavid du Colombier snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno);
5778e32b400SDavid du Colombier kproc(name, smctproc, edev);
5788e32b400SDavid du Colombier #endif
5798e32b400SDavid du Colombier
5808e32b400SDavid du Colombier iprint("smcattach:");
5818e32b400SDavid du Colombier #ifdef USE_KPROCS
5828e32b400SDavid du Colombier iprint(" with kprocs");
5838e32b400SDavid du Colombier #else
5848e32b400SDavid du Colombier iprint(" no kprocs");
5858e32b400SDavid du Colombier #endif
5868e32b400SDavid du Colombier iprint(", no dma");
5878e32b400SDavid du Colombier /* can now accept real or simulated interrupts */
5888e32b400SDavid du Colombier
5898e32b400SDavid du Colombier smconce(edev);
5908e32b400SDavid du Colombier attached = 1;
5918e32b400SDavid du Colombier iprint("\n");
5928e32b400SDavid du Colombier }
5938e32b400SDavid du Colombier qunlock(&ctlr->alock);
5948e32b400SDavid du Colombier poperror();
5958e32b400SDavid du Colombier }
5968e32b400SDavid du Colombier
5978e32b400SDavid du Colombier static int
isrdcpydone(void * arg)5988e32b400SDavid du Colombier isrdcpydone(void *arg)
5998e32b400SDavid du Colombier {
6008e32b400SDavid du Colombier return ((Ctlr *)arg)->rdcpydone;
6018e32b400SDavid du Colombier }
6028e32b400SDavid du Colombier
6038e32b400SDavid du Colombier static void
smcreceive(Ether * edev)6048e32b400SDavid du Colombier smcreceive(Ether *edev)
6058e32b400SDavid du Colombier {
6068e32b400SDavid du Colombier uint wds, len, sts;
6078e32b400SDavid du Colombier ulong *wdp, *rxdp;
6088e32b400SDavid du Colombier Block *bp;
6098e32b400SDavid du Colombier Ctlr *ctlr;
6108e32b400SDavid du Colombier Regs *regs;
6118e32b400SDavid du Colombier
6128e32b400SDavid du Colombier ctlr = edev->ctlr;
6138e32b400SDavid du Colombier regs = ctlr->regs;
6148e32b400SDavid du Colombier coherence(); /* back-to-back read/read delay per §6.2.2 */
6158e32b400SDavid du Colombier /*
6168e32b400SDavid du Colombier * is there a full packet in the rx data fifo?
6178e32b400SDavid du Colombier */
6188e32b400SDavid du Colombier while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) {
6198e32b400SDavid du Colombier coherence();
6208e32b400SDavid du Colombier sts = regs->rxsts; /* pop rx status */
6218e32b400SDavid du Colombier if(sts & Rxerr)
6228e32b400SDavid du Colombier iprint("smcreceive: rx error\n");
6238e32b400SDavid du Colombier len = (sts & Rxpktlenmask) >> Rxpktlenshift;
6248e32b400SDavid du Colombier if (len > ETHERMAXTU + Slop)
6258e32b400SDavid du Colombier iprint("smcreceive: oversized rx pkt (%d)\n", len);
6268e32b400SDavid du Colombier else if (len < ETHERMINTU)
6278e32b400SDavid du Colombier iprint("smcreceive: too-short (%d) pkt\n", len);
6288e32b400SDavid du Colombier wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong);
6298e32b400SDavid du Colombier if (wds > 0) {
6308e32b400SDavid du Colombier /* copy aligned words from rx fifo into a Block */
6318e32b400SDavid du Colombier bp = iallocb(len + sizeof(ulong) /* - 1 */);
6328e32b400SDavid du Colombier if (bp == nil)
6338e32b400SDavid du Colombier panic("smcreceive: nil Block*");
6348e32b400SDavid du Colombier
6358e32b400SDavid du Colombier /* bp->rp should be 32-byte aligned, more than we need */
6368e32b400SDavid du Colombier assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0);
6378e32b400SDavid du Colombier wdp = (ulong *)bp->rp;
6388e32b400SDavid du Colombier rxdp = ®s->rxdata;
6398e32b400SDavid du Colombier wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1;
6408e32b400SDavid du Colombier while (--wds > 0)
6418e32b400SDavid du Colombier *wdp++ = *rxdp;
6428e32b400SDavid du Colombier bp->wp = bp->rp + len;
6438e32b400SDavid du Colombier
6448e32b400SDavid du Colombier /* and push the Block upstream */
6458e32b400SDavid du Colombier if (ctlr->inited)
6468e32b400SDavid du Colombier etheriq(edev, bp, 1);
6478e32b400SDavid du Colombier else
6488e32b400SDavid du Colombier freeb(bp);
6498e32b400SDavid du Colombier
6508e32b400SDavid du Colombier regs->intsts = Rxintrs; /* dismiss intr */
6518e32b400SDavid du Colombier coherence();
6528e32b400SDavid du Colombier regs->inten |= Rxintrs;
6538e32b400SDavid du Colombier }
6548e32b400SDavid du Colombier coherence();
6558e32b400SDavid du Colombier }
6568e32b400SDavid du Colombier regs->inten |= Rxintrs;
6578e32b400SDavid du Colombier coherence();
6588e32b400SDavid du Colombier }
6598e32b400SDavid du Colombier
6608e32b400SDavid du Colombier /*
6618e32b400SDavid du Colombier * disable the stsclr bits in inten and write them to intsts to ack and dismiss
6628e32b400SDavid du Colombier * the interrupt source.
6638e32b400SDavid du Colombier */
6648e32b400SDavid du Colombier void
ackintr(Regs * regs,ulong stsclr)6658e32b400SDavid du Colombier ackintr(Regs *regs, ulong stsclr)
6668e32b400SDavid du Colombier {
6678e32b400SDavid du Colombier if (stsclr == 0)
6688e32b400SDavid du Colombier return;
6698e32b400SDavid du Colombier
6708e32b400SDavid du Colombier regs->inten &= ~stsclr;
6718e32b400SDavid du Colombier coherence();
6728e32b400SDavid du Colombier
6738e32b400SDavid du Colombier // regs->intsts = stsclr; /* acknowledge & clear intr(s) */
6748e32b400SDavid du Colombier // coherence();
6758e32b400SDavid du Colombier }
6768e32b400SDavid du Colombier
6778e32b400SDavid du Colombier static void
smcinterrupt(Ureg *,void * arg)6788e32b400SDavid du Colombier smcinterrupt(Ureg*, void* arg)
6798e32b400SDavid du Colombier {
6808e32b400SDavid du Colombier int junk;
6818e32b400SDavid du Colombier unsigned intsts, intr;
6828e32b400SDavid du Colombier Ctlr *ctlr;
6838e32b400SDavid du Colombier Ether *edev;
6848e32b400SDavid du Colombier Regs *regs;
6858e32b400SDavid du Colombier
6868e32b400SDavid du Colombier edev = arg;
6878e32b400SDavid du Colombier ctlr = edev->ctlr;
6888e32b400SDavid du Colombier ilock(&ctlr->imlock);
6898e32b400SDavid du Colombier regs = ctlr->regs;
6908e32b400SDavid du Colombier
6918e32b400SDavid du Colombier gpioirqclr();
6928e32b400SDavid du Colombier
6938e32b400SDavid du Colombier coherence(); /* back-to-back read/read delay per §6.2.2 */
6948e32b400SDavid du Colombier intsts = regs->intsts;
6958e32b400SDavid du Colombier coherence();
6968e32b400SDavid du Colombier
6978e32b400SDavid du Colombier intsts &= ~MASK(3); /* ignore gpio bits */
6988e32b400SDavid du Colombier if (0 && intsts == 0) {
6998e32b400SDavid du Colombier coherence();
7008e32b400SDavid du Colombier iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n",
7018e32b400SDavid du Colombier intsts, regs->inten);
7028e32b400SDavid du Colombier }
7038e32b400SDavid du Colombier
7048e32b400SDavid du Colombier intr = intsts & Rxintrs;
7058e32b400SDavid du Colombier if(intr) {
7068e32b400SDavid du Colombier /* disable interrupt sources; kproc/smcreceive will reenable */
7078e32b400SDavid du Colombier ackintr(regs, intr);
7088e32b400SDavid du Colombier
7098e32b400SDavid du Colombier ctlr->rintr++;
7108e32b400SDavid du Colombier ctlr->gotinput = 1;
7118e32b400SDavid du Colombier #ifdef USE_KPROCS
7128e32b400SDavid du Colombier wakeup(&ctlr->rrendez);
7138e32b400SDavid du Colombier #else
7148e32b400SDavid du Colombier smcreceive(edev);
7158e32b400SDavid du Colombier #endif
7168e32b400SDavid du Colombier }
7178e32b400SDavid du Colombier
7188e32b400SDavid du Colombier while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) {
7198e32b400SDavid du Colombier /* probably indicates tx completion, just toss it */
7208e32b400SDavid du Colombier junk = regs->txsts; /* pop tx sts */
7218e32b400SDavid du Colombier USED(junk);
7228e32b400SDavid du Colombier coherence();
7238e32b400SDavid du Colombier }
7248e32b400SDavid du Colombier
7258e32b400SDavid du Colombier intr = intsts & Txintrs;
7268e32b400SDavid du Colombier if (ctlr->gotoutput || intr) {
7278e32b400SDavid du Colombier /* disable interrupt sources; kproc/smctransmit will reenable */
7288e32b400SDavid du Colombier ackintr(regs, intr);
7298e32b400SDavid du Colombier
7308e32b400SDavid du Colombier ctlr->tintr++;
7318e32b400SDavid du Colombier ctlr->gotoutput = 1;
7328e32b400SDavid du Colombier #ifdef USE_KPROCS
7338e32b400SDavid du Colombier wakeup(&ctlr->trendez);
7348e32b400SDavid du Colombier #else
7358e32b400SDavid du Colombier smctransmit(edev);
7368e32b400SDavid du Colombier #endif
7378e32b400SDavid du Colombier }
7388e32b400SDavid du Colombier
7398e32b400SDavid du Colombier iunlock(&ctlr->imlock);
7408e32b400SDavid du Colombier }
7418e32b400SDavid du Colombier
7428e32b400SDavid du Colombier static void
etherclock(void)7438e32b400SDavid du Colombier etherclock(void)
7448e32b400SDavid du Colombier {
7458e32b400SDavid du Colombier smcinterrupt(nil, thisether);
7468e32b400SDavid du Colombier }
7478e32b400SDavid du Colombier
7488e32b400SDavid du Colombier static int
smcmii(Ctlr *)7498e32b400SDavid du Colombier smcmii(Ctlr *)
7508e32b400SDavid du Colombier {
7518e32b400SDavid du Colombier return 0;
7528e32b400SDavid du Colombier }
7538e32b400SDavid du Colombier
7548e32b400SDavid du Colombier static int
smcdetach(Ctlr * ctlr)7558e32b400SDavid du Colombier smcdetach(Ctlr* ctlr)
7568e32b400SDavid du Colombier {
7578e32b400SDavid du Colombier Regs *regs;
7588e32b400SDavid du Colombier
7598e32b400SDavid du Colombier if (ctlr == nil || ctlr->regs == nil)
7608e32b400SDavid du Colombier return -1;
7618e32b400SDavid du Colombier regs = ctlr->regs;
7628e32b400SDavid du Colombier /* verify that it's real by reading a few registers */
7638e32b400SDavid du Colombier switch (regs->id) {
7648e32b400SDavid du Colombier case Vid9221:
7658e32b400SDavid du Colombier break;
7668e32b400SDavid du Colombier default:
7678e32b400SDavid du Colombier print("smc: unknown chip id %#ux\n", regs->id);
7688e32b400SDavid du Colombier return -1;
7698e32b400SDavid du Colombier }
7708e32b400SDavid du Colombier regs->inten = 0; /* no interrupts */
7718e32b400SDavid du Colombier regs->intsts = ~0; /* clear any pending */
7728e32b400SDavid du Colombier regs->gptcfg = 0;
7738e32b400SDavid du Colombier coherence();
7748e32b400SDavid du Colombier regs->rxcfg = Rxdump;
7758e32b400SDavid du Colombier regs->txcfg = Txsdump | Txddump;
7768e32b400SDavid du Colombier regs->irqcfg &= ~Irqen;
7778e32b400SDavid du Colombier coherence();
7788e32b400SDavid du Colombier return 0;
7798e32b400SDavid du Colombier }
7808e32b400SDavid du Colombier
7818e32b400SDavid du Colombier static void
smcshutdown(Ether * ether)7828e32b400SDavid du Colombier smcshutdown(Ether* ether)
7838e32b400SDavid du Colombier {
7848e32b400SDavid du Colombier smcdetach(ether->ctlr);
7858e32b400SDavid du Colombier }
7868e32b400SDavid du Colombier
7878e32b400SDavid du Colombier static void
powerwait(Regs * regs)7888e32b400SDavid du Colombier powerwait(Regs *regs)
7898e32b400SDavid du Colombier {
7908e32b400SDavid du Colombier long bound;
7918e32b400SDavid du Colombier
7928e32b400SDavid du Colombier regs->bytetest = 0; /* bring power on */
793*df2dbabfSDavid du Colombier for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--)
7948e32b400SDavid du Colombier ;
7958e32b400SDavid du Colombier if (bound <= 0)
7968e32b400SDavid du Colombier iprint("smc: pmtctl didn't come ready\n");
7978e32b400SDavid du Colombier }
7988e32b400SDavid du Colombier
7998e32b400SDavid du Colombier static int
smcreset(Ctlr * ctlr)8008e32b400SDavid du Colombier smcreset(Ctlr* ctlr)
8018e32b400SDavid du Colombier {
8028e32b400SDavid du Colombier int r;
8038e32b400SDavid du Colombier Regs *regs;
8048e32b400SDavid du Colombier static char zea[Eaddrlen];
8058e32b400SDavid du Colombier
8068e32b400SDavid du Colombier regs = ctlr->regs;
8078e32b400SDavid du Colombier powerwait(regs);
8088e32b400SDavid du Colombier
8098e32b400SDavid du Colombier if(smcdetach(ctlr))
8108e32b400SDavid du Colombier return -1;
8118e32b400SDavid du Colombier
8128e32b400SDavid du Colombier /* verify that it's real by reading a few registers */
8138e32b400SDavid du Colombier switch (regs->id) {
8148e32b400SDavid du Colombier case Vid9221:
8158e32b400SDavid du Colombier break;
8168e32b400SDavid du Colombier default:
8178e32b400SDavid du Colombier print("smc: unknown chip id %#ux\n", regs->id);
8188e32b400SDavid du Colombier return -1;
8198e32b400SDavid du Colombier }
8208e32b400SDavid du Colombier if (regs->bytetest != 0x87654321) {
8218e32b400SDavid du Colombier print("smc: bytetest reg %#p (%#lux) != 0x87654321\n",
8228e32b400SDavid du Colombier ®s->bytetest, regs->bytetest);
8238e32b400SDavid du Colombier return -1;
8248e32b400SDavid du Colombier }
8258e32b400SDavid du Colombier
8268e32b400SDavid du Colombier #ifdef TODO /* read MAC from EEPROM */
8278e32b400SDavid du Colombier // int ctrl, i, pause, swdpio, txcw;
8288e32b400SDavid du Colombier /*
8298e32b400SDavid du Colombier * Snarf and set up the receive addresses.
8308e32b400SDavid du Colombier * There are 16 addresses. The first should be the MAC address.
8318e32b400SDavid du Colombier * The others are cleared and not marked valid (MS bit of Rah).
8328e32b400SDavid du Colombier */
8338e32b400SDavid du Colombier for(i = Ea; i < Eaddrlen/2; i++){
8348e32b400SDavid du Colombier ctlr->ra[2*i] = ctlr->eeprom[i];
8358e32b400SDavid du Colombier ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
8368e32b400SDavid du Colombier }
8378e32b400SDavid du Colombier
8388e32b400SDavid du Colombier /*
8398e32b400SDavid du Colombier * Clear the Multicast Table Array.
8408e32b400SDavid du Colombier * It's a 4096 bit vector accessed as 128 32-bit registers.
8418e32b400SDavid du Colombier */
8428e32b400SDavid du Colombier memset(ctlr->mta, 0, sizeof(ctlr->mta));
8438e32b400SDavid du Colombier for(i = 0; i < 128; i++)
8448e32b400SDavid du Colombier csr32w(ctlr, Mta+i*4, 0);
8458e32b400SDavid du Colombier #endif
8468e32b400SDavid du Colombier regs->hwcfg |= Mbo;
8478e32b400SDavid du Colombier
8488e32b400SDavid du Colombier /* don't overwrite existing ea */
8498e32b400SDavid du Colombier // if (memcmp(edev->ea, zea, Eaddrlen) == 0)
8508e32b400SDavid du Colombier // memmove(edev->ea, ctlr->ra, Eaddrlen);
8518e32b400SDavid du Colombier
8528e32b400SDavid du Colombier r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
8538e32b400SDavid du Colombier macwr(regs, Macaddrl, r);
8548e32b400SDavid du Colombier macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]);
8558e32b400SDavid du Colombier
8568e32b400SDavid du Colombier /* turn on the controller */
8578e32b400SDavid du Colombier macwr(regs, Maccoe, 0);
8588e32b400SDavid du Colombier regs->inten = 0; /* no interrupts yet */
8598e32b400SDavid du Colombier regs->intsts = ~0; /* clear any pending */
8608e32b400SDavid du Colombier regs->gptcfg = 0;
8618e32b400SDavid du Colombier coherence();
8628e32b400SDavid du Colombier regs->rxcfg = Rxdump;
8638e32b400SDavid du Colombier regs->txcfg = Txsdump | Txddump | Txon;
8648e32b400SDavid du Colombier regs->fifoint = 72<<24; /* default values */
8658e32b400SDavid du Colombier macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen);
8668e32b400SDavid du Colombier coherence(); /* back-to-back write/read delay per §6.2.1 */
8678e32b400SDavid du Colombier regs->irqcfg = 1<<24 | Irqen | Irqpushpull; /* deas for 10µs (linux) */
8688e32b400SDavid du Colombier coherence(); /* back-to-back write/read delay per §6.2.1 */
8698e32b400SDavid du Colombier regs->inten = Rxintrs | Txintrs;
8708e32b400SDavid du Colombier coherence();
8718e32b400SDavid du Colombier
8728e32b400SDavid du Colombier if(smcmii(ctlr) < 0)
8738e32b400SDavid du Colombier return -1;
8748e32b400SDavid du Colombier return 0;
8758e32b400SDavid du Colombier }
8768e32b400SDavid du Colombier
8778e32b400SDavid du Colombier static void
smcpci(void)8788e32b400SDavid du Colombier smcpci(void)
8798e32b400SDavid du Colombier {
8808e32b400SDavid du Colombier Ctlr *ctlr;
8818e32b400SDavid du Colombier static int beenhere;
8828e32b400SDavid du Colombier
8838e32b400SDavid du Colombier if (beenhere)
8848e32b400SDavid du Colombier return;
8858e32b400SDavid du Colombier beenhere = 1;
8868e32b400SDavid du Colombier
8878e32b400SDavid du Colombier if (probeaddr(PHYSETHER) < 0)
8888e32b400SDavid du Colombier return;
8898e32b400SDavid du Colombier ctlr = malloc(sizeof(Ctlr));
8908e32b400SDavid du Colombier ctlr->id = Vid9221<<16 | 0x0424; /* smsc 9221 */
8918e32b400SDavid du Colombier ctlr->port = PHYSETHER;
8928e32b400SDavid du Colombier ctlr->nic = (int *)PHYSETHER;
8938e32b400SDavid du Colombier ctlr->regs = (Regs *)PHYSETHER;
8948e32b400SDavid du Colombier
8958e32b400SDavid du Colombier if(smcreset(ctlr)){
8968e32b400SDavid du Colombier free(ctlr);
8978e32b400SDavid du Colombier return;
8988e32b400SDavid du Colombier }
8998e32b400SDavid du Colombier if(smcctlrhead != nil)
9008e32b400SDavid du Colombier smcctlrtail->next = ctlr;
9018e32b400SDavid du Colombier else
9028e32b400SDavid du Colombier smcctlrhead = ctlr;
9038e32b400SDavid du Colombier smcctlrtail = ctlr;
9048e32b400SDavid du Colombier }
9058e32b400SDavid du Colombier
9068e32b400SDavid du Colombier static int
smcpnp(Ether * edev)9078e32b400SDavid du Colombier smcpnp(Ether* edev)
9088e32b400SDavid du Colombier {
9098e32b400SDavid du Colombier Ctlr *ctlr;
9108e32b400SDavid du Colombier static char zea[Eaddrlen];
9118e32b400SDavid du Colombier
9128e32b400SDavid du Colombier if(smcctlrhead == nil)
9138e32b400SDavid du Colombier smcpci();
9148e32b400SDavid du Colombier
9158e32b400SDavid du Colombier /*
9168e32b400SDavid du Colombier * Any adapter matches if no edev->port is supplied,
9178e32b400SDavid du Colombier * otherwise the ports must match.
9188e32b400SDavid du Colombier */
9198e32b400SDavid du Colombier for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){
9208e32b400SDavid du Colombier if(ctlr->active)
9218e32b400SDavid du Colombier continue;
9228e32b400SDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
9238e32b400SDavid du Colombier ctlr->active = 1;
9248e32b400SDavid du Colombier break;
9258e32b400SDavid du Colombier }
9268e32b400SDavid du Colombier }
9278e32b400SDavid du Colombier if(ctlr == nil)
9288e32b400SDavid du Colombier return -1;
9298e32b400SDavid du Colombier
9308e32b400SDavid du Colombier edev->ctlr = ctlr;
9318e32b400SDavid du Colombier ctlr->edev = edev; /* point back to Ether* */
9328e32b400SDavid du Colombier edev->port = ctlr->port;
9338e32b400SDavid du Colombier edev->irq = 34;
9348e32b400SDavid du Colombier // TODO: verify speed (100Mb/s) and duplicity (full-duplex)
9358e32b400SDavid du Colombier edev->mbps = 100;
9368e32b400SDavid du Colombier
9378e32b400SDavid du Colombier /* don't overwrite existing ea */
9388e32b400SDavid du Colombier if (memcmp(edev->ea, zea, Eaddrlen) == 0)
9398e32b400SDavid du Colombier memmove(edev->ea, ctlr->ra, Eaddrlen);
9408e32b400SDavid du Colombier
9418e32b400SDavid du Colombier /*
9428e32b400SDavid du Colombier * Linkage to the generic ethernet driver.
9438e32b400SDavid du Colombier */
9448e32b400SDavid du Colombier edev->attach = smcattach;
9458e32b400SDavid du Colombier edev->transmit = smctransmitcall;
9468e32b400SDavid du Colombier edev->interrupt = smcinterrupt;
9478e32b400SDavid du Colombier edev->ifstat = smcifstat;
9488e32b400SDavid du Colombier /* edev->ctl = smcctl; /* no ctl msgs supported */
9498e32b400SDavid du Colombier
9508e32b400SDavid du Colombier edev->arg = edev;
9518e32b400SDavid du Colombier edev->promiscuous = smcpromiscuous;
9528e32b400SDavid du Colombier edev->multicast = smcmulticast;
9538e32b400SDavid du Colombier edev->shutdown = smcshutdown;
9548e32b400SDavid du Colombier return 0;
9558e32b400SDavid du Colombier }
9568e32b400SDavid du Colombier
9578e32b400SDavid du Colombier void
ether9221link(void)9588e32b400SDavid du Colombier ether9221link(void)
9598e32b400SDavid du Colombier {
9608e32b400SDavid du Colombier addethercard("9221", smcpnp);
9618e32b400SDavid du Colombier }
962