1 /* 2 * Asix USB ether adapters 3 * I got no documentation for it, thus the bits 4 * come from other systems; it's likely this is 5 * doing more than needed in some places and 6 * less than required in others. 7 */ 8 #include <u.h> 9 #include <libc.h> 10 #include <fcall.h> 11 #include <thread.h> 12 #include "usb.h" 13 #include "usbfs.h" 14 #include "ether.h" 15 16 enum 17 { 18 19 /* Asix commands */ 20 Cswmii = 0x06, /* set sw mii */ 21 Crmii = 0x07, /* read mii reg */ 22 Cwmii = 0x08, /* write mii reg */ 23 Chwmii = 0x0a, /* set hw mii */ 24 Creeprom = 0x0b, /* read eeprom */ 25 Cwdis = 0x0e, /* write disable */ 26 Cwena = 0x0d, /* write enable */ 27 Crrxctl = 0x0f, /* read rx ctl */ 28 Cwrxctl = 0x10, /* write rx ctl */ 29 Cwipg = 0x12, /* write ipg */ 30 Crmac = 0x13, /* read mac addr */ 31 Crphy = 0x19, /* read phy id */ 32 Cwmedium = 0x1b, /* write medium mode */ 33 Crgpio = 0x1e, /* read gpio */ 34 Cwgpio = 0x1f, /* write gpios */ 35 Creset = 0x20, /* reset */ 36 Cwphy = 0x22, /* select phy */ 37 38 /* reset codes */ 39 Rclear = 0x00, 40 Rprte = 0x04, 41 Rprl = 0x08, 42 Riprl = 0x20, 43 Rippd = 0x40, 44 45 Gpiogpo1en = 0x04, /* gpio1 enable */, 46 Gpiogpo1 = 0x08, /* gpio1 value */ 47 Gpiogpo2en = 0x10, /* gpio2 enable */ 48 Gpiogpo2 = 0x20, /* gpio2 value */ 49 Gpiorse = 0x80, /* gpio reload serial eeprom */ 50 51 Pmask = 0x1F, 52 Pembed = 0x10, /* embedded phy */ 53 54 Mfd = 0x002, /* media */ 55 Mac = 0x004, 56 Mrfc = 0x010, 57 Mtfc = 0x020, 58 Mjfe = 0x040, 59 Mre = 0x100, 60 Mps = 0x200, 61 Mall772 = Mfd|Mrfc|Mtfc|Mps|Mac|Mre, 62 Mall178 = Mps|Mfd|Mac|Mrfc|Mtfc|Mjfe|Mre, 63 64 Ipgdflt = 0x15|0x0c|0x12, /* default ipg0, 1, 2 */ 65 Rxctlso = 0x80, 66 Rxctlab = 0x08, 67 Rxctlsep = 0x04, 68 Rxctlamall = 0x02, /* all multicast */ 69 Rxctlprom = 0x01, /* promiscuous */ 70 71 /* MII */ 72 Miibmcr = 0x00, /* basic mode ctrl reg. */ 73 Bmcrreset = 0x8000, /* reset */ 74 Bmcranena = 0x1000, /* auto neg. enable */ 75 Bmcrar = 0x0200, /* announce restart */ 76 77 Miiad = 0x04, /* advertise reg. */ 78 Adcsma = 0x0001, 79 Ad1000f = 0x0200, 80 Ad1000h = 0x0100, 81 Ad10h = 0x0020, 82 Ad10f = 0x0040, 83 Ad100h = 0x0080, 84 Ad100f = 0x0100, 85 Adpause = 0x0400, 86 Adall = Ad10h|Ad10f|Ad100h|Ad100f, 87 88 Miimctl = 0x14, /* marvell ctl */ 89 Mtxdly = 0x02, 90 Mrxdly = 0x80, 91 Mtxrxdly = 0x82, 92 93 Miic1000 = 0x09, 94 95 }; 96 97 static int 98 asixset(Dev *d, int c, int v) 99 { 100 int r; 101 int ec; 102 103 r = Rh2d|Rvendor|Rdev; 104 ec = usbcmd(d, r, c, v, 0, nil, 0); 105 if(ec < 0) 106 deprint(2, "%s: asixset %x %x: %r\n", argv0, c, v); 107 return ec; 108 } 109 110 static int 111 asixget(Dev *d, int c, uchar *buf, int l) 112 { 113 int r; 114 int ec; 115 116 r = Rd2h|Rvendor|Rdev; 117 ec = usbcmd(d, r, c, 0, 0, buf, l); 118 if(ec < 0) 119 deprint(2, "%s: asixget %x: %r\n", argv0, c); 120 return ec; 121 } 122 123 static int 124 getgpio(Dev *d) 125 { 126 uchar c; 127 128 if(asixget(d, Crgpio, &c, 1) < 0) 129 return -1; 130 return c; 131 } 132 133 static int 134 getphy(Dev *d) 135 { 136 uchar buf[2]; 137 138 if(asixget(d, Crphy, buf, sizeof(buf)) < 0) 139 return -1; 140 deprint(2, "%s: phy addr %#ux\n", argv0, buf[1]); 141 return buf[1]; 142 } 143 144 static int 145 getrxctl(Dev *d) 146 { 147 uchar buf[2]; 148 int r; 149 150 memset(buf, 0, sizeof(buf)); 151 if(asixget(d, Crrxctl, buf, sizeof(buf)) < 0) 152 return -1; 153 r = GET2(buf); 154 deprint(2, "%s: rxctl %#x\n", argv0, r); 155 return r; 156 } 157 158 static int 159 getmac(Dev *d, uchar buf[]) 160 { 161 if(asixget(d, Crmac, buf, Eaddrlen) < 0) 162 return -1; 163 return Eaddrlen; 164 } 165 166 static int 167 miiread(Dev *d, int phy, int reg) 168 { 169 int r; 170 uchar v[2]; 171 172 r = Rd2h|Rvendor|Rdev; 173 if(usbcmd(d, r, Crmii, phy, reg, v, 2) < 0){ 174 dprint(2, "%s: miiwrite: %r\n", argv0); 175 return -1; 176 } 177 r = GET2(v); 178 if(r == 0xFFFF) 179 return -1; 180 return r; 181 } 182 183 184 static int 185 miiwrite(Dev *d, int phy, int reg, int val) 186 { 187 int r; 188 uchar v[2]; 189 190 if(asixset(d, Cswmii, 0) < 0) 191 return -1; 192 r = Rh2d|Rvendor|Rdev; 193 PUT2(v, val); 194 if(usbcmd(d, r, Cwmii, phy, reg, v, 2) < 0){ 195 deprint(2, "%s: miiwrite: %#x %#x %r\n", argv0, reg, val); 196 return -1; 197 } 198 if(asixset(d, Chwmii, 0) < 0) 199 return -1; 200 return 0; 201 } 202 203 static int 204 eepromread(Dev *d, int i) 205 { 206 int r; 207 int ec; 208 uchar buf[2]; 209 210 r = Rd2h|Rvendor|Rdev; 211 ec = usbcmd(d, r, Creeprom, i, 0, buf, sizeof(buf)); 212 if(ec < 0) 213 deprint(2, "%s: eepromread %d: %r\n", argv0, i); 214 ec = GET2(buf); 215 deprint(2, "%s: eeprom %#x = %#x\n", argv0, i, ec); 216 if(ec == 0xFFFF) 217 ec = -1; 218 return ec; 219 } 220 221 /* 222 * No doc. we are doing what Linux does as closely 223 * as we can. 224 */ 225 static int 226 ctlrinit(Ether *ether) 227 { 228 Dev *d; 229 int i; 230 int bmcr; 231 int gpio; 232 int ee17; 233 int rc; 234 235 d = ether->dev; 236 switch(ether->cid){ 237 default: 238 fprint(2, "%s: card known but not implemented\n", argv0); 239 return -1; 240 241 case A88178: 242 deprint(2, "%s: setting up A88178\n", argv0); 243 gpio = getgpio(d); 244 if(gpio < 0) 245 return -1; 246 deprint(2, "%s: gpio sts %#x\n", argv0, gpio); 247 asixset(d, Cwena, 0); 248 ee17 = eepromread(d, 0x0017); 249 asixset(d, Cwdis, 0); 250 asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en); 251 if((ee17 >> 8) != 1){ 252 asixset(d, Cwgpio, 0x003c); 253 asixset(d, Cwgpio, 0x001c); 254 asixset(d, Cwgpio, 0x003c); 255 }else{ 256 asixset(d, Cwgpio, Gpiogpo1en); 257 asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en); 258 } 259 asixset(d, Creset, Rclear); 260 sleep(150); 261 asixset(d, Creset, Rippd|Rprl); 262 sleep(150); 263 asixset(d, Cwrxctl, 0); 264 if(getmac(d, ether->addr) < 0) 265 return -1; 266 ether->phy = getphy(d); 267 if(ee17 < 0 || (ee17 & 0x7) == 0){ 268 miiwrite(d, ether->phy, Miimctl, Mtxrxdly); 269 sleep(60); 270 } 271 miiwrite(d, ether->phy, Miibmcr, Bmcrreset|Bmcranena); 272 miiwrite(d, ether->phy, Miiad, Adall|Adcsma|Adpause); 273 miiwrite(d, ether->phy, Miic1000, Ad1000f); 274 bmcr = miiread(d, ether->phy, Miibmcr); 275 if((bmcr & Bmcranena) != 0){ 276 bmcr |= Bmcrar; 277 miiwrite(d, ether->phy, Miibmcr, bmcr); 278 } 279 asixset(d, Cwmedium, Mall178); 280 asixset(d, Cwrxctl, Rxctlso|Rxctlab); 281 break; 282 283 case A88772: 284 deprint(2, "%s: setting up A88772\n", argv0); 285 if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0) 286 return -1; 287 ether->phy = getphy(d); 288 dprint(2, "%s: phy %#x\n", argv0, ether->phy); 289 if((ether->phy & Pmask) == Pembed){ 290 /* embedded 10/100 ethernet */ 291 rc = asixset(d, Cwphy, 1); 292 }else 293 rc = asixset(d, Cwphy, 0); 294 if(rc < 0) 295 return -1; 296 if(asixset(d, Creset, Rippd|Rprl) < 0) 297 return -1; 298 sleep(150); 299 if((ether->phy & Pmask) == Pembed) 300 rc = asixset(d, Creset, Riprl); 301 else 302 rc = asixset(d, Creset, Rprte); 303 if(rc < 0) 304 return -1; 305 sleep(150); 306 rc = getrxctl(d); 307 deprint(2, "%s: rxctl is %#x\n", argv0, rc); 308 if(asixset(d, Cwrxctl, 0) < 0) 309 return -1; 310 if(getmac(d, ether->addr) < 0) 311 return -1; 312 313 314 if(asixset(d, Creset, Rprl) < 0) 315 return -1; 316 sleep(150); 317 if(asixset(d, Creset, Riprl|Rprl) < 0) 318 return -1; 319 sleep(150); 320 321 miiwrite(d, ether->phy, Miibmcr, Bmcrreset); 322 miiwrite(d, ether->phy, Miiad, Adall|Adcsma); 323 bmcr = miiread(d, ether->phy, Miibmcr); 324 if((bmcr & Bmcranena) != 0){ 325 bmcr |= Bmcrar; 326 miiwrite(d, ether->phy, Miibmcr, bmcr); 327 } 328 if(asixset(d, Cwmedium, Mall772) < 0) 329 return -1; 330 if(asixset(d, Cwipg, Ipgdflt) < 0) 331 return -1; 332 if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0) 333 return -1; 334 deprint(2, "%s: final rxctl: %#x\n", argv0, getrxctl(d)); 335 break; 336 } 337 338 if(etherdebug){ 339 fprint(2, "%s: ether: phy %#x addr ", argv0, ether->phy); 340 for(i = 0; i < sizeof(ether->addr); i++) 341 fprint(2, "%02x", ether->addr[i]); 342 fprint(2, "\n"); 343 } 344 return 0; 345 } 346 347 348 static long 349 asixbread(Ether *e, Buf *bp) 350 { 351 ulong nr; 352 ulong hd; 353 Buf *rbp; 354 355 rbp = e->aux; 356 if(rbp == nil || rbp->ndata < 4){ 357 rbp->rp = rbp->data; 358 rbp->ndata = read(e->epin->dfd, rbp->rp, sizeof(bp->data)); 359 if(rbp->ndata < 0) 360 return -1; 361 } 362 if(rbp->ndata < 4){ 363 werrstr("short frame"); 364 deprint(2, "%s: asixbread got %d bytes\n", argv0, rbp->ndata); 365 rbp->ndata = 0; 366 return 0; 367 } 368 hd = GET4(rbp->rp); 369 nr = hd & 0xFFFF; 370 hd = (hd>>16) & 0xFFFF; 371 if(nr != (~hd & 0xFFFF)){ 372 if(0)deprint(2, "%s: asixread: bad header %#ulx %#ulx\n", 373 argv0, nr, (~hd & 0xFFFF)); 374 werrstr("bad usb packet header"); 375 rbp->ndata = 0; 376 return 0; 377 } 378 rbp->rp += 4; 379 if(nr < 6 || nr > Epktlen){ 380 if(nr < 6) 381 werrstr("short frame"); 382 else 383 werrstr("long frame"); 384 deprint(2, "%s: asixbread %r (%ld)\n", argv0, nr); 385 rbp->ndata = 0; 386 return 0; 387 } 388 bp->rp = bp->data + Hdrsize; 389 memmove(bp->rp, rbp->rp, nr); 390 bp->ndata = nr; 391 rbp->rp += 4 + nr; 392 rbp->ndata -= (4 + nr); 393 return bp->ndata; 394 } 395 396 static long 397 asixbwrite(Ether *e, Buf *bp) 398 { 399 ulong len; 400 long n; 401 402 deprint(2, "%s: asixbwrite %d bytes\n", argv0, bp->ndata); 403 assert(bp->rp - bp->data >= Hdrsize); 404 bp->ndata &= 0xFFFF; 405 len = (0xFFFF0000 & ~(bp->ndata<<16)) | bp->ndata; 406 bp->rp -= 4; 407 PUT4(bp->rp, len); 408 bp->ndata += 4; 409 if((bp->ndata % e->epout->maxpkt) == 0){ 410 PUT4(bp->rp+bp->ndata, 0xFFFF0000); 411 bp->ndata += 4; 412 } 413 n = write(e->epout->dfd, bp->rp, bp->ndata); 414 deprint(2, "%s: asixbwrite wrote %ld bytes\n", argv0, n); 415 if(n <= 0) 416 return n; 417 return n; 418 } 419 420 static int 421 asixpromiscuous(Ether *e, int on) 422 { 423 int rxctl; 424 425 deprint(2, "%s: aixprompiscuous %d\n", argv0, on); 426 rxctl = getrxctl(e->dev); 427 if(on != 0) 428 rxctl |= Rxctlprom; 429 else 430 rxctl &= ~Rxctlprom; 431 return asixset(e->dev, Cwrxctl, rxctl); 432 } 433 434 static int 435 asixmulticast(Ether *e, uchar *addr, int on) 436 { 437 int rxctl; 438 439 USED(addr); 440 USED(on); 441 /* BUG: should write multicast filter */ 442 rxctl = getrxctl(e->dev); 443 if(e->nmcasts != 0) 444 rxctl |= Rxctlamall; 445 else 446 rxctl &= ~Rxctlamall; 447 deprint(2, "%s: asixmulticast %d\n", argv0, e->nmcasts); 448 return asixset(e->dev, Cwrxctl, rxctl); 449 } 450 451 static void 452 asixfree(Ether *ether) 453 { 454 deprint(2, "%s: aixfree %#p\n", argv0, ether); 455 free(ether->aux); 456 ether->aux = nil; 457 } 458 459 int 460 asixreset(Ether *ether) 461 { 462 Cinfo *ip; 463 Dev *dev; 464 465 dev = ether->dev; 466 for(ip = cinfo; ip->vid != 0; ip++) 467 if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){ 468 ether->cid = ip->cid; 469 if(ctlrinit(ether) < 0){ 470 deprint(2, "%s: init failed: %r\n", argv0); 471 return -1; 472 } 473 deprint(2, "%s: asix reset done\n", argv0); 474 ether->aux = emallocz(sizeof(Buf), 1); 475 ether->bread = asixbread; 476 ether->bwrite = asixbwrite; 477 ether->free = asixfree; 478 ether->promiscuous = asixpromiscuous; 479 ether->multicast = asixmulticast; 480 ether->mbps = 100; /* BUG */ 481 return 0; 482 } 483 return -1; 484 } 485