|
|
1.1 ! root 1: /* ! 2: * 3c90x.c -- This file implements a iPXE API 3c90x driver ! 3: * ! 4: * Originally written for etherboot by: ! 5: * Greg Beeley, [email protected] ! 6: * Modified by Steve Smith, ! 7: * [email protected]. Alignment bug fix Neil Newell ([email protected]). ! 8: * Almost totally Rewritten to use iPXE API, implementation of tx/rx ring support ! 9: * by Thomas Miletich, [email protected] ! 10: * Thanks to Marty Connor and Stefan Hajnoczi for their help and feedback, ! 11: * and to Daniel Verkamp for his help with testing. ! 12: * ! 13: * Copyright (c) 2009 Thomas Miletich ! 14: * ! 15: * Copyright (c) 1999 LightSys Technology Services, Inc. ! 16: * Portions Copyright (c) 1999 Steve Smith ! 17: * ! 18: * This program may be re-distributed in source or binary form, modified, ! 19: * sold, or copied for any purpose, provided that the above copyright message ! 20: * and this text are included with all source copies or derivative works, and ! 21: * provided that the above copyright message and this text are included in the ! 22: * documentation of any binary-only distributions. This program is distributed ! 23: * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR ! 24: * PURPOSE or MERCHANTABILITY. Please read the associated documentation ! 25: * "3c90x.txt" before compiling and using this driver. ! 26: * ! 27: * [ --mdc 20090313 The 3c90x.txt file is now at: ! 28: * http://etherboot.org/wiki/appnotes/3c90x_issues ] ! 29: * ! 30: * This program was written with the assistance of the 3com documentation for ! 31: * the 3c905B-TX card, as well as with some assistance from the 3c59x ! 32: * driver Donald Becker wrote for the Linux kernel, and with some assistance ! 33: * from the remainder of the Etherboot distribution. ! 34: * ! 35: * Indented with unix 'indent' command: ! 36: * $ indent -kr -i8 3c90x.c ! 37: */ ! 38: ! 39: FILE_LICENCE ( BSD2 ); ! 40: ! 41: #include <stdint.h> ! 42: #include <stdio.h> ! 43: #include <stdlib.h> ! 44: #include <stddef.h> ! 45: #include <string.h> ! 46: #include <unistd.h> ! 47: #include <assert.h> ! 48: #include <byteswap.h> ! 49: #include <errno.h> ! 50: #include <ipxe/ethernet.h> ! 51: #include <ipxe/if_ether.h> ! 52: #include <ipxe/io.h> ! 53: #include <ipxe/iobuf.h> ! 54: #include <ipxe/malloc.h> ! 55: #include <ipxe/netdevice.h> ! 56: #include <ipxe/pci.h> ! 57: #include <ipxe/timer.h> ! 58: #include <ipxe/nvs.h> ! 59: ! 60: #include "3c90x.h" ! 61: ! 62: /** ! 63: * a3c90x_internal_IssueCommand: sends a command to the 3c90x card ! 64: * and waits for it's completion ! 65: * ! 66: * @v ioaddr IOAddress of the NIC ! 67: * @v cmd Command to be issued ! 68: * @v param Command parameter ! 69: */ ! 70: static void a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param) ! 71: { ! 72: unsigned int val = (cmd << 11) | param; ! 73: int cnt = 0; ! 74: ! 75: DBGP("a3c90x_internal_IssueCommand\n"); ! 76: ! 77: /* Send the cmd to the cmd register */ ! 78: outw(val, ioaddr + regCommandIntStatus_w); ! 79: ! 80: /* Wait for the cmd to complete */ ! 81: for (cnt = 0; cnt < 100000; cnt++) { ! 82: if (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) { ! 83: continue; ! 84: } else { ! 85: DBG2("Command 0x%04X finished in time. cnt = %d.\n", cmd, cnt); ! 86: return; ! 87: } ! 88: } ! 89: ! 90: DBG("Command 0x%04X DID NOT finish in time. cnt = %d.\n", cmd, cnt); ! 91: } ! 92: ! 93: /** ! 94: * a3c90x_internal_SetWindow: selects a register window set. ! 95: * ! 96: * @v inf_3c90x private NIC data ! 97: * @v window window to be selected ! 98: */ ! 99: static void a3c90x_internal_SetWindow(struct INF_3C90X *inf_3c90x, int window) ! 100: { ! 101: DBGP("a3c90x_internal_SetWindow\n"); ! 102: /* Window already as set? */ ! 103: if (inf_3c90x->CurrentWindow == window) ! 104: return; ! 105: ! 106: /* Issue the window command. */ ! 107: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 108: cmdSelectRegisterWindow, window); ! 109: inf_3c90x->CurrentWindow = window; ! 110: ! 111: return; ! 112: } ! 113: ! 114: static void a3c90x_internal_WaitForEeprom(struct INF_3C90X *inf_3c90x) ! 115: { ! 116: int cnt = 0; ! 117: ! 118: DBGP("a3c90x_internal_WaitForEeprom\n"); ! 119: ! 120: while (eepromBusy & inw(inf_3c90x->IOAddr + regEepromCommand_0_w)) { ! 121: if (cnt == EEPROM_TIMEOUT) { ! 122: DBG("Read from eeprom failed: timeout\n"); ! 123: return; ! 124: } ! 125: udelay(1); ! 126: cnt++; ! 127: } ! 128: } ! 129: ! 130: /** ! 131: * a3c90x_internal_ReadEeprom - nvs routine to read eeprom data ! 132: * We only support reading one word(2 byte). The nvs subsystem will make sure ! 133: * that the routine will never be called with len != 2. ! 134: * ! 135: * @v nvs nvs data. ! 136: * @v address eeprom address to read data from. ! 137: * @v data data is put here. ! 138: * @v len number of bytes to read. ! 139: */ ! 140: static int ! 141: a3c90x_internal_ReadEeprom(struct nvs_device *nvs, unsigned int address, void *data, size_t len) ! 142: { ! 143: unsigned short *dest = (unsigned short *) data; ! 144: struct INF_3C90X *inf_3c90x = ! 145: container_of(nvs, struct INF_3C90X, nvs); ! 146: ! 147: DBGP("a3c90x_internal_ReadEeprom\n"); ! 148: ! 149: /* we support reading 2 bytes only */ ! 150: assert(len == 2); ! 151: ! 152: /* Select correct window */ ! 153: a3c90x_internal_SetWindow(inf_3c90x, winEepromBios0); ! 154: ! 155: /* set eepromRead bits in command sent to NIC */ ! 156: address += (inf_3c90x->is3c556 ? eepromRead_556 : eepromRead); ! 157: ! 158: a3c90x_internal_WaitForEeprom(inf_3c90x); ! 159: /* send address to NIC */ ! 160: outw(address, inf_3c90x->IOAddr + regEepromCommand_0_w); ! 161: a3c90x_internal_WaitForEeprom(inf_3c90x); ! 162: ! 163: /* read value */ ! 164: *dest = inw(inf_3c90x->IOAddr + regEepromData_0_w); ! 165: ! 166: return 0; ! 167: } ! 168: ! 169: /** ! 170: * a3c90x_internal_WriteEeprom - nvs routine to write eeprom data ! 171: * currently not implemented ! 172: * ! 173: * @v nvs nvs data. ! 174: * @v address eeprom address to read data from. ! 175: * @v data data is put here. ! 176: * @v len number of bytes to read. ! 177: */ ! 178: static int ! 179: a3c90x_internal_WriteEeprom(struct nvs_device *nvs __unused, ! 180: unsigned int address __unused, ! 181: const void *data __unused, size_t len __unused) ! 182: { ! 183: return -ENOTSUP; ! 184: } ! 185: ! 186: static void a3c90x_internal_ReadEepromContents(struct INF_3C90X *inf_3c90x) ! 187: { ! 188: int eeprom_size = (inf_3c90x->isBrev ? 0x20 : 0x17) * 2; ! 189: ! 190: DBGP("a3c90x_internal_ReadEepromContents\n"); ! 191: ! 192: nvs_read(&inf_3c90x->nvs, 0, inf_3c90x->eeprom, eeprom_size); ! 193: } ! 194: ! 195: /** ! 196: * a3c90x_reset: exported function that resets the card to its default ! 197: * state. This is so the Linux driver can re-set the card up the way ! 198: * it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will ! 199: * not alter the selected transceiver that we used to download the boot ! 200: * image. ! 201: * ! 202: * @v inf_3c90x Private NIC data ! 203: */ ! 204: static void a3c90x_reset(struct INF_3C90X *inf_3c90x) ! 205: { ! 206: DBGP("a3c90x_reset\n"); ! 207: /* Send the reset command to the card */ ! 208: DBG2("3c90x: Issuing RESET\n"); ! 209: ! 210: /* reset of the receiver on B-revision cards re-negotiates the link ! 211: * takes several seconds (a computer eternity), so we don't reset ! 212: * it here. ! 213: */ ! 214: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 215: cmdGlobalReset, ! 216: globalResetMaskNetwork); ! 217: ! 218: /* global reset command resets station mask, non-B revision cards ! 219: * require explicit reset of values ! 220: */ ! 221: a3c90x_internal_SetWindow(inf_3c90x, winAddressing2); ! 222: outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 0); ! 223: outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 2); ! 224: outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 4); ! 225: ! 226: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0); ! 227: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0); ! 228: ! 229: /* enable rxComplete and txComplete indications */ ! 230: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 231: cmdSetIndicationEnable, ! 232: INT_TXCOMPLETE | INT_UPCOMPLETE); ! 233: ! 234: /* acknowledge any pending status flags */ ! 235: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 236: cmdAcknowledgeInterrupt, 0x661); ! 237: ! 238: return; ! 239: } ! 240: ! 241: /** ! 242: * a3c90x_setup_tx_ring - Allocates TX ring, initialize tx_desc values ! 243: * ! 244: * @v p Private NIC data ! 245: * ! 246: * @ret Returns 0 on success, negative on failure ! 247: */ ! 248: static int a3c90x_setup_tx_ring(struct INF_3C90X *p) ! 249: { ! 250: DBGP("a3c90x_setup_tx_ring\n"); ! 251: p->tx_ring = ! 252: malloc_dma(TX_RING_SIZE * sizeof(struct TXD), TX_RING_ALIGN); ! 253: ! 254: if (!p->tx_ring) { ! 255: DBG("Could not allocate TX-ring\n"); ! 256: return -ENOMEM; ! 257: } ! 258: ! 259: memset(p->tx_ring, 0, TX_RING_SIZE * sizeof(struct TXD)); ! 260: p->tx_cur = 0; ! 261: p->tx_cnt = 0; ! 262: p->tx_tail = 0; ! 263: ! 264: return 0; ! 265: } ! 266: ! 267: /** ! 268: * a3c90x_process_tx_packets - Checks for successfully sent packets, ! 269: * reports them to iPXE with netdev_tx_complete(); ! 270: * ! 271: * @v netdev Network device info ! 272: */ ! 273: static void a3c90x_process_tx_packets(struct net_device *netdev) ! 274: { ! 275: struct INF_3C90X *p = netdev_priv(netdev); ! 276: unsigned int downlist_ptr; ! 277: ! 278: DBGP("a3c90x_process_tx_packets\n"); ! 279: ! 280: DBG2(" tx_cnt: %d\n", p->tx_cnt); ! 281: ! 282: while (p->tx_tail != p->tx_cur) { ! 283: ! 284: downlist_ptr = inl(p->IOAddr + regDnListPtr_l); ! 285: ! 286: DBG2(" downlist_ptr: %#08x\n", downlist_ptr); ! 287: DBG2(" tx_tail: %d tx_cur: %d\n", p->tx_tail, p->tx_cur); ! 288: ! 289: /* NIC is currently working on this tx desc */ ! 290: if(downlist_ptr == virt_to_bus(p->tx_ring + p->tx_tail)) ! 291: return; ! 292: ! 293: netdev_tx_complete(netdev, p->tx_iobuf[p->tx_tail]); ! 294: ! 295: DBG2("transmitted packet\n"); ! 296: DBG2(" size: %zd\n", iob_len(p->tx_iobuf[p->tx_tail])); ! 297: ! 298: p->tx_tail = (p->tx_tail + 1) % TX_RING_SIZE; ! 299: p->tx_cnt--; ! 300: } ! 301: } ! 302: ! 303: static void a3c90x_free_tx_ring(struct INF_3C90X *p) ! 304: { ! 305: DBGP("a3c90x_free_tx_ring\n"); ! 306: ! 307: free_dma(p->tx_ring, TX_RING_SIZE * sizeof(struct TXD)); ! 308: p->tx_ring = NULL; ! 309: /* io_buffers are free()ed by netdev_tx_complete[,_err]() */ ! 310: } ! 311: ! 312: /** ! 313: * a3c90x_transmit - Transmits a packet. ! 314: * ! 315: * @v netdev Network device info ! 316: * @v iob io_buffer containing the data to be send ! 317: * ! 318: * @ret Returns 0 on success, negative on failure ! 319: */ ! 320: static int a3c90x_transmit(struct net_device *netdev, ! 321: struct io_buffer *iob) ! 322: { ! 323: struct INF_3C90X *inf_3c90x = netdev_priv(netdev); ! 324: struct TXD *tx_cur_desc; ! 325: struct TXD *tx_prev_desc; ! 326: ! 327: unsigned int len; ! 328: unsigned int downlist_ptr; ! 329: ! 330: DBGP("a3c90x_transmit\n"); ! 331: ! 332: if (inf_3c90x->tx_cnt == TX_RING_SIZE) { ! 333: DBG("TX-Ring overflow\n"); ! 334: return -ENOBUFS; ! 335: } ! 336: ! 337: inf_3c90x->tx_iobuf[inf_3c90x->tx_cur] = iob; ! 338: tx_cur_desc = inf_3c90x->tx_ring + inf_3c90x->tx_cur; ! 339: ! 340: tx_prev_desc = inf_3c90x->tx_ring + ! 341: (((inf_3c90x->tx_cur + TX_RING_SIZE) - 1) % TX_RING_SIZE); ! 342: ! 343: len = iob_len(iob); ! 344: ! 345: /* Setup the DPD (download descriptor) */ ! 346: tx_cur_desc->DnNextPtr = 0; ! 347: ! 348: /* FrameStartHeader differs in 90x and >= 90xB ! 349: * It contains length in 90x and a round up boundary and packet ID for ! 350: * 90xB and 90xC. We can leave this to 0 for 90xB and 90xC. ! 351: */ ! 352: tx_cur_desc->FrameStartHeader = ! 353: fshTxIndicate | (inf_3c90x->isBrev ? 0x00 : len); ! 354: ! 355: tx_cur_desc->DataAddr = virt_to_bus(iob->data); ! 356: tx_cur_desc->DataLength = len | downLastFrag; ! 357: ! 358: /* We have to stall the download engine, so the NIC won't access the ! 359: * tx descriptor while we modify it. There is a way around this ! 360: * from revision B and upwards. To stay compatible with older revisions ! 361: * we don't use it here. ! 362: */ ! 363: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl, ! 364: dnStall); ! 365: ! 366: tx_prev_desc->DnNextPtr = virt_to_bus(tx_cur_desc); ! 367: ! 368: downlist_ptr = inl(inf_3c90x->IOAddr + regDnListPtr_l); ! 369: if (downlist_ptr == 0) { ! 370: /* currently no DownList, sending a new one */ ! 371: outl(virt_to_bus(tx_cur_desc), ! 372: inf_3c90x->IOAddr + regDnListPtr_l); ! 373: } ! 374: ! 375: /* End Stall */ ! 376: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl, ! 377: dnUnStall); ! 378: ! 379: inf_3c90x->tx_cur = (inf_3c90x->tx_cur + 1) % TX_RING_SIZE; ! 380: inf_3c90x->tx_cnt++; ! 381: ! 382: return 0; ! 383: } ! 384: ! 385: /** ! 386: * a3c90x_prepare_rx_desc - fills the rx desc with initial data ! 387: * ! 388: * @v p NIC private data ! 389: * @v index Index for rx_iobuf and rx_ring array ! 390: */ ! 391: ! 392: static void a3c90x_prepare_rx_desc(struct INF_3C90X *p, unsigned int index) ! 393: { ! 394: DBGP("a3c90x_prepare_rx_desc\n"); ! 395: DBG2("Populating rx_desc %d\n", index); ! 396: ! 397: /* We have to stall the upload engine, so the NIC won't access the ! 398: * rx descriptor while we modify it. There is a way around this ! 399: * from revision B and upwards. To stay compatible with older revisions ! 400: * we don't use it here. ! 401: */ ! 402: a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upStall); ! 403: ! 404: p->rx_ring[index].DataAddr = virt_to_bus(p->rx_iobuf[index]->data); ! 405: p->rx_ring[index].DataLength = RX_BUF_SIZE | upLastFrag; ! 406: p->rx_ring[index].UpPktStatus = 0; ! 407: ! 408: /* unstall upload engine */ ! 409: a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upUnStall); ! 410: } ! 411: ! 412: /** ! 413: * a3c90x_refill_rx_ring -checks every entry in the rx ring and reallocates ! 414: * them as necessary. Then it calls a3c90x_prepare_rx_desc to fill the rx desc ! 415: * with initial data. ! 416: * ! 417: * @v p NIC private data ! 418: */ ! 419: static void a3c90x_refill_rx_ring(struct INF_3C90X *p) ! 420: { ! 421: int i; ! 422: unsigned int status; ! 423: struct RXD *rx_cur_desc; ! 424: ! 425: DBGP("a3c90x_refill_rx_ring\n"); ! 426: ! 427: for (i = 0; i < RX_RING_SIZE; i++) { ! 428: rx_cur_desc = p->rx_ring + i; ! 429: status = rx_cur_desc->UpPktStatus; ! 430: ! 431: /* only refill used descriptor */ ! 432: if (!(status & upComplete)) ! 433: continue; ! 434: ! 435: /* we still need to process this descriptor */ ! 436: if (p->rx_iobuf[i] != NULL) ! 437: continue; ! 438: ! 439: p->rx_iobuf[i] = alloc_iob(RX_BUF_SIZE); ! 440: if (p->rx_iobuf[i] == NULL) { ! 441: DBG("alloc_iob() failed\n"); ! 442: break; ! 443: } ! 444: ! 445: a3c90x_prepare_rx_desc(p, i); ! 446: } ! 447: } ! 448: ! 449: /** ! 450: * a3c90x_setup_rx_ring - Allocates RX ring, initialize rx_desc values ! 451: * ! 452: * @v p Private NIC data ! 453: * ! 454: * @ret Returns 0 on success, negative on failure ! 455: */ ! 456: static int a3c90x_setup_rx_ring(struct INF_3C90X *p) ! 457: { ! 458: int i; ! 459: ! 460: DBGP("a3c90x_setup_rx_ring\n"); ! 461: ! 462: p->rx_ring = ! 463: malloc_dma(RX_RING_SIZE * sizeof(struct RXD), RX_RING_ALIGN); ! 464: ! 465: if (!p->rx_ring) { ! 466: DBG("Could not allocate RX-ring\n"); ! 467: return -ENOMEM; ! 468: } ! 469: ! 470: p->rx_cur = 0; ! 471: ! 472: for (i = 0; i < RX_RING_SIZE; i++) { ! 473: p->rx_ring[i].UpNextPtr = ! 474: virt_to_bus(p->rx_ring + (i + 1)); ! 475: ! 476: /* these are needed so refill_rx_ring initializes the ring */ ! 477: p->rx_ring[i].UpPktStatus = upComplete; ! 478: p->rx_iobuf[i] = NULL; ! 479: } ! 480: ! 481: /* Loop the ring */ ! 482: p->rx_ring[i - 1].UpNextPtr = virt_to_bus(p->rx_ring); ! 483: ! 484: a3c90x_refill_rx_ring(p); ! 485: ! 486: return 0; ! 487: } ! 488: ! 489: static void a3c90x_free_rx_ring(struct INF_3C90X *p) ! 490: { ! 491: DBGP("a3c90x_free_rx_ring\n"); ! 492: ! 493: free_dma(p->rx_ring, RX_RING_SIZE * sizeof(struct RXD)); ! 494: p->rx_ring = NULL; ! 495: } ! 496: ! 497: static void a3c90x_free_rx_iobuf(struct INF_3C90X *p) ! 498: { ! 499: int i; ! 500: ! 501: DBGP("a3c90x_free_rx_iobuf\n"); ! 502: ! 503: for (i = 0; i < RX_RING_SIZE; i++) { ! 504: free_iob(p->rx_iobuf[i]); ! 505: p->rx_iobuf[i] = NULL; ! 506: } ! 507: } ! 508: ! 509: /** ! 510: * a3c90x_process_rx_packets - Checks for received packets, ! 511: * reports them to iPXE with netdev_rx() or netdev_rx_err() if there was an ! 512: * error while receiving the packet ! 513: * ! 514: * @v netdev Network device info ! 515: */ ! 516: static void a3c90x_process_rx_packets(struct net_device *netdev) ! 517: { ! 518: int i; ! 519: unsigned int rx_status; ! 520: struct INF_3C90X *p = netdev_priv(netdev); ! 521: struct RXD *rx_cur_desc; ! 522: ! 523: DBGP("a3c90x_process_rx_packets\n"); ! 524: ! 525: for (i = 0; i < RX_RING_SIZE; i++) { ! 526: rx_cur_desc = p->rx_ring + p->rx_cur; ! 527: rx_status = rx_cur_desc->UpPktStatus; ! 528: ! 529: if (!(rx_status & upComplete) && !(rx_status & upError)) ! 530: break; ! 531: ! 532: if (p->rx_iobuf[p->rx_cur] == NULL) ! 533: break; ! 534: ! 535: if (rx_status & upError) { ! 536: DBG("Corrupted packet received: %#x\n", rx_status); ! 537: netdev_rx_err(netdev, p->rx_iobuf[p->rx_cur], ! 538: -EINVAL); ! 539: } else { ! 540: /* if we're here, we've got good packet */ ! 541: int packet_len; ! 542: ! 543: packet_len = rx_status & 0x1FFF; ! 544: iob_put(p->rx_iobuf[p->rx_cur], packet_len); ! 545: ! 546: DBG2("received packet\n"); ! 547: DBG2(" size: %d\n", packet_len); ! 548: ! 549: netdev_rx(netdev, p->rx_iobuf[p->rx_cur]); ! 550: } ! 551: ! 552: p->rx_iobuf[p->rx_cur] = NULL; /* invalidate rx desc */ ! 553: p->rx_cur = (p->rx_cur + 1) % RX_RING_SIZE; ! 554: } ! 555: a3c90x_refill_rx_ring(p); ! 556: ! 557: } ! 558: ! 559: /** ! 560: * a3c90x_poll - Routine that gets called periodically. ! 561: * Here we hanle transmitted and received packets. ! 562: * We could also check the link status from time to time, which we ! 563: * currently don't do. ! 564: * ! 565: * @v netdev Network device info ! 566: */ ! 567: static void a3c90x_poll(struct net_device *netdev) ! 568: { ! 569: struct INF_3C90X *p = netdev_priv(netdev); ! 570: uint16_t raw_status, int_status; ! 571: ! 572: DBGP("a3c90x_poll\n"); ! 573: ! 574: raw_status = inw(p->IOAddr + regCommandIntStatus_w); ! 575: int_status = (raw_status & 0x0FFF); ! 576: ! 577: if ( int_status == 0 ) ! 578: return; ! 579: ! 580: a3c90x_internal_IssueCommand(p->IOAddr, cmdAcknowledgeInterrupt, ! 581: int_status); ! 582: ! 583: if (int_status & INT_TXCOMPLETE) ! 584: outb(0x00, p->IOAddr + regTxStatus_b); ! 585: ! 586: DBG2("poll: status = %#04x\n", raw_status); ! 587: ! 588: a3c90x_process_tx_packets(netdev); ! 589: ! 590: a3c90x_process_rx_packets(netdev); ! 591: } ! 592: ! 593: ! 594: ! 595: static void a3c90x_free_resources(struct INF_3C90X *p) ! 596: { ! 597: DBGP("a3c90x_free_resources\n"); ! 598: ! 599: a3c90x_free_tx_ring(p); ! 600: a3c90x_free_rx_ring(p); ! 601: a3c90x_free_rx_iobuf(p); ! 602: } ! 603: ! 604: /** ! 605: * a3c90x_remove - Routine to remove the card. Unregisters ! 606: * the NIC from iPXE, disables RX/TX and resets the card. ! 607: * ! 608: * @v pci PCI device info ! 609: */ ! 610: static void a3c90x_remove(struct pci_device *pci) ! 611: { ! 612: struct net_device *netdev = pci_get_drvdata(pci); ! 613: struct INF_3C90X *inf_3c90x = netdev_priv(netdev); ! 614: ! 615: DBGP("a3c90x_remove\n"); ! 616: ! 617: a3c90x_reset(inf_3c90x); ! 618: ! 619: /* Disable the receiver and transmitter. */ ! 620: outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w); ! 621: outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w); ! 622: ! 623: unregister_netdev(netdev); ! 624: netdev_nullify(netdev); ! 625: netdev_put(netdev); ! 626: } ! 627: ! 628: static void a3c90x_irq(struct net_device *netdev, int enable) ! 629: { ! 630: struct INF_3C90X *p = netdev_priv(netdev); ! 631: ! 632: DBGP("a3c90x_irq\n"); ! 633: ! 634: if (enable == 0) { ! 635: /* disable interrupts */ ! 636: a3c90x_internal_IssueCommand(p->IOAddr, ! 637: cmdSetInterruptEnable, 0); ! 638: } else { ! 639: a3c90x_internal_IssueCommand(p->IOAddr, ! 640: cmdSetInterruptEnable, ! 641: INT_TXCOMPLETE | ! 642: INT_UPCOMPLETE); ! 643: a3c90x_internal_IssueCommand(p->IOAddr, ! 644: cmdAcknowledgeInterrupt, ! 645: 0x661); ! 646: } ! 647: } ! 648: ! 649: /** ! 650: * a3c90x_hw_start - Initialize hardware, copy MAC address ! 651: * to NIC registers, set default receiver ! 652: */ ! 653: static void a3c90x_hw_start(struct net_device *netdev) ! 654: { ! 655: int i, c; ! 656: unsigned int cfg; ! 657: unsigned int mopt; ! 658: unsigned short linktype; ! 659: struct INF_3C90X *inf_3c90x = netdev_priv(netdev); ! 660: ! 661: DBGP("a3c90x_hw_start\n"); ! 662: ! 663: /* 3C556: Invert MII power */ ! 664: if (inf_3c90x->is3c556) { ! 665: unsigned int tmp; ! 666: a3c90x_internal_SetWindow(inf_3c90x, winAddressing2); ! 667: tmp = inw(inf_3c90x->IOAddr + regResetOptions_2_w); ! 668: tmp |= 0x4000; ! 669: outw(tmp, inf_3c90x->IOAddr + regResetOptions_2_w); ! 670: } ! 671: ! 672: /* Copy MAC address into the NIC registers */ ! 673: a3c90x_internal_SetWindow(inf_3c90x, winAddressing2); ! 674: for (i = 0; i < ETH_ALEN; i++) ! 675: outb(netdev->ll_addr[i], ! 676: inf_3c90x->IOAddr + regStationAddress_2_3w + i); ! 677: for (i = 0; i < ETH_ALEN; i++) ! 678: outb(0, inf_3c90x->IOAddr + regStationMask_2_3w + i); ! 679: ! 680: /* Read the media options register, print a message and set default ! 681: * xcvr. ! 682: * ! 683: * Uses Media Option command on B revision, Reset Option on non-B ! 684: * revision cards -- same register address ! 685: */ ! 686: a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3); ! 687: mopt = inw(inf_3c90x->IOAddr + regResetMediaOptions_3_w); ! 688: ! 689: /* mask out VCO bit that is defined as 10baseFL bit on B-rev cards */ ! 690: if (!inf_3c90x->isBrev) { ! 691: mopt &= 0x7F; ! 692: } ! 693: ! 694: DBG2("Connectors present: "); ! 695: c = 0; ! 696: linktype = 0x0008; ! 697: if (mopt & 0x01) { ! 698: DBG2("%s100Base-T4", (c++) ? ", " : ""); ! 699: linktype = linkMII; ! 700: } ! 701: if (mopt & 0x04) { ! 702: DBG2("%s100Base-FX", (c++) ? ", " : ""); ! 703: linktype = link100BaseFX; ! 704: } ! 705: if (mopt & 0x10) { ! 706: DBG2("%s10Base-2", (c++) ? ", " : ""); ! 707: linktype = link10Base2; ! 708: } ! 709: if (mopt & 0x20) { ! 710: DBG2("%sAUI", (c++) ? ", " : ""); ! 711: linktype = linkAUI; ! 712: } ! 713: if (mopt & 0x40) { ! 714: DBG2("%sMII", (c++) ? ", " : ""); ! 715: linktype = linkMII; ! 716: } ! 717: if ((mopt & 0xA) == 0xA) { ! 718: DBG2("%s10Base-T / 100Base-TX", (c++) ? ", " : ""); ! 719: linktype = linkAutoneg; ! 720: } else if ((mopt & 0xA) == 0x2) { ! 721: DBG2("%s100Base-TX", (c++) ? ", " : ""); ! 722: linktype = linkAutoneg; ! 723: } else if ((mopt & 0xA) == 0x8) { ! 724: DBG2("%s10Base-T", (c++) ? ", " : ""); ! 725: linktype = linkAutoneg; ! 726: } ! 727: DBG2(".\n"); ! 728: ! 729: /* Determine transceiver type to use, depending on value stored in ! 730: * eeprom 0x16 ! 731: */ ! 732: if (inf_3c90x->isBrev) { ! 733: if ((inf_3c90x->eeprom[0x16] & 0xFF00) == XCVR_MAGIC) { ! 734: /* User-defined */ ! 735: linktype = inf_3c90x->eeprom[0x16] & 0x000F; ! 736: } ! 737: } else { ! 738: /* I don't know what MII MAC only mode is!!! */ ! 739: if (linktype == linkExternalMII) { ! 740: if (inf_3c90x->isBrev) ! 741: DBG("WARNING: MII External MAC Mode only supported on B-revision " "cards!!!!\nFalling Back to MII Mode\n"); ! 742: linktype = linkMII; ! 743: } ! 744: } ! 745: ! 746: /* enable DC converter for 10-Base-T */ ! 747: if (linktype == link10Base2) { ! 748: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 749: cmdEnableDcConverter, 0); ! 750: } ! 751: ! 752: /* Set the link to the type we just determined. */ ! 753: a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3); ! 754: cfg = inl(inf_3c90x->IOAddr + regInternalConfig_3_l); ! 755: cfg &= ~(0xF << 20); ! 756: cfg |= (linktype << 20); ! 757: ! 758: DBG2("Setting internal cfg register: 0x%08X (linktype: 0x%02X)\n", ! 759: cfg, linktype); ! 760: ! 761: outl(cfg, inf_3c90x->IOAddr + regInternalConfig_3_l); ! 762: ! 763: /* Now that we set the xcvr type, reset the Tx and Rx */ ! 764: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxReset, 0x00); ! 765: ! 766: if (!inf_3c90x->isBrev) ! 767: outb(0x01, inf_3c90x->IOAddr + regTxFreeThresh_b); ! 768: ! 769: /* Set the RX filter = receive only individual pkts & multicast & bcast. */ ! 770: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdSetRxFilter, ! 771: 0x01 + 0x02 + 0x04); ! 772: ! 773: ! 774: /* ! 775: * set Indication and Interrupt flags , acknowledge any IRQ's ! 776: */ ! 777: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 778: cmdSetInterruptEnable, ! 779: INT_TXCOMPLETE | INT_UPCOMPLETE); ! 780: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 781: cmdSetIndicationEnable, ! 782: INT_TXCOMPLETE | INT_UPCOMPLETE); ! 783: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, ! 784: cmdAcknowledgeInterrupt, 0x661); ! 785: } ! 786: ! 787: /** ! 788: * a3c90x_open - Routine to initialize the card. Initialize hardware, ! 789: * allocate TX and RX ring, send RX ring address to the NIC. ! 790: * ! 791: * @v netdev Network device info ! 792: * ! 793: * @ret Returns 0 on success, negative on failure ! 794: */ ! 795: static int a3c90x_open(struct net_device *netdev) ! 796: { ! 797: int rc; ! 798: struct INF_3C90X *inf_3c90x = netdev_priv(netdev); ! 799: ! 800: DBGP("a3c90x_open\n"); ! 801: ! 802: a3c90x_hw_start(netdev); ! 803: ! 804: rc = a3c90x_setup_tx_ring(inf_3c90x); ! 805: if (rc != 0) { ! 806: DBG("Error setting up TX Ring\n"); ! 807: goto error; ! 808: } ! 809: ! 810: rc = a3c90x_setup_rx_ring(inf_3c90x); ! 811: if (rc != 0) { ! 812: DBG("Error setting up RX Ring\n"); ! 813: goto error; ! 814: } ! 815: ! 816: /* send rx_ring address to NIC */ ! 817: outl(virt_to_bus(inf_3c90x->rx_ring), ! 818: inf_3c90x->IOAddr + regUpListPtr_l); ! 819: ! 820: /* enable packet transmission and reception */ ! 821: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0); ! 822: a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0); ! 823: ! 824: return 0; ! 825: ! 826: error: ! 827: a3c90x_free_resources(inf_3c90x); ! 828: a3c90x_reset(inf_3c90x); ! 829: return rc; ! 830: } ! 831: ! 832: /** ! 833: * a3c90x_close - free()s TX and RX ring, disablex RX/TX, resets NIC ! 834: * ! 835: * @v netdev Network device info ! 836: */ ! 837: static void a3c90x_close(struct net_device *netdev) ! 838: { ! 839: struct INF_3C90X *inf_3c90x = netdev_priv(netdev); ! 840: ! 841: DBGP("a3c90x_close\n"); ! 842: ! 843: a3c90x_reset(inf_3c90x); ! 844: outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w); ! 845: outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w); ! 846: a3c90x_free_resources(inf_3c90x); ! 847: } ! 848: ! 849: static struct net_device_operations a3c90x_operations = { ! 850: .open = a3c90x_open, ! 851: .close = a3c90x_close, ! 852: .poll = a3c90x_poll, ! 853: .transmit = a3c90x_transmit, ! 854: .irq = a3c90x_irq, ! 855: }; ! 856: ! 857: /** ! 858: * a3c90x_probe: exported routine to probe for the 3c905 card. ! 859: * If this routine is called, the pci functions did find the ! 860: * card. We read the eeprom here and get the MAC address. ! 861: * Initialization is done in a3c90x_open(). ! 862: * ! 863: * @v pci PCI device info ! 864: * @ pci_id PCI device IDs ! 865: * ! 866: * @ret rc Returns 0 on success, negative on failure ! 867: */ ! 868: static int a3c90x_probe(struct pci_device *pci) ! 869: { ! 870: ! 871: struct net_device *netdev; ! 872: struct INF_3C90X *inf_3c90x; ! 873: unsigned char *HWAddr; ! 874: int rc; ! 875: ! 876: DBGP("a3c90x_probe\n"); ! 877: ! 878: if (pci->ioaddr == 0) ! 879: return -EINVAL; ! 880: ! 881: netdev = alloc_etherdev(sizeof(*inf_3c90x)); ! 882: if (!netdev) ! 883: return -ENOMEM; ! 884: ! 885: netdev_init(netdev, &a3c90x_operations); ! 886: pci_set_drvdata(pci, netdev); ! 887: netdev->dev = &pci->dev; ! 888: ! 889: inf_3c90x = netdev_priv(netdev); ! 890: memset(inf_3c90x, 0, sizeof(*inf_3c90x)); ! 891: ! 892: adjust_pci_device(pci); ! 893: ! 894: inf_3c90x->is3c556 = (pci->device == 0x6055); ! 895: inf_3c90x->IOAddr = pci->ioaddr; ! 896: inf_3c90x->CurrentWindow = winNone; ! 897: ! 898: inf_3c90x->isBrev = 1; ! 899: switch (pci->device) { ! 900: case 0x9000: /* 10 Base TPO */ ! 901: case 0x9001: /* 10/100 T4 */ ! 902: case 0x9050: /* 10/100 TPO */ ! 903: case 0x9051: /* 10 Base Combo */ ! 904: inf_3c90x->isBrev = 0; ! 905: break; ! 906: } ! 907: ! 908: DBG2("[3c90x]: found NIC(0x%04X, 0x%04X), isBrev=%d, is3c556=%d\n", ! 909: pci->vendor, pci->device, inf_3c90x->isBrev, ! 910: inf_3c90x->is3c556); ! 911: ! 912: /* initialize nvs device */ ! 913: inf_3c90x->nvs.word_len_log2 = 1; /* word */ ! 914: inf_3c90x->nvs.size = (inf_3c90x->isBrev ? 0x20 : 0x17); ! 915: inf_3c90x->nvs.block_size = 1; ! 916: inf_3c90x->nvs.read = a3c90x_internal_ReadEeprom; ! 917: inf_3c90x->nvs.write = a3c90x_internal_WriteEeprom; ! 918: ! 919: /* reset NIC before accessing any data from it */ ! 920: a3c90x_reset(inf_3c90x); ! 921: ! 922: /* load eeprom contents to inf_3c90x->eeprom */ ! 923: a3c90x_internal_ReadEepromContents(inf_3c90x); ! 924: ! 925: HWAddr = netdev->hw_addr; ! 926: ! 927: /* Retrieve the Hardware address */ ! 928: HWAddr[0] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] >> 8; ! 929: HWAddr[1] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] & 0xFF; ! 930: HWAddr[2] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] >> 8; ! 931: HWAddr[3] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] & 0xFF; ! 932: HWAddr[4] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] >> 8; ! 933: HWAddr[5] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] & 0xFF; ! 934: ! 935: if ((rc = register_netdev(netdev)) != 0) { ! 936: DBG("3c90x: register_netdev() failed\n"); ! 937: netdev_put(netdev); ! 938: return rc; ! 939: } ! 940: ! 941: /* we don't handle linkstates yet, so we're always up */ ! 942: netdev_link_up(netdev); ! 943: ! 944: return 0; ! 945: } ! 946: ! 947: static struct pci_device_id a3c90x_nics[] = { ! 948: /* Original 90x revisions: */ ! 949: PCI_ROM(0x10b7, 0x6055, "3c556", "3C556", 0), /* Huricane */ ! 950: PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO", 0), /* 10 Base TPO */ ! 951: PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo", 0), /* 10/100 T4 */ ! 952: PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX", 0), /* 100 Base TX / 10/100 TPO */ ! 953: PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4", 0), /* 100 Base T4 / 10 Base Combo */ ! 954: /* Newer 90xB revisions: */ ! 955: PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO", 0), /* 10 Base TPO */ ! 956: PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo", 0), /* 10 Base Combo */ ! 957: PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T", 0), /* 10 Base TP and Base2 */ ! 958: PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL", 0), /* 10 Base FL */ ! 959: PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX", 0), /* 10/100 TPO */ ! 960: PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4", 0), /* 10/100 T4 */ ! 961: PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058", 0), /* Cyclone 10/100/BNC */ ! 962: PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL", 0), /* 100 Base FX / 10 Base FX */ ! 963: /* Newer 90xC revision: */ ! 964: PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM", 0), /* 10/100 TPO (3C905C-TXM) */ ! 965: PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)", 0), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */ ! 966: PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm", "3Com20B-EMB WNM", 0), ! 967: PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone", 0), /* Cyclone */ ! 968: PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805", 0), /* Dual Port Server Cyclone */ ! 969: PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX", 0), /* Hurricane */ ! 970: PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado", 0), ! 971: PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A", 0), ! 972: PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B", 0), ! 973: }; ! 974: ! 975: struct pci_driver a3c90x_driver __pci_driver = { ! 976: .ids = a3c90x_nics, ! 977: .id_count = (sizeof(a3c90x_nics) / sizeof(a3c90x_nics[0])), ! 978: .probe = a3c90x_probe, ! 979: .remove = a3c90x_remove, ! 980: }; ! 981: ! 982: /* ! 983: * Local variables: ! 984: * c-basic-offset: 8 ! 985: * c-indent-level: 8 ! 986: * tab-width: 8 ! 987: * End: ! 988: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.