natsemi.c

Go to the documentation of this file.
00001 /* 
00002    natsemi.c - gPXE driver for the NatSemi DP8381x series. 
00003  
00004    Based on:
00005 
00006    natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
00007 
00008    Copyright (C) 2001 Entity Cyber, Inc.
00009    
00010    This development of this Etherboot driver was funded by 
00011    
00012       Sicom Systems: http://www.sicompos.com/
00013    
00014    Author: Marty Connor <mdc@etherboot.org>
00015    Adapted from a Linux driver which was written by Donald Becker
00016    
00017    This software may be used and distributed according to the terms
00018    of the GNU Public License (GPL), incorporated herein by reference.
00019    
00020    Original Copyright Notice:
00021    
00022    Written/copyright 1999-2001 by Donald Becker.
00023    
00024    This software may be used and distributed according to the terms of
00025    the GNU General Public License (GPL), incorporated herein by reference.
00026    Drivers based on or derived from this code fall under the GPL and must
00027    retain the authorship, copyright and license notice.  This file is not
00028    a complete program and may only be used when the entire operating
00029    system is licensed under the GPL.  License for under other terms may be
00030    available.  Contact the original author for details.
00031    
00032    The original author may be reached as becker@scyld.com, or at
00033    Scyld Computing Corporation
00034    410 Severn Ave., Suite 210
00035    Annapolis MD 21403
00036    
00037    Support information and updates available at
00038    http://www.scyld.com/network/netsemi.html
00039    
00040    References:
00041    
00042    http://www.scyld.com/expert/100mbps.html
00043    http://www.scyld.com/expert/NWay.html
00044    Datasheet is available from:
00045    http://www.national.com/pf/DP/DP83815.html
00046 
00047 */
00048 
00049 FILE_LICENCE ( GPL_ANY );
00050 
00051 /* Revision History */
00052 
00053 /*
00054   02 Jul 2007  Udayan Kumar      1.2 ported the driver from etherboot to gPXE API.
00055                                      Fully rewritten,adapting the old driver.
00056                                      Added a circular buffer for transmit and receive.
00057                                      transmit routine will not wait for transmission to finish.
00058                                      poll routine deals with it.
00059   13 Dec 2003  Tim Legge         1.1 Enabled Multicast Support
00060   29 May 2001  Marty Connor      1.0 Initial Release. Tested with Netgear FA311 and FA312 boards
00061 */
00062 
00063 #include <stdint.h>
00064 #include <stdlib.h>
00065 #include <stdio.h>
00066 #include <string.h>
00067 #include <gpxe/io.h>
00068 #include <errno.h>
00069 #include <byteswap.h>
00070 #include <unistd.h>
00071 #include <gpxe/pci.h>
00072 #include <gpxe/if_ether.h>
00073 #include <gpxe/ethernet.h>
00074 #include <gpxe/iobuf.h>
00075 #include <gpxe/netdevice.h>
00076 #include <gpxe/spi_bit.h>
00077 #include <gpxe/threewire.h>
00078 #include <gpxe/nvo.h>
00079 #include "natsemi.h"
00080 
00081 /*  Function Prototypes: */
00082  
00083 static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int );
00084 static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long ); 
00085 static void natsemi_init_eeprom ( struct natsemi_private * ); 
00086 static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id);
00087 static void natsemi_reset (struct net_device *netdev);
00088 static int natsemi_open (struct net_device *netdev);
00089 static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf);
00090 static void natsemi_poll (struct net_device *netdev);
00091 static void natsemi_close (struct net_device *netdev);
00092 static void natsemi_irq (struct net_device *netdev, int enable);
00093 static void natsemi_remove (struct pci_device *pci);
00094 
00095 /** natsemi net device operations */
00096 static struct net_device_operations natsemi_operations = {
00097         .open           = natsemi_open,
00098         .close          = natsemi_close,
00099         .transmit       = natsemi_transmit,
00100         .poll           = natsemi_poll,
00101         .irq            = natsemi_irq,
00102 };
00103 
00104 static int natsemi_spi_read_bit ( struct bit_basher *basher,
00105                               unsigned int bit_id ) {
00106         struct natsemi_private *np = container_of ( basher, struct natsemi_private,
00107                                                  spibit.basher );
00108         uint8_t mask = natsemi_ee_bits[bit_id];
00109         uint8_t eereg;
00110 
00111         eereg = inb ( np->ioaddr + EE_REG );
00112         return ( eereg & mask );
00113 }
00114 
00115 static void natsemi_spi_write_bit ( struct bit_basher *basher,
00116                                 unsigned int bit_id, unsigned long data ) {
00117         struct natsemi_private *np = container_of ( basher, struct natsemi_private,
00118                                                  spibit.basher );
00119         uint8_t mask = natsemi_ee_bits[bit_id];
00120         uint8_t eereg;
00121 
00122         eereg = inb ( np->ioaddr + EE_REG );
00123         eereg &= ~mask;
00124         eereg |= ( data & mask );
00125         outb ( eereg, np->ioaddr + EE_REG );
00126 }
00127 
00128 static struct bit_basher_operations natsemi_basher_ops = {
00129         .read = natsemi_spi_read_bit,
00130         .write = natsemi_spi_write_bit,
00131 };
00132 
00133 /* It looks that this portion of EEPROM can be used for 
00134  * non-volatile stored options. Data sheet does not talk about this region.
00135  * Currently it is not working. But with some efforts it can.
00136  */
00137 static struct nvo_fragment natsemi_nvo_fragments[] = {
00138         { 0x0c, 0x68 },
00139         { 0, 0 }
00140 };
00141 
00142 /*
00143  * Set up for EEPROM access
00144  *
00145  * @v NAT               NATSEMI NIC
00146  */
00147 static void natsemi_init_eeprom ( struct natsemi_private *np ) {
00148 
00149         /* Initialise three-wire bus 
00150          */
00151         np->spibit.basher.op = &natsemi_basher_ops;
00152         np->spibit.bus.mode = SPI_MODE_THREEWIRE;
00153         np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN;
00154         init_spi_bit_basher ( &np->spibit );
00155 
00156         /*natsemi DP 83815 only supports at93c46
00157          */
00158         init_at93c46 ( &np->eeprom, 16 );
00159         np->eeprom.bus = &np->spibit.bus;
00160         np->nvo.nvs = &np->eeprom.nvs;
00161         np->nvo.fragments = natsemi_nvo_fragments;
00162 }
00163 
00164 /**
00165  * Probe PCI device
00166  *
00167  * @v pci       PCI device
00168  * @v id        PCI ID
00169  * @ret rc      Return status code
00170  */
00171 static int natsemi_probe (struct pci_device *pci,
00172                        const struct pci_device_id *id __unused) {
00173         struct net_device *netdev;
00174         struct natsemi_private *np = NULL;
00175         uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN];
00176         uint8_t last=0,last1=0;
00177         uint8_t prev_bytes[2];
00178         int i;
00179         int rc;
00180 
00181         /* Allocate net device 
00182          */
00183         netdev = alloc_etherdev (sizeof (*np));
00184         if (! netdev) 
00185                 return -ENOMEM;
00186 
00187         netdev_init (netdev, &natsemi_operations);
00188         np = netdev->priv;
00189         pci_set_drvdata (pci, netdev);
00190         netdev->dev = &pci->dev;
00191         memset (np, 0, sizeof (*np));
00192         np->ioaddr = pci->ioaddr;
00193 
00194         adjust_pci_device (pci);
00195 
00196         natsemi_reset (netdev);
00197         natsemi_init_eeprom ( np );
00198         nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 );
00199         nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN );
00200 
00201         /* decoding the MAC address read from NVS 
00202          * and save it in netdev->ll_addr
00203          */
00204         last = prev_bytes[1] >> 7;
00205         for ( i = 0 ; i < ETH_ALEN ; i++ ) {
00206                 last1 = ll_addr_encoded[i] >> 7;
00207                 netdev->hw_addr[i] = ll_addr_encoded[i] << 1 | last;
00208                 last = last1;
00209         }
00210 
00211         /* Mark as link up; we don't yet handle link state */
00212         netdev_link_up ( netdev );
00213 
00214         if ((rc = register_netdev (netdev)) != 0)
00215                 goto err_register_netdev;
00216 
00217         return 0;
00218 
00219 err_register_netdev:
00220 
00221         natsemi_reset (netdev);
00222         netdev_put (netdev);
00223         return rc;
00224 }
00225 
00226 /**
00227  * Remove PCI device
00228  *
00229  * @v pci       PCI device
00230  */
00231 static void natsemi_remove (struct pci_device *pci) {
00232         struct net_device *netdev = pci_get_drvdata (pci);
00233  
00234         unregister_netdev (netdev);
00235         natsemi_reset (netdev);
00236         netdev_nullify ( netdev );
00237         netdev_put (netdev);
00238 }
00239 
00240 /**
00241  * Reset NIC
00242  *
00243  * @v           NATSEMI NIC
00244  *
00245  * Issues a hardware reset and waits for the reset to complete.
00246  */
00247 static void natsemi_reset (struct net_device *netdev) 
00248 {
00249         struct natsemi_private *np = netdev->priv;
00250         int i;
00251         u32 cfg;
00252         u32 wcsr;
00253         u32 rfcr;
00254         u16 pmatch[3];
00255         u16 sopass[3];
00256 
00257         natsemi_irq (netdev, 0);
00258 
00259         /*
00260          * Resetting the chip causes some registers to be lost.
00261          * Natsemi suggests NOT reloading the EEPROM while live, so instead
00262          * we save the state that would have been loaded from EEPROM
00263          * on a normal power-up (see the spec EEPROM map).
00264          */
00265 
00266         /* CFG */
00267         cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE;
00268 
00269         /* WCSR */
00270         wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
00271 
00272         /* RFCR */
00273         rfcr = inl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
00274 
00275         /* PMATCH */
00276         for (i = 0; i < 3; i++) {
00277                 outl(i*2, np->ioaddr + RxFilterAddr);
00278                 pmatch[i] = inw(np->ioaddr + RxFilterData);
00279         }
00280 
00281         /* SOPAS */
00282         for (i = 0; i < 3; i++) {
00283                 outl(0xa+(i*2), np->ioaddr + RxFilterAddr);
00284                 sopass[i] = inw(np->ioaddr + RxFilterData);
00285         }
00286 
00287         /* now whack the chip */
00288         outl(ChipReset, np->ioaddr + ChipCmd);
00289         for (i=0; i<NATSEMI_HW_TIMEOUT; i++) {
00290                 if (! (inl (np->ioaddr + ChipCmd) & ChipReset))
00291                        break;
00292                 udelay(5);
00293         }
00294         if (i == NATSEMI_HW_TIMEOUT) {
00295                 DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5);
00296         }
00297 
00298         /* restore CFG */
00299         cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
00300         cfg &= ~(CfgExtPhy | CfgPhyDis);
00301         outl (cfg, np->ioaddr + ChipConfig);
00302 
00303         /* restore WCSR */
00304         wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
00305         outl (wcsr, np->ioaddr + WOLCmd);
00306 
00307         /* read RFCR */
00308         rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
00309 
00310         /* restore PMATCH */
00311         for (i = 0; i < 3; i++) {
00312                 outl (i*2, np->ioaddr + RxFilterAddr);
00313                 outw (pmatch[i], np->ioaddr + RxFilterData);
00314         }
00315         for (i = 0; i < 3; i++) {
00316                 outl (0xa+(i*2), np->ioaddr + RxFilterAddr);
00317                 outw (sopass[i], np->ioaddr + RxFilterData);
00318         }
00319         /* restore RFCR */
00320         outl (rfcr, np->ioaddr + RxFilterAddr);
00321 }
00322 
00323 /**
00324  * Open NIC
00325  *
00326  * @v netdev            Net device
00327  * @ret rc              Return status code
00328  */
00329 static int natsemi_open (struct net_device *netdev)
00330 {
00331         struct natsemi_private *np = netdev->priv;
00332         uint32_t tx_config, rx_config;
00333         int i;
00334         
00335         /* Disable PME:
00336          * The PME bit is initialized from the EEPROM contents.
00337          * PCI cards probably have PME disabled, but motherboard
00338          * implementations may have PME set to enable WakeOnLan. 
00339          * With PME set the chip will scan incoming packets but
00340          * nothing will be written to memory. 
00341          */
00342         outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun);
00343 
00344         /* Set MAC address in NIC
00345          */
00346         for (i = 0 ; i < ETH_ALEN ; i+=2) {
00347                 outl (i, np->ioaddr + RxFilterAddr);
00348                 outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8),
00349                        np->ioaddr + RxFilterData);
00350         }
00351 
00352         /* Setup Tx Ring 
00353          */
00354         np->tx_cur = 0;
00355         np->tx_dirty = 0;
00356         for (i = 0 ; i < TX_RING_SIZE ; i++) {
00357                 np->tx[i].link   = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]);
00358                 np->tx[i].cmdsts = 0;
00359                 np->tx[i].bufptr = 0;
00360         }
00361         outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr);
00362 
00363         DBG ("Natsemi Tx descriptor loaded with: %#08x\n",
00364              inl (np->ioaddr + TxRingPtr));
00365 
00366         /* Setup RX ring
00367          */
00368         np->rx_cur = 0;
00369         for (i = 0 ; i < NUM_RX_DESC ; i++) {
00370                 np->iobuf[i] = alloc_iob (RX_BUF_SIZE);
00371                 if (! np->iobuf[i])
00372                         goto memory_alloc_err;
00373                 np->rx[i].link   = virt_to_bus ((i + 1 < NUM_RX_DESC) 
00374                                                 ? &np->rx[i + 1] : &np->rx[0]);
00375                 np->rx[i].cmdsts = RX_BUF_SIZE;
00376                 np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data);
00377                 DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i, 
00378                       &np->iobuf[i],  &np->iobuf[i]->data);
00379         }
00380         outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr);
00381 
00382         DBG ("Natsemi Rx descriptor loaded with: %#08x\n",
00383               inl (np->ioaddr + RxRingPtr));            
00384 
00385         /* Setup RX Filter 
00386          */
00387         outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys,
00388               np->ioaddr + RxFilterAddr);
00389 
00390         /* Initialize other registers. 
00391          * Configure the PCI bus bursts and FIFO thresholds. 
00392          * Configure for standard, in-spec Ethernet. 
00393          */
00394         if (inl (np->ioaddr + ChipConfig) & 0x20000000) {       /* Full duplex */
00395                 DBG ("Full duplex\n");
00396                 tx_config = 0xD0801002 |  0xC0000000;
00397                 rx_config = 0x10000020 |  0x10000000;
00398         } else {
00399                 DBG ("Half duplex\n");
00400                 tx_config = 0x10801002 & ~0xC0000000;
00401                 rx_config = 0x00000020 & ~0x10000000;
00402         }
00403         outl (tx_config, np->ioaddr + TxConfig);
00404         outl (rx_config, np->ioaddr + RxConfig);
00405 
00406         DBG ("Tx config register = %#08x Rx config register = %#08x\n", 
00407                inl (np->ioaddr + TxConfig),
00408                inl (np->ioaddr + RxConfig));
00409 
00410         /*Set the Interrupt Mask register
00411          */
00412         outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask);
00413         /*start the receiver 
00414          */
00415         outl (RxOn, np->ioaddr + ChipCmd);
00416         
00417         return 0;
00418                        
00419 memory_alloc_err:
00420 
00421         /* Frees any allocated buffers when memory
00422          * for all buffers requested is not available
00423          */
00424         i = 0;
00425         while (np->rx[i].cmdsts == RX_BUF_SIZE) {
00426                 free_iob (np->iobuf[i]);
00427                 i++;
00428         }
00429         return -ENOMEM; 
00430 }
00431 
00432 /**
00433  * Close NIC
00434  *
00435  * @v netdev            Net device
00436  */
00437 static void natsemi_close (struct net_device *netdev) 
00438 {
00439         struct natsemi_private *np = netdev->priv;
00440         int i;
00441 
00442         natsemi_reset (netdev);
00443 
00444         for (i = 0; i < NUM_RX_DESC ; i++) {
00445                 free_iob (np->iobuf[i]);
00446         }
00447 }
00448 
00449 /** 
00450  * Transmit packet
00451  *
00452  * @v netdev    Network device
00453  * @v iobuf     I/O buffer
00454  * @ret rc      Return status code
00455  */
00456 static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf)
00457 {
00458         struct natsemi_private *np = netdev->priv;
00459 
00460         if (np->tx[np->tx_cur].cmdsts != 0) {
00461                 DBG ("TX overflow\n");
00462                 return -ENOBUFS;
00463         }
00464 
00465         /* Used by netdev_tx_complete ()
00466          */
00467         np->tx_iobuf[np->tx_cur] = iobuf;
00468 
00469         /* Pad and align packet has not been used because its not required 
00470          * by the hardware.
00471          *      iob_pad (iobuf, ETH_ZLEN); 
00472          * can be used to achieve it, if required
00473          */
00474 
00475         /* Add the packet to TX ring
00476          */
00477         np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data);
00478         np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN;
00479 
00480         DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur,
00481              virt_to_bus (&iobuf->data), iob_len (iobuf));
00482 
00483         /* increment the circular buffer pointer to the next buffer location
00484          */
00485         np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE;
00486 
00487         /*start the transmitter 
00488          */
00489         outl (TxOn, np->ioaddr + ChipCmd);
00490 
00491         return 0;
00492 }
00493 
00494 /** 
00495  * Poll for received packets
00496  *
00497  * @v netdev    Network device
00498  */
00499 static void natsemi_poll (struct net_device *netdev)
00500 {
00501         struct natsemi_private *np = netdev->priv;
00502         unsigned int tx_status;
00503         unsigned int rx_status;
00504         unsigned int intr_status;
00505         unsigned int rx_len;
00506         struct io_buffer *rx_iob;
00507         int i;
00508         
00509         /* read the interrupt register
00510          */
00511         intr_status = inl (np->ioaddr + IntrStatus);
00512 
00513         if (!intr_status)
00514                 goto end;
00515 
00516         DBG ("natsemi_poll: intr_status = %#08x\n", intr_status);
00517 
00518         /* Check status of transmitted packets
00519          */
00520         i = np->tx_dirty;
00521         while (i != np->tx_cur) {
00522                 tx_status = np->tx[np->tx_dirty].cmdsts;
00523 
00524                 DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n",
00525                      np->tx_dirty, np->tx_cur, tx_status);
00526                 
00527                 if (tx_status & OWN) 
00528                         break;
00529 
00530                 if (! (tx_status & DescPktOK)) {
00531                         netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL);
00532                         DBG ("Error transmitting packet, tx_status: %#08x\n",
00533                              tx_status);
00534                 } else {
00535                         netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]);
00536                         DBG ("Success transmitting packet\n");
00537                 }
00538 
00539                 np->tx[np->tx_dirty].cmdsts = 0;
00540                 np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE;
00541                 i = (i + 1) % TX_RING_SIZE;
00542         }
00543         
00544         /* Process received packets 
00545          */
00546         rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts; 
00547         while ((rx_status & OWN)) {
00548                 rx_len = (rx_status & DSIZE) - CRC_SIZE;
00549 
00550                 DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n",
00551                      np->rx_cur, rx_status, rx_len);
00552                 
00553                 if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) {
00554                         netdev_rx_err (netdev, NULL, -EINVAL);
00555 
00556                         DBG ("natsemi_poll: Corrupted packet received!"
00557                              " Status = %#08x\n",
00558                               np->rx[np->rx_cur].cmdsts);
00559 
00560                 } else  {
00561 
00562 
00563                         /* If unable allocate space for this packet,
00564                          *  try again next poll
00565                          */
00566                         rx_iob = alloc_iob (rx_len);
00567                         if (! rx_iob) 
00568                                 goto end;
00569                         memcpy (iob_put (rx_iob, rx_len), 
00570                                 np->iobuf[np->rx_cur]->data, rx_len);
00571                         /* Add this packet to the receive queue. 
00572                          */
00573                         netdev_rx (netdev, rx_iob);
00574                 }
00575                 np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE;
00576                 np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC;
00577                 rx_status = np->rx[np->rx_cur].cmdsts; 
00578         }
00579 end:
00580         /* re-enable the potentially idle receive state machine 
00581          */
00582         outl (RxOn, np->ioaddr + ChipCmd);      
00583 }                               
00584 
00585 /**
00586  * Enable/disable interrupts
00587  *
00588  * @v netdev    Network device
00589  * @v enable    Non-zero for enable, zero for disable
00590  */
00591 static void natsemi_irq (struct net_device *netdev, int enable)
00592 {
00593         struct natsemi_private *np = netdev->priv;
00594 
00595         outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0),
00596               np->ioaddr + IntrMask); 
00597         outl ((enable ? 1 : 0), np->ioaddr + IntrEnable);
00598 }
00599 
00600 static struct pci_device_id natsemi_nics[] = {
00601         PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0),
00602 };
00603 
00604 struct pci_driver natsemi_driver __pci_driver = {
00605         .ids = natsemi_nics,
00606         .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])),
00607         .probe = natsemi_probe,
00608         .remove = natsemi_remove,
00609 };

Generated on Tue Apr 6 20:01:01 2010 for gPXE by  doxygen 1.5.7.1