2 natsemi.c - gPXE driver for the NatSemi DP8381x series.
6 natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
8 Copyright (C) 2001 Entity Cyber, Inc.
10 This development of this Etherboot driver was funded by
12 Sicom Systems: http://www.sicompos.com/
14 Author: Marty Connor <mdc@etherboot.org>
15 Adapted from a Linux driver which was written by Donald Becker
17 This software may be used and distributed according to the terms
18 of the GNU Public License (GPL), incorporated herein by reference.
20 Original Copyright Notice:
22 Written/copyright 1999-2001 by Donald Becker.
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.
32 The original author may be reached as becker@scyld.com, or at
33 Scyld Computing Corporation
34 410 Severn Ave., Suite 210
37 Support information and updates available at
38 http://www.scyld.com/network/netsemi.html
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
49 /* Revision History */
52 02 Jul 2007 Udayan Kumar 1.2 ported the driver from etherboot to gPXE API.
53 Fully rewritten,adapting the old driver.
54 Added a circular buffer for transmit and receive.
55 transmit routine will not wait for transmission to finish.
56 poll routine deals with it.
57 13 Dec 2003 Tim Legge 1.1 Enabled Multicast Support
58 29 May 2001 Marty Connor 1.0 Initial Release. Tested with Netgear FA311 and FA312 boards
70 #include <gpxe/if_ether.h>
71 #include <gpxe/ethernet.h>
72 #include <gpxe/iobuf.h>
73 #include <gpxe/netdevice.h>
74 #include <gpxe/spi_bit.h>
75 #include <gpxe/threewire.h>
79 /* Function Prototypes: */
81 static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int );
82 static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long );
83 void natsemi_init_eeprom ( struct natsemi_private * );
84 static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id);
85 static void natsemi_reset (struct net_device *netdev);
86 static int natsemi_open (struct net_device *netdev);
87 static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf);
88 static void natsemi_poll (struct net_device *netdev);
89 static void natsemi_close (struct net_device *netdev);
90 static void natsemi_irq (struct net_device *netdev, int enable);
91 static void natsemi_remove (struct pci_device *pci);
93 /** natsemi net device operations */
94 static struct net_device_operations natsemi_operations = {
96 .close = natsemi_close,
97 .transmit = natsemi_transmit,
102 static int natsemi_spi_read_bit ( struct bit_basher *basher,
103 unsigned int bit_id ) {
104 struct natsemi_private *np = container_of ( basher, struct natsemi_private,
106 uint8_t mask = natsemi_ee_bits[bit_id];
109 eereg = inb ( np->ioaddr + EE_REG );
110 return ( eereg & mask );
113 static void natsemi_spi_write_bit ( struct bit_basher *basher,
114 unsigned int bit_id, unsigned long data ) {
115 struct natsemi_private *np = container_of ( basher, struct natsemi_private,
117 uint8_t mask = natsemi_ee_bits[bit_id];
120 eereg = inb ( np->ioaddr + EE_REG );
122 eereg |= ( data & mask );
123 outb ( eereg, np->ioaddr + EE_REG );
126 static struct bit_basher_operations natsemi_basher_ops = {
127 .read = natsemi_spi_read_bit,
128 .write = natsemi_spi_write_bit,
131 /* It looks that this portion of EEPROM can be used for
132 * non-volatile stored options. Data sheet does not talk about this region.
133 * Currently it is not working. But with some efforts it can.
135 static struct nvo_fragment natsemi_nvo_fragments[] = {
141 * Set up for EEPROM access
145 void natsemi_init_eeprom ( struct natsemi_private *np ) {
147 /* Initialise three-wire bus
149 np->spibit.basher.op = &natsemi_basher_ops;
150 np->spibit.bus.mode = SPI_MODE_THREEWIRE;
151 np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN;
152 init_spi_bit_basher ( &np->spibit );
154 /*natsemi DP 83815 only supports at93c46
156 init_at93c46 ( &np->eeprom, 16 );
157 np->eeprom.bus = &np->spibit.bus;
158 np->nvo.nvs = &np->eeprom.nvs;
159 np->nvo.fragments = natsemi_nvo_fragments;
167 * @ret rc Return status code
169 static int natsemi_probe (struct pci_device *pci,
170 const struct pci_device_id *id __unused) {
171 struct net_device *netdev;
172 struct natsemi_private *np = NULL;
173 uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN];
174 uint8_t last=0,last1=0;
175 uint8_t prev_bytes[2];
179 /* Allocate net device
181 netdev = alloc_etherdev (sizeof (*np));
185 netdev_init (netdev, &natsemi_operations);
187 pci_set_drvdata (pci, netdev);
188 netdev->dev = &pci->dev;
189 memset (np, 0, sizeof (*np));
190 np->ioaddr = pci->ioaddr;
192 adjust_pci_device (pci);
194 natsemi_reset (netdev);
195 natsemi_init_eeprom ( np );
196 nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 );
197 nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN );
199 /* decoding the MAC address read from NVS
200 * and save it in netdev->ll_addr
202 last = prev_bytes[1] >> 7;
203 for ( i = 0 ; i < ETH_ALEN ; i++ ) {
204 last1 = ll_addr_encoded[i] >> 7;
205 netdev->ll_addr[i] = ll_addr_encoded[i] << 1 | last;
209 if ((rc = register_netdev (netdev)) != 0)
210 goto err_register_netdev;
216 natsemi_reset (netdev);
226 static void natsemi_remove (struct pci_device *pci) {
227 struct net_device *netdev = pci_get_drvdata (pci);
229 unregister_netdev (netdev);
230 natsemi_reset (netdev);
239 * Issues a hardware reset and waits for the reset to complete.
241 static void natsemi_reset (struct net_device *netdev)
243 struct natsemi_private *np = netdev->priv;
251 natsemi_irq (netdev, 0);
254 * Resetting the chip causes some registers to be lost.
255 * Natsemi suggests NOT reloading the EEPROM while live, so instead
256 * we save the state that would have been loaded from EEPROM
257 * on a normal power-up (see the spec EEPROM map).
261 cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE;
264 wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
267 rfcr = readl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
270 for (i = 0; i < 3; i++) {
271 outl(i*2, np->ioaddr + RxFilterAddr);
272 pmatch[i] = inw(np->ioaddr + RxFilterData);
276 for (i = 0; i < 3; i++) {
277 outl(0xa+(i*2), np->ioaddr + RxFilterAddr);
278 sopass[i] = inw(np->ioaddr + RxFilterData);
281 /* now whack the chip */
282 outl(ChipReset, np->ioaddr + ChipCmd);
283 for (i=0; i<NATSEMI_HW_TIMEOUT; i++) {
284 if (! (inl (np->ioaddr + ChipCmd) & ChipReset))
288 if (i == NATSEMI_HW_TIMEOUT) {
289 DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5);
293 cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
294 cfg &= ~(CfgExtPhy | CfgPhyDis);
295 outl (cfg, np->ioaddr + ChipConfig);
298 wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
299 outl (wcsr, np->ioaddr + WOLCmd);
302 rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
305 for (i = 0; i < 3; i++) {
306 outl (i*2, np->ioaddr + RxFilterAddr);
307 outw (pmatch[i], np->ioaddr + RxFilterData);
309 for (i = 0; i < 3; i++) {
310 outl (0xa+(i*2), np->ioaddr + RxFilterAddr);
311 outw (sopass[i], np->ioaddr + RxFilterData);
314 outl (rfcr, np->ioaddr + RxFilterAddr);
320 * @v netdev Net device
321 * @ret rc Return status code
323 static int natsemi_open (struct net_device *netdev)
325 struct natsemi_private *np = netdev->priv;
326 uint32_t tx_config, rx_config;
330 * The PME bit is initialized from the EEPROM contents.
331 * PCI cards probably have PME disabled, but motherboard
332 * implementations may have PME set to enable WakeOnLan.
333 * With PME set the chip will scan incoming packets but
334 * nothing will be written to memory.
336 SavedClkRun = inl (np->ioaddr + ClkRun);
337 outl (SavedClkRun & ~0x100, np->ioaddr + ClkRun);
339 /* Set MAC address in NIC
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);
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;
356 outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr);
358 DBG ("Natsemi Tx descriptor loaded with: %#08x\n",
359 (unsigned int) inl (np->ioaddr + TxRingPtr));
364 for (i = 0 ; i < NUM_RX_DESC ; i++) {
365 np->iobuf[i] = alloc_iob (RX_BUF_SIZE);
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] = %#08x and iobuf->data = %#08x \n", i,
373 (unsigned int) &np->iobuf[i], (unsigned int) &np->iobuf[i]->data);
375 outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr);
377 DBG ("Natsemi Rx descriptor loaded with: %#08x\n",
378 (unsigned int) inl (np->ioaddr + RxRingPtr));
382 outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys,
383 np->ioaddr + RxFilterAddr);
385 /* Initialize other registers.
386 * Configure the PCI bus bursts and FIFO thresholds.
387 * Configure for standard, in-spec Ethernet.
389 if (inl (np->ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
390 DBG ("Full duplex\n");
391 tx_config = 0xD0801002 | 0xC0000000;
392 rx_config = 0x10000020 | 0x10000000;
394 DBG ("Half duplex\n");
395 tx_config = 0x10801002 & ~0xC0000000;
396 rx_config = 0x00000020 & ~0x10000000;
398 outl (tx_config, np->ioaddr + TxConfig);
399 outl (rx_config, np->ioaddr + RxConfig);
401 DBG ("Tx config register = %#08x Rx config register = %#08x\n",
402 (unsigned int) inl (np->ioaddr + TxConfig),
403 (unsigned int) inl (np->ioaddr + RxConfig));
405 /*Set the Interrupt Mask register
407 outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask);
410 outl (RxOn, np->ioaddr + ChipCmd);
416 /* Frees any allocated buffers when memory
417 * for all buffers requested is not available
420 while (np->rx[i].cmdsts == RX_BUF_SIZE) {
421 free_iob (np->iobuf[i]);
430 * @v netdev Net device
432 static void natsemi_close (struct net_device *netdev)
434 struct natsemi_private *np = netdev->priv;
437 natsemi_reset (netdev);
439 for (i = 0; i < NUM_RX_DESC ; i++) {
440 free_iob (np->iobuf[i]);
447 * @v netdev Network device
448 * @v iobuf I/O buffer
449 * @ret rc Return status code
451 static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf)
453 struct natsemi_private *np = netdev->priv;
455 if (np->tx[np->tx_cur].cmdsts != 0) {
456 DBG ("TX overflow\n");
460 /* Used by netdev_tx_complete ()
462 np->tx_iobuf[np->tx_cur] = iobuf;
464 /* Pad and align packet has not been used because its not required
466 * iob_pad (iobuf, ETH_ZLEN);
467 * can be used to achieve it, if required
470 /* Add the packet to TX ring
472 np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data);
473 np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN;
475 DBG ("TX id %d at %#08x + %#08x\n", np->tx_cur,
476 (unsigned int) virt_to_bus (&iobuf->data), iob_len (iobuf));
478 /* increment the circular buffer pointer to the next buffer location
480 np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE;
482 /*start the transmitter
484 outl (TxOn, np->ioaddr + ChipCmd);
490 * Poll for received packets
492 * @v netdev Network device
494 static void natsemi_poll (struct net_device *netdev)
496 struct natsemi_private *np = netdev->priv;
497 unsigned int tx_status;
498 unsigned int rx_status;
499 unsigned int intr_status;
501 struct io_buffer *rx_iob;
504 /* read the interrupt register
506 intr_status = inl (np->ioaddr + IntrStatus);
511 DBG ("natsemi_poll: intr_status = %#08x\n", intr_status);
513 /* Check status of transmitted packets
516 while (i != np->tx_cur) {
517 tx_status = np->tx[np->tx_dirty].cmdsts;
519 DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n",
520 np->tx_dirty, np->tx_cur, tx_status);
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 (unsigned int) tx_status);
530 netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]);
531 DBG ("Success transmitting packet\n");
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;
539 /* Process received packets
541 rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts;
542 while ((rx_status & OWN)) {
543 rx_len = (rx_status & DSIZE) - CRC_SIZE;
545 DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n",
546 np->rx_cur, rx_status, rx_len);
548 if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) {
549 netdev_rx_err (netdev, NULL, -EINVAL);
551 DBG ("natsemi_poll: Corrupted packet received!"
553 (unsigned int) np->rx[np->rx_cur].cmdsts);
554 //DBG_HD (np->iobuf[np->rx_cur]->data, 30);
558 //DBG_HD (np->iobuf[np->rx_cur]->data, 30);
560 /* If unable allocate space for this packet,
561 * try again next poll
563 rx_iob = alloc_iob (rx_len);
566 memcpy (iob_put (rx_iob, rx_len),
567 np->iobuf[np->rx_cur]->data, rx_len);
568 /* Add this packet to the receive queue.
570 netdev_rx (netdev, rx_iob);
572 np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE;
573 np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC;
574 rx_status = np->rx[np->rx_cur].cmdsts;
577 /* re-enable the potentially idle receive state machine
579 outl (RxOn, np->ioaddr + ChipCmd);
583 * Enable/disable interrupts
585 * @v netdev Network device
586 * @v enable Non-zero for enable, zero for disable
588 static void natsemi_irq (struct net_device *netdev, int enable)
590 struct natsemi_private *np = netdev->priv;
592 outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0),
593 np->ioaddr + IntrMask);
594 outl ((enable ? 1 : 0), np->ioaddr + IntrEnable);
597 static struct pci_device_id natsemi_nics[] = {
598 PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"),
601 struct pci_driver natsemi_driver __pci_driver = {
603 .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])),
604 .probe = natsemi_probe,
605 .remove = natsemi_remove,