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 case A8817x: 238 case A88179: 239 fprint(2, "%s: card known but not implemented\n", argv0); 240 /* fall through */ 241 default: 242 return -1; 243 244 case A88178: 245 deprint(2, "%s: setting up A88178\n", argv0); 246 gpio = getgpio(d); 247 if(gpio < 0) 248 return -1; 249 deprint(2, "%s: gpio sts %#x\n", argv0, gpio); 250 asixset(d, Cwena, 0); 251 ee17 = eepromread(d, 0x0017); 252 asixset(d, Cwdis, 0); 253 asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en); 254 if((ee17 >> 8) != 1){ 255 asixset(d, Cwgpio, 0x003c); 256 asixset(d, Cwgpio, 0x001c); 257 asixset(d, Cwgpio, 0x003c); 258 }else{ 259 asixset(d, Cwgpio, Gpiogpo1en); 260 asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en); 261 } 262 asixset(d, Creset, Rclear); 263 sleep(150); 264 asixset(d, Creset, Rippd|Rprl); 265 sleep(150); 266 asixset(d, Cwrxctl, 0); 267 if(getmac(d, ether->addr) < 0) 268 return -1; 269 ether->phy = getphy(d); 270 if(ee17 < 0 || (ee17 & 0x7) == 0){ 271 miiwrite(d, ether->phy, Miimctl, Mtxrxdly); 272 sleep(60); 273 } 274 miiwrite(d, ether->phy, Miibmcr, Bmcrreset|Bmcranena); 275 miiwrite(d, ether->phy, Miiad, Adall|Adcsma|Adpause); 276 miiwrite(d, ether->phy, Miic1000, Ad1000f); 277 bmcr = miiread(d, ether->phy, Miibmcr); 278 if((bmcr & Bmcranena) != 0){ 279 bmcr |= Bmcrar; 280 miiwrite(d, ether->phy, Miibmcr, bmcr); 281 } 282 asixset(d, Cwmedium, Mall178); 283 asixset(d, Cwrxctl, Rxctlso|Rxctlab); 284 break; 285 286 case A88772: 287 deprint(2, "%s: setting up A88772\n", argv0); 288 if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0) 289 return -1; 290 ether->phy = getphy(d); 291 dprint(2, "%s: phy %#x\n", argv0, ether->phy); 292 if((ether->phy & Pmask) == Pembed){ 293 /* embedded 10/100 ethernet */ 294 rc = asixset(d, Cwphy, 1); 295 }else 296 rc = asixset(d, Cwphy, 0); 297 if(rc < 0) 298 return -1; 299 if(asixset(d, Creset, Rippd|Rprl) < 0) 300 return -1; 301 sleep(150); 302 if((ether->phy & Pmask) == Pembed) 303 rc = asixset(d, Creset, Riprl); 304 else 305 rc = asixset(d, Creset, Rprte); 306 if(rc < 0) 307 return -1; 308 sleep(150); 309 rc = getrxctl(d); 310 deprint(2, "%s: rxctl is %#x\n", argv0, rc); 311 if(asixset(d, Cwrxctl, 0) < 0) 312 return -1; 313 if(getmac(d, ether->addr) < 0) 314 return -1; 315 316 317 if(asixset(d, Creset, Rprl) < 0) 318 return -1; 319 sleep(150); 320 if(asixset(d, Creset, Riprl|Rprl) < 0) 321 return -1; 322 sleep(150); 323 324 miiwrite(d, ether->phy, Miibmcr, Bmcrreset); 325 miiwrite(d, ether->phy, Miiad, Adall|Adcsma); 326 bmcr = miiread(d, ether->phy, Miibmcr); 327 if((bmcr & Bmcranena) != 0){ 328 bmcr |= Bmcrar; 329 miiwrite(d, ether->phy, Miibmcr, bmcr); 330 } 331 if(asixset(d, Cwmedium, Mall772) < 0) 332 return -1; 333 if(asixset(d, Cwipg, Ipgdflt) < 0) 334 return -1; 335 if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0) 336 return -1; 337 deprint(2, "%s: final rxctl: %#x\n", argv0, getrxctl(d)); 338 break; 339 } 340 341 if(etherdebug){ 342 fprint(2, "%s: ether: phy %#x addr ", argv0, ether->phy); 343 for(i = 0; i < sizeof(ether->addr); i++) 344 fprint(2, "%02x", ether->addr[i]); 345 fprint(2, "\n"); 346 } 347 return 0; 348 } 349 350 351 static long 352 asixbread(Ether *e, Buf *bp) 353 { 354 ulong nr; 355 ulong hd; 356 Buf *rbp; 357 358 rbp = e->aux; 359 if(rbp == nil || rbp->ndata < 4){ 360 rbp->rp = rbp->data; 361 rbp->ndata = read(e->epin->dfd, rbp->rp, sizeof(bp->data)); 362 if(rbp->ndata < 0) 363 return -1; 364 } 365 if(rbp->ndata < 4){ 366 werrstr("short frame"); 367 deprint(2, "%s: asixbread got %d bytes\n", argv0, rbp->ndata); 368 rbp->ndata = 0; 369 return 0; 370 } 371 hd = GET4(rbp->rp); 372 nr = hd & 0xFFFF; 373 hd = (hd>>16) & 0xFFFF; 374 if(nr != (~hd & 0xFFFF)){ 375 if(0)deprint(2, "%s: asixread: bad header %#ulx %#ulx\n", 376 argv0, nr, (~hd & 0xFFFF)); 377 werrstr("bad usb packet header"); 378 rbp->ndata = 0; 379 return 0; 380 } 381 rbp->rp += 4; 382 if(nr < 6 || nr > Epktlen){ 383 if(nr < 6) 384 werrstr("short frame"); 385 else 386 werrstr("long frame"); 387 deprint(2, "%s: asixbread %r (%ld)\n", argv0, nr); 388 rbp->ndata = 0; 389 return 0; 390 } 391 bp->rp = bp->data + Hdrsize; 392 memmove(bp->rp, rbp->rp, nr); 393 bp->ndata = nr; 394 rbp->rp += 4 + nr; 395 rbp->ndata -= (4 + nr); 396 return bp->ndata; 397 } 398 399 static long 400 asixbwrite(Ether *e, Buf *bp) 401 { 402 ulong len; 403 long n; 404 405 deprint(2, "%s: asixbwrite %d bytes\n", argv0, bp->ndata); 406 assert(bp->rp - bp->data >= Hdrsize); 407 bp->ndata &= 0xFFFF; 408 len = (0xFFFF0000 & ~(bp->ndata<<16)) | bp->ndata; 409 bp->rp -= 4; 410 PUT4(bp->rp, len); 411 bp->ndata += 4; 412 if((bp->ndata % e->epout->maxpkt) == 0){ 413 PUT4(bp->rp+bp->ndata, 0xFFFF0000); 414 bp->ndata += 4; 415 } 416 n = write(e->epout->dfd, bp->rp, bp->ndata); 417 deprint(2, "%s: asixbwrite wrote %ld bytes\n", argv0, n); 418 if(n <= 0) 419 return n; 420 return n; 421 } 422 423 static int 424 asixpromiscuous(Ether *e, int on) 425 { 426 int rxctl; 427 428 deprint(2, "%s: asixpromiscuous %d\n", argv0, on); 429 rxctl = getrxctl(e->dev); 430 if(on != 0) 431 rxctl |= Rxctlprom; 432 else 433 rxctl &= ~Rxctlprom; 434 return asixset(e->dev, Cwrxctl, rxctl); 435 } 436 437 static int 438 asixmulticast(Ether *e, uchar *addr, int on) 439 { 440 int rxctl; 441 442 USED(addr); 443 USED(on); 444 /* BUG: should write multicast filter */ 445 rxctl = getrxctl(e->dev); 446 if(e->nmcasts != 0) 447 rxctl |= Rxctlamall; 448 else 449 rxctl &= ~Rxctlamall; 450 deprint(2, "%s: asixmulticast %d\n", argv0, e->nmcasts); 451 return asixset(e->dev, Cwrxctl, rxctl); 452 } 453 454 static void 455 asixfree(Ether *ether) 456 { 457 deprint(2, "%s: aixfree %#p\n", argv0, ether); 458 free(ether->aux); 459 ether->aux = nil; 460 } 461 462 int 463 asixreset(Ether *ether) 464 { 465 Cinfo *ip; 466 Dev *dev; 467 468 dev = ether->dev; 469 for(ip = cinfo; ip->vid != 0; ip++) 470 if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){ 471 ether->cid = ip->cid; 472 if(ctlrinit(ether) < 0){ 473 deprint(2, "%s: asix init failed: %r\n", argv0); 474 return -1; 475 } 476 deprint(2, "%s: asix reset done\n", argv0); 477 ether->name = "asix"; 478 ether->aux = emallocz(sizeof(Buf), 1); 479 ether->bufsize = Hdrsize+Maxpkt; 480 ether->bread = asixbread; 481 ether->bwrite = asixbwrite; 482 ether->free = asixfree; 483 ether->promiscuous = asixpromiscuous; 484 ether->multicast = asixmulticast; 485 ether->mbps = 100; /* BUG */ 486 return 0; 487 } 488 return -1; 489 } 490