|
|
1.1 ! root 1: /* ! 2: natsemi.c - iPXE driver for the NatSemi DP8381x series. ! 3: ! 4: Based on: ! 5: ! 6: natsemi.c: An Etherboot driver for the NatSemi DP8381x series. ! 7: ! 8: Copyright (C) 2001 Entity Cyber, Inc. ! 9: ! 10: This development of this Etherboot driver was funded by ! 11: ! 12: Sicom Systems: http://www.sicompos.com/ ! 13: ! 14: Author: Marty Connor <[email protected]> ! 15: Adapted from a Linux driver which was written by Donald Becker ! 16: ! 17: This software may be used and distributed according to the terms ! 18: of the GNU Public License (GPL), incorporated herein by reference. ! 19: ! 20: Original Copyright Notice: ! 21: ! 22: Written/copyright 1999-2001 by Donald Becker. ! 23: ! 24: This software may be used and distributed according to the terms of ! 25: the GNU General Public License (GPL), incorporated herein by reference. ! 26: Drivers based on or derived from this code fall under the GPL and must ! 27: retain the authorship, copyright and license notice. This file is not ! 28: a complete program and may only be used when the entire operating ! 29: system is licensed under the GPL. License for under other terms may be ! 30: available. Contact the original author for details. ! 31: ! 32: The original author may be reached as [email protected], or at ! 33: Scyld Computing Corporation ! 34: 410 Severn Ave., Suite 210 ! 35: Annapolis MD 21403 ! 36: ! 37: Support information and updates available at ! 38: http://www.scyld.com/network/netsemi.html ! 39: ! 40: References: ! 41: ! 42: http://www.scyld.com/expert/100mbps.html ! 43: http://www.scyld.com/expert/NWay.html ! 44: Datasheet is available from: ! 45: http://www.national.com/pf/DP/DP83815.html ! 46: ! 47: */ ! 48: ! 49: FILE_LICENCE ( GPL_ANY ); ! 50: ! 51: /* Revision History */ ! 52: ! 53: /* ! 54: 02 Jul 2007 Udayan Kumar 1.2 ported the driver from etherboot to iPXE API. ! 55: Fully rewritten,adapting the old driver. ! 56: Added a circular buffer for transmit and receive. ! 57: transmit routine will not wait for transmission to finish. ! 58: poll routine deals with it. ! 59: 13 Dec 2003 Tim Legge 1.1 Enabled Multicast Support ! 60: 29 May 2001 Marty Connor 1.0 Initial Release. Tested with Netgear FA311 and FA312 boards ! 61: */ ! 62: ! 63: #include <stdint.h> ! 64: #include <stdlib.h> ! 65: #include <stdio.h> ! 66: #include <string.h> ! 67: #include <ipxe/io.h> ! 68: #include <errno.h> ! 69: #include <byteswap.h> ! 70: #include <unistd.h> ! 71: #include <ipxe/pci.h> ! 72: #include <ipxe/if_ether.h> ! 73: #include <ipxe/ethernet.h> ! 74: #include <ipxe/iobuf.h> ! 75: #include <ipxe/netdevice.h> ! 76: #include <ipxe/spi_bit.h> ! 77: #include <ipxe/threewire.h> ! 78: #include <ipxe/nvo.h> ! 79: #include "natsemi.h" ! 80: ! 81: /* Function Prototypes: */ ! 82: ! 83: static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int ); ! 84: static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long ); ! 85: static void natsemi_init_eeprom ( struct natsemi_private * ); ! 86: static int natsemi_probe (struct pci_device *pci); ! 87: static void natsemi_reset (struct net_device *netdev); ! 88: static int natsemi_open (struct net_device *netdev); ! 89: static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf); ! 90: static void natsemi_poll (struct net_device *netdev); ! 91: static void natsemi_close (struct net_device *netdev); ! 92: static void natsemi_irq (struct net_device *netdev, int enable); ! 93: static void natsemi_remove (struct pci_device *pci); ! 94: ! 95: /** natsemi net device operations */ ! 96: static struct net_device_operations natsemi_operations = { ! 97: .open = natsemi_open, ! 98: .close = natsemi_close, ! 99: .transmit = natsemi_transmit, ! 100: .poll = natsemi_poll, ! 101: .irq = natsemi_irq, ! 102: }; ! 103: ! 104: static int natsemi_spi_read_bit ( struct bit_basher *basher, ! 105: unsigned int bit_id ) { ! 106: struct natsemi_private *np = container_of ( basher, struct natsemi_private, ! 107: spibit.basher ); ! 108: uint8_t mask = natsemi_ee_bits[bit_id]; ! 109: uint8_t eereg; ! 110: ! 111: eereg = inb ( np->ioaddr + EE_REG ); ! 112: return ( eereg & mask ); ! 113: } ! 114: ! 115: static void natsemi_spi_write_bit ( struct bit_basher *basher, ! 116: unsigned int bit_id, unsigned long data ) { ! 117: struct natsemi_private *np = container_of ( basher, struct natsemi_private, ! 118: spibit.basher ); ! 119: uint8_t mask = natsemi_ee_bits[bit_id]; ! 120: uint8_t eereg; ! 121: ! 122: eereg = inb ( np->ioaddr + EE_REG ); ! 123: eereg &= ~mask; ! 124: eereg |= ( data & mask ); ! 125: outb ( eereg, np->ioaddr + EE_REG ); ! 126: } ! 127: ! 128: static struct bit_basher_operations natsemi_basher_ops = { ! 129: .read = natsemi_spi_read_bit, ! 130: .write = natsemi_spi_write_bit, ! 131: }; ! 132: ! 133: /* ! 134: * Set up for EEPROM access ! 135: * ! 136: * @v NAT NATSEMI NIC ! 137: */ ! 138: static void natsemi_init_eeprom ( struct natsemi_private *np ) { ! 139: ! 140: /* Initialise three-wire bus ! 141: */ ! 142: np->spibit.basher.op = &natsemi_basher_ops; ! 143: np->spibit.bus.mode = SPI_MODE_THREEWIRE; ! 144: np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN; ! 145: init_spi_bit_basher ( &np->spibit ); ! 146: ! 147: /*natsemi DP 83815 only supports at93c46 ! 148: */ ! 149: init_at93c46 ( &np->eeprom, 16 ); ! 150: np->eeprom.bus = &np->spibit.bus; ! 151: ! 152: /* It looks that this portion of EEPROM can be used for ! 153: * non-volatile stored options. Data sheet does not talk about ! 154: * this region. Currently it is not working. But with some ! 155: * efforts it can. ! 156: */ ! 157: nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL ); ! 158: } ! 159: ! 160: /** ! 161: * Probe PCI device ! 162: * ! 163: * @v pci PCI device ! 164: * @v id PCI ID ! 165: * @ret rc Return status code ! 166: */ ! 167: static int natsemi_probe (struct pci_device *pci) { ! 168: struct net_device *netdev; ! 169: struct natsemi_private *np = NULL; ! 170: uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN]; ! 171: uint8_t last=0,last1=0; ! 172: uint8_t prev_bytes[2]; ! 173: int i; ! 174: int rc; ! 175: ! 176: /* Allocate net device ! 177: */ ! 178: netdev = alloc_etherdev (sizeof (*np)); ! 179: if (! netdev) ! 180: return -ENOMEM; ! 181: ! 182: netdev_init (netdev, &natsemi_operations); ! 183: np = netdev->priv; ! 184: pci_set_drvdata (pci, netdev); ! 185: netdev->dev = &pci->dev; ! 186: memset (np, 0, sizeof (*np)); ! 187: np->ioaddr = pci->ioaddr; ! 188: ! 189: adjust_pci_device (pci); ! 190: ! 191: natsemi_reset (netdev); ! 192: natsemi_init_eeprom ( np ); ! 193: nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 ); ! 194: nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN ); ! 195: ! 196: /* decoding the MAC address read from NVS ! 197: * and save it in netdev->ll_addr ! 198: */ ! 199: last = prev_bytes[1] >> 7; ! 200: for ( i = 0 ; i < ETH_ALEN ; i++ ) { ! 201: last1 = ll_addr_encoded[i] >> 7; ! 202: netdev->hw_addr[i] = ll_addr_encoded[i] << 1 | last; ! 203: last = last1; ! 204: } ! 205: ! 206: if ((rc = register_netdev (netdev)) != 0) ! 207: goto err_register_netdev; ! 208: ! 209: /* Mark as link up; we don't yet handle link state */ ! 210: netdev_link_up ( netdev ); ! 211: ! 212: return 0; ! 213: ! 214: err_register_netdev: ! 215: ! 216: natsemi_reset (netdev); ! 217: netdev_put (netdev); ! 218: return rc; ! 219: } ! 220: ! 221: /** ! 222: * Remove PCI device ! 223: * ! 224: * @v pci PCI device ! 225: */ ! 226: static void natsemi_remove (struct pci_device *pci) { ! 227: struct net_device *netdev = pci_get_drvdata (pci); ! 228: ! 229: unregister_netdev (netdev); ! 230: natsemi_reset (netdev); ! 231: netdev_nullify ( netdev ); ! 232: netdev_put (netdev); ! 233: } ! 234: ! 235: /** ! 236: * Reset NIC ! 237: * ! 238: * @v NATSEMI NIC ! 239: * ! 240: * Issues a hardware reset and waits for the reset to complete. ! 241: */ ! 242: static void natsemi_reset (struct net_device *netdev) ! 243: { ! 244: struct natsemi_private *np = netdev->priv; ! 245: int i; ! 246: u32 cfg; ! 247: u32 wcsr; ! 248: u32 rfcr; ! 249: u16 pmatch[3]; ! 250: u16 sopass[3]; ! 251: ! 252: natsemi_irq (netdev, 0); ! 253: ! 254: /* ! 255: * Resetting the chip causes some registers to be lost. ! 256: * Natsemi suggests NOT reloading the EEPROM while live, so instead ! 257: * we save the state that would have been loaded from EEPROM ! 258: * on a normal power-up (see the spec EEPROM map). ! 259: */ ! 260: ! 261: /* CFG */ ! 262: cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE; ! 263: ! 264: /* WCSR */ ! 265: wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE; ! 266: ! 267: /* RFCR */ ! 268: rfcr = inl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE; ! 269: ! 270: /* PMATCH */ ! 271: for (i = 0; i < 3; i++) { ! 272: outl(i*2, np->ioaddr + RxFilterAddr); ! 273: pmatch[i] = inw(np->ioaddr + RxFilterData); ! 274: } ! 275: ! 276: /* SOPAS */ ! 277: for (i = 0; i < 3; i++) { ! 278: outl(0xa+(i*2), np->ioaddr + RxFilterAddr); ! 279: sopass[i] = inw(np->ioaddr + RxFilterData); ! 280: } ! 281: ! 282: /* now whack the chip */ ! 283: outl(ChipReset, np->ioaddr + ChipCmd); ! 284: for (i=0; i<NATSEMI_HW_TIMEOUT; i++) { ! 285: if (! (inl (np->ioaddr + ChipCmd) & ChipReset)) ! 286: break; ! 287: udelay(5); ! 288: } ! 289: if (i == NATSEMI_HW_TIMEOUT) { ! 290: DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5); ! 291: } ! 292: ! 293: /* restore CFG */ ! 294: cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE; ! 295: cfg &= ~(CfgExtPhy | CfgPhyDis); ! 296: outl (cfg, np->ioaddr + ChipConfig); ! 297: ! 298: /* restore WCSR */ ! 299: wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE; ! 300: outl (wcsr, np->ioaddr + WOLCmd); ! 301: ! 302: /* read RFCR */ ! 303: rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE; ! 304: ! 305: /* restore PMATCH */ ! 306: for (i = 0; i < 3; i++) { ! 307: outl (i*2, np->ioaddr + RxFilterAddr); ! 308: outw (pmatch[i], np->ioaddr + RxFilterData); ! 309: } ! 310: for (i = 0; i < 3; i++) { ! 311: outl (0xa+(i*2), np->ioaddr + RxFilterAddr); ! 312: outw (sopass[i], np->ioaddr + RxFilterData); ! 313: } ! 314: /* restore RFCR */ ! 315: outl (rfcr, np->ioaddr + RxFilterAddr); ! 316: } ! 317: ! 318: /** ! 319: * Open NIC ! 320: * ! 321: * @v netdev Net device ! 322: * @ret rc Return status code ! 323: */ ! 324: static int natsemi_open (struct net_device *netdev) ! 325: { ! 326: struct natsemi_private *np = netdev->priv; ! 327: uint32_t tx_config, rx_config; ! 328: int i; ! 329: ! 330: /* Disable PME: ! 331: * The PME bit is initialized from the EEPROM contents. ! 332: * PCI cards probably have PME disabled, but motherboard ! 333: * implementations may have PME set to enable WakeOnLan. ! 334: * With PME set the chip will scan incoming packets but ! 335: * nothing will be written to memory. ! 336: */ ! 337: outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun); ! 338: ! 339: /* Set MAC address in NIC ! 340: */ ! 341: for (i = 0 ; i < ETH_ALEN ; i+=2) { ! 342: outl (i, np->ioaddr + RxFilterAddr); ! 343: outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8), ! 344: np->ioaddr + RxFilterData); ! 345: } ! 346: ! 347: /* Setup Tx Ring ! 348: */ ! 349: np->tx_cur = 0; ! 350: np->tx_dirty = 0; ! 351: for (i = 0 ; i < TX_RING_SIZE ; i++) { ! 352: np->tx[i].link = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]); ! 353: np->tx[i].cmdsts = 0; ! 354: np->tx[i].bufptr = 0; ! 355: } ! 356: outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr); ! 357: ! 358: DBG ("Natsemi Tx descriptor loaded with: %#08x\n", ! 359: inl (np->ioaddr + TxRingPtr)); ! 360: ! 361: /* Setup RX ring ! 362: */ ! 363: np->rx_cur = 0; ! 364: for (i = 0 ; i < NUM_RX_DESC ; i++) { ! 365: np->iobuf[i] = alloc_iob (RX_BUF_SIZE); ! 366: if (! np->iobuf[i]) ! 367: goto memory_alloc_err; ! 368: np->rx[i].link = virt_to_bus ((i + 1 < NUM_RX_DESC) ! 369: ? &np->rx[i + 1] : &np->rx[0]); ! 370: np->rx[i].cmdsts = RX_BUF_SIZE; ! 371: np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data); ! 372: DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i, ! 373: &np->iobuf[i], &np->iobuf[i]->data); ! 374: } ! 375: outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr); ! 376: ! 377: DBG ("Natsemi Rx descriptor loaded with: %#08x\n", ! 378: inl (np->ioaddr + RxRingPtr)); ! 379: ! 380: /* Setup RX Filter ! 381: */ ! 382: outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys, ! 383: np->ioaddr + RxFilterAddr); ! 384: ! 385: /* Initialize other registers. ! 386: * Configure the PCI bus bursts and FIFO thresholds. ! 387: * Configure for standard, in-spec Ethernet. ! 388: */ ! 389: if (inl (np->ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */ ! 390: DBG ("Full duplex\n"); ! 391: tx_config = 0xD0801002 | 0xC0000000; ! 392: rx_config = 0x10000020 | 0x10000000; ! 393: } else { ! 394: DBG ("Half duplex\n"); ! 395: tx_config = 0x10801002 & ~0xC0000000; ! 396: rx_config = 0x00000020 & ~0x10000000; ! 397: } ! 398: outl (tx_config, np->ioaddr + TxConfig); ! 399: outl (rx_config, np->ioaddr + RxConfig); ! 400: ! 401: DBG ("Tx config register = %#08x Rx config register = %#08x\n", ! 402: inl (np->ioaddr + TxConfig), ! 403: inl (np->ioaddr + RxConfig)); ! 404: ! 405: /*Set the Interrupt Mask register ! 406: */ ! 407: outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask); ! 408: /*start the receiver ! 409: */ ! 410: outl (RxOn, np->ioaddr + ChipCmd); ! 411: ! 412: return 0; ! 413: ! 414: memory_alloc_err: ! 415: ! 416: /* Frees any allocated buffers when memory ! 417: * for all buffers requested is not available ! 418: */ ! 419: i = 0; ! 420: while (np->rx[i].cmdsts == RX_BUF_SIZE) { ! 421: free_iob (np->iobuf[i]); ! 422: i++; ! 423: } ! 424: return -ENOMEM; ! 425: } ! 426: ! 427: /** ! 428: * Close NIC ! 429: * ! 430: * @v netdev Net device ! 431: */ ! 432: static void natsemi_close (struct net_device *netdev) ! 433: { ! 434: struct natsemi_private *np = netdev->priv; ! 435: int i; ! 436: ! 437: natsemi_reset (netdev); ! 438: ! 439: for (i = 0; i < NUM_RX_DESC ; i++) { ! 440: free_iob (np->iobuf[i]); ! 441: } ! 442: } ! 443: ! 444: /** ! 445: * Transmit packet ! 446: * ! 447: * @v netdev Network device ! 448: * @v iobuf I/O buffer ! 449: * @ret rc Return status code ! 450: */ ! 451: static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf) ! 452: { ! 453: struct natsemi_private *np = netdev->priv; ! 454: ! 455: if (np->tx[np->tx_cur].cmdsts != 0) { ! 456: DBG ("TX overflow\n"); ! 457: return -ENOBUFS; ! 458: } ! 459: ! 460: /* Used by netdev_tx_complete () ! 461: */ ! 462: np->tx_iobuf[np->tx_cur] = iobuf; ! 463: ! 464: /* Pad and align packet has not been used because its not required ! 465: * by the hardware. ! 466: * iob_pad (iobuf, ETH_ZLEN); ! 467: * can be used to achieve it, if required ! 468: */ ! 469: ! 470: /* Add the packet to TX ring ! 471: */ ! 472: np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data); ! 473: np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN; ! 474: ! 475: DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur, ! 476: virt_to_bus (&iobuf->data), iob_len (iobuf)); ! 477: ! 478: /* increment the circular buffer pointer to the next buffer location ! 479: */ ! 480: np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE; ! 481: ! 482: /*start the transmitter ! 483: */ ! 484: outl (TxOn, np->ioaddr + ChipCmd); ! 485: ! 486: return 0; ! 487: } ! 488: ! 489: /** ! 490: * Poll for received packets ! 491: * ! 492: * @v netdev Network device ! 493: */ ! 494: static void natsemi_poll (struct net_device *netdev) ! 495: { ! 496: struct natsemi_private *np = netdev->priv; ! 497: unsigned int tx_status; ! 498: unsigned int rx_status; ! 499: unsigned int intr_status; ! 500: unsigned int rx_len; ! 501: struct io_buffer *rx_iob; ! 502: int i; ! 503: ! 504: /* read the interrupt register ! 505: */ ! 506: intr_status = inl (np->ioaddr + IntrStatus); ! 507: ! 508: if (!intr_status) ! 509: goto end; ! 510: ! 511: DBG ("natsemi_poll: intr_status = %#08x\n", intr_status); ! 512: ! 513: /* Check status of transmitted packets ! 514: */ ! 515: i = np->tx_dirty; ! 516: while (i != np->tx_cur) { ! 517: tx_status = np->tx[np->tx_dirty].cmdsts; ! 518: ! 519: DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n", ! 520: np->tx_dirty, np->tx_cur, tx_status); ! 521: ! 522: if (tx_status & OWN) ! 523: break; ! 524: ! 525: if (! (tx_status & DescPktOK)) { ! 526: netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL); ! 527: DBG ("Error transmitting packet, tx_status: %#08x\n", ! 528: tx_status); ! 529: } else { ! 530: netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]); ! 531: DBG ("Success transmitting packet\n"); ! 532: } ! 533: ! 534: np->tx[np->tx_dirty].cmdsts = 0; ! 535: np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE; ! 536: i = (i + 1) % TX_RING_SIZE; ! 537: } ! 538: ! 539: /* Process received packets ! 540: */ ! 541: rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts; ! 542: while ((rx_status & OWN)) { ! 543: rx_len = (rx_status & DSIZE) - CRC_SIZE; ! 544: ! 545: DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n", ! 546: np->rx_cur, rx_status, rx_len); ! 547: ! 548: if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) { ! 549: netdev_rx_err (netdev, NULL, -EINVAL); ! 550: ! 551: DBG ("natsemi_poll: Corrupted packet received!" ! 552: " Status = %#08x\n", ! 553: np->rx[np->rx_cur].cmdsts); ! 554: ! 555: } else { ! 556: ! 557: ! 558: /* If unable allocate space for this packet, ! 559: * try again next poll ! 560: */ ! 561: rx_iob = alloc_iob (rx_len); ! 562: if (! rx_iob) ! 563: goto end; ! 564: memcpy (iob_put (rx_iob, rx_len), ! 565: np->iobuf[np->rx_cur]->data, rx_len); ! 566: /* Add this packet to the receive queue. ! 567: */ ! 568: netdev_rx (netdev, rx_iob); ! 569: } ! 570: np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE; ! 571: np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC; ! 572: rx_status = np->rx[np->rx_cur].cmdsts; ! 573: } ! 574: end: ! 575: /* re-enable the potentially idle receive state machine ! 576: */ ! 577: outl (RxOn, np->ioaddr + ChipCmd); ! 578: } ! 579: ! 580: /** ! 581: * Enable/disable interrupts ! 582: * ! 583: * @v netdev Network device ! 584: * @v enable Non-zero for enable, zero for disable ! 585: */ ! 586: static void natsemi_irq (struct net_device *netdev, int enable) ! 587: { ! 588: struct natsemi_private *np = netdev->priv; ! 589: ! 590: outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0), ! 591: np->ioaddr + IntrMask); ! 592: outl ((enable ? 1 : 0), np->ioaddr + IntrEnable); ! 593: } ! 594: ! 595: static struct pci_device_id natsemi_nics[] = { ! 596: PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0), ! 597: }; ! 598: ! 599: struct pci_driver natsemi_driver __pci_driver = { ! 600: .ids = natsemi_nics, ! 601: .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])), ! 602: .probe = natsemi_probe, ! 603: .remove = natsemi_remove, ! 604: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.