summaryrefslogtreecommitdiff
path: root/gpxe/src/drivers/net/natsemi.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpxe/src/drivers/net/natsemi.c')
-rw-r--r--gpxe/src/drivers/net/natsemi.c609
1 files changed, 0 insertions, 609 deletions
diff --git a/gpxe/src/drivers/net/natsemi.c b/gpxe/src/drivers/net/natsemi.c
deleted file mode 100644
index db3f3209..00000000
--- a/gpxe/src/drivers/net/natsemi.c
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
- natsemi.c - gPXE driver for the NatSemi DP8381x series.
-
- Based on:
-
- natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
-
- Copyright (C) 2001 Entity Cyber, Inc.
-
- This development of this Etherboot driver was funded by
-
- Sicom Systems: http://www.sicompos.com/
-
- Author: Marty Connor <mdc@etherboot.org>
- Adapted from a Linux driver which was written by Donald Becker
-
- This software may be used and distributed according to the terms
- of the GNU Public License (GPL), incorporated herein by reference.
-
- Original Copyright Notice:
-
- Written/copyright 1999-2001 by Donald Becker.
-
- This software may be used and distributed according to the terms of
- the GNU General Public License (GPL), incorporated herein by reference.
- Drivers based on or derived from this code fall under the GPL and must
- retain the authorship, copyright and license notice. This file is not
- a complete program and may only be used when the entire operating
- system is licensed under the GPL. License for under other terms may be
- available. Contact the original author for details.
-
- The original author may be reached as becker@scyld.com, or at
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
- Support information and updates available at
- http://www.scyld.com/network/netsemi.html
-
- References:
-
- http://www.scyld.com/expert/100mbps.html
- http://www.scyld.com/expert/NWay.html
- Datasheet is available from:
- http://www.national.com/pf/DP/DP83815.html
-
-*/
-
-FILE_LICENCE ( GPL_ANY );
-
-/* Revision History */
-
-/*
- 02 Jul 2007 Udayan Kumar 1.2 ported the driver from etherboot to gPXE API.
- Fully rewritten,adapting the old driver.
- Added a circular buffer for transmit and receive.
- transmit routine will not wait for transmission to finish.
- poll routine deals with it.
- 13 Dec 2003 Tim Legge 1.1 Enabled Multicast Support
- 29 May 2001 Marty Connor 1.0 Initial Release. Tested with Netgear FA311 and FA312 boards
-*/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <gpxe/io.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <unistd.h>
-#include <gpxe/pci.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/ethernet.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/spi_bit.h>
-#include <gpxe/threewire.h>
-#include <gpxe/nvo.h>
-#include "natsemi.h"
-
-/* Function Prototypes: */
-
-static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int );
-static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long );
-static void natsemi_init_eeprom ( struct natsemi_private * );
-static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id);
-static void natsemi_reset (struct net_device *netdev);
-static int natsemi_open (struct net_device *netdev);
-static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf);
-static void natsemi_poll (struct net_device *netdev);
-static void natsemi_close (struct net_device *netdev);
-static void natsemi_irq (struct net_device *netdev, int enable);
-static void natsemi_remove (struct pci_device *pci);
-
-/** natsemi net device operations */
-static struct net_device_operations natsemi_operations = {
- .open = natsemi_open,
- .close = natsemi_close,
- .transmit = natsemi_transmit,
- .poll = natsemi_poll,
- .irq = natsemi_irq,
-};
-
-static int natsemi_spi_read_bit ( struct bit_basher *basher,
- unsigned int bit_id ) {
- struct natsemi_private *np = container_of ( basher, struct natsemi_private,
- spibit.basher );
- uint8_t mask = natsemi_ee_bits[bit_id];
- uint8_t eereg;
-
- eereg = inb ( np->ioaddr + EE_REG );
- return ( eereg & mask );
-}
-
-static void natsemi_spi_write_bit ( struct bit_basher *basher,
- unsigned int bit_id, unsigned long data ) {
- struct natsemi_private *np = container_of ( basher, struct natsemi_private,
- spibit.basher );
- uint8_t mask = natsemi_ee_bits[bit_id];
- uint8_t eereg;
-
- eereg = inb ( np->ioaddr + EE_REG );
- eereg &= ~mask;
- eereg |= ( data & mask );
- outb ( eereg, np->ioaddr + EE_REG );
-}
-
-static struct bit_basher_operations natsemi_basher_ops = {
- .read = natsemi_spi_read_bit,
- .write = natsemi_spi_write_bit,
-};
-
-/* It looks that this portion of EEPROM can be used for
- * non-volatile stored options. Data sheet does not talk about this region.
- * Currently it is not working. But with some efforts it can.
- */
-static struct nvo_fragment natsemi_nvo_fragments[] = {
- { 0x0c, 0x68 },
- { 0, 0 }
-};
-
-/*
- * Set up for EEPROM access
- *
- * @v NAT NATSEMI NIC
- */
-static void natsemi_init_eeprom ( struct natsemi_private *np ) {
-
- /* Initialise three-wire bus
- */
- np->spibit.basher.op = &natsemi_basher_ops;
- np->spibit.bus.mode = SPI_MODE_THREEWIRE;
- np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN;
- init_spi_bit_basher ( &np->spibit );
-
- /*natsemi DP 83815 only supports at93c46
- */
- init_at93c46 ( &np->eeprom, 16 );
- np->eeprom.bus = &np->spibit.bus;
- np->nvo.nvs = &np->eeprom.nvs;
- np->nvo.fragments = natsemi_nvo_fragments;
-}
-
-/**
- * Probe PCI device
- *
- * @v pci PCI device
- * @v id PCI ID
- * @ret rc Return status code
- */
-static int natsemi_probe (struct pci_device *pci,
- const struct pci_device_id *id __unused) {
- struct net_device *netdev;
- struct natsemi_private *np = NULL;
- uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN];
- uint8_t last=0,last1=0;
- uint8_t prev_bytes[2];
- int i;
- int rc;
-
- /* Allocate net device
- */
- netdev = alloc_etherdev (sizeof (*np));
- if (! netdev)
- return -ENOMEM;
-
- netdev_init (netdev, &natsemi_operations);
- np = netdev->priv;
- pci_set_drvdata (pci, netdev);
- netdev->dev = &pci->dev;
- memset (np, 0, sizeof (*np));
- np->ioaddr = pci->ioaddr;
-
- adjust_pci_device (pci);
-
- natsemi_reset (netdev);
- natsemi_init_eeprom ( np );
- nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 );
- nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN );
-
- /* decoding the MAC address read from NVS
- * and save it in netdev->ll_addr
- */
- last = prev_bytes[1] >> 7;
- for ( i = 0 ; i < ETH_ALEN ; i++ ) {
- last1 = ll_addr_encoded[i] >> 7;
- netdev->hw_addr[i] = ll_addr_encoded[i] << 1 | last;
- last = last1;
- }
-
- /* Mark as link up; we don't yet handle link state */
- netdev_link_up ( netdev );
-
- if ((rc = register_netdev (netdev)) != 0)
- goto err_register_netdev;
-
- return 0;
-
-err_register_netdev:
-
- natsemi_reset (netdev);
- netdev_put (netdev);
- return rc;
-}
-
-/**
- * Remove PCI device
- *
- * @v pci PCI device
- */
-static void natsemi_remove (struct pci_device *pci) {
- struct net_device *netdev = pci_get_drvdata (pci);
-
- unregister_netdev (netdev);
- natsemi_reset (netdev);
- netdev_nullify ( netdev );
- netdev_put (netdev);
-}
-
-/**
- * Reset NIC
- *
- * @v NATSEMI NIC
- *
- * Issues a hardware reset and waits for the reset to complete.
- */
-static void natsemi_reset (struct net_device *netdev)
-{
- struct natsemi_private *np = netdev->priv;
- int i;
- u32 cfg;
- u32 wcsr;
- u32 rfcr;
- u16 pmatch[3];
- u16 sopass[3];
-
- natsemi_irq (netdev, 0);
-
- /*
- * Resetting the chip causes some registers to be lost.
- * Natsemi suggests NOT reloading the EEPROM while live, so instead
- * we save the state that would have been loaded from EEPROM
- * on a normal power-up (see the spec EEPROM map).
- */
-
- /* CFG */
- cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE;
-
- /* WCSR */
- wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE;
-
- /* RFCR */
- rfcr = inl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
-
- /* PMATCH */
- for (i = 0; i < 3; i++) {
- outl(i*2, np->ioaddr + RxFilterAddr);
- pmatch[i] = inw(np->ioaddr + RxFilterData);
- }
-
- /* SOPAS */
- for (i = 0; i < 3; i++) {
- outl(0xa+(i*2), np->ioaddr + RxFilterAddr);
- sopass[i] = inw(np->ioaddr + RxFilterData);
- }
-
- /* now whack the chip */
- outl(ChipReset, np->ioaddr + ChipCmd);
- for (i=0; i<NATSEMI_HW_TIMEOUT; i++) {
- if (! (inl (np->ioaddr + ChipCmd) & ChipReset))
- break;
- udelay(5);
- }
- if (i == NATSEMI_HW_TIMEOUT) {
- DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5);
- }
-
- /* restore CFG */
- cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
- cfg &= ~(CfgExtPhy | CfgPhyDis);
- outl (cfg, np->ioaddr + ChipConfig);
-
- /* restore WCSR */
- wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
- outl (wcsr, np->ioaddr + WOLCmd);
-
- /* read RFCR */
- rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
-
- /* restore PMATCH */
- for (i = 0; i < 3; i++) {
- outl (i*2, np->ioaddr + RxFilterAddr);
- outw (pmatch[i], np->ioaddr + RxFilterData);
- }
- for (i = 0; i < 3; i++) {
- outl (0xa+(i*2), np->ioaddr + RxFilterAddr);
- outw (sopass[i], np->ioaddr + RxFilterData);
- }
- /* restore RFCR */
- outl (rfcr, np->ioaddr + RxFilterAddr);
-}
-
-/**
- * Open NIC
- *
- * @v netdev Net device
- * @ret rc Return status code
- */
-static int natsemi_open (struct net_device *netdev)
-{
- struct natsemi_private *np = netdev->priv;
- uint32_t tx_config, rx_config;
- int i;
-
- /* Disable PME:
- * The PME bit is initialized from the EEPROM contents.
- * PCI cards probably have PME disabled, but motherboard
- * implementations may have PME set to enable WakeOnLan.
- * With PME set the chip will scan incoming packets but
- * nothing will be written to memory.
- */
- outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun);
-
- /* Set MAC address in NIC
- */
- for (i = 0 ; i < ETH_ALEN ; i+=2) {
- outl (i, np->ioaddr + RxFilterAddr);
- outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8),
- np->ioaddr + RxFilterData);
- }
-
- /* Setup Tx Ring
- */
- np->tx_cur = 0;
- np->tx_dirty = 0;
- for (i = 0 ; i < TX_RING_SIZE ; i++) {
- np->tx[i].link = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]);
- np->tx[i].cmdsts = 0;
- np->tx[i].bufptr = 0;
- }
- outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr);
-
- DBG ("Natsemi Tx descriptor loaded with: %#08x\n",
- inl (np->ioaddr + TxRingPtr));
-
- /* Setup RX ring
- */
- np->rx_cur = 0;
- for (i = 0 ; i < NUM_RX_DESC ; i++) {
- np->iobuf[i] = alloc_iob (RX_BUF_SIZE);
- if (! np->iobuf[i])
- goto memory_alloc_err;
- np->rx[i].link = virt_to_bus ((i + 1 < NUM_RX_DESC)
- ? &np->rx[i + 1] : &np->rx[0]);
- np->rx[i].cmdsts = RX_BUF_SIZE;
- np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data);
- DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i,
- &np->iobuf[i], &np->iobuf[i]->data);
- }
- outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr);
-
- DBG ("Natsemi Rx descriptor loaded with: %#08x\n",
- inl (np->ioaddr + RxRingPtr));
-
- /* Setup RX Filter
- */
- outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys,
- np->ioaddr + RxFilterAddr);
-
- /* Initialize other registers.
- * Configure the PCI bus bursts and FIFO thresholds.
- * Configure for standard, in-spec Ethernet.
- */
- if (inl (np->ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
- DBG ("Full duplex\n");
- tx_config = 0xD0801002 | 0xC0000000;
- rx_config = 0x10000020 | 0x10000000;
- } else {
- DBG ("Half duplex\n");
- tx_config = 0x10801002 & ~0xC0000000;
- rx_config = 0x00000020 & ~0x10000000;
- }
- outl (tx_config, np->ioaddr + TxConfig);
- outl (rx_config, np->ioaddr + RxConfig);
-
- DBG ("Tx config register = %#08x Rx config register = %#08x\n",
- inl (np->ioaddr + TxConfig),
- inl (np->ioaddr + RxConfig));
-
- /*Set the Interrupt Mask register
- */
- outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask);
- /*start the receiver
- */
- outl (RxOn, np->ioaddr + ChipCmd);
-
- return 0;
-
-memory_alloc_err:
-
- /* Frees any allocated buffers when memory
- * for all buffers requested is not available
- */
- i = 0;
- while (np->rx[i].cmdsts == RX_BUF_SIZE) {
- free_iob (np->iobuf[i]);
- i++;
- }
- return -ENOMEM;
-}
-
-/**
- * Close NIC
- *
- * @v netdev Net device
- */
-static void natsemi_close (struct net_device *netdev)
-{
- struct natsemi_private *np = netdev->priv;
- int i;
-
- natsemi_reset (netdev);
-
- for (i = 0; i < NUM_RX_DESC ; i++) {
- free_iob (np->iobuf[i]);
- }
-}
-
-/**
- * Transmit packet
- *
- * @v netdev Network device
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
-static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf)
-{
- struct natsemi_private *np = netdev->priv;
-
- if (np->tx[np->tx_cur].cmdsts != 0) {
- DBG ("TX overflow\n");
- return -ENOBUFS;
- }
-
- /* Used by netdev_tx_complete ()
- */
- np->tx_iobuf[np->tx_cur] = iobuf;
-
- /* Pad and align packet has not been used because its not required
- * by the hardware.
- * iob_pad (iobuf, ETH_ZLEN);
- * can be used to achieve it, if required
- */
-
- /* Add the packet to TX ring
- */
- np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data);
- np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN;
-
- DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur,
- virt_to_bus (&iobuf->data), iob_len (iobuf));
-
- /* increment the circular buffer pointer to the next buffer location
- */
- np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE;
-
- /*start the transmitter
- */
- outl (TxOn, np->ioaddr + ChipCmd);
-
- return 0;
-}
-
-/**
- * Poll for received packets
- *
- * @v netdev Network device
- */
-static void natsemi_poll (struct net_device *netdev)
-{
- struct natsemi_private *np = netdev->priv;
- unsigned int tx_status;
- unsigned int rx_status;
- unsigned int intr_status;
- unsigned int rx_len;
- struct io_buffer *rx_iob;
- int i;
-
- /* read the interrupt register
- */
- intr_status = inl (np->ioaddr + IntrStatus);
-
- if (!intr_status)
- goto end;
-
- DBG ("natsemi_poll: intr_status = %#08x\n", intr_status);
-
- /* Check status of transmitted packets
- */
- i = np->tx_dirty;
- while (i != np->tx_cur) {
- tx_status = np->tx[np->tx_dirty].cmdsts;
-
- DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n",
- np->tx_dirty, np->tx_cur, tx_status);
-
- if (tx_status & OWN)
- break;
-
- if (! (tx_status & DescPktOK)) {
- netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL);
- DBG ("Error transmitting packet, tx_status: %#08x\n",
- tx_status);
- } else {
- netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]);
- DBG ("Success transmitting packet\n");
- }
-
- np->tx[np->tx_dirty].cmdsts = 0;
- np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE;
- i = (i + 1) % TX_RING_SIZE;
- }
-
- /* Process received packets
- */
- rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts;
- while ((rx_status & OWN)) {
- rx_len = (rx_status & DSIZE) - CRC_SIZE;
-
- DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n",
- np->rx_cur, rx_status, rx_len);
-
- if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) {
- netdev_rx_err (netdev, NULL, -EINVAL);
-
- DBG ("natsemi_poll: Corrupted packet received!"
- " Status = %#08x\n",
- np->rx[np->rx_cur].cmdsts);
-
- } else {
-
-
- /* If unable allocate space for this packet,
- * try again next poll
- */
- rx_iob = alloc_iob (rx_len);
- if (! rx_iob)
- goto end;
- memcpy (iob_put (rx_iob, rx_len),
- np->iobuf[np->rx_cur]->data, rx_len);
- /* Add this packet to the receive queue.
- */
- netdev_rx (netdev, rx_iob);
- }
- np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE;
- np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC;
- rx_status = np->rx[np->rx_cur].cmdsts;
- }
-end:
- /* re-enable the potentially idle receive state machine
- */
- outl (RxOn, np->ioaddr + ChipCmd);
-}
-
-/**
- * Enable/disable interrupts
- *
- * @v netdev Network device
- * @v enable Non-zero for enable, zero for disable
- */
-static void natsemi_irq (struct net_device *netdev, int enable)
-{
- struct natsemi_private *np = netdev->priv;
-
- outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0),
- np->ioaddr + IntrMask);
- outl ((enable ? 1 : 0), np->ioaddr + IntrEnable);
-}
-
-static struct pci_device_id natsemi_nics[] = {
- PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0),
-};
-
-struct pci_driver natsemi_driver __pci_driver = {
- .ids = natsemi_nics,
- .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])),
- .probe = natsemi_probe,
- .remove = natsemi_remove,
-};