1*897ae9c1SDavid du Colombier /* 2*897ae9c1SDavid du Colombier * marvell kirkwood ethernet (88e1116) driver 3*897ae9c1SDavid du Colombier * (as found in the sheevaplug & openrd). 4*897ae9c1SDavid du Colombier * from /public/doc/marvell/88f61xx.kirkwood.pdf 5*897ae9c1SDavid du Colombier * and /public/doc/marvell/88e1116.pdf. 6*897ae9c1SDavid du Colombier */ 7*897ae9c1SDavid du Colombier 8*897ae9c1SDavid du Colombier #include "u.h" 9*897ae9c1SDavid du Colombier #include "../port/lib.h" 10*897ae9c1SDavid du Colombier #include "mem.h" 11*897ae9c1SDavid du Colombier #include "dat.h" 12*897ae9c1SDavid du Colombier #include "fns.h" 13*897ae9c1SDavid du Colombier #include "io.h" 14*897ae9c1SDavid du Colombier #include "../port/error.h" 15*897ae9c1SDavid du Colombier #include "../port/netif.h" 16*897ae9c1SDavid du Colombier 17*897ae9c1SDavid du Colombier #include "etherif.h" 18*897ae9c1SDavid du Colombier #include "ethermii.h" 19*897ae9c1SDavid du Colombier #include "../ip/ip.h" 20*897ae9c1SDavid du Colombier 21*897ae9c1SDavid du Colombier #define MIIDBG if(0)iprint 22*897ae9c1SDavid du Colombier 23*897ae9c1SDavid du Colombier enum { 24*897ae9c1SDavid du Colombier Gbe0regs = PHYSIO + 0x72000, 25*897ae9c1SDavid du Colombier Gbe1regs = PHYSIO + 0x76000, 26*897ae9c1SDavid du Colombier 27*897ae9c1SDavid du Colombier Nrx = 512, 28*897ae9c1SDavid du Colombier Ntx = 512, 29*897ae9c1SDavid du Colombier Nrxblks = 1024, 30*897ae9c1SDavid du Colombier Rxblklen = 2+1522, /* ifc. supplies first 2 bytes as padding */ 31*897ae9c1SDavid du Colombier 32*897ae9c1SDavid du Colombier Maxrxintrsec = 20*1000, /* max. rx intrs. / sec */ 33*897ae9c1SDavid du Colombier Etherstuck = 90, /* must send or receive a packet in this many sec.s */ 34*897ae9c1SDavid du Colombier 35*897ae9c1SDavid du Colombier Descralign = 16, 36*897ae9c1SDavid du Colombier Bufalign = 8, 37*897ae9c1SDavid du Colombier 38*897ae9c1SDavid du Colombier Pass = 1, /* accept packets */ 39*897ae9c1SDavid du Colombier 40*897ae9c1SDavid du Colombier Qno = 0, /* do everything on queue zero */ 41*897ae9c1SDavid du Colombier }; 42*897ae9c1SDavid du Colombier 43*897ae9c1SDavid du Colombier typedef struct Ctlr Ctlr; 44*897ae9c1SDavid du Colombier typedef struct Gbereg Gbereg; 45*897ae9c1SDavid du Colombier typedef struct Mibstats Mibstats; 46*897ae9c1SDavid du Colombier typedef struct Rx Rx; 47*897ae9c1SDavid du Colombier typedef struct Tx Tx; 48*897ae9c1SDavid du Colombier 49*897ae9c1SDavid du Colombier static struct { 50*897ae9c1SDavid du Colombier Lock; 51*897ae9c1SDavid du Colombier Block *head; 52*897ae9c1SDavid du Colombier } freeblocks; 53*897ae9c1SDavid du Colombier 54*897ae9c1SDavid du Colombier /* hardware receive buffer descriptor */ 55*897ae9c1SDavid du Colombier struct Rx { 56*897ae9c1SDavid du Colombier ulong cs; 57*897ae9c1SDavid du Colombier ulong countsize; /* bytes, buffer size */ 58*897ae9c1SDavid du Colombier ulong buf; /* phys. addr. of packet buffer */ 59*897ae9c1SDavid du Colombier ulong next; /* phys. addr. of next Rx */ 60*897ae9c1SDavid du Colombier }; 61*897ae9c1SDavid du Colombier 62*897ae9c1SDavid du Colombier /* hardware transmit buffer descriptor */ 63*897ae9c1SDavid du Colombier struct Tx { 64*897ae9c1SDavid du Colombier ulong cs; 65*897ae9c1SDavid du Colombier ulong countchk; /* bytes, checksum */ 66*897ae9c1SDavid du Colombier ulong buf; /* phys. addr. of packet buffer */ 67*897ae9c1SDavid du Colombier ulong next; /* phys. addr. of next Tx */ 68*897ae9c1SDavid du Colombier }; 69*897ae9c1SDavid du Colombier 70*897ae9c1SDavid du Colombier /* fixed by hw; part of Gberegs */ 71*897ae9c1SDavid du Colombier struct Mibstats { 72*897ae9c1SDavid du Colombier union { 73*897ae9c1SDavid du Colombier uvlong rxby; /* good bytes rcv'd */ 74*897ae9c1SDavid du Colombier struct { 75*897ae9c1SDavid du Colombier ulong rxbylo; 76*897ae9c1SDavid du Colombier ulong rxbyhi; 77*897ae9c1SDavid du Colombier }; 78*897ae9c1SDavid du Colombier }; 79*897ae9c1SDavid du Colombier ulong badrxby; /* bad bytes rcv'd */ 80*897ae9c1SDavid du Colombier ulong mactxerr; /* tx err pkts */ 81*897ae9c1SDavid du Colombier ulong rxpkt; /* good pkts rcv'd */ 82*897ae9c1SDavid du Colombier ulong badrxpkt; /* bad pkts rcv'd */ 83*897ae9c1SDavid du Colombier ulong rxbcastpkt; /* b'cast pkts rcv'd */ 84*897ae9c1SDavid du Colombier ulong rxmcastpkt; /* m'cast pkts rcv'd */ 85*897ae9c1SDavid du Colombier 86*897ae9c1SDavid du Colombier ulong rx64; /* pkts <= 64 bytes */ 87*897ae9c1SDavid du Colombier ulong rx65_127; /* pkts 65—127 bytes */ 88*897ae9c1SDavid du Colombier ulong rx128_255; /* pkts 128—255 bytes */ 89*897ae9c1SDavid du Colombier ulong rx256_511; /* pkts 256—511 bytes */ 90*897ae9c1SDavid du Colombier ulong rx512_1023; /* pkts 512—1023 bytes */ 91*897ae9c1SDavid du Colombier ulong rx1024_max; /* pkts >= 1024 bytes */ 92*897ae9c1SDavid du Colombier 93*897ae9c1SDavid du Colombier union { 94*897ae9c1SDavid du Colombier uvlong txby; /* good bytes sent */ 95*897ae9c1SDavid du Colombier struct { 96*897ae9c1SDavid du Colombier ulong txbylo; 97*897ae9c1SDavid du Colombier ulong txbyhi; 98*897ae9c1SDavid du Colombier }; 99*897ae9c1SDavid du Colombier }; 100*897ae9c1SDavid du Colombier ulong txpkt; /* good pkts sent */ 101*897ae9c1SDavid du Colombier /* half-duplex: pkts dropped due to excessive collisions */ 102*897ae9c1SDavid du Colombier ulong txcollpktdrop; 103*897ae9c1SDavid du Colombier ulong txmcastpkt; /* m'cast pkts sent */ 104*897ae9c1SDavid du Colombier ulong txbcastpkt; /* b'cast pkts sent */ 105*897ae9c1SDavid du Colombier 106*897ae9c1SDavid du Colombier ulong badmacctlpkts; /* bad mac ctl pkts */ 107*897ae9c1SDavid du Colombier ulong txflctl; /* flow-control pkts sent */ 108*897ae9c1SDavid du Colombier ulong rxflctl; /* good flow-control pkts rcv'd */ 109*897ae9c1SDavid du Colombier ulong badrxflctl; /* bad flow-control pkts rcv'd */ 110*897ae9c1SDavid du Colombier 111*897ae9c1SDavid du Colombier ulong rxundersized; /* runts */ 112*897ae9c1SDavid du Colombier ulong rxfrags; /* fragments rcv'd */ 113*897ae9c1SDavid du Colombier ulong rxtoobig; /* oversized pkts rcv'd */ 114*897ae9c1SDavid du Colombier ulong rxjabber; /* jabber pkts rcv'd */ 115*897ae9c1SDavid du Colombier ulong rxerr; /* rx error events */ 116*897ae9c1SDavid du Colombier ulong crcerr; /* crc error events */ 117*897ae9c1SDavid du Colombier ulong collisions; /* collision events */ 118*897ae9c1SDavid du Colombier ulong latecoll; /* late collisions */ 119*897ae9c1SDavid du Colombier }; 120*897ae9c1SDavid du Colombier 121*897ae9c1SDavid du Colombier struct Ctlr { 122*897ae9c1SDavid du Colombier Lock; 123*897ae9c1SDavid du Colombier Gbereg *reg; 124*897ae9c1SDavid du Colombier 125*897ae9c1SDavid du Colombier Lock initlock; 126*897ae9c1SDavid du Colombier int init; 127*897ae9c1SDavid du Colombier 128*897ae9c1SDavid du Colombier Rx *rx; /* receive descriptors */ 129*897ae9c1SDavid du Colombier Block *rxb[Nrx]; /* blocks belonging to the descriptors */ 130*897ae9c1SDavid du Colombier int rxhead; /* descr ethernet will write to next */ 131*897ae9c1SDavid du Colombier int rxtail; /* next descr that might need a buffer */ 132*897ae9c1SDavid du Colombier Rendez rrendez; /* interrupt wakes up read process */ 133*897ae9c1SDavid du Colombier int haveinput; 134*897ae9c1SDavid du Colombier 135*897ae9c1SDavid du Colombier Tx *tx; 136*897ae9c1SDavid du Colombier Block *txb[Ntx]; 137*897ae9c1SDavid du Colombier int txhead; /* next descr we can use for new packet */ 138*897ae9c1SDavid du Colombier int txtail; /* next descr to reclaim on tx complete */ 139*897ae9c1SDavid du Colombier 140*897ae9c1SDavid du Colombier Mii *mii; 141*897ae9c1SDavid du Colombier int port; 142*897ae9c1SDavid du Colombier 143*897ae9c1SDavid du Colombier /* stats */ 144*897ae9c1SDavid du Colombier ulong intrs; 145*897ae9c1SDavid du Colombier ulong newintrs; 146*897ae9c1SDavid du Colombier ulong txunderrun; 147*897ae9c1SDavid du Colombier ulong txringfull; 148*897ae9c1SDavid du Colombier ulong rxdiscard; 149*897ae9c1SDavid du Colombier ulong rxoverrun; 150*897ae9c1SDavid du Colombier ulong nofirstlast; 151*897ae9c1SDavid du Colombier 152*897ae9c1SDavid du Colombier Mibstats; 153*897ae9c1SDavid du Colombier }; 154*897ae9c1SDavid du Colombier 155*897ae9c1SDavid du Colombier #define Rxqon(q) (1<<(q)) 156*897ae9c1SDavid du Colombier #define Txqon(q) (1<<(q)) 157*897ae9c1SDavid du Colombier 158*897ae9c1SDavid du Colombier enum { 159*897ae9c1SDavid du Colombier /* sdma config, sdc */ 160*897ae9c1SDavid du Colombier Burst1 = 0, 161*897ae9c1SDavid du Colombier Burst2, 162*897ae9c1SDavid du Colombier Burst4, 163*897ae9c1SDavid du Colombier Burst8, 164*897ae9c1SDavid du Colombier Burst16, 165*897ae9c1SDavid du Colombier SDCrifb = 1<<0, /* rx intr on pkt boundaries */ 166*897ae9c1SDavid du Colombier #define SDCrxburst(v) ((v)<<1) 167*897ae9c1SDavid du Colombier SDCrxnobyteswap = 1<<4, 168*897ae9c1SDavid du Colombier SDCtxnobyteswap = 1<<5, 169*897ae9c1SDavid du Colombier SDCswap64byte = 1<<6, 170*897ae9c1SDavid du Colombier #define SDCtxburst(v) ((v)<<22) 171*897ae9c1SDavid du Colombier /* rx intr ipg (inter packet gap) */ 172*897ae9c1SDavid du Colombier #define SDCipgintrx(v) ((((v)>>15) & 1)<<25) | (((v) & MASK(15))<<7) 173*897ae9c1SDavid du Colombier 174*897ae9c1SDavid du Colombier /* portcfg */ 175*897ae9c1SDavid du Colombier PCFGupromisc = 1<<0, /* unicast promiscuous mode */ 176*897ae9c1SDavid du Colombier #define Rxqdefault(q) ((q)<<1) 177*897ae9c1SDavid du Colombier #define Rxqarp(q) ((q)<<4) 178*897ae9c1SDavid du Colombier PCFGbcrejectnoiparp = 1<<7, 179*897ae9c1SDavid du Colombier PCFGbcrejectip = 1<<8, 180*897ae9c1SDavid du Colombier PCFGbcrejectarp = 1<<9, 181*897ae9c1SDavid du Colombier PCFGamnotxes = 1<<12, /* auto mode, no summary update on tx */ 182*897ae9c1SDavid du Colombier PCFGtcpq = 1<<14, 183*897ae9c1SDavid du Colombier PCFGudpq = 1<<15, 184*897ae9c1SDavid du Colombier #define Rxqtcp(q) ((q)<<16) 185*897ae9c1SDavid du Colombier #define Rxqudp(q) ((q)<<19) 186*897ae9c1SDavid du Colombier #define Rxqbpdu(q) ((q)<<22) 187*897ae9c1SDavid du Colombier PCFGrxcs = 1<<25, /* rx tcp checksum mode with header */ 188*897ae9c1SDavid du Colombier 189*897ae9c1SDavid du Colombier /* portcfgx */ 190*897ae9c1SDavid du Colombier PCFGXspanq = 1<<1, 191*897ae9c1SDavid du Colombier PCFGXcrcoff = 1<<2, /* no ethernet crc */ 192*897ae9c1SDavid du Colombier 193*897ae9c1SDavid du Colombier /* port serial control0, psc0 */ 194*897ae9c1SDavid du Colombier PSC0porton = 1<<0, 195*897ae9c1SDavid du Colombier PSC0forcelinkup = 1<<1, 196*897ae9c1SDavid du Colombier PSC0an_dplxoff = 1<<2, /* an_ = auto. negotiate */ 197*897ae9c1SDavid du Colombier PSC0an_flctloff = 1<<3, 198*897ae9c1SDavid du Colombier PSC0an_pauseadv = 1<<4, 199*897ae9c1SDavid du Colombier PSC0nofrclinkdown = 1<<10, 200*897ae9c1SDavid du Colombier PSC0an_spdoff = 1<<13, 201*897ae9c1SDavid du Colombier PSC0dteadv = 1<<14, 202*897ae9c1SDavid du Colombier 203*897ae9c1SDavid du Colombier /* max. input pkt size */ 204*897ae9c1SDavid du Colombier #define PSC0mru(v) ((v)<<17) 205*897ae9c1SDavid du Colombier PSC0mrumask = PSC0mru(MASK(3)), 206*897ae9c1SDavid du Colombier PSC0mru1518 = 0, /* 1500+2* 6(addrs) +2 + 4(crc) */ 207*897ae9c1SDavid du Colombier PSC0mru1522, /* 1518 + 4(vlan tags) */ 208*897ae9c1SDavid du Colombier PSC0mru1552, /* `baby giant' */ 209*897ae9c1SDavid du Colombier PSC0mru9022, /* `jumbo' */ 210*897ae9c1SDavid du Colombier PSC0mru9192, /* bigger jumbo */ 211*897ae9c1SDavid du Colombier PSC0mru9700, /* still bigger jumbo */ 212*897ae9c1SDavid du Colombier 213*897ae9c1SDavid du Colombier PSC0fd_frc = 1<<21, /* force full duplex */ 214*897ae9c1SDavid du Colombier PSC0flctlfrc = 1<<22, 215*897ae9c1SDavid du Colombier PSC0gmiispd_gbfrc = 1<<23, 216*897ae9c1SDavid du Colombier PSC0miispdfrc100mbps = 1<<24, 217*897ae9c1SDavid du Colombier 218*897ae9c1SDavid du Colombier /* port status 0, ps0 */ 219*897ae9c1SDavid du Colombier PS0linkup = 1<<1, 220*897ae9c1SDavid du Colombier PS0fd = 1<<2, /* full duplex */ 221*897ae9c1SDavid du Colombier PS0flctl = 1<<3, 222*897ae9c1SDavid du Colombier PS0gmii_gb = 1<<4, 223*897ae9c1SDavid du Colombier PS0mii100mbps = 1<<5, 224*897ae9c1SDavid du Colombier PS0txbusy = 1<<7, 225*897ae9c1SDavid du Colombier PS0txfifoempty = 1<<10, 226*897ae9c1SDavid du Colombier PS0rxfifo1empty = 1<<11, 227*897ae9c1SDavid du Colombier PS0rxfifo2empty = 1<<12, 228*897ae9c1SDavid du Colombier 229*897ae9c1SDavid du Colombier /* port serial control 1, psc1 */ 230*897ae9c1SDavid du Colombier PSC1loopback = 1<<1, 231*897ae9c1SDavid du Colombier PSC1mii = 0<<2, 232*897ae9c1SDavid du Colombier PSC1rgmii = 1<<3, /* enable RGMII */ 233*897ae9c1SDavid du Colombier PSC1portreset = 1<<4, 234*897ae9c1SDavid du Colombier PSC1clockbypass = 1<<5, 235*897ae9c1SDavid du Colombier PSC1iban = 1<<6, 236*897ae9c1SDavid du Colombier PSC1iban_bypass = 1<<7, 237*897ae9c1SDavid du Colombier PSC1iban_restart= 1<<8, 238*897ae9c1SDavid du Colombier PSC1_gbonly = 1<<11, 239*897ae9c1SDavid du Colombier PSC1encolonbp = 1<<15, /* "collision during back-pressure mib counting" */ 240*897ae9c1SDavid du Colombier PSC1coldomlimmask= MASK(6)<<16, 241*897ae9c1SDavid du Colombier #define PSC1coldomlim(v) (((v) & MASK(6))<<16) 242*897ae9c1SDavid du Colombier PSC1miiallowoddpreamble = 1<<22, 243*897ae9c1SDavid du Colombier 244*897ae9c1SDavid du Colombier /* port status 1, ps1 */ 245*897ae9c1SDavid du Colombier PS1rxpause = 1<<0, 246*897ae9c1SDavid du Colombier PS1txpause = 1<<1, 247*897ae9c1SDavid du Colombier PS1pressure = 1<<2, 248*897ae9c1SDavid du Colombier PS1syncfail10ms = 1<<3, 249*897ae9c1SDavid du Colombier PS1an_done = 1<<4, 250*897ae9c1SDavid du Colombier PS1inbandan_bypassed = 1<<5, 251*897ae9c1SDavid du Colombier PS1serdesplllocked = 1<<6, 252*897ae9c1SDavid du Colombier PS1syncok = 1<<7, 253*897ae9c1SDavid du Colombier PS1nosquelch = 1<<8, 254*897ae9c1SDavid du Colombier 255*897ae9c1SDavid du Colombier /* irq */ 256*897ae9c1SDavid du Colombier Irx = 1<<0, 257*897ae9c1SDavid du Colombier Iextend = 1<<1, 258*897ae9c1SDavid du Colombier #define Irxbufferq(q) (1<<((q)+2)) 259*897ae9c1SDavid du Colombier Irxerr = 1<<10, 260*897ae9c1SDavid du Colombier #define Irxerrq(q) (1<<((q)+11)) 261*897ae9c1SDavid du Colombier #define Itxendq(q) (1<<((q)+19)) 262*897ae9c1SDavid du Colombier Isum = 1<<31, 263*897ae9c1SDavid du Colombier 264*897ae9c1SDavid du Colombier /* irq extended, irqe */ 265*897ae9c1SDavid du Colombier #define IEtxbufferq(q) (1<<((q)+0)) 266*897ae9c1SDavid du Colombier #define IEtxerrq(q) (1<<((q)+8)) 267*897ae9c1SDavid du Colombier IEphystschg = 1<<16, 268*897ae9c1SDavid du Colombier IEptp = 1<<17, 269*897ae9c1SDavid du Colombier IErxoverrun = 1<<18, 270*897ae9c1SDavid du Colombier IEtxunderrun = 1<<19, 271*897ae9c1SDavid du Colombier IElinkchg = 1<<20, 272*897ae9c1SDavid du Colombier IEintaddrerr = 1<<23, 273*897ae9c1SDavid du Colombier IEprbserr = 1<<25, 274*897ae9c1SDavid du Colombier IEsum = 1<<31, 275*897ae9c1SDavid du Colombier 276*897ae9c1SDavid du Colombier /* tx fifo urgent threshold (tx interrupt coalescing), pxtfut */ 277*897ae9c1SDavid du Colombier #define TFUTipginttx(v) (((v) & MASK(16))<<4); 278*897ae9c1SDavid du Colombier 279*897ae9c1SDavid du Colombier /* minimal frame size, mfs */ 280*897ae9c1SDavid du Colombier MFS40by = 10<<2, 281*897ae9c1SDavid du Colombier MFS44by = 11<<2, 282*897ae9c1SDavid du Colombier MFS48by = 12<<2, 283*897ae9c1SDavid du Colombier MFS52by = 13<<2, 284*897ae9c1SDavid du Colombier MFS56by = 14<<2, 285*897ae9c1SDavid du Colombier MFS60by = 15<<2, 286*897ae9c1SDavid du Colombier MFS64by = 16<<2, 287*897ae9c1SDavid du Colombier 288*897ae9c1SDavid du Colombier /* receive descriptor */ 289*897ae9c1SDavid du Colombier #define Bufsize(v) ((v)<<3) 290*897ae9c1SDavid du Colombier 291*897ae9c1SDavid du Colombier /* receive descriptor status */ 292*897ae9c1SDavid du Colombier RCSmacerr = 1<<0, 293*897ae9c1SDavid du Colombier RCSmacmask = 3<<1, 294*897ae9c1SDavid du Colombier RCSmacce = 0<<1, 295*897ae9c1SDavid du Colombier RCSmacor = 1<<1, 296*897ae9c1SDavid du Colombier RCSmacmf = 2<<1, 297*897ae9c1SDavid du Colombier RCSl4chkshift = 3, 298*897ae9c1SDavid du Colombier RCSl4chkmask = MASK(16), 299*897ae9c1SDavid du Colombier RCSvlan = 1<<17, 300*897ae9c1SDavid du Colombier RCSbpdu = 1<<18, 301*897ae9c1SDavid du Colombier RCSl4mask = 3<<21, 302*897ae9c1SDavid du Colombier RCSl4tcp4 = 0<<21, 303*897ae9c1SDavid du Colombier RCSl4udp4 = 1<<21, 304*897ae9c1SDavid du Colombier RCSl4other = 2<<21, 305*897ae9c1SDavid du Colombier RCSl4rsvd = 3<<21, 306*897ae9c1SDavid du Colombier RCSl2ev2 = 1<<23, 307*897ae9c1SDavid du Colombier RCSl3ip4 = 1<<24, 308*897ae9c1SDavid du Colombier RCSip4headok = 1<<25, 309*897ae9c1SDavid du Colombier RCSlast = 1<<26, 310*897ae9c1SDavid du Colombier RCSfirst = 1<<27, 311*897ae9c1SDavid du Colombier RCSunknownaddr = 1<<28, 312*897ae9c1SDavid du Colombier RCSenableintr = 1<<29, 313*897ae9c1SDavid du Colombier RCSl4chkok = 1<<30, 314*897ae9c1SDavid du Colombier RCSdmaown = 1<<31, 315*897ae9c1SDavid du Colombier 316*897ae9c1SDavid du Colombier /* transmit descriptor status */ 317*897ae9c1SDavid du Colombier TCSmacerr = 1<<0, 318*897ae9c1SDavid du Colombier TCSmacmask = 3<<1, 319*897ae9c1SDavid du Colombier TCSmaclc = 0<<1, 320*897ae9c1SDavid du Colombier TCSmacur = 1<<1, 321*897ae9c1SDavid du Colombier TCSmacrl = 2<<1, 322*897ae9c1SDavid du Colombier TCSllc = 1<<9, 323*897ae9c1SDavid du Colombier TCSl4chkmode = 1<<10, 324*897ae9c1SDavid du Colombier TCSipv4hdlenshift= 11, 325*897ae9c1SDavid du Colombier TCSvlan = 1<<15, 326*897ae9c1SDavid du Colombier TCSl4type = 1<<16, 327*897ae9c1SDavid du Colombier TCSgl4chk = 1<<17, 328*897ae9c1SDavid du Colombier TCSgip4chk = 1<<18, 329*897ae9c1SDavid du Colombier TCSpadding = 1<<19, 330*897ae9c1SDavid du Colombier TCSlast = 1<<20, 331*897ae9c1SDavid du Colombier TCSfirst = 1<<21, 332*897ae9c1SDavid du Colombier TCSenableintr = 1<<23, 333*897ae9c1SDavid du Colombier TCSautomode = 1<<30, 334*897ae9c1SDavid du Colombier TCSdmaown = 1<<31, 335*897ae9c1SDavid du Colombier }; 336*897ae9c1SDavid du Colombier 337*897ae9c1SDavid du Colombier enum { 338*897ae9c1SDavid du Colombier /* SMI regs */ 339*897ae9c1SDavid du Colombier PhysmiTimeout = 10000, /* what units? in ms. */ 340*897ae9c1SDavid du Colombier Physmidataoff = 0, /* Data */ 341*897ae9c1SDavid du Colombier Physmidatamask = 0xffff<<Physmidataoff, 342*897ae9c1SDavid du Colombier 343*897ae9c1SDavid du Colombier Physmiaddroff = 16, /* PHY device addr */ 344*897ae9c1SDavid du Colombier Physmiaddrmask = 0x1f << Physmiaddroff, 345*897ae9c1SDavid du Colombier 346*897ae9c1SDavid du Colombier Physmiop = 26, 347*897ae9c1SDavid du Colombier Physmiopmask = 3<<Physmiop, 348*897ae9c1SDavid du Colombier PhysmiopWr = 0<<Physmiop, 349*897ae9c1SDavid du Colombier PhysmiopRd = 1<<Physmiop, 350*897ae9c1SDavid du Colombier 351*897ae9c1SDavid du Colombier PhysmiReadok = 1<<27, 352*897ae9c1SDavid du Colombier PhysmiBusy = 1<<28, 353*897ae9c1SDavid du Colombier 354*897ae9c1SDavid du Colombier SmiRegaddroff = 21, /* PHY device register addr */ 355*897ae9c1SDavid du Colombier SmiRegaddrmask = 0x1f << SmiRegaddroff, 356*897ae9c1SDavid du Colombier }; 357*897ae9c1SDavid du Colombier 358*897ae9c1SDavid du Colombier struct Gbereg { 359*897ae9c1SDavid du Colombier ulong phy; /* PHY address */ 360*897ae9c1SDavid du Colombier ulong smi; /* serial mgmt. interface */ 361*897ae9c1SDavid du Colombier ulong euda; /* ether default address */ 362*897ae9c1SDavid du Colombier ulong eudid; /* ether default id */ 363*897ae9c1SDavid du Colombier uchar _pad0[0x80-0x10]; 364*897ae9c1SDavid du Colombier 365*897ae9c1SDavid du Colombier ulong euirq; /* interrupt cause */ 366*897ae9c1SDavid du Colombier ulong euirqmask; /* interrupt mask */ 367*897ae9c1SDavid du Colombier uchar _pad1[0x94-0x88]; 368*897ae9c1SDavid du Colombier 369*897ae9c1SDavid du Colombier ulong euea; /* error address */ 370*897ae9c1SDavid du Colombier ulong euiae; /* internal error address */ 371*897ae9c1SDavid du Colombier uchar _pad2[0xb0-0x9c]; 372*897ae9c1SDavid du Colombier 373*897ae9c1SDavid du Colombier ulong euc; /* control */ 374*897ae9c1SDavid du Colombier uchar _pad3[0x200-0xb4]; 375*897ae9c1SDavid du Colombier 376*897ae9c1SDavid du Colombier struct { 377*897ae9c1SDavid du Colombier ulong base; /* window base */ 378*897ae9c1SDavid du Colombier ulong size; /* window size */ 379*897ae9c1SDavid du Colombier } base[6]; 380*897ae9c1SDavid du Colombier uchar _pad4[0x280-0x230]; 381*897ae9c1SDavid du Colombier 382*897ae9c1SDavid du Colombier ulong harr[4]; /* high address remap */ 383*897ae9c1SDavid du Colombier ulong bare; /* base address enable */ 384*897ae9c1SDavid du Colombier ulong epap; /* port access protect */ 385*897ae9c1SDavid du Colombier uchar _pad5[0x400-0x298]; 386*897ae9c1SDavid du Colombier 387*897ae9c1SDavid du Colombier ulong portcfg; /* port configuration */ 388*897ae9c1SDavid du Colombier ulong portcfgx; /* port config. extend */ 389*897ae9c1SDavid du Colombier ulong mii; /* mii serial parameters */ 390*897ae9c1SDavid du Colombier ulong _pad6; 391*897ae9c1SDavid du Colombier ulong evlane; /* vlan ether type */ 392*897ae9c1SDavid du Colombier ulong macal; /* mac address low */ 393*897ae9c1SDavid du Colombier ulong macah; /* mac address high */ 394*897ae9c1SDavid du Colombier ulong sdc; /* sdma config. */ 395*897ae9c1SDavid du Colombier ulong dscp[7]; /* ip diff. serv. code point -> pri */ 396*897ae9c1SDavid du Colombier ulong psc0; /* port serial control 0 */ 397*897ae9c1SDavid du Colombier ulong vpt2p; /* vlan priority tag -> pri */ 398*897ae9c1SDavid du Colombier ulong ps0; /* ether port status 0 */ 399*897ae9c1SDavid du Colombier ulong tqc; /* transmit queue command */ 400*897ae9c1SDavid du Colombier ulong psc1; /* port serial control 1 */ 401*897ae9c1SDavid du Colombier ulong ps1; /* ether port status 1 */ 402*897ae9c1SDavid du Colombier ulong mvhdr; /* marvell header */ 403*897ae9c1SDavid du Colombier ulong _pad8[2]; 404*897ae9c1SDavid du Colombier 405*897ae9c1SDavid du Colombier /* interrupts */ 406*897ae9c1SDavid du Colombier ulong irq; /* interrupt cause; some rw0c bits */ 407*897ae9c1SDavid du Colombier ulong irqe; /* " " extended; some rw0c bits */ 408*897ae9c1SDavid du Colombier ulong irqmask; /* interrupt mask (actually enable) */ 409*897ae9c1SDavid du Colombier ulong irqemask; /* " " extended */ 410*897ae9c1SDavid du Colombier 411*897ae9c1SDavid du Colombier ulong _pad9; 412*897ae9c1SDavid du Colombier ulong pxtfut; /* port tx fifo urgent threshold */ 413*897ae9c1SDavid du Colombier ulong _pad10; 414*897ae9c1SDavid du Colombier ulong pxmfs; /* port rx minimum frame size */ 415*897ae9c1SDavid du Colombier ulong _pad11; 416*897ae9c1SDavid du Colombier 417*897ae9c1SDavid du Colombier /* 418*897ae9c1SDavid du Colombier * # of input frames discarded by addr filtering or lack of resources; 419*897ae9c1SDavid du Colombier * zeroed upon read. 420*897ae9c1SDavid du Colombier */ 421*897ae9c1SDavid du Colombier ulong pxdfc; /* port rx discard frame counter */ 422*897ae9c1SDavid du Colombier ulong pxofc; /* port overrun frame counter */ 423*897ae9c1SDavid du Colombier ulong _pad12[2]; 424*897ae9c1SDavid du Colombier ulong piae; /* port internal address error */ 425*897ae9c1SDavid du Colombier uchar _pad13[0x4bc-0x498]; 426*897ae9c1SDavid du Colombier ulong etherprio; /* ether type priority */ 427*897ae9c1SDavid du Colombier uchar _pad14[0x4dc-0x4c0]; 428*897ae9c1SDavid du Colombier ulong tqfpc; /* tx queue fixed priority config. */ 429*897ae9c1SDavid du Colombier ulong pttbrc; /* port tx token-bucket rate config. */ 430*897ae9c1SDavid du Colombier ulong tqc1; /* tx queue command 1 */ 431*897ae9c1SDavid du Colombier ulong pmtu; /* port maximum transmit unit */ 432*897ae9c1SDavid du Colombier ulong pmtbs; /* port maximum token bucket size */ 433*897ae9c1SDavid du Colombier uchar _pad15[0x600-0x4f0]; 434*897ae9c1SDavid du Colombier 435*897ae9c1SDavid du Colombier struct { 436*897ae9c1SDavid du Colombier ulong _pad[3]; 437*897ae9c1SDavid du Colombier ulong r; /* phys. addr.: cur. rx desc. ptrs */ 438*897ae9c1SDavid du Colombier } crdp[8]; 439*897ae9c1SDavid du Colombier ulong rqc; /* rx queue command */ 440*897ae9c1SDavid du Colombier ulong tcsdp; /* phys. addr.: cur. tx desc. ptr */ 441*897ae9c1SDavid du Colombier uchar _pad16[0x6c0-0x688]; 442*897ae9c1SDavid du Colombier 443*897ae9c1SDavid du Colombier ulong tcqdp[8]; /* phys. addr.: cur. tx q. desc. ptr */ 444*897ae9c1SDavid du Colombier uchar _pad17[0x700-0x6e0]; 445*897ae9c1SDavid du Colombier 446*897ae9c1SDavid du Colombier struct { 447*897ae9c1SDavid du Colombier ulong tbctr; /* queue tx token-bucket counter */ 448*897ae9c1SDavid du Colombier ulong tbcfg; /* tx queue token-bucket config. */ 449*897ae9c1SDavid du Colombier ulong acfg; /* tx queue arbiter config. */ 450*897ae9c1SDavid du Colombier ulong _pad; 451*897ae9c1SDavid du Colombier } tq[8]; 452*897ae9c1SDavid du Colombier ulong pttbc; /* port tx token-bucket counter */ 453*897ae9c1SDavid du Colombier uchar _pad18[0x7a8-0x784]; 454*897ae9c1SDavid du Colombier 455*897ae9c1SDavid du Colombier ulong ipg2; /* tx queue ipg */ 456*897ae9c1SDavid du Colombier ulong _pad19[3]; 457*897ae9c1SDavid du Colombier ulong ipg3; 458*897ae9c1SDavid du Colombier ulong _pad20; 459*897ae9c1SDavid du Colombier ulong htlp; /* high token in low packet */ 460*897ae9c1SDavid du Colombier ulong htap; /* high token in async packet */ 461*897ae9c1SDavid du Colombier ulong ltap; /* low token in async packet */ 462*897ae9c1SDavid du Colombier ulong _pad21; 463*897ae9c1SDavid du Colombier ulong ts; /* tx speed */ 464*897ae9c1SDavid du Colombier uchar _pad22[0x1000-0x7d4]; 465*897ae9c1SDavid du Colombier 466*897ae9c1SDavid du Colombier /* mac mib counters: statistics */ 467*897ae9c1SDavid du Colombier Mibstats; 468*897ae9c1SDavid du Colombier uchar _pad23[0x1400-0x1080]; 469*897ae9c1SDavid du Colombier 470*897ae9c1SDavid du Colombier /* multicast filtering; each byte: Qno<<1 | Pass */ 471*897ae9c1SDavid du Colombier ulong dfsmt[64]; /* dest addr filter special m'cast table */ 472*897ae9c1SDavid du Colombier ulong dfomt[64]; /* dest addr filter other m'cast table */ 473*897ae9c1SDavid du Colombier /* unicast filtering */ 474*897ae9c1SDavid du Colombier ulong dfut[4]; /* dest addr filter unicast table */ 475*897ae9c1SDavid du Colombier }; 476*897ae9c1SDavid du Colombier 477*897ae9c1SDavid du Colombier static void getmibstats(Ctlr *); 478*897ae9c1SDavid du Colombier 479*897ae9c1SDavid du Colombier static void 480*897ae9c1SDavid du Colombier rxfreeb(Block *b) 481*897ae9c1SDavid du Colombier { 482*897ae9c1SDavid du Colombier /* freeb(b) will have previously decremented b->ref to 0; raise to 1 */ 483*897ae9c1SDavid du Colombier _xinc(&b->ref); 484*897ae9c1SDavid du Colombier //iprint("fr %ld ", b->ref); 485*897ae9c1SDavid du Colombier b->wp = b->rp = 486*897ae9c1SDavid du Colombier (uchar*)((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1)); 487*897ae9c1SDavid du Colombier assert(((uintptr)b->rp & (Bufalign - 1)) == 0); 488*897ae9c1SDavid du Colombier b->free = rxfreeb; 489*897ae9c1SDavid du Colombier 490*897ae9c1SDavid du Colombier ilock(&freeblocks); 491*897ae9c1SDavid du Colombier b->next = freeblocks.head; 492*897ae9c1SDavid du Colombier freeblocks.head = b; 493*897ae9c1SDavid du Colombier iunlock(&freeblocks); 494*897ae9c1SDavid du Colombier } 495*897ae9c1SDavid du Colombier 496*897ae9c1SDavid du Colombier static Block * 497*897ae9c1SDavid du Colombier rxallocb(void) 498*897ae9c1SDavid du Colombier { 499*897ae9c1SDavid du Colombier Block *b; 500*897ae9c1SDavid du Colombier 501*897ae9c1SDavid du Colombier ilock(&freeblocks); 502*897ae9c1SDavid du Colombier b = freeblocks.head; 503*897ae9c1SDavid du Colombier if(b != nil) { 504*897ae9c1SDavid du Colombier freeblocks.head = b->next; 505*897ae9c1SDavid du Colombier b->next = nil; 506*897ae9c1SDavid du Colombier b->free = rxfreeb; 507*897ae9c1SDavid du Colombier } 508*897ae9c1SDavid du Colombier iunlock(&freeblocks); 509*897ae9c1SDavid du Colombier return b; 510*897ae9c1SDavid du Colombier } 511*897ae9c1SDavid du Colombier 512*897ae9c1SDavid du Colombier static void 513*897ae9c1SDavid du Colombier rxkick(Ctlr *ctlr) 514*897ae9c1SDavid du Colombier { 515*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 516*897ae9c1SDavid du Colombier 517*897ae9c1SDavid du Colombier if (reg->crdp[Qno].r == 0) 518*897ae9c1SDavid du Colombier reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]); 519*897ae9c1SDavid du Colombier if ((reg->rqc & 0xff) == 0) /* all queues are stopped? */ 520*897ae9c1SDavid du Colombier reg->rqc = Rxqon(Qno); /* restart */ 521*897ae9c1SDavid du Colombier coherence(); 522*897ae9c1SDavid du Colombier } 523*897ae9c1SDavid du Colombier 524*897ae9c1SDavid du Colombier static void 525*897ae9c1SDavid du Colombier txkick(Ctlr *ctlr) 526*897ae9c1SDavid du Colombier { 527*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 528*897ae9c1SDavid du Colombier 529*897ae9c1SDavid du Colombier if (reg->tcqdp[Qno] == 0) 530*897ae9c1SDavid du Colombier reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]); 531*897ae9c1SDavid du Colombier if ((reg->tqc & 0xff) == 0) /* all q's stopped? */ 532*897ae9c1SDavid du Colombier reg->tqc = Txqon(Qno); /* restart */ 533*897ae9c1SDavid du Colombier coherence(); 534*897ae9c1SDavid du Colombier } 535*897ae9c1SDavid du Colombier 536*897ae9c1SDavid du Colombier static void 537*897ae9c1SDavid du Colombier rxreplenish(Ctlr *ctlr) 538*897ae9c1SDavid du Colombier { 539*897ae9c1SDavid du Colombier Rx *r; 540*897ae9c1SDavid du Colombier Block *b; 541*897ae9c1SDavid du Colombier 542*897ae9c1SDavid du Colombier while(ctlr->rxb[ctlr->rxtail] == nil) { 543*897ae9c1SDavid du Colombier b = rxallocb(); 544*897ae9c1SDavid du Colombier if(b == nil) { 545*897ae9c1SDavid du Colombier iprint("ether1116: rxreplenish out of buffers\n"); 546*897ae9c1SDavid du Colombier break; 547*897ae9c1SDavid du Colombier } 548*897ae9c1SDavid du Colombier 549*897ae9c1SDavid du Colombier ctlr->rxb[ctlr->rxtail] = b; 550*897ae9c1SDavid du Colombier 551*897ae9c1SDavid du Colombier /* set up receive descriptor */ 552*897ae9c1SDavid du Colombier r = &ctlr->rx[ctlr->rxtail]; 553*897ae9c1SDavid du Colombier assert(((uintptr)r & (Descralign - 1)) == 0); 554*897ae9c1SDavid du Colombier r->countsize = Bufsize(Rxblklen); 555*897ae9c1SDavid du Colombier r->buf = PADDR(b->rp); 556*897ae9c1SDavid du Colombier cachedwbse(r, sizeof *r); 557*897ae9c1SDavid du Colombier l2cacheuwbse(r, sizeof *r); 558*897ae9c1SDavid du Colombier 559*897ae9c1SDavid du Colombier /* and fire */ 560*897ae9c1SDavid du Colombier r->cs = RCSdmaown | RCSenableintr; 561*897ae9c1SDavid du Colombier cachedwbse(&r->cs, BY2SE); 562*897ae9c1SDavid du Colombier l2cacheuwbse(&r->cs, BY2SE); 563*897ae9c1SDavid du Colombier 564*897ae9c1SDavid du Colombier ctlr->rxtail = NEXT(ctlr->rxtail, Nrx); 565*897ae9c1SDavid du Colombier } 566*897ae9c1SDavid du Colombier rxkick(ctlr); 567*897ae9c1SDavid du Colombier } 568*897ae9c1SDavid du Colombier 569*897ae9c1SDavid du Colombier static void 570*897ae9c1SDavid du Colombier dump(uchar *bp, long max) 571*897ae9c1SDavid du Colombier { 572*897ae9c1SDavid du Colombier if (max > 64) 573*897ae9c1SDavid du Colombier max = 64; 574*897ae9c1SDavid du Colombier for (; max > 0; max--, bp++) 575*897ae9c1SDavid du Colombier iprint("%02.2ux ", *bp); 576*897ae9c1SDavid du Colombier print("...\n"); 577*897ae9c1SDavid du Colombier } 578*897ae9c1SDavid du Colombier 579*897ae9c1SDavid du Colombier static void 580*897ae9c1SDavid du Colombier etheractive(Ether *ether) 581*897ae9c1SDavid du Colombier { 582*897ae9c1SDavid du Colombier ether->starttime = TK2MS(MACHP(0)->ticks)/1000; 583*897ae9c1SDavid du Colombier } 584*897ae9c1SDavid du Colombier 585*897ae9c1SDavid du Colombier static void 586*897ae9c1SDavid du Colombier ethercheck(Ether *ether) 587*897ae9c1SDavid du Colombier { 588*897ae9c1SDavid du Colombier if (ether->starttime != 0 && 589*897ae9c1SDavid du Colombier TK2MS(MACHP(0)->ticks)/1000 - ether->starttime > Etherstuck) { 590*897ae9c1SDavid du Colombier etheractive(ether); 591*897ae9c1SDavid du Colombier iprint("ethernet stuck\n"); 592*897ae9c1SDavid du Colombier } 593*897ae9c1SDavid du Colombier } 594*897ae9c1SDavid du Colombier 595*897ae9c1SDavid du Colombier static void 596*897ae9c1SDavid du Colombier receive(Ether *ether) 597*897ae9c1SDavid du Colombier { 598*897ae9c1SDavid du Colombier int i; 599*897ae9c1SDavid du Colombier ulong n; 600*897ae9c1SDavid du Colombier Block *b; 601*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 602*897ae9c1SDavid du Colombier Rx *r; 603*897ae9c1SDavid du Colombier 604*897ae9c1SDavid du Colombier ethercheck(ether); 605*897ae9c1SDavid du Colombier for (i = Nrx-2; i > 0; i--) { 606*897ae9c1SDavid du Colombier r = &ctlr->rx[ctlr->rxhead]; 607*897ae9c1SDavid du Colombier assert(((uintptr)r & (Descralign - 1)) == 0); 608*897ae9c1SDavid du Colombier l2cacheuinvse(r, sizeof *r); 609*897ae9c1SDavid du Colombier cachedinvse(r, sizeof *r); 610*897ae9c1SDavid du Colombier if(r->cs & RCSdmaown) 611*897ae9c1SDavid du Colombier break; 612*897ae9c1SDavid du Colombier 613*897ae9c1SDavid du Colombier b = ctlr->rxb[ctlr->rxhead]; 614*897ae9c1SDavid du Colombier if (b == nil) 615*897ae9c1SDavid du Colombier panic("ether1116: nil ctlr->rxb[ctlr->rxhead] " 616*897ae9c1SDavid du Colombier "in receive"); 617*897ae9c1SDavid du Colombier ctlr->rxb[ctlr->rxhead] = nil; 618*897ae9c1SDavid du Colombier ctlr->rxhead = NEXT(ctlr->rxhead, Nrx); 619*897ae9c1SDavid du Colombier 620*897ae9c1SDavid du Colombier if((r->cs & (RCSfirst|RCSlast)) != (RCSfirst|RCSlast)) { 621*897ae9c1SDavid du Colombier ctlr->nofirstlast++; 622*897ae9c1SDavid du Colombier freeb(b); 623*897ae9c1SDavid du Colombier continue; 624*897ae9c1SDavid du Colombier } 625*897ae9c1SDavid du Colombier if(r->cs & RCSmacerr) { 626*897ae9c1SDavid du Colombier freeb(b); 627*897ae9c1SDavid du Colombier continue; 628*897ae9c1SDavid du Colombier } 629*897ae9c1SDavid du Colombier 630*897ae9c1SDavid du Colombier n = r->countsize >> 16; 631*897ae9c1SDavid du Colombier assert(n >= 2 && n < 2048); 632*897ae9c1SDavid du Colombier 633*897ae9c1SDavid du Colombier l2cacheuinvse(b->rp, n); 634*897ae9c1SDavid du Colombier cachedinvse(b->rp, n); 635*897ae9c1SDavid du Colombier b->wp = b->rp + n; 636*897ae9c1SDavid du Colombier /* 637*897ae9c1SDavid du Colombier * skip hardware padding intended to align ipv4 address 638*897ae9c1SDavid du Colombier * in memory (mv-s104860-u0 §8.3.4.1) 639*897ae9c1SDavid du Colombier */ 640*897ae9c1SDavid du Colombier b->rp += 2; 641*897ae9c1SDavid du Colombier etheriq(ether, b, 1); 642*897ae9c1SDavid du Colombier etheractive(ether); 643*897ae9c1SDavid du Colombier if (i % (Nrx / 2) == 0) 644*897ae9c1SDavid du Colombier rxreplenish(ctlr); 645*897ae9c1SDavid du Colombier } 646*897ae9c1SDavid du Colombier rxreplenish(ctlr); 647*897ae9c1SDavid du Colombier } 648*897ae9c1SDavid du Colombier 649*897ae9c1SDavid du Colombier static void 650*897ae9c1SDavid du Colombier txreplenish(Ether *ether) /* free transmitted packets */ 651*897ae9c1SDavid du Colombier { 652*897ae9c1SDavid du Colombier Ctlr *ctlr; 653*897ae9c1SDavid du Colombier 654*897ae9c1SDavid du Colombier ctlr = ether->ctlr; 655*897ae9c1SDavid du Colombier while(ctlr->txtail != ctlr->txhead) { 656*897ae9c1SDavid du Colombier l2cacheuinvse(&ctlr->tx[ctlr->txtail].cs, BY2SE); 657*897ae9c1SDavid du Colombier cachedinvse(&ctlr->tx[ctlr->txtail].cs, BY2SE); 658*897ae9c1SDavid du Colombier if(ctlr->tx[ctlr->txtail].cs & TCSdmaown) 659*897ae9c1SDavid du Colombier break; 660*897ae9c1SDavid du Colombier if(ctlr->txb[ctlr->txtail] == nil) 661*897ae9c1SDavid du Colombier panic("no block for sent packet?!"); 662*897ae9c1SDavid du Colombier freeb(ctlr->txb[ctlr->txtail]); 663*897ae9c1SDavid du Colombier ctlr->txb[ctlr->txtail] = nil; 664*897ae9c1SDavid du Colombier ctlr->txtail = NEXT(ctlr->txtail, Ntx); 665*897ae9c1SDavid du Colombier etheractive(ether); 666*897ae9c1SDavid du Colombier } 667*897ae9c1SDavid du Colombier } 668*897ae9c1SDavid du Colombier 669*897ae9c1SDavid du Colombier /* 670*897ae9c1SDavid du Colombier * transmit strategy: fill the output ring as far as possible, 671*897ae9c1SDavid du Colombier * perhaps leaving a few spare; kick off the output and take 672*897ae9c1SDavid du Colombier * an interrupt only when the transmit queue is empty. 673*897ae9c1SDavid du Colombier */ 674*897ae9c1SDavid du Colombier static void 675*897ae9c1SDavid du Colombier transmit(Ether *ether) 676*897ae9c1SDavid du Colombier { 677*897ae9c1SDavid du Colombier int i, kick, len; 678*897ae9c1SDavid du Colombier Block *b; 679*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 680*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 681*897ae9c1SDavid du Colombier Tx *t; 682*897ae9c1SDavid du Colombier 683*897ae9c1SDavid du Colombier ethercheck(ether); 684*897ae9c1SDavid du Colombier ilock(ctlr); 685*897ae9c1SDavid du Colombier txreplenish(ether); /* reap old packets */ 686*897ae9c1SDavid du Colombier 687*897ae9c1SDavid du Colombier /* queue new packets; don't use more than half the tx descs. */ 688*897ae9c1SDavid du Colombier kick = 0; 689*897ae9c1SDavid du Colombier for (i = Ntx/2 - 2; i > 0; i--) { 690*897ae9c1SDavid du Colombier t = &ctlr->tx[ctlr->txhead]; 691*897ae9c1SDavid du Colombier assert(((uintptr)t & (Descralign - 1)) == 0); 692*897ae9c1SDavid du Colombier l2cacheuinvse(t, sizeof *t); 693*897ae9c1SDavid du Colombier cachedinvse(t, sizeof *t); 694*897ae9c1SDavid du Colombier if(t->cs & TCSdmaown) { /* free descriptor? */ 695*897ae9c1SDavid du Colombier ctlr->txringfull++; 696*897ae9c1SDavid du Colombier break; 697*897ae9c1SDavid du Colombier } 698*897ae9c1SDavid du Colombier 699*897ae9c1SDavid du Colombier b = qget(ether->oq); /* outgoing packet? */ 700*897ae9c1SDavid du Colombier if (b == nil) 701*897ae9c1SDavid du Colombier break; 702*897ae9c1SDavid du Colombier len = BLEN(b); 703*897ae9c1SDavid du Colombier if(len < ether->minmtu || len > ether->maxmtu) { 704*897ae9c1SDavid du Colombier freeb(b); 705*897ae9c1SDavid du Colombier continue; 706*897ae9c1SDavid du Colombier } 707*897ae9c1SDavid du Colombier ctlr->txb[ctlr->txhead] = b; 708*897ae9c1SDavid du Colombier 709*897ae9c1SDavid du Colombier /* make sure the whole packet is in memory */ 710*897ae9c1SDavid du Colombier cachedwbse(b->rp, len); 711*897ae9c1SDavid du Colombier l2cacheuwbse(b->rp, len); 712*897ae9c1SDavid du Colombier 713*897ae9c1SDavid du Colombier /* set up the transmit descriptor */ 714*897ae9c1SDavid du Colombier t->buf = PADDR(b->rp); 715*897ae9c1SDavid du Colombier t->countchk = len << 16; 716*897ae9c1SDavid du Colombier cachedwbse(t, sizeof *t); 717*897ae9c1SDavid du Colombier l2cacheuwbse(t, sizeof *t); 718*897ae9c1SDavid du Colombier 719*897ae9c1SDavid du Colombier /* and fire */ 720*897ae9c1SDavid du Colombier t->cs = TCSpadding | TCSfirst | TCSlast | TCSdmaown | 721*897ae9c1SDavid du Colombier TCSenableintr; 722*897ae9c1SDavid du Colombier cachedwbse(&t->cs, BY2SE); 723*897ae9c1SDavid du Colombier l2cacheuwbse(&t->cs, BY2SE); 724*897ae9c1SDavid du Colombier 725*897ae9c1SDavid du Colombier kick++; 726*897ae9c1SDavid du Colombier ctlr->txhead = NEXT(ctlr->txhead, Ntx); 727*897ae9c1SDavid du Colombier } 728*897ae9c1SDavid du Colombier if (kick) { 729*897ae9c1SDavid du Colombier txkick(ctlr); 730*897ae9c1SDavid du Colombier 731*897ae9c1SDavid du Colombier reg->irqmask |= Itxendq(Qno); 732*897ae9c1SDavid du Colombier reg->irqemask |= IEtxerrq(Qno) | IEtxunderrun; 733*897ae9c1SDavid du Colombier } 734*897ae9c1SDavid du Colombier iunlock(ctlr); 735*897ae9c1SDavid du Colombier } 736*897ae9c1SDavid du Colombier 737*897ae9c1SDavid du Colombier static void 738*897ae9c1SDavid du Colombier dumprxdescs(Ctlr *ctlr) 739*897ae9c1SDavid du Colombier { 740*897ae9c1SDavid du Colombier int i; 741*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 742*897ae9c1SDavid du Colombier 743*897ae9c1SDavid du Colombier iprint("\nrxhead %d rxtail %d; txcdp %#p rxcdp %#p\n", 744*897ae9c1SDavid du Colombier ctlr->rxhead, ctlr->rxtail, reg->tcqdp[Qno], reg->crdp[Qno].r); 745*897ae9c1SDavid du Colombier for (i = 0; i < Nrx; i++) 746*897ae9c1SDavid du Colombier iprint("rxb %d @ %#p: %#p\n", i, &ctlr->rxb[i], ctlr->rxb[i]); 747*897ae9c1SDavid du Colombier for (i = 0; i < Nrx; i++) 748*897ae9c1SDavid du Colombier iprint("rx %d @ %#p: cs %#lux countsize %lud buf %#lux next %#lux\n", 749*897ae9c1SDavid du Colombier i, &ctlr->rx[i], ctlr->rx[i].cs, 750*897ae9c1SDavid du Colombier ctlr->rx[i].countsize >> 3, ctlr->rx[i].buf, 751*897ae9c1SDavid du Colombier ctlr->rx[i].next); 752*897ae9c1SDavid du Colombier delay(1000); 753*897ae9c1SDavid du Colombier } 754*897ae9c1SDavid du Colombier 755*897ae9c1SDavid du Colombier static int 756*897ae9c1SDavid du Colombier gotinput(void* ctlr) 757*897ae9c1SDavid du Colombier { 758*897ae9c1SDavid du Colombier return ((Ctlr*)ctlr)->haveinput != 0; 759*897ae9c1SDavid du Colombier } 760*897ae9c1SDavid du Colombier 761*897ae9c1SDavid du Colombier /* 762*897ae9c1SDavid du Colombier * process any packets in the input ring. 763*897ae9c1SDavid du Colombier * also sum mib stats frequently to avoid the overflow 764*897ae9c1SDavid du Colombier * mentioned in the errata. 765*897ae9c1SDavid du Colombier */ 766*897ae9c1SDavid du Colombier static void 767*897ae9c1SDavid du Colombier rcvproc(void* arg) 768*897ae9c1SDavid du Colombier { 769*897ae9c1SDavid du Colombier Ctlr *ctlr; 770*897ae9c1SDavid du Colombier Ether *ether; 771*897ae9c1SDavid du Colombier 772*897ae9c1SDavid du Colombier ether = arg; 773*897ae9c1SDavid du Colombier ctlr = ether->ctlr; 774*897ae9c1SDavid du Colombier for(;;){ 775*897ae9c1SDavid du Colombier tsleep(&ctlr->rrendez, gotinput, ctlr, 10*1000); 776*897ae9c1SDavid du Colombier ilock(ctlr); 777*897ae9c1SDavid du Colombier getmibstats(ctlr); 778*897ae9c1SDavid du Colombier if (ctlr->haveinput) { 779*897ae9c1SDavid du Colombier ctlr->haveinput = 0; 780*897ae9c1SDavid du Colombier iunlock(ctlr); 781*897ae9c1SDavid du Colombier receive(ether); 782*897ae9c1SDavid du Colombier } else 783*897ae9c1SDavid du Colombier iunlock(ctlr); 784*897ae9c1SDavid du Colombier } 785*897ae9c1SDavid du Colombier } 786*897ae9c1SDavid du Colombier 787*897ae9c1SDavid du Colombier static void 788*897ae9c1SDavid du Colombier interrupt(Ureg*, void *arg) 789*897ae9c1SDavid du Colombier { 790*897ae9c1SDavid du Colombier ulong irq, irqe, handled; 791*897ae9c1SDavid du Colombier Ether *ether = arg; 792*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 793*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 794*897ae9c1SDavid du Colombier 795*897ae9c1SDavid du Colombier handled = 0; 796*897ae9c1SDavid du Colombier irq = reg->irq; 797*897ae9c1SDavid du Colombier irqe = reg->irqe; 798*897ae9c1SDavid du Colombier reg->irq = 0; /* extinguish intr causes */ 799*897ae9c1SDavid du Colombier reg->irqe = 0; /* " " " */ 800*897ae9c1SDavid du Colombier ethercheck(ether); 801*897ae9c1SDavid du Colombier 802*897ae9c1SDavid du Colombier if(irq & Irxbufferq(Qno)) { 803*897ae9c1SDavid du Colombier /* 804*897ae9c1SDavid du Colombier * letting a kproc process the input takes far less real time 805*897ae9c1SDavid du Colombier * than doing it all at interrupt level. 806*897ae9c1SDavid du Colombier */ 807*897ae9c1SDavid du Colombier ctlr->haveinput = 1; 808*897ae9c1SDavid du Colombier wakeup(&ctlr->rrendez); 809*897ae9c1SDavid du Colombier handled++; 810*897ae9c1SDavid du Colombier } else 811*897ae9c1SDavid du Colombier rxkick(ctlr); 812*897ae9c1SDavid du Colombier 813*897ae9c1SDavid du Colombier if(irq & Itxendq(Qno)) { /* transmit ring empty? */ 814*897ae9c1SDavid du Colombier reg->irqmask &= ~Itxendq(Qno); /* prevent more interrupts */ 815*897ae9c1SDavid du Colombier reg->irqemask &= ~(IEtxerrq(Qno) | IEtxunderrun); 816*897ae9c1SDavid du Colombier transmit(ether); 817*897ae9c1SDavid du Colombier handled++; 818*897ae9c1SDavid du Colombier } 819*897ae9c1SDavid du Colombier 820*897ae9c1SDavid du Colombier if(irqe & IEsum) { 821*897ae9c1SDavid du Colombier /* 822*897ae9c1SDavid du Colombier * IElinkchg appears to only be set when unplugging. 823*897ae9c1SDavid du Colombier * autonegotiation is likely not done yet, so linkup not valid, 824*897ae9c1SDavid du Colombier * thus we note the link change here, and check for 825*897ae9c1SDavid du Colombier * that and autonegotiation done below. 826*897ae9c1SDavid du Colombier */ 827*897ae9c1SDavid du Colombier if(irqe & IEphystschg) { 828*897ae9c1SDavid du Colombier ether->link = (reg->ps0 & PS0linkup) != 0; 829*897ae9c1SDavid du Colombier ether->linkchg = 1; 830*897ae9c1SDavid du Colombier } 831*897ae9c1SDavid du Colombier if(irqe & IEtxerrq(Qno)) 832*897ae9c1SDavid du Colombier ether->oerrs++; 833*897ae9c1SDavid du Colombier if(irqe & IErxoverrun) 834*897ae9c1SDavid du Colombier ether->overflows++; 835*897ae9c1SDavid du Colombier if(irqe & IEtxunderrun) 836*897ae9c1SDavid du Colombier ctlr->txunderrun++; 837*897ae9c1SDavid du Colombier if(irqe & (IEphystschg | IEtxerrq(Qno) | IErxoverrun | 838*897ae9c1SDavid du Colombier IEtxunderrun)) 839*897ae9c1SDavid du Colombier handled++; 840*897ae9c1SDavid du Colombier } 841*897ae9c1SDavid du Colombier if (irq & Isum) { 842*897ae9c1SDavid du Colombier /* TODO we get these continually on the guruplug */ 843*897ae9c1SDavid du Colombier if (irq & Irxerrq(Qno)) { 844*897ae9c1SDavid du Colombier ether->buffs++; /* approx. error */ 845*897ae9c1SDavid du Colombier /* null descriptor pointer or descriptor owned by cpu */ 846*897ae9c1SDavid du Colombier // iprint("ether1116: rx err on queue 0 - input ring full\n"); 847*897ae9c1SDavid du Colombier } 848*897ae9c1SDavid du Colombier if (irq & Irxerr) { 849*897ae9c1SDavid du Colombier ether->buffs++; /* approx. error */ 850*897ae9c1SDavid du Colombier /* null descriptor pointer or descriptor owned by cpu */ 851*897ae9c1SDavid du Colombier // iprint("ether1116: rx err - input ring full\n"); 852*897ae9c1SDavid du Colombier } 853*897ae9c1SDavid du Colombier if(irq & (Irxerr | Irxerrq(Qno))) 854*897ae9c1SDavid du Colombier handled++; 855*897ae9c1SDavid du Colombier } 856*897ae9c1SDavid du Colombier 857*897ae9c1SDavid du Colombier if(ether->linkchg && (reg->ps1 & PS1an_done)) { 858*897ae9c1SDavid du Colombier handled++; 859*897ae9c1SDavid du Colombier ether->link = (reg->ps0 & PS0linkup) != 0; 860*897ae9c1SDavid du Colombier ether->linkchg = 0; 861*897ae9c1SDavid du Colombier } 862*897ae9c1SDavid du Colombier ctlr->newintrs++; 863*897ae9c1SDavid du Colombier 864*897ae9c1SDavid du Colombier if (!handled) { 865*897ae9c1SDavid du Colombier irq &= ~Isum; 866*897ae9c1SDavid du Colombier irqe &= ~IEtxbufferq(Qno); 867*897ae9c1SDavid du Colombier if (irq == 0 && irqe == 0) { 868*897ae9c1SDavid du Colombier /* seems to be triggered by continuous output */ 869*897ae9c1SDavid du Colombier // iprint("ether1116: spurious interrupt\n"); 870*897ae9c1SDavid du Colombier } else 871*897ae9c1SDavid du Colombier iprint("ether1116: interrupt cause unknown; " 872*897ae9c1SDavid du Colombier "irq %#lux irqe %#lux\n", irq, irqe); 873*897ae9c1SDavid du Colombier } 874*897ae9c1SDavid du Colombier intrclear(Irqlo, ether->irq); 875*897ae9c1SDavid du Colombier } 876*897ae9c1SDavid du Colombier 877*897ae9c1SDavid du Colombier void 878*897ae9c1SDavid du Colombier promiscuous(void *arg, int on) 879*897ae9c1SDavid du Colombier { 880*897ae9c1SDavid du Colombier Ether *ether = arg; 881*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 882*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 883*897ae9c1SDavid du Colombier 884*897ae9c1SDavid du Colombier ilock(ctlr); 885*897ae9c1SDavid du Colombier ether->prom = on; 886*897ae9c1SDavid du Colombier if(on) 887*897ae9c1SDavid du Colombier reg->portcfg |= PCFGupromisc; 888*897ae9c1SDavid du Colombier else 889*897ae9c1SDavid du Colombier reg->portcfg &= ~PCFGupromisc; 890*897ae9c1SDavid du Colombier iunlock(ctlr); 891*897ae9c1SDavid du Colombier } 892*897ae9c1SDavid du Colombier 893*897ae9c1SDavid du Colombier void 894*897ae9c1SDavid du Colombier multicast(void *, uchar *, int) 895*897ae9c1SDavid du Colombier { 896*897ae9c1SDavid du Colombier /* nothing to do; we always accept multicast */ 897*897ae9c1SDavid du Colombier } 898*897ae9c1SDavid du Colombier 899*897ae9c1SDavid du Colombier static void quiesce(Gbereg *reg); 900*897ae9c1SDavid du Colombier 901*897ae9c1SDavid du Colombier static void 902*897ae9c1SDavid du Colombier shutdown(Ether *ether) 903*897ae9c1SDavid du Colombier { 904*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 905*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 906*897ae9c1SDavid du Colombier 907*897ae9c1SDavid du Colombier ilock(ctlr); 908*897ae9c1SDavid du Colombier quiesce(reg); 909*897ae9c1SDavid du Colombier reg->tcqdp[Qno] = 0; 910*897ae9c1SDavid du Colombier reg->crdp[Qno].r = 0; 911*897ae9c1SDavid du Colombier reg->psc0 = 0; /* no PSC0porton */ 912*897ae9c1SDavid du Colombier reg->psc1 |= PSC1portreset; 913*897ae9c1SDavid du Colombier iunlock(ctlr); 914*897ae9c1SDavid du Colombier coherence(); 915*897ae9c1SDavid du Colombier delay(100); 916*897ae9c1SDavid du Colombier } 917*897ae9c1SDavid du Colombier 918*897ae9c1SDavid du Colombier enum { 919*897ae9c1SDavid du Colombier CMjumbo, 920*897ae9c1SDavid du Colombier }; 921*897ae9c1SDavid du Colombier 922*897ae9c1SDavid du Colombier static Cmdtab ctlmsg[] = { 923*897ae9c1SDavid du Colombier CMjumbo, "jumbo", 2, 924*897ae9c1SDavid du Colombier }; 925*897ae9c1SDavid du Colombier 926*897ae9c1SDavid du Colombier long 927*897ae9c1SDavid du Colombier ctl(Ether *e, void *p, long n) 928*897ae9c1SDavid du Colombier { 929*897ae9c1SDavid du Colombier Cmdbuf *cb; 930*897ae9c1SDavid du Colombier Cmdtab *ct; 931*897ae9c1SDavid du Colombier Ctlr *ctlr = e->ctlr; 932*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 933*897ae9c1SDavid du Colombier 934*897ae9c1SDavid du Colombier cb = parsecmd(p, n); 935*897ae9c1SDavid du Colombier if(waserror()) { 936*897ae9c1SDavid du Colombier free(cb); 937*897ae9c1SDavid du Colombier nexterror(); 938*897ae9c1SDavid du Colombier } 939*897ae9c1SDavid du Colombier 940*897ae9c1SDavid du Colombier ct = lookupcmd(cb, ctlmsg, nelem(ctlmsg)); 941*897ae9c1SDavid du Colombier switch(ct->index) { 942*897ae9c1SDavid du Colombier case CMjumbo: 943*897ae9c1SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) { 944*897ae9c1SDavid du Colombier /* incoming packet queue doesn't expect jumbo frames */ 945*897ae9c1SDavid du Colombier error("jumbo disabled"); 946*897ae9c1SDavid du Colombier reg->psc0 = (reg->psc0 & ~PSC0mrumask) | 947*897ae9c1SDavid du Colombier PSC0mru(PSC0mru9022); 948*897ae9c1SDavid du Colombier e->maxmtu = 9022; 949*897ae9c1SDavid du Colombier } else if(strcmp(cb->f[1], "off") == 0) { 950*897ae9c1SDavid du Colombier reg->psc0 = (reg->psc0 & ~PSC0mrumask) | 951*897ae9c1SDavid du Colombier PSC0mru(PSC0mru1522); 952*897ae9c1SDavid du Colombier e->maxmtu = ETHERMAXTU; 953*897ae9c1SDavid du Colombier } else 954*897ae9c1SDavid du Colombier error(Ebadctl); 955*897ae9c1SDavid du Colombier break; 956*897ae9c1SDavid du Colombier default: 957*897ae9c1SDavid du Colombier error(Ebadctl); 958*897ae9c1SDavid du Colombier break; 959*897ae9c1SDavid du Colombier } 960*897ae9c1SDavid du Colombier free(cb); 961*897ae9c1SDavid du Colombier poperror(); 962*897ae9c1SDavid du Colombier return n; 963*897ae9c1SDavid du Colombier } 964*897ae9c1SDavid du Colombier 965*897ae9c1SDavid du Colombier /* 966*897ae9c1SDavid du Colombier * phy/mii goo 967*897ae9c1SDavid du Colombier */ 968*897ae9c1SDavid du Colombier 969*897ae9c1SDavid du Colombier static int 970*897ae9c1SDavid du Colombier smibusywait(Gbereg *reg, ulong waitbit) 971*897ae9c1SDavid du Colombier { 972*897ae9c1SDavid du Colombier ulong timeout, smi_reg; 973*897ae9c1SDavid du Colombier 974*897ae9c1SDavid du Colombier timeout = PhysmiTimeout; 975*897ae9c1SDavid du Colombier /* wait till the SMI is not busy */ 976*897ae9c1SDavid du Colombier do { 977*897ae9c1SDavid du Colombier /* read smi register */ 978*897ae9c1SDavid du Colombier smi_reg = reg->smi; 979*897ae9c1SDavid du Colombier if (timeout-- == 0) { 980*897ae9c1SDavid du Colombier MIIDBG("SMI busy timeout\n"); 981*897ae9c1SDavid du Colombier return -1; 982*897ae9c1SDavid du Colombier } 983*897ae9c1SDavid du Colombier // delay(1); 984*897ae9c1SDavid du Colombier } while (smi_reg & waitbit); 985*897ae9c1SDavid du Colombier return 0; 986*897ae9c1SDavid du Colombier } 987*897ae9c1SDavid du Colombier 988*897ae9c1SDavid du Colombier static int 989*897ae9c1SDavid du Colombier miird(Mii *mii, int pa, int ra) 990*897ae9c1SDavid du Colombier { 991*897ae9c1SDavid du Colombier ulong smi_reg, timeout; 992*897ae9c1SDavid du Colombier Ctlr *ctlr; 993*897ae9c1SDavid du Colombier Gbereg *reg; 994*897ae9c1SDavid du Colombier 995*897ae9c1SDavid du Colombier ctlr = (Ctlr*)mii->ctlr; 996*897ae9c1SDavid du Colombier reg = ctlr->reg; 997*897ae9c1SDavid du Colombier 998*897ae9c1SDavid du Colombier /* check params */ 999*897ae9c1SDavid du Colombier if ((pa<<Physmiaddroff) & ~Physmiaddrmask || 1000*897ae9c1SDavid du Colombier (ra<<SmiRegaddroff) & ~SmiRegaddrmask) 1001*897ae9c1SDavid du Colombier return -1; 1002*897ae9c1SDavid du Colombier 1003*897ae9c1SDavid du Colombier smibusywait(reg, PhysmiBusy); 1004*897ae9c1SDavid du Colombier 1005*897ae9c1SDavid du Colombier /* fill the phy address and register offset and read opcode */ 1006*897ae9c1SDavid du Colombier reg->smi = pa << Physmiaddroff | ra << SmiRegaddroff | PhysmiopRd; 1007*897ae9c1SDavid du Colombier coherence(); 1008*897ae9c1SDavid du Colombier 1009*897ae9c1SDavid du Colombier /* wait til read value is ready */ 1010*897ae9c1SDavid du Colombier // if (smibusywait(reg, PhysmiReadok) < 0) 1011*897ae9c1SDavid du Colombier // return -1; 1012*897ae9c1SDavid du Colombier timeout = PhysmiTimeout; 1013*897ae9c1SDavid du Colombier do { 1014*897ae9c1SDavid du Colombier smi_reg = reg->smi; 1015*897ae9c1SDavid du Colombier if (timeout-- == 0) { 1016*897ae9c1SDavid du Colombier MIIDBG("SMI read-valid timeout\n"); 1017*897ae9c1SDavid du Colombier return -1; 1018*897ae9c1SDavid du Colombier } 1019*897ae9c1SDavid du Colombier } while (!(smi_reg & PhysmiReadok)); 1020*897ae9c1SDavid du Colombier 1021*897ae9c1SDavid du Colombier /* Wait for the data to update in the SMI register */ 1022*897ae9c1SDavid du Colombier for (timeout = 0; timeout < PhysmiTimeout; timeout++) 1023*897ae9c1SDavid du Colombier ; 1024*897ae9c1SDavid du Colombier return reg->smi & Physmidatamask; 1025*897ae9c1SDavid du Colombier } 1026*897ae9c1SDavid du Colombier 1027*897ae9c1SDavid du Colombier static int 1028*897ae9c1SDavid du Colombier miiwr(Mii *mii, int pa, int ra, int v) 1029*897ae9c1SDavid du Colombier { 1030*897ae9c1SDavid du Colombier Ctlr *ctlr; 1031*897ae9c1SDavid du Colombier Gbereg *reg; 1032*897ae9c1SDavid du Colombier ulong smi_reg; 1033*897ae9c1SDavid du Colombier 1034*897ae9c1SDavid du Colombier ctlr = (Ctlr*)mii->ctlr; 1035*897ae9c1SDavid du Colombier reg = ctlr->reg; 1036*897ae9c1SDavid du Colombier 1037*897ae9c1SDavid du Colombier /* check params */ 1038*897ae9c1SDavid du Colombier if (((pa<<Physmiaddroff) & ~Physmiaddrmask) || 1039*897ae9c1SDavid du Colombier ((ra<<SmiRegaddroff) & ~SmiRegaddrmask)) 1040*897ae9c1SDavid du Colombier return -1; 1041*897ae9c1SDavid du Colombier 1042*897ae9c1SDavid du Colombier smibusywait(reg, PhysmiBusy); 1043*897ae9c1SDavid du Colombier 1044*897ae9c1SDavid du Colombier /* fill the phy address and register offset and read opcode */ 1045*897ae9c1SDavid du Colombier smi_reg = v << Physmidataoff | pa << Physmiaddroff | ra << SmiRegaddroff; 1046*897ae9c1SDavid du Colombier reg->smi = smi_reg & ~PhysmiopRd; 1047*897ae9c1SDavid du Colombier coherence(); 1048*897ae9c1SDavid du Colombier return 0; 1049*897ae9c1SDavid du Colombier } 1050*897ae9c1SDavid du Colombier 1051*897ae9c1SDavid du Colombier static int 1052*897ae9c1SDavid du Colombier kirkwoodmii(Ether *ether) 1053*897ae9c1SDavid du Colombier { 1054*897ae9c1SDavid du Colombier int i; 1055*897ae9c1SDavid du Colombier Ctlr *ctlr; 1056*897ae9c1SDavid du Colombier MiiPhy *phy; 1057*897ae9c1SDavid du Colombier 1058*897ae9c1SDavid du Colombier MIIDBG("mii\n"); 1059*897ae9c1SDavid du Colombier ctlr = ether->ctlr; 1060*897ae9c1SDavid du Colombier if((ctlr->mii = malloc(sizeof(Mii))) == nil) 1061*897ae9c1SDavid du Colombier return -1; 1062*897ae9c1SDavid du Colombier ctlr->mii->ctlr = ctlr; 1063*897ae9c1SDavid du Colombier ctlr->mii->mir = miird; 1064*897ae9c1SDavid du Colombier ctlr->mii->miw = miiwr; 1065*897ae9c1SDavid du Colombier 1066*897ae9c1SDavid du Colombier if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ 1067*897ae9c1SDavid du Colombier print("#l%d: ether1116: init mii failure\n", ether->ctlrno); 1068*897ae9c1SDavid du Colombier free(ctlr->mii); 1069*897ae9c1SDavid du Colombier ctlr->mii = nil; 1070*897ae9c1SDavid du Colombier return -1; 1071*897ae9c1SDavid du Colombier } 1072*897ae9c1SDavid du Colombier 1073*897ae9c1SDavid du Colombier /* oui 005043 is marvell */ 1074*897ae9c1SDavid du Colombier MIIDBG("oui %#X phyno %d\n", phy->oui, phy->phyno); 1075*897ae9c1SDavid du Colombier if(miistatus(ctlr->mii) < 0){ 1076*897ae9c1SDavid du Colombier miireset(ctlr->mii); 1077*897ae9c1SDavid du Colombier MIIDBG("miireset\n"); 1078*897ae9c1SDavid du Colombier if(miiane(ctlr->mii, ~0, 0, ~0) < 0){ 1079*897ae9c1SDavid du Colombier iprint("miiane failed\n"); 1080*897ae9c1SDavid du Colombier return -1; 1081*897ae9c1SDavid du Colombier } 1082*897ae9c1SDavid du Colombier MIIDBG("miistatus\n"); 1083*897ae9c1SDavid du Colombier miistatus(ctlr->mii); 1084*897ae9c1SDavid du Colombier if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){ 1085*897ae9c1SDavid du Colombier for(i = 0; ; i++){ 1086*897ae9c1SDavid du Colombier if(i > 600){ 1087*897ae9c1SDavid du Colombier iprint("ether1116: autonegotiation failed\n"); 1088*897ae9c1SDavid du Colombier break; 1089*897ae9c1SDavid du Colombier } 1090*897ae9c1SDavid du Colombier if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc) 1091*897ae9c1SDavid du Colombier break; 1092*897ae9c1SDavid du Colombier delay(10); 1093*897ae9c1SDavid du Colombier } 1094*897ae9c1SDavid du Colombier if(miistatus(ctlr->mii) < 0) 1095*897ae9c1SDavid du Colombier iprint("miistatus failed\n"); 1096*897ae9c1SDavid du Colombier }else{ 1097*897ae9c1SDavid du Colombier iprint("ether1116: no link\n"); 1098*897ae9c1SDavid du Colombier phy->speed = 10; /* simple default */ 1099*897ae9c1SDavid du Colombier } 1100*897ae9c1SDavid du Colombier } 1101*897ae9c1SDavid du Colombier 1102*897ae9c1SDavid du Colombier ether->mbps = phy->speed; 1103*897ae9c1SDavid du Colombier // iprint("#l%d: kirkwoodmii: fd %d speed %d tfc %d rfc %d\n", 1104*897ae9c1SDavid du Colombier // ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc); 1105*897ae9c1SDavid du Colombier MIIDBG("mii done\n"); 1106*897ae9c1SDavid du Colombier return 0; 1107*897ae9c1SDavid du Colombier } 1108*897ae9c1SDavid du Colombier 1109*897ae9c1SDavid du Colombier enum { /* PHY register pages */ 1110*897ae9c1SDavid du Colombier Pagcopper, 1111*897ae9c1SDavid du Colombier Pagfiber, 1112*897ae9c1SDavid du Colombier Pagrgmii, 1113*897ae9c1SDavid du Colombier Pagled, 1114*897ae9c1SDavid du Colombier Pagrsvd1, 1115*897ae9c1SDavid du Colombier Pagvct, 1116*897ae9c1SDavid du Colombier Pagtest, 1117*897ae9c1SDavid du Colombier Pagrsvd2, 1118*897ae9c1SDavid du Colombier Pagfactest, 1119*897ae9c1SDavid du Colombier }; 1120*897ae9c1SDavid du Colombier 1121*897ae9c1SDavid du Colombier static void 1122*897ae9c1SDavid du Colombier miiregpage(Mii *mii, ulong dev, ulong page) 1123*897ae9c1SDavid du Colombier { 1124*897ae9c1SDavid du Colombier miiwr(mii, dev, Eadr, page); 1125*897ae9c1SDavid du Colombier } 1126*897ae9c1SDavid du Colombier 1127*897ae9c1SDavid du Colombier static int 1128*897ae9c1SDavid du Colombier miiphyinit(Mii *mii) 1129*897ae9c1SDavid du Colombier { 1130*897ae9c1SDavid du Colombier ulong dev; 1131*897ae9c1SDavid du Colombier Ctlr *ctlr; 1132*897ae9c1SDavid du Colombier Gbereg *reg; 1133*897ae9c1SDavid du Colombier 1134*897ae9c1SDavid du Colombier ctlr = (Ctlr*)mii->ctlr; 1135*897ae9c1SDavid du Colombier reg = ctlr->reg; 1136*897ae9c1SDavid du Colombier dev = reg->phy; 1137*897ae9c1SDavid du Colombier MIIDBG("phy dev addr %lux\n", dev); 1138*897ae9c1SDavid du Colombier 1139*897ae9c1SDavid du Colombier /* leds link & activity */ 1140*897ae9c1SDavid du Colombier miiregpage(mii, dev, Pagled); 1141*897ae9c1SDavid du Colombier /* low 4 bits == 1: on - link, blink - activity, off - no link */ 1142*897ae9c1SDavid du Colombier miiwr(mii, dev, Scr, (miird(mii, dev, Scr) & ~0xf) | 1); 1143*897ae9c1SDavid du Colombier 1144*897ae9c1SDavid du Colombier miiregpage(mii, dev, Pagrgmii); 1145*897ae9c1SDavid du Colombier miiwr(mii, dev, Scr, miird(mii, dev, Scr) | Rgmiipwrup); 1146*897ae9c1SDavid du Colombier /* TODO must now do a software reset, sez the manual */ 1147*897ae9c1SDavid du Colombier 1148*897ae9c1SDavid du Colombier /* enable RGMII delay on Tx and Rx for CPU port */ 1149*897ae9c1SDavid du Colombier miiwr(mii, dev, Recr, miird(mii, dev, Recr) | Rxtiming | Rxtiming); 1150*897ae9c1SDavid du Colombier 1151*897ae9c1SDavid du Colombier miiregpage(mii, dev, Pagcopper); 1152*897ae9c1SDavid du Colombier miiwr(mii, dev, Scr, 1153*897ae9c1SDavid du Colombier (miird(mii, dev, Scr) & ~(Pwrdown|Endetect)) | Mdix); 1154*897ae9c1SDavid du Colombier 1155*897ae9c1SDavid du Colombier return 0; 1156*897ae9c1SDavid du Colombier } 1157*897ae9c1SDavid du Colombier 1158*897ae9c1SDavid du Colombier /* 1159*897ae9c1SDavid du Colombier * initialisation 1160*897ae9c1SDavid du Colombier */ 1161*897ae9c1SDavid du Colombier 1162*897ae9c1SDavid du Colombier static void 1163*897ae9c1SDavid du Colombier quiesce(Gbereg *reg) 1164*897ae9c1SDavid du Colombier { 1165*897ae9c1SDavid du Colombier ulong v; 1166*897ae9c1SDavid du Colombier 1167*897ae9c1SDavid du Colombier v = reg->tqc; 1168*897ae9c1SDavid du Colombier if (v & 0xFF) 1169*897ae9c1SDavid du Colombier reg->tqc = v << 8; /* stop active channels */ 1170*897ae9c1SDavid du Colombier v = reg->rqc; 1171*897ae9c1SDavid du Colombier if (v & 0xFF) 1172*897ae9c1SDavid du Colombier reg->rqc = v << 8; /* stop active channels */ 1173*897ae9c1SDavid du Colombier /* wait for all queues to stop */ 1174*897ae9c1SDavid du Colombier while (reg->tqc & 0xFF || reg->rqc & 0xFF) 1175*897ae9c1SDavid du Colombier ; 1176*897ae9c1SDavid du Colombier } 1177*897ae9c1SDavid du Colombier 1178*897ae9c1SDavid du Colombier static void 1179*897ae9c1SDavid du Colombier portreset(Gbereg *reg) 1180*897ae9c1SDavid du Colombier { 1181*897ae9c1SDavid du Colombier ulong i; 1182*897ae9c1SDavid du Colombier 1183*897ae9c1SDavid du Colombier quiesce(reg); 1184*897ae9c1SDavid du Colombier reg->psc0 &= ~PSC0porton; /* disable port */ 1185*897ae9c1SDavid du Colombier reg->psc1 &= ~(PSC1rgmii|PSC1portreset); /* set port & MII active */ 1186*897ae9c1SDavid du Colombier coherence(); 1187*897ae9c1SDavid du Colombier for (i = 0; i < 4000; i++) /* magic delay */ 1188*897ae9c1SDavid du Colombier ; 1189*897ae9c1SDavid du Colombier } 1190*897ae9c1SDavid du Colombier 1191*897ae9c1SDavid du Colombier static void 1192*897ae9c1SDavid du Colombier p16(uchar *p, ulong v) 1193*897ae9c1SDavid du Colombier { 1194*897ae9c1SDavid du Colombier *p++ = v>>8; 1195*897ae9c1SDavid du Colombier *p = v; 1196*897ae9c1SDavid du Colombier } 1197*897ae9c1SDavid du Colombier 1198*897ae9c1SDavid du Colombier static void 1199*897ae9c1SDavid du Colombier p32(uchar *p, ulong v) 1200*897ae9c1SDavid du Colombier { 1201*897ae9c1SDavid du Colombier *p++ = v>>24; 1202*897ae9c1SDavid du Colombier *p++ = v>>16; 1203*897ae9c1SDavid du Colombier *p++ = v>>8; 1204*897ae9c1SDavid du Colombier *p = v; 1205*897ae9c1SDavid du Colombier } 1206*897ae9c1SDavid du Colombier 1207*897ae9c1SDavid du Colombier /* 1208*897ae9c1SDavid du Colombier * set ether->ea from hw mac address, 1209*897ae9c1SDavid du Colombier * configure unicast filtering to accept it. 1210*897ae9c1SDavid du Colombier */ 1211*897ae9c1SDavid du Colombier void 1212*897ae9c1SDavid du Colombier archetheraddr(Ether *ether, Gbereg *reg, int rxqno) 1213*897ae9c1SDavid du Colombier { 1214*897ae9c1SDavid du Colombier ulong nibble, ucreg, tbloff, regoff; 1215*897ae9c1SDavid du Colombier 1216*897ae9c1SDavid du Colombier p32(ether->ea, reg->macah); 1217*897ae9c1SDavid du Colombier p16(ether->ea+4, reg->macal); 1218*897ae9c1SDavid du Colombier 1219*897ae9c1SDavid du Colombier /* accept frames on ea */ 1220*897ae9c1SDavid du Colombier nibble = ether->ea[5] & 0xf; 1221*897ae9c1SDavid du Colombier tbloff = nibble / 4; 1222*897ae9c1SDavid du Colombier regoff = nibble % 4; 1223*897ae9c1SDavid du Colombier 1224*897ae9c1SDavid du Colombier regoff *= 8; 1225*897ae9c1SDavid du Colombier ucreg = reg->dfut[tbloff]; 1226*897ae9c1SDavid du Colombier ucreg &= 0xff << regoff; 1227*897ae9c1SDavid du Colombier ucreg |= (rxqno << 1 | Pass) << regoff; 1228*897ae9c1SDavid du Colombier reg->dfut[tbloff] = ucreg; 1229*897ae9c1SDavid du Colombier 1230*897ae9c1SDavid du Colombier /* accept all multicast too. set up special & other tables. */ 1231*897ae9c1SDavid du Colombier memset(reg->dfsmt, Qno<<1 | Pass, sizeof reg->dfsmt); 1232*897ae9c1SDavid du Colombier memset(reg->dfomt, Qno<<1 | Pass, sizeof reg->dfomt); 1233*897ae9c1SDavid du Colombier coherence(); 1234*897ae9c1SDavid du Colombier } 1235*897ae9c1SDavid du Colombier 1236*897ae9c1SDavid du Colombier static void 1237*897ae9c1SDavid du Colombier ctlrinit(Ether *ether) 1238*897ae9c1SDavid du Colombier { 1239*897ae9c1SDavid du Colombier int i; 1240*897ae9c1SDavid du Colombier Block *b; 1241*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 1242*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 1243*897ae9c1SDavid du Colombier Rx *r; 1244*897ae9c1SDavid du Colombier Tx *t; 1245*897ae9c1SDavid du Colombier static char name[KNAMELEN]; 1246*897ae9c1SDavid du Colombier static Ctlr fakectlr; /* bigger than 4K; keep off the stack */ 1247*897ae9c1SDavid du Colombier 1248*897ae9c1SDavid du Colombier ilock(&freeblocks); 1249*897ae9c1SDavid du Colombier for(i = 0; i < Nrxblks; i++) { 1250*897ae9c1SDavid du Colombier b = iallocb(Rxblklen+Bufalign-1); 1251*897ae9c1SDavid du Colombier if(b == nil) { 1252*897ae9c1SDavid du Colombier iprint("ether1116: no memory for rx buffers\n"); 1253*897ae9c1SDavid du Colombier break; 1254*897ae9c1SDavid du Colombier } 1255*897ae9c1SDavid du Colombier assert(b->ref == 1); 1256*897ae9c1SDavid du Colombier b->wp = b->rp = (uchar*) 1257*897ae9c1SDavid du Colombier ((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1)); 1258*897ae9c1SDavid du Colombier assert(((uintptr)b->rp & (Bufalign - 1)) == 0); 1259*897ae9c1SDavid du Colombier b->free = rxfreeb; 1260*897ae9c1SDavid du Colombier b->next = freeblocks.head; 1261*897ae9c1SDavid du Colombier freeblocks.head = b; 1262*897ae9c1SDavid du Colombier } 1263*897ae9c1SDavid du Colombier iunlock(&freeblocks); 1264*897ae9c1SDavid du Colombier 1265*897ae9c1SDavid du Colombier ctlr->rx = xspanalloc(Nrx * sizeof(Rx), Descralign, 0); 1266*897ae9c1SDavid du Colombier if(ctlr->rx == nil) 1267*897ae9c1SDavid du Colombier panic("ether1116: no memory for rx ring"); 1268*897ae9c1SDavid du Colombier for(i = 0; i < Nrx; i++) { 1269*897ae9c1SDavid du Colombier r = &ctlr->rx[i]; 1270*897ae9c1SDavid du Colombier assert(((uintptr)r & (Descralign - 1)) == 0); 1271*897ae9c1SDavid du Colombier r->cs = 0; /* not owned by hardware until r->buf is set */ 1272*897ae9c1SDavid du Colombier r->buf = 0; 1273*897ae9c1SDavid du Colombier r->next = PADDR(&ctlr->rx[NEXT(i, Nrx)]); 1274*897ae9c1SDavid du Colombier ctlr->rxb[i] = nil; 1275*897ae9c1SDavid du Colombier } 1276*897ae9c1SDavid du Colombier ctlr->rxtail = ctlr->rxhead = 0; 1277*897ae9c1SDavid du Colombier cachedwb(); 1278*897ae9c1SDavid du Colombier l2cacheuwb(); 1279*897ae9c1SDavid du Colombier rxreplenish(ctlr); 1280*897ae9c1SDavid du Colombier 1281*897ae9c1SDavid du Colombier ctlr->tx = xspanalloc(Ntx * sizeof(Tx), Descralign, 0); 1282*897ae9c1SDavid du Colombier if(ctlr->tx == nil) 1283*897ae9c1SDavid du Colombier panic("ether1116: no memory for tx ring"); 1284*897ae9c1SDavid du Colombier for(i = 0; i < Ntx; i++) { 1285*897ae9c1SDavid du Colombier t = &ctlr->tx[i]; 1286*897ae9c1SDavid du Colombier assert(((uintptr)t & (Descralign - 1)) == 0); 1287*897ae9c1SDavid du Colombier t->cs = 0; 1288*897ae9c1SDavid du Colombier t->buf = 0; 1289*897ae9c1SDavid du Colombier t->next = PADDR(&ctlr->tx[NEXT(i, Ntx)]); 1290*897ae9c1SDavid du Colombier ctlr->txb[i] = nil; 1291*897ae9c1SDavid du Colombier } 1292*897ae9c1SDavid du Colombier ctlr->txtail = ctlr->txhead = 0; 1293*897ae9c1SDavid du Colombier cachedwb(); 1294*897ae9c1SDavid du Colombier l2cacheuwb(); 1295*897ae9c1SDavid du Colombier 1296*897ae9c1SDavid du Colombier /* clear stats by reading them into fake ctlr */ 1297*897ae9c1SDavid du Colombier getmibstats(&fakectlr); 1298*897ae9c1SDavid du Colombier 1299*897ae9c1SDavid du Colombier reg->pxmfs = MFS64by; 1300*897ae9c1SDavid du Colombier 1301*897ae9c1SDavid du Colombier /* 1302*897ae9c1SDavid du Colombier * ipg's (inter packet gaps) for interrupt coalescing, 1303*897ae9c1SDavid du Colombier * values in units of 64 clock cycles. A full-sized 1304*897ae9c1SDavid du Colombier * packet (1514 bytes) takes just over 12µs to transmit. 1305*897ae9c1SDavid du Colombier */ 1306*897ae9c1SDavid du Colombier if (CLOCKFREQ/(Maxrxintrsec*64) >= (1<<16)) 1307*897ae9c1SDavid du Colombier panic("rx coalescing value %d too big for short", 1308*897ae9c1SDavid du Colombier CLOCKFREQ/(Maxrxintrsec*64)); 1309*897ae9c1SDavid du Colombier reg->sdc = SDCrifb | SDCrxburst(Burst16) | SDCtxburst(Burst16) | 1310*897ae9c1SDavid du Colombier SDCrxnobyteswap | SDCtxnobyteswap | 1311*897ae9c1SDavid du Colombier SDCipgintrx(CLOCKFREQ/(Maxrxintrsec*64)); 1312*897ae9c1SDavid du Colombier reg->pxtfut = 0; /* TFUTipginttx(CLOCKFREQ/(Maxrxintrsec*64)) */ 1313*897ae9c1SDavid du Colombier 1314*897ae9c1SDavid du Colombier /* allow just these interrupts */ 1315*897ae9c1SDavid du Colombier /* no Irxerr interrupts since the guru plug generates them continually */ 1316*897ae9c1SDavid du Colombier // reg->irqmask = Irxbufferq(Qno) | Irxerr | Itxendq(Qno); 1317*897ae9c1SDavid du Colombier reg->irqmask = Irxbufferq(Qno) | Itxendq(Qno); 1318*897ae9c1SDavid du Colombier reg->irqemask = IEtxerrq(Qno) | IEphystschg | IErxoverrun | IEtxunderrun; 1319*897ae9c1SDavid du Colombier 1320*897ae9c1SDavid du Colombier reg->irq = 0; 1321*897ae9c1SDavid du Colombier reg->irqe = 0; 1322*897ae9c1SDavid du Colombier reg->euirqmask = 0; 1323*897ae9c1SDavid du Colombier reg->euirq = 0; 1324*897ae9c1SDavid du Colombier 1325*897ae9c1SDavid du Colombier // archetheraddr(ether, ctlr->reg, Qno); /* 2nd location */ 1326*897ae9c1SDavid du Colombier 1327*897ae9c1SDavid du Colombier reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]); 1328*897ae9c1SDavid du Colombier for (i = 1; i < nelem(reg->tcqdp); i++) 1329*897ae9c1SDavid du Colombier reg->tcqdp[i] = 0; 1330*897ae9c1SDavid du Colombier reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]); 1331*897ae9c1SDavid du Colombier for (i = 1; i < nelem(reg->crdp); i++) 1332*897ae9c1SDavid du Colombier reg->crdp[i].r = 0; 1333*897ae9c1SDavid du Colombier coherence(); 1334*897ae9c1SDavid du Colombier 1335*897ae9c1SDavid du Colombier reg->portcfg = Rxqdefault(Qno) | Rxqarp(Qno); 1336*897ae9c1SDavid du Colombier reg->portcfgx = 0; 1337*897ae9c1SDavid du Colombier 1338*897ae9c1SDavid du Colombier reg->psc1 = PSC1rgmii | PSC1encolonbp | PSC1coldomlim(0x23); 1339*897ae9c1SDavid du Colombier reg->psc0 = PSC0porton | PSC0an_flctloff | 1340*897ae9c1SDavid du Colombier PSC0an_pauseadv | PSC0nofrclinkdown | PSC0mru(PSC0mru1522); 1341*897ae9c1SDavid du Colombier 1342*897ae9c1SDavid du Colombier ether->link = (reg->ps0 & PS0linkup) != 0; 1343*897ae9c1SDavid du Colombier 1344*897ae9c1SDavid du Colombier /* set ethernet MTU for leaky bucket mechanism to 0 (disabled) */ 1345*897ae9c1SDavid du Colombier reg->pmtu = 0; 1346*897ae9c1SDavid du Colombier 1347*897ae9c1SDavid du Colombier reg->rqc = Rxqon(Qno); 1348*897ae9c1SDavid du Colombier coherence(); 1349*897ae9c1SDavid du Colombier etheractive(ether); 1350*897ae9c1SDavid du Colombier 1351*897ae9c1SDavid du Colombier snprint(name, sizeof name, "#l%drproc", ether->ctlrno); 1352*897ae9c1SDavid du Colombier kproc(name, rcvproc, ether); 1353*897ae9c1SDavid du Colombier } 1354*897ae9c1SDavid du Colombier 1355*897ae9c1SDavid du Colombier static void 1356*897ae9c1SDavid du Colombier attach(Ether* ether) 1357*897ae9c1SDavid du Colombier { 1358*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 1359*897ae9c1SDavid du Colombier 1360*897ae9c1SDavid du Colombier lock(&ctlr->initlock); 1361*897ae9c1SDavid du Colombier if(ctlr->init == 0) { 1362*897ae9c1SDavid du Colombier ctlrinit(ether); 1363*897ae9c1SDavid du Colombier ctlr->init = 1; 1364*897ae9c1SDavid du Colombier } 1365*897ae9c1SDavid du Colombier unlock(&ctlr->initlock); 1366*897ae9c1SDavid du Colombier } 1367*897ae9c1SDavid du Colombier 1368*897ae9c1SDavid du Colombier /* 1369*897ae9c1SDavid du Colombier * statistics goo. 1370*897ae9c1SDavid du Colombier * mib registers clear on read. 1371*897ae9c1SDavid du Colombier */ 1372*897ae9c1SDavid du Colombier 1373*897ae9c1SDavid du Colombier static void 1374*897ae9c1SDavid du Colombier getmibstats(Ctlr *ctlr) 1375*897ae9c1SDavid du Colombier { 1376*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 1377*897ae9c1SDavid du Colombier 1378*897ae9c1SDavid du Colombier /* 1379*897ae9c1SDavid du Colombier * Marvell 88f6281 errata FE-ETH-120: high long of rxby and txby 1380*897ae9c1SDavid du Colombier * can't be read correctly, so read the low long frequently 1381*897ae9c1SDavid du Colombier * (every 30 seconds or less), thus avoiding overflow into high long. 1382*897ae9c1SDavid du Colombier */ 1383*897ae9c1SDavid du Colombier ctlr->rxby += reg->rxbylo; 1384*897ae9c1SDavid du Colombier ctlr->txby += reg->txbylo; 1385*897ae9c1SDavid du Colombier 1386*897ae9c1SDavid du Colombier ctlr->badrxby += reg->badrxby; 1387*897ae9c1SDavid du Colombier ctlr->mactxerr += reg->mactxerr; 1388*897ae9c1SDavid du Colombier ctlr->rxpkt += reg->rxpkt; 1389*897ae9c1SDavid du Colombier ctlr->badrxpkt += reg->badrxpkt; 1390*897ae9c1SDavid du Colombier ctlr->rxbcastpkt+= reg->rxbcastpkt; 1391*897ae9c1SDavid du Colombier ctlr->rxmcastpkt+= reg->rxmcastpkt; 1392*897ae9c1SDavid du Colombier ctlr->rx64 += reg->rx64; 1393*897ae9c1SDavid du Colombier ctlr->rx65_127 += reg->rx65_127; 1394*897ae9c1SDavid du Colombier ctlr->rx128_255 += reg->rx128_255; 1395*897ae9c1SDavid du Colombier ctlr->rx256_511 += reg->rx256_511; 1396*897ae9c1SDavid du Colombier ctlr->rx512_1023+= reg->rx512_1023; 1397*897ae9c1SDavid du Colombier ctlr->rx1024_max+= reg->rx1024_max; 1398*897ae9c1SDavid du Colombier ctlr->txpkt += reg->txpkt; 1399*897ae9c1SDavid du Colombier ctlr->txcollpktdrop+= reg->txcollpktdrop; 1400*897ae9c1SDavid du Colombier ctlr->txmcastpkt+= reg->txmcastpkt; 1401*897ae9c1SDavid du Colombier ctlr->txbcastpkt+= reg->txbcastpkt; 1402*897ae9c1SDavid du Colombier ctlr->badmacctlpkts+= reg->badmacctlpkts; 1403*897ae9c1SDavid du Colombier ctlr->txflctl += reg->txflctl; 1404*897ae9c1SDavid du Colombier ctlr->rxflctl += reg->rxflctl; 1405*897ae9c1SDavid du Colombier ctlr->badrxflctl+= reg->badrxflctl; 1406*897ae9c1SDavid du Colombier ctlr->rxundersized+= reg->rxundersized; 1407*897ae9c1SDavid du Colombier ctlr->rxfrags += reg->rxfrags; 1408*897ae9c1SDavid du Colombier ctlr->rxtoobig += reg->rxtoobig; 1409*897ae9c1SDavid du Colombier ctlr->rxjabber += reg->rxjabber; 1410*897ae9c1SDavid du Colombier ctlr->rxerr += reg->rxerr; 1411*897ae9c1SDavid du Colombier ctlr->crcerr += reg->crcerr; 1412*897ae9c1SDavid du Colombier ctlr->collisions+= reg->collisions; 1413*897ae9c1SDavid du Colombier ctlr->latecoll += reg->latecoll; 1414*897ae9c1SDavid du Colombier } 1415*897ae9c1SDavid du Colombier 1416*897ae9c1SDavid du Colombier long 1417*897ae9c1SDavid du Colombier ifstat(Ether *ether, void *a, long n, ulong off) 1418*897ae9c1SDavid du Colombier { 1419*897ae9c1SDavid du Colombier Ctlr *ctlr = ether->ctlr; 1420*897ae9c1SDavid du Colombier Gbereg *reg = ctlr->reg; 1421*897ae9c1SDavid du Colombier char *buf, *p, *e; 1422*897ae9c1SDavid du Colombier 1423*897ae9c1SDavid du Colombier buf = p = malloc(READSTR); 1424*897ae9c1SDavid du Colombier e = p + READSTR; 1425*897ae9c1SDavid du Colombier 1426*897ae9c1SDavid du Colombier ilock(ctlr); 1427*897ae9c1SDavid du Colombier getmibstats(ctlr); 1428*897ae9c1SDavid du Colombier 1429*897ae9c1SDavid du Colombier ctlr->intrs += ctlr->newintrs; 1430*897ae9c1SDavid du Colombier p = seprint(p, e, "interrupts: %lud\n", ctlr->intrs); 1431*897ae9c1SDavid du Colombier p = seprint(p, e, "new interrupts: %lud\n", ctlr->newintrs); 1432*897ae9c1SDavid du Colombier ctlr->newintrs = 0; 1433*897ae9c1SDavid du Colombier p = seprint(p, e, "tx underrun: %lud\n", ctlr->txunderrun); 1434*897ae9c1SDavid du Colombier p = seprint(p, e, "tx ring full: %lud\n", ctlr->txringfull); 1435*897ae9c1SDavid du Colombier 1436*897ae9c1SDavid du Colombier ctlr->rxdiscard += reg->pxdfc; 1437*897ae9c1SDavid du Colombier ctlr->rxoverrun += reg->pxofc; 1438*897ae9c1SDavid du Colombier p = seprint(p, e, "rx discarded frames: %lud\n", ctlr->rxdiscard); 1439*897ae9c1SDavid du Colombier p = seprint(p, e, "rx overrun frames: %lud\n", ctlr->rxoverrun); 1440*897ae9c1SDavid du Colombier p = seprint(p, e, "no first+last flag: %lud\n", ctlr->nofirstlast); 1441*897ae9c1SDavid du Colombier 1442*897ae9c1SDavid du Colombier p = seprint(p, e, "duplex: %s\n", (reg->ps0 & PS0fd)? "full": "half"); 1443*897ae9c1SDavid du Colombier p = seprint(p, e, "flow control: %s\n", (reg->ps0 & PS0flctl)? "on": "off"); 1444*897ae9c1SDavid du Colombier /* p = seprint(p, e, "speed: %d mbps\n", ); */ 1445*897ae9c1SDavid du Colombier 1446*897ae9c1SDavid du Colombier p = seprint(p, e, "received bytes: %llud\n", ctlr->rxby); 1447*897ae9c1SDavid du Colombier p = seprint(p, e, "bad received bytes: %lud\n", ctlr->badrxby); 1448*897ae9c1SDavid du Colombier p = seprint(p, e, "internal mac transmit errors: %lud\n", ctlr->mactxerr); 1449*897ae9c1SDavid du Colombier p = seprint(p, e, "total received frames: %lud\n", ctlr->rxpkt); 1450*897ae9c1SDavid du Colombier p = seprint(p, e, "received broadcast frames: %lud\n", ctlr->rxbcastpkt); 1451*897ae9c1SDavid du Colombier p = seprint(p, e, "received multicast frames: %lud\n", ctlr->rxmcastpkt); 1452*897ae9c1SDavid du Colombier p = seprint(p, e, "bad received frames: %lud\n", ctlr->badrxpkt); 1453*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 0-64: %lud\n", ctlr->rx64); 1454*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 65-127: %lud\n", ctlr->rx65_127); 1455*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 128-255: %lud\n", ctlr->rx128_255); 1456*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 256-511: %lud\n", ctlr->rx256_511); 1457*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 512-1023: %lud\n", ctlr->rx512_1023); 1458*897ae9c1SDavid du Colombier p = seprint(p, e, "received frames 1024-max: %lud\n", ctlr->rx1024_max); 1459*897ae9c1SDavid du Colombier p = seprint(p, e, "transmitted bytes: %llud\n", ctlr->txby); 1460*897ae9c1SDavid du Colombier p = seprint(p, e, "total transmitted frames: %lud\n", ctlr->txpkt); 1461*897ae9c1SDavid du Colombier p = seprint(p, e, "transmitted broadcast frames: %lud\n", ctlr->txbcastpkt); 1462*897ae9c1SDavid du Colombier p = seprint(p, e, "transmitted multicast frames: %lud\n", ctlr->txmcastpkt); 1463*897ae9c1SDavid du Colombier p = seprint(p, e, "transmit frames dropped by collision: %lud\n", ctlr->txcollpktdrop); 1464*897ae9c1SDavid du Colombier p = seprint(p, e, "misaligned buffers: %lud\n", ether->pktsmisaligned); 1465*897ae9c1SDavid du Colombier 1466*897ae9c1SDavid du Colombier p = seprint(p, e, "bad mac control frames: %lud\n", ctlr->badmacctlpkts); 1467*897ae9c1SDavid du Colombier p = seprint(p, e, "transmitted flow control messages: %lud\n", ctlr->txflctl); 1468*897ae9c1SDavid du Colombier p = seprint(p, e, "received flow control messages: %lud\n", ctlr->rxflctl); 1469*897ae9c1SDavid du Colombier p = seprint(p, e, "bad received flow control messages: %lud\n", ctlr->badrxflctl); 1470*897ae9c1SDavid du Colombier p = seprint(p, e, "received undersized packets: %lud\n", ctlr->rxundersized); 1471*897ae9c1SDavid du Colombier p = seprint(p, e, "received fragments: %lud\n", ctlr->rxfrags); 1472*897ae9c1SDavid du Colombier p = seprint(p, e, "received oversized packets: %lud\n", ctlr->rxtoobig); 1473*897ae9c1SDavid du Colombier p = seprint(p, e, "received jabber packets: %lud\n", ctlr->rxjabber); 1474*897ae9c1SDavid du Colombier p = seprint(p, e, "mac receive errors: %lud\n", ctlr->rxerr); 1475*897ae9c1SDavid du Colombier p = seprint(p, e, "crc errors: %lud\n", ctlr->crcerr); 1476*897ae9c1SDavid du Colombier p = seprint(p, e, "collisions: %lud\n", ctlr->collisions); 1477*897ae9c1SDavid du Colombier p = seprint(p, e, "late collisions: %lud\n", ctlr->latecoll); 1478*897ae9c1SDavid du Colombier USED(p); 1479*897ae9c1SDavid du Colombier iunlock(ctlr); 1480*897ae9c1SDavid du Colombier 1481*897ae9c1SDavid du Colombier n = readstr(off, a, n, buf); 1482*897ae9c1SDavid du Colombier free(buf); 1483*897ae9c1SDavid du Colombier return n; 1484*897ae9c1SDavid du Colombier } 1485*897ae9c1SDavid du Colombier 1486*897ae9c1SDavid du Colombier 1487*897ae9c1SDavid du Colombier static int 1488*897ae9c1SDavid du Colombier reset(Ether *ether) 1489*897ae9c1SDavid du Colombier { 1490*897ae9c1SDavid du Colombier Ctlr *ctlr; 1491*897ae9c1SDavid du Colombier static uchar zeroea[Eaddrlen]; 1492*897ae9c1SDavid du Colombier 1493*897ae9c1SDavid du Colombier ether->ctlr = ctlr = malloc(sizeof *ctlr); 1494*897ae9c1SDavid du Colombier switch(ether->ctlrno) { 1495*897ae9c1SDavid du Colombier case 0: 1496*897ae9c1SDavid du Colombier ctlr->reg = (Gbereg*)Gbe0regs; 1497*897ae9c1SDavid du Colombier ether->irq = IRQ0gbe0sum; 1498*897ae9c1SDavid du Colombier break; 1499*897ae9c1SDavid du Colombier case 1: 1500*897ae9c1SDavid du Colombier ctlr->reg = (Gbereg*)Gbe1regs; 1501*897ae9c1SDavid du Colombier ether->irq = IRQ0gbe1sum; 1502*897ae9c1SDavid du Colombier break; 1503*897ae9c1SDavid du Colombier default: 1504*897ae9c1SDavid du Colombier panic("ether1116: bad ether ctlr #%d", ether->ctlrno); 1505*897ae9c1SDavid du Colombier } 1506*897ae9c1SDavid du Colombier 1507*897ae9c1SDavid du Colombier /* TODO need this for guruplug, at least */ 1508*897ae9c1SDavid du Colombier *(ulong *)AddrIocfg0 |= 1 << 7 | 1 << 15; /* io cfg 0: 1.8v gbe */ 1509*897ae9c1SDavid du Colombier coherence(); 1510*897ae9c1SDavid du Colombier 1511*897ae9c1SDavid du Colombier portreset(ctlr->reg); 1512*897ae9c1SDavid du Colombier /* ensure that both interfaces are set to RGMII before calling mii */ 1513*897ae9c1SDavid du Colombier ((Gbereg*)Gbe0regs)->psc1 |= PSC1rgmii; 1514*897ae9c1SDavid du Colombier ((Gbereg*)Gbe1regs)->psc1 |= PSC1rgmii; 1515*897ae9c1SDavid du Colombier 1516*897ae9c1SDavid du Colombier /* Set phy address of the port */ 1517*897ae9c1SDavid du Colombier ctlr->port = ether->ctlrno; 1518*897ae9c1SDavid du Colombier ctlr->reg->phy = ether->ctlrno; 1519*897ae9c1SDavid du Colombier coherence(); 1520*897ae9c1SDavid du Colombier ether->port = (uintptr)ctlr->reg; 1521*897ae9c1SDavid du Colombier 1522*897ae9c1SDavid du Colombier if(kirkwoodmii(ether) < 0){ 1523*897ae9c1SDavid du Colombier free(ctlr); 1524*897ae9c1SDavid du Colombier ether->ctlr = nil; 1525*897ae9c1SDavid du Colombier return -1; 1526*897ae9c1SDavid du Colombier } 1527*897ae9c1SDavid du Colombier miiphyinit(ctlr->mii); 1528*897ae9c1SDavid du Colombier archetheraddr(ether, ctlr->reg, Qno); /* original location */ 1529*897ae9c1SDavid du Colombier if (memcmp(ether->ea, zeroea, sizeof zeroea) == 0){ 1530*897ae9c1SDavid du Colombier free(ctlr); 1531*897ae9c1SDavid du Colombier ether->ctlr = nil; 1532*897ae9c1SDavid du Colombier return -1; /* no rj45 for this ether */ 1533*897ae9c1SDavid du Colombier } 1534*897ae9c1SDavid du Colombier ether->attach = attach; 1535*897ae9c1SDavid du Colombier ether->transmit = transmit; 1536*897ae9c1SDavid du Colombier ether->interrupt = interrupt; 1537*897ae9c1SDavid du Colombier ether->ifstat = ifstat; 1538*897ae9c1SDavid du Colombier ether->shutdown = shutdown; 1539*897ae9c1SDavid du Colombier ether->ctl = ctl; 1540*897ae9c1SDavid du Colombier 1541*897ae9c1SDavid du Colombier ether->arg = ether; 1542*897ae9c1SDavid du Colombier ether->promiscuous = promiscuous; 1543*897ae9c1SDavid du Colombier ether->multicast = multicast; 1544*897ae9c1SDavid du Colombier 1545*897ae9c1SDavid du Colombier return 0; 1546*897ae9c1SDavid du Colombier } 1547*897ae9c1SDavid du Colombier 1548*897ae9c1SDavid du Colombier void 1549*897ae9c1SDavid du Colombier ether1116link(void) 1550*897ae9c1SDavid du Colombier { 1551*897ae9c1SDavid du Colombier addethercard("88e1116", reset); 1552*897ae9c1SDavid du Colombier } 1553