diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c index 17154281..fb1eb822 100644 --- a/src/drivers/net/natsemi.c +++ b/src/drivers/net/natsemi.c @@ -1,604 +1,927 @@ -/* - natsemi.c - iPXE 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 - 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 iPXE 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 -*/ + * Copyright (C) 2012 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); #include -#include -#include #include -#include +#include #include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #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); -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); +/** @file + * + * National Semiconductor "MacPhyter" network card driver + * + * Based on the following datasheets: + * + * http://www.ti.com/lit/ds/symlink/dp83820.pdf + * http://www.datasheets.org.uk/indexdl/Datasheet-03/DSA0041338.pdf + * + */ -/** 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, +/****************************************************************************** + * + * EEPROM interface + * + ****************************************************************************** + */ + +/** Pin mapping for SPI bit-bashing interface */ +static const uint8_t natsemi_eeprom_bits[] = { + [SPI_BIT_SCLK] = NATSEMI_MEAR_EECLK, + [SPI_BIT_MOSI] = NATSEMI_MEAR_EEDI, + [SPI_BIT_MISO] = NATSEMI_MEAR_EEDO, + [SPI_BIT_SS(0)] = NATSEMI_MEAR_EESEL, }; +/** + * Read input bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret zero Input is a logic 0 + * @ret non-zero Input is a logic 1 + */ 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; + unsigned int bit_id ) { + struct natsemi_nic *natsemi = container_of ( basher, struct natsemi_nic, + spibit.basher ); + uint32_t mask = natsemi_eeprom_bits[bit_id]; + uint32_t reg; - eereg = inb ( np->ioaddr + EE_REG ); - return ( eereg & mask ); + DBG_DISABLE ( DBGLVL_IO ); + reg = readl ( natsemi->regs + NATSEMI_MEAR ); + DBG_ENABLE ( DBGLVL_IO ); + return ( reg & mask ); } +/** + * Set/clear output bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + */ 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; + unsigned int bit_id, unsigned long data ) { + struct natsemi_nic *natsemi = container_of ( basher, struct natsemi_nic, + spibit.basher ); + uint32_t mask = natsemi_eeprom_bits[bit_id]; + uint32_t reg; - eereg = inb ( np->ioaddr + EE_REG ); - eereg &= ~mask; - eereg |= ( data & mask ); - outb ( eereg, np->ioaddr + EE_REG ); + DBG_DISABLE ( DBGLVL_IO ); + reg = readl ( natsemi->regs + NATSEMI_MEAR ); + reg &= ~mask; + reg |= ( data & mask ); + writel ( reg, natsemi->regs + NATSEMI_MEAR ); + DBG_ENABLE ( DBGLVL_IO ); } +/** SPI bit-bashing interface */ static struct bit_basher_operations natsemi_basher_ops = { .read = natsemi_spi_read_bit, .write = natsemi_spi_write_bit, }; -/* - * Set up for EEPROM access +/** + * Initialise EEPROM * - * @v NAT NATSEMI NIC + * @v natsemi National Semiconductor device */ -static void natsemi_init_eeprom ( struct natsemi_private *np ) { +static void natsemi_init_eeprom ( struct natsemi_nic *natsemi ) { - /* 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 ); + /* Initialise SPI bit-bashing interface */ + natsemi->spibit.basher.op = &natsemi_basher_ops; + natsemi->spibit.bus.mode = SPI_MODE_THREEWIRE; + natsemi->spibit.endianness = + ( ( natsemi->flags & NATSEMI_EEPROM_LITTLE_ENDIAN ) ? + SPI_BIT_LITTLE_ENDIAN : SPI_BIT_BIG_ENDIAN ); + init_spi_bit_basher ( &natsemi->spibit ); - /*natsemi DP 83815 only supports at93c46 - */ - init_at93c46 ( &np->eeprom, 16 ); - np->eeprom.bus = &np->spibit.bus; - - /* 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. - */ - nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL ); + /* Initialise EEPROM device */ + init_at93c06 ( &natsemi->eeprom, 16 ); + natsemi->eeprom.bus = &natsemi->spibit.bus; } +/** + * Get hardware address from sane EEPROM data + * + * @v natsemi National Semiconductor device + * @v eeprom EEPROM data + * @v hw_addr Hardware address to fill in + */ +static void natsemi_hwaddr_sane ( struct natsemi_nic *natsemi, + const uint16_t *eeprom, uint16_t *hw_addr ) { + int i; + + /* Copy MAC address from EEPROM data */ + for ( i = ( ( ETH_ALEN / 2 ) - 1 ) ; i >= 0 ; i-- ) + *(hw_addr++) = eeprom[ NATSEMI_EEPROM_MAC_SANE + i ]; + + DBGC ( natsemi, "NATSEMI %p has sane EEPROM layout\n", natsemi ); +} + +/** + * Get hardware address from insane EEPROM data + * + * @v natsemi National Semiconductor device + * @v eeprom EEPROM data + * @v hw_addr Hardware address to fill in + */ +static void natsemi_hwaddr_insane ( struct natsemi_nic *natsemi, + const uint16_t *eeprom, + uint16_t *hw_addr ) { + unsigned int i; + unsigned int offset; + uint16_t word; + + /* Copy MAC address from EEPROM data */ + for ( i = 0 ; i < ( ETH_ALEN / 2 ) ; i++ ) { + offset = ( NATSEMI_EEPROM_MAC_INSANE + i ); + word = ( ( le16_to_cpu ( eeprom[ offset ] ) >> 15 ) | + ( le16_to_cpu ( eeprom[ offset + 1 ] << 1 ) ) ); + hw_addr[i] = cpu_to_le16 ( word ); + } + + DBGC ( natsemi, "NATSEMI %p has insane EEPROM layout\n", natsemi ); +} + +/** + * Get hardware address from EEPROM + * + * @v natsemi National Semiconductor device + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +static int natsemi_hwaddr ( struct natsemi_nic *natsemi, void *hw_addr ) { + uint16_t buf[NATSEMI_EEPROM_SIZE]; + void ( * extract ) ( struct natsemi_nic *natsemi, + const uint16_t *eeprom, uint16_t *hw_addr ); + int rc; + + /* Read EEPROM contents */ + if ( ( rc = nvs_read ( &natsemi->eeprom.nvs, 0, buf, + sizeof ( buf ) ) ) != 0 ) { + DBGC ( natsemi, "NATSEMI %p could not read EEPROM: %s\n", + natsemi, strerror ( rc ) ); + return rc; + } + DBGC2 ( natsemi, "NATSEMI %p EEPROM contents:\n", natsemi ); + DBGC2_HDA ( natsemi, 0, buf, sizeof ( buf ) ); + + /* Extract MAC address from EEPROM contents */ + extract = ( ( natsemi->flags & NATSEMI_EEPROM_INSANE ) ? + natsemi_hwaddr_insane : natsemi_hwaddr_sane ); + extract ( natsemi, buf, hw_addr ); + + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset controller chip + * + * @v natsemi National Semiconductor device + * @ret rc Return status code + */ +static int natsemi_soft_reset ( struct natsemi_nic *natsemi ) { + unsigned int i; + + /* Initiate reset */ + writel ( NATSEMI_CR_RST, natsemi->regs + NATSEMI_CR ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < NATSEMI_RESET_MAX_WAIT_MS ; i++ ) { + + /* If reset is not complete, delay 1ms and retry */ + if ( readl ( natsemi->regs + NATSEMI_CR ) & NATSEMI_CR_RST ) { + mdelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( natsemi, "NATSEMI %p timed out waiting for reset\n", natsemi ); + return -ETIMEDOUT; +} + +/** + * Reload configuration from EEPROM + * + * @v natsemi National Semiconductor device + * @ret rc Return status code + */ +static int natsemi_reload_config ( struct natsemi_nic *natsemi ) { + unsigned int i; + + /* Initiate reload */ + writel ( NATSEMI_PTSCR_EELOAD_EN, natsemi->regs + NATSEMI_PTSCR ); + + /* Wait for reload to complete */ + for ( i = 0 ; i < NATSEMI_EELOAD_MAX_WAIT_MS ; i++ ) { + + /* If reload is not complete, delay 1ms and retry */ + if ( readl ( natsemi->regs + NATSEMI_PTSCR ) & + NATSEMI_PTSCR_EELOAD_EN ) { + mdelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( natsemi, "NATSEMI %p timed out waiting for configuration " + "reload\n", natsemi ); + return -ETIMEDOUT; +} + +/** + * Reset hardware + * + * @v natsemi National Semiconductor device + * @ret rc Return status code + */ +static int natsemi_reset ( struct natsemi_nic *natsemi ) { + uint32_t cfg; + int rc; + + /* Perform soft reset */ + if ( ( rc = natsemi_soft_reset ( natsemi ) ) != 0 ) + return rc; + + /* Reload configuration from EEPROM */ + if ( ( rc = natsemi_reload_config ( natsemi ) ) != 0 ) + return rc; + + /* Configure 64-bit operation, if applicable */ + cfg = readl ( natsemi->regs + NATSEMI_CFG ); + if ( natsemi->flags & NATSEMI_64BIT ) { + cfg |= ( NATSEMI_CFG_M64ADDR | NATSEMI_CFG_EXTSTS_EN ); + if ( ! ( cfg & NATSEMI_CFG_PCI64_DET ) ) + cfg &= ~NATSEMI_CFG_DATA64_EN; + } + writel ( cfg, natsemi->regs + NATSEMI_CFG ); + + /* Invalidate link status cache to force an update */ + natsemi->cfg = ~cfg; + + DBGC ( natsemi, "NATSEMI %p using configuration %08x\n", + natsemi, cfg ); + return 0; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void natsemi_check_link ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + uint32_t cfg; + + /* Read link status */ + cfg = readl ( natsemi->regs + NATSEMI_CFG ); + + /* Do nothing unless link status has changed */ + if ( cfg == natsemi->cfg ) + return; + + /* Set gigabit mode (if applicable) */ + if ( natsemi->flags & NATSEMI_1000 ) { + cfg &= ~NATSEMI_CFG_MODE_1000; + if ( ! ( cfg & NATSEMI_CFG_SPDSTS1 ) ) + cfg |= NATSEMI_CFG_MODE_1000; + writel ( cfg, natsemi->regs + NATSEMI_CFG ); + } + + /* Update link status */ + natsemi->cfg = cfg; + DBGC ( natsemi, "NATSEMI %p link status is %08x\n", natsemi, cfg ); + + /* Update network device */ + if ( cfg & NATSEMI_CFG_LNKSTS ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Set perfect match filter address + * + * @v natsemi National Semiconductor device + * @v mac MAC address + */ +static void natsemi_pmatch ( struct natsemi_nic *natsemi, const void *mac ) { + const uint16_t *pmatch = mac; + uint32_t rfcr; + unsigned int rfaddr; + unsigned int i; + + for ( i = 0 ; i < ETH_ALEN ; i += sizeof ( *pmatch ) ) { + + /* Select receive filter register address */ + rfaddr = ( NATSEMI_RFADDR_PMATCH_BASE + i ); + rfcr = readl ( natsemi->regs + NATSEMI_RFCR ); + rfcr &= ~NATSEMI_RFCR_RFADDR_MASK; + rfcr |= NATSEMI_RFCR_RFADDR ( rfaddr ); + writel ( rfcr, natsemi->regs + NATSEMI_RFCR ); + + /* Write receive filter data */ + writel ( ( le16_to_cpu ( *(pmatch++) ) | NATSEMI_RFDR_BMASK ), + natsemi->regs + NATSEMI_RFDR ); + } +} + +/** + * Create descriptor ring + * + * @v natsemi National Semiconductor device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int natsemi_create_ring ( struct natsemi_nic *natsemi, + struct natsemi_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + union natsemi_descriptor *desc; + union natsemi_descriptor *linked_desc; + physaddr_t address; + physaddr_t link; + size_t offset; + unsigned int i; + int rc; + + /* Calculate descriptor offset */ + offset = ( ( natsemi->flags & NATSEMI_64BIT ) ? 0 : + offsetof ( typeof ( desc[i].d32pad ), d32 ) ); + + /* Allocate descriptor ring. Align ring on its own size to + * ensure that it can't possibly cross the boundary of 32-bit + * address space. + */ + ring->desc = malloc_dma ( len, len ); + if ( ! ring->desc ) { + rc = -ENOMEM; + goto err_alloc; + } + address = ( virt_to_bus ( ring->desc ) + offset ); + + /* Check address is usable by card */ + if ( ! natsemi_address_ok ( natsemi, address ) ) { + DBGC ( natsemi, "NATSEMI %p cannot support 64-bit ring " + "address\n", natsemi ); + rc = -ENOTSUP; + goto err_64bit; + } + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + linked_desc = &ring->desc [ ( i + 1 ) % ring->count ]; + link = ( virt_to_bus ( linked_desc ) + offset ); + if ( natsemi->flags & NATSEMI_64BIT ) { + ring->desc[i].d64.link = cpu_to_le64 ( link ); + } else { + ring->desc[i].d32pad.d32.link = cpu_to_le32 ( link ); + } + } + + /* Program ring address */ + writel ( ( address & 0xffffffffUL ), natsemi->regs + ring->reg ); + if ( natsemi->flags & NATSEMI_64BIT ) { + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) address ) >> 32 ), + natsemi->regs + ring->reg + 4 ); + } else { + writel ( 0, natsemi->regs + ring->reg + 4 ); + } + } + + DBGC ( natsemi, "NATSEMI %p ring %02x is at [%08llx,%08llx)\n", + natsemi, ring->reg, + ( ( unsigned long long ) virt_to_bus ( ring->desc ) ), + ( ( unsigned long long ) virt_to_bus ( ring->desc ) + len ) ); + + return 0; + + err_64bit: + free_dma ( ring->desc, len ); + ring->desc = NULL; + err_alloc: + return rc; +} + +/** + * Destroy descriptor ring + * + * @v natsemi National Semiconductor device + * @v ring Descriptor ring + */ +static void natsemi_destroy_ring ( struct natsemi_nic *natsemi, + struct natsemi_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writel ( 0, natsemi->regs + ring->reg ); + if ( natsemi->flags & NATSEMI_64BIT ) + writel ( 0, natsemi->regs + ring->reg + 4 ); + + /* Free descriptor ring */ + free_dma ( ring->desc, len ); + ring->desc = NULL; + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill receive descriptor ring + * + * @v netdev Network device + */ +static void natsemi_refill_rx ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + union natsemi_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + + while ( ( natsemi->rx.prod - natsemi->rx.cons ) < NATSEMI_NUM_RX_DESC ){ + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( NATSEMI_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + return; + } + + /* Check address is usable by card */ + address = virt_to_bus ( iobuf->data ); + if ( ! natsemi_address_ok ( natsemi, address ) ) { + DBGC ( natsemi, "NATSEMI %p cannot support 64-bit RX " + "buffer address\n", natsemi ); + netdev_rx_err ( netdev, iobuf, -ENOTSUP ); + return; + } + + /* Get next receive descriptor */ + rx_idx = ( natsemi->rx.prod++ % NATSEMI_NUM_RX_DESC ); + rx = &natsemi->rx.desc[rx_idx]; + + /* Populate receive descriptor */ + if ( natsemi->flags & NATSEMI_64BIT ) { + rx->d64.bufptr = cpu_to_le64 ( address ); + } else { + rx->d32pad.d32.bufptr = cpu_to_le32 ( address ); + } + wmb(); + rx->common.cmdsts = cpu_to_le32 ( NATSEMI_DESC_INTR | + NATSEMI_RX_MAX_LEN ); + wmb(); + + /* Record I/O buffer */ + assert ( natsemi->rx_iobuf[rx_idx] == NULL ); + natsemi->rx_iobuf[rx_idx] = iobuf; + + /* Notify card that there are descriptors available */ + writel ( NATSEMI_CR_RXE, natsemi->regs + NATSEMI_CR ); + + DBGC2 ( natsemi, "NATSEMI %p RX %d is [%llx,%llx)\n", natsemi, + rx_idx, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + NATSEMI_RX_MAX_LEN)); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int natsemi_open ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + int rc; + + /* Set MAC address */ + natsemi_pmatch ( natsemi, netdev->ll_addr ); + + /* Create transmit descriptor ring */ + if ( ( rc = natsemi_create_ring ( natsemi, &natsemi->tx ) ) != 0 ) + goto err_create_tx; + + /* Set transmit configuration */ + writel ( ( NATSEMI_TXCFG_CSI | NATSEMI_TXCFG_HBI | NATSEMI_TXCFG_ATP | + NATSEMI_TXCFG_ECRETRY | NATSEMI_TXCFG_MXDMA_DEFAULT | + NATSEMI_TXCFG_FLTH_DEFAULT | NATSEMI_TXCFG_DRTH_DEFAULT ), + ( natsemi->regs + ( ( natsemi->flags & NATSEMI_64BIT ) ? + NATSEMI_TXCFG_64 : NATSEMI_TXCFG_32 ) ) ); + + /* Create receive descriptor ring */ + if ( ( rc = natsemi_create_ring ( natsemi, &natsemi->rx ) ) != 0 ) + goto err_create_rx; + + /* Set receive configuration */ + writel ( ( NATSEMI_RXCFG_ARP | NATSEMI_RXCFG_ATX | NATSEMI_RXCFG_ALP | + NATSEMI_RXCFG_MXDMA_DEFAULT | NATSEMI_RXCFG_DRTH_DEFAULT ), + ( natsemi->regs + ( ( natsemi->flags & NATSEMI_64BIT ) ? + NATSEMI_RXCFG_64 : NATSEMI_RXCFG_32 ) ) ); + + /* Set receive filter configuration */ + writel ( ( NATSEMI_RFCR_RFEN | NATSEMI_RFCR_AAB | NATSEMI_RFCR_AAM | + NATSEMI_RFCR_AAU ), natsemi->regs + NATSEMI_RFCR ); + + /* Fill receive ring */ + natsemi_refill_rx ( netdev ); + + /* Unmask transmit and receive interrupts. (Interrupts will + * not be generated unless enabled via the IER.) + */ + writel ( ( NATSEMI_IRQ_TXDESC | NATSEMI_IRQ_RXDESC ), + natsemi->regs + NATSEMI_IMR ); + + /* Update link state */ + natsemi_check_link ( netdev ); + + return 0; + + natsemi_destroy_ring ( natsemi, &natsemi->rx ); + err_create_rx: + natsemi_destroy_ring ( natsemi, &natsemi->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void natsemi_close ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + unsigned int i; + + /* Mask transmit and receive interrupts */ + writel ( 0, natsemi->regs + NATSEMI_IMR ); + + /* Reset and disable transmitter and receiver */ + writel ( ( NATSEMI_CR_RXR | NATSEMI_CR_TXR ), + natsemi->regs + NATSEMI_CR ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < NATSEMI_NUM_RX_DESC ; i++ ) { + if ( natsemi->rx_iobuf[i] ) + free_iob ( natsemi->rx_iobuf[i] ); + natsemi->rx_iobuf[i] = NULL; + } + + /* Destroy receive descriptor ring */ + natsemi_destroy_ring ( natsemi, &natsemi->rx ); + + /* Destroy transmit descriptor ring */ + natsemi_destroy_ring ( natsemi, &natsemi->tx ); +} + +/** + * 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_nic *natsemi = netdev->priv; + union natsemi_descriptor *tx; + unsigned int tx_idx; + physaddr_t address; + + /* Check address is usable by card */ + address = virt_to_bus ( iobuf->data ); + if ( ! natsemi_address_ok ( natsemi, address ) ) { + DBGC ( natsemi, "NATSEMI %p cannot support 64-bit TX buffer " + "address\n", natsemi ); + return -ENOTSUP; + } + + /* Get next transmit descriptor */ + if ( ( natsemi->tx.prod - natsemi->tx.cons ) >= NATSEMI_NUM_TX_DESC ) { + DBGC ( natsemi, "NATSEMI %p out of transmit descriptors\n", + natsemi ); + return -ENOBUFS; + } + tx_idx = ( natsemi->tx.prod++ % NATSEMI_NUM_TX_DESC ); + tx = &natsemi->tx.desc[tx_idx]; + + /* Populate transmit descriptor */ + if ( natsemi->flags & NATSEMI_64BIT ) { + tx->d64.bufptr = cpu_to_le64 ( address ); + } else { + tx->d32pad.d32.bufptr = cpu_to_le32 ( address ); + } + wmb(); + tx->common.cmdsts = cpu_to_le32 ( NATSEMI_DESC_OWN | NATSEMI_DESC_INTR | + iob_len ( iobuf ) ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writel ( NATSEMI_CR_TXE, natsemi->regs + NATSEMI_CR ); + + DBGC2 ( natsemi, "NATSEMI %p TX %d is [%llx,%llx)\n", natsemi, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void natsemi_poll_tx ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + union natsemi_descriptor *tx; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( natsemi->tx.cons != natsemi->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( natsemi->tx.cons % NATSEMI_NUM_TX_DESC ); + tx = &natsemi->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( tx->common.cmdsts & cpu_to_le32 ( NATSEMI_DESC_OWN ) ) + return; + + /* Complete TX descriptor */ + if ( tx->common.cmdsts & cpu_to_le32 ( NATSEMI_DESC_OK ) ) { + DBGC2 ( natsemi, "NATSEMI %p TX %d complete\n", + natsemi, tx_idx ); + netdev_tx_complete_next ( netdev ); + } else { + DBGC ( natsemi, "NATSEMI %p TX %d completion error " + "(%08x)\n", natsemi, tx_idx, + le32_to_cpu ( tx->common.cmdsts ) ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } + natsemi->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void natsemi_poll_rx ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + union natsemi_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( natsemi->rx.cons != natsemi->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( natsemi->rx.cons % NATSEMI_NUM_RX_DESC ); + rx = &natsemi->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( ! ( rx->common.cmdsts & NATSEMI_DESC_OWN ) ) + return; + + /* Populate I/O buffer */ + iobuf = natsemi->rx_iobuf[rx_idx]; + natsemi->rx_iobuf[rx_idx] = NULL; + len = ( le32_to_cpu ( rx->common.cmdsts ) & + NATSEMI_DESC_SIZE_MASK ); + iob_put ( iobuf, len - 4 /* strip CRC */ ); + + /* Hand off to network stack */ + if ( rx->common.cmdsts & cpu_to_le32 ( NATSEMI_DESC_OK ) ) { + DBGC2 ( natsemi, "NATSEMI %p RX %d complete (length " + "%zd)\n", natsemi, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } else { + DBGC ( natsemi, "NATSEMI %p RX %d error (length %zd, " + "status %08x)\n", natsemi, rx_idx, len, + le32_to_cpu ( rx->common.cmdsts ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } + natsemi->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void natsemi_poll ( struct net_device *netdev ) { + struct natsemi_nic *natsemi = netdev->priv; + uint32_t isr; + + /* Poll for link state. The PHY interrupt seems not to + * function as expected, and polling for the link state is + * only a single register read. + */ + natsemi_check_link ( netdev ); + + /* Check for and acknowledge interrupts */ + isr = readl ( natsemi->regs + NATSEMI_ISR ); + if ( ! isr ) + return; + + /* Poll for TX completions, if applicable */ + if ( isr & NATSEMI_IRQ_TXDESC ) + natsemi_poll_tx ( netdev ); + + /* Poll for RX completionsm, if applicable */ + if ( isr & NATSEMI_IRQ_RXDESC ) + natsemi_poll_rx ( netdev ); + + /* Refill RX ring */ + natsemi_refill_rx ( netdev ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void natsemi_irq ( struct net_device *netdev, int enable ) { + struct natsemi_nic *natsemi = netdev->priv; + + /* Enable or disable interrupts */ + writel ( ( enable ? NATSEMI_IER_IE : 0 ), natsemi->regs + NATSEMI_IER ); +} + +/** National Semiconductor network device operations */ +static struct net_device_operations natsemi_operations = { + .open = natsemi_open, + .close = natsemi_close, + .transmit = natsemi_transmit, + .poll = natsemi_poll, + .irq = natsemi_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + /** * Probe PCI device * - * @v pci PCI device - * @v id PCI ID - * @ret rc Return status code + * @v pci PCI device + * @ret rc Return status code */ -static int natsemi_probe (struct pci_device *pci) { +static int natsemi_probe ( struct pci_device *pci ) { 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; + struct natsemi_nic *natsemi; 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; + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *natsemi ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; } + netdev_init ( netdev, &natsemi_operations ); + natsemi = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( natsemi, 0, sizeof ( *natsemi ) ); + natsemi->flags = pci->id->driver_data; + natsemi_init_ring ( &natsemi->tx, NATSEMI_NUM_TX_DESC, NATSEMI_TXDP ); + natsemi_init_ring ( &natsemi->rx, NATSEMI_NUM_RX_DESC, NATSEMI_RXDP ); - if ((rc = register_netdev (netdev)) != 0) + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + natsemi->regs = ioremap ( pci->membase, NATSEMI_BAR_SIZE ); + + /* Reset the NIC */ + if ( ( rc = natsemi_reset ( natsemi ) ) != 0 ) + goto err_reset; + + /* Initialise EEPROM */ + natsemi_init_eeprom ( natsemi ); + + /* Read initial MAC address */ + if ( ( rc = natsemi_hwaddr ( natsemi, netdev->hw_addr ) ) != 0 ) + goto err_hwaddr; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; - /* Mark as link up; we don't yet handle link state */ - netdev_link_up ( netdev ); + /* Set initial link state */ + natsemi_check_link ( netdev ); return 0; -err_register_netdev: - - natsemi_reset (netdev); - netdev_put (netdev); + unregister_netdev ( netdev ); + err_register_netdev: + err_hwaddr: + natsemi_reset ( natsemi ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: return rc; } /** * Remove PCI device * - * @v pci 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); +static void natsemi_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct natsemi_nic *natsemi = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + natsemi_reset ( natsemi ); + + /* Free network device */ netdev_nullify ( netdev ); - netdev_put (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]; +/** Flags for DP83815 */ +#define DP83815_FLAGS ( NATSEMI_EEPROM_LITTLE_ENDIAN | NATSEMI_EEPROM_INSANE ) - 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; iioaddr + 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); -} +/** Flags for DP83820 */ +#define DP83820_FLAGS ( NATSEMI_64BIT | NATSEMI_1000 ) +/** National Semiconductor PCI device IDs */ static struct pci_device_id natsemi_nics[] = { - PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815", 0), + PCI_ROM ( 0x100b, 0x0020, "dp83815", "DP83815", DP83815_FLAGS ), + PCI_ROM ( 0x100b, 0x0022, "dp83820", "DP83820", DP83820_FLAGS ), }; +/** National Semiconductor PCI driver */ struct pci_driver natsemi_driver __pci_driver = { .ids = natsemi_nics, - .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])), + .id_count = ( sizeof ( natsemi_nics ) / sizeof ( natsemi_nics[0] ) ), .probe = natsemi_probe, .remove = natsemi_remove, }; diff --git a/src/drivers/net/natsemi.h b/src/drivers/net/natsemi.h index ae827ba3..e3444712 100644 --- a/src/drivers/net/natsemi.h +++ b/src/drivers/net/natsemi.h @@ -1,232 +1,329 @@ -FILE_LICENCE ( GPL_ANY ); +#ifndef _NATSEMI_H +#define _NATSEMI_H -#define NATSEMI_HW_TIMEOUT 400 +/** @file + * + * National Semiconductor "MacPhyter" network card driver + * + */ -#define TX_RING_SIZE 4 -#define NUM_RX_DESC 4 -#define RX_BUF_SIZE 1536 -#define OWN 0x80000000 -#define DSIZE 0x00000FFF -#define CRC_SIZE 4 +FILE_LICENCE ( GPL2_OR_LATER ); -struct natsemi_tx { +#include +#include +#include + +/** BAR size */ +#define NATSEMI_BAR_SIZE 0x100 + +/** A 32-bit packet descriptor */ +struct natsemi_descriptor_32 { + /** Link to next descriptor */ uint32_t link; + /** Command / status */ uint32_t cmdsts; + /** Buffer pointer */ uint32_t bufptr; +} __attribute__ (( packed )); + +/** A 64-bit packet descriptor */ +struct natsemi_descriptor_64 { + /** Link to next descriptor */ + uint64_t link; + /** Buffer pointer */ + uint64_t bufptr; + /** Command / status */ + uint32_t cmdsts; + /** Extended status */ + uint32_t extsts; +} __attribute__ (( packed )); + +/** A packet descriptor + * + * The 32-bit and 64-bit variants are overlaid such that "cmdsts" can + * be accessed as a common field, and the overall size is a power of + * two (to allow the descriptor ring length to be used as an + * alignment). + */ +union natsemi_descriptor { + /** Common fields */ + struct { + /** Reserved */ + uint8_t reserved_a[16]; + /** Command / status */ + uint32_t cmdsts; + /** Reserved */ + uint8_t reserved_b[12]; + } __attribute__ (( packed )) common; + /** 64-bit descriptor */ + struct natsemi_descriptor_64 d64; + /** 32-bit descriptor */ + struct { + /** Reserved */ + uint8_t reserved[12]; + /** Descriptor */ + struct natsemi_descriptor_32 d32; + } __attribute__ (( packed )) d32pad; }; -struct natsemi_rx { - uint32_t link; - uint32_t cmdsts; - uint32_t bufptr; +/** Descriptor buffer size mask */ +#define NATSEMI_DESC_SIZE_MASK 0xfff + +/** Packet descriptor flags */ +enum natsemi_descriptor_flags { + /** Descriptor is owned by NIC */ + NATSEMI_DESC_OWN = 0x80000000UL, + /** Request descriptor interrupt */ + NATSEMI_DESC_INTR = 0x20000000UL, + /** Packet OK */ + NATSEMI_DESC_OK = 0x08000000UL, }; -struct natsemi_private { - unsigned short ioaddr; - unsigned short tx_cur; - unsigned short tx_dirty; - unsigned short rx_cur; - struct natsemi_tx tx[TX_RING_SIZE]; - struct natsemi_rx rx[NUM_RX_DESC]; +/** Command Register */ +#define NATSEMI_CR 0x0000 +#define NATSEMI_CR_RST 0x00000100UL /**< Reset */ +#define NATSEMI_CR_RXR 0x00000020UL /**< Receiver reset */ +#define NATSEMI_CR_TXR 0x00000010UL /**< Transmit reset */ +#define NATSEMI_CR_RXE 0x00000004UL /**< Receiver enable */ +#define NATSEMI_CR_TXE 0x00000001UL /**< Transmit enable */ - /* need to add iobuf as we cannot free iobuf->data in close without this - * alternatively substracting sizeof(head) and sizeof(list_head) can also - * give the same. - */ - struct io_buffer *iobuf[NUM_RX_DESC]; +/** Maximum time to wait for a reset, in milliseconds */ +#define NATSEMI_RESET_MAX_WAIT_MS 100 - /* netdev_tx_complete needs pointer to the iobuf of the data so as to free - * it from the memory. - */ - struct io_buffer *tx_iobuf[TX_RING_SIZE]; +/** Configuration and Media Status Register */ +#define NATSEMI_CFG 0x0004 +#define NATSEMI_CFG_LNKSTS 0x80000000UL /**< Link status */ +#define NATSEMI_CFG_SPDSTS1 0x40000000UL /**< Speed status bit 1 */ +#define NATSEMI_CFG_MODE_1000 0x00400000UL /**< 1000 Mb/s mode control */ +#define NATSEMI_CFG_PCI64_DET 0x00002000UL /**< PCI 64-bit bus detected */ +#define NATSEMI_CFG_DATA64_EN 0x00001000UL /**< 64-bit data enable */ +#define NATSEMI_CFG_M64ADDR 0x00000800UL /**< 64-bit address enable */ +#define NATSEMI_CFG_EXTSTS_EN 0x00000100UL /**< Extended status enable */ + +/** EEPROM Access Register */ +#define NATSEMI_MEAR 0x0008 +#define NATSEMI_MEAR_EESEL 0x00000008UL /**< EEPROM chip select */ +#define NATSEMI_MEAR_EECLK 0x00000004UL /**< EEPROM serial clock */ +#define NATSEMI_MEAR_EEDO 0x00000002UL /**< EEPROM data out */ +#define NATSEMI_MEAR_EEDI 0x00000001UL /**< EEPROM data in */ + +/** Size of EEPROM (in bytes) */ +#define NATSEMI_EEPROM_SIZE 32 + +/** Word offset of MAC address within sane EEPROM layout */ +#define NATSEMI_EEPROM_MAC_SANE 0x0a + +/** Word offset of MAC address within insane EEPROM layout */ +#define NATSEMI_EEPROM_MAC_INSANE 0x06 + +/** PCI Test Control Register */ +#define NATSEMI_PTSCR 0x000c +#define NATSEMI_PTSCR_EELOAD_EN 0x00000004UL /**< Enable EEPROM load */ + +/** Maximum time to wait for a configuration reload, in milliseconds */ +#define NATSEMI_EELOAD_MAX_WAIT_MS 100 + +/** Interrupt Status Register */ +#define NATSEMI_ISR 0x0010 +#define NATSEMI_IRQ_TXDESC 0x00000080UL /**< TX descriptor */ +#define NATSEMI_IRQ_RXDESC 0x00000002UL /**< RX descriptor */ + +/** Interrupt Mask Register */ +#define NATSEMI_IMR 0x0014 + +/** Interrupt Enable Register */ +#define NATSEMI_IER 0x0018 +#define NATSEMI_IER_IE 0x00000001UL /**< Interrupt enable */ + +/** Transmit Descriptor Pointer */ +#define NATSEMI_TXDP 0x0020 + +/** Transmit Descriptor Pointer High Dword (64-bit) */ +#define NATSEMI_TXDP_HI_64 0x0024 + +/** Number of transmit descriptors */ +#define NATSEMI_NUM_TX_DESC 4 + +/** Transmit configuration register (32-bit) */ +#define NATSEMI_TXCFG_32 0x24 + +/** Transmit configuration register (64-bit) */ +#define NATSEMI_TXCFG_64 0x28 +#define NATSEMI_TXCFG_CSI 0x80000000UL /**< Carrier sense ignore */ +#define NATSEMI_TXCFG_HBI 0x40000000UL /**< Heartbeat ignore */ +#define NATSEMI_TXCFG_ATP 0x10000000UL /**< Automatic padding */ +#define NATSEMI_TXCFG_ECRETRY 0x00800000UL /**< Excess collision retry */ +#define NATSEMI_TXCFG_MXDMA(x) ( (x) << 20 ) /**< Max DMA burst size */ +#define NATSEMI_TXCFG_FLTH(x) ( (x) << 8 ) /**< Fill threshold */ +#define NATSEMI_TXCFG_DRTH(x) ( (x) << 0 ) /**< Drain threshold */ + +/** Max DMA burst size (encoded value) + * + * This represents 256-byte bursts on 83815 controllers and 512-byte + * bursts on 83820 controllers. + */ +#define NATSEMI_TXCFG_MXDMA_DEFAULT NATSEMI_TXCFG_MXDMA ( 0x7 ) + +/** Fill threshold (in units of 32 bytes) + * + * Must be at least as large as the max DMA burst size, so use a value + * of 512 bytes. + */ +#define NATSEMI_TXCFG_FLTH_DEFAULT NATSEMI_TXCFG_FLTH ( 512 / 32 ) + +/** Drain threshold (in units of 32 bytes) + * + * Start transmission once we receive a conservative 1024 bytes, to + * avoid FIFO underrun errors. (83815 does not allow us to specify a + * value of 0 for "wait until whole packet is present".) + * + * Fill threshold plus drain threshold must be less than the transmit + * FIFO size, which is 2kB on 83815 and 8kB on 83820. + */ +#define NATSEMI_TXCFG_DRTH_DEFAULT NATSEMI_TXCFG_DRTH ( 1024 / 32 ) + +/** Receive Descriptor Pointer */ +#define NATSEMI_RXDP 0x0030 + +/** Receive Descriptor Pointer High Dword (64-bit) */ +#define NATSEMI_RXDP_HI_64 0x0034 + +/** Number of receive descriptors */ +#define NATSEMI_NUM_RX_DESC 4 + +/** Receive buffer length */ +#define NATSEMI_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) + +/** Receive configuration register (32-bit) */ +#define NATSEMI_RXCFG_32 0x34 + +/** Receive configuration register (64-bit) */ +#define NATSEMI_RXCFG_64 0x38 +#define NATSEMI_RXCFG_ARP 0x40000000UL /**< Accept runt packets */ +#define NATSEMI_RXCFG_ATX 0x10000000UL /**< Accept transmit packets */ +#define NATSEMI_RXCFG_ALP 0x08000000UL /**< Accept long packets */ +#define NATSEMI_RXCFG_MXDMA(x) ( (x) << 20 ) /**< Max DMA burst size */ +#define NATSEMI_RXCFG_DRTH(x) ( (x) << 1 ) /**< Drain threshold */ + +/** Max DMA burst size (encoded value) + * + * This represents 256-byte bursts on 83815 controllers and 512-byte + * bursts on 83820 controllers. + */ +#define NATSEMI_RXCFG_MXDMA_DEFAULT NATSEMI_RXCFG_MXDMA ( 0x7 ) + +/** Drain threshold (in units of 8 bytes) + * + * Start draining after 64 bytes. + * + * Must be large enough to allow packet's accept/reject status to be + * determined before draining begins. + */ +#define NATSEMI_RXCFG_DRTH_DEFAULT NATSEMI_RXCFG_DRTH ( 64 / 8 ) + +/** Receive Filter/Match Control Register */ +#define NATSEMI_RFCR 0x0048 +#define NATSEMI_RFCR_RFEN 0x80000000UL /**< RX filter enable */ +#define NATSEMI_RFCR_AAB 0x40000000UL /**< Accept all broadcast */ +#define NATSEMI_RFCR_AAM 0x20000000UL /**< Accept all multicast */ +#define NATSEMI_RFCR_AAU 0x10000000UL /**< Accept all unicast */ +#define NATSEMI_RFCR_RFADDR( addr ) ( (addr) << 0 ) /**< Extended address */ +#define NATSEMI_RFCR_RFADDR_MASK NATSEMI_RFCR_RFADDR ( 0x3ff ) + +/** Perfect match filter address base */ +#define NATSEMI_RFADDR_PMATCH_BASE 0x000 + +/** Receive Filter/Match Data Register */ +#define NATSEMI_RFDR 0x004c +#define NATSEMI_RFDR_BMASK 0x00030000UL /**< Byte mask */ +#define NATSEMI_RFDR_DATA( value ) ( (value) & 0xffff ) /**< Filter data */ + +/** National Semiconductor network card flags */ +enum natsemi_nic_flags { + /** EEPROM is little-endian */ + NATSEMI_EEPROM_LITTLE_ENDIAN = 0x0001, + /** EEPROM layout is insane */ + NATSEMI_EEPROM_INSANE = 0x0002, + /** Card supports 64-bit operation */ + NATSEMI_64BIT = 0x0004, + /** Card supports 1000Mbps link */ + NATSEMI_1000 = 0x0008, +}; + +/** A National Semiconductor descriptor ring */ +struct natsemi_ring { + /** Descriptors */ + union natsemi_descriptor *desc; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Descriptor start address register */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v reg Descriptor start address register + */ +static inline __attribute__ (( always_inline)) void +natsemi_init_ring ( struct natsemi_ring *ring, unsigned int count, + unsigned int reg ) { + ring->count = count; + ring->reg = reg; +} + +/** A National Semiconductor network card */ +struct natsemi_nic { + /** Flags */ + unsigned int flags; + /** Registers */ + void *regs; + /** SPI bit-bashing interface */ struct spi_bit_basher spibit; + /** EEPROM */ struct spi_device eeprom; - struct nvo_block nvo; + + /** Transmit descriptor ring */ + struct natsemi_ring tx; + /** Receive descriptor ring */ + struct natsemi_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[NATSEMI_NUM_RX_DESC]; + + /** Link status (cache) */ + uint32_t cfg; }; -/* - * Support for fibre connections on Am79C874: - * This phy needs a special setup when connected to a fibre cable. - * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf +/** + * Check if card can access physical address + * + * @v natsemi National Semiconductor device + * @v address Physical address + * @v address_ok Card can access physical address */ -#define PHYID_AM79C874 0x0022561b +static inline __attribute__ (( always_inline )) int +natsemi_address_ok ( struct natsemi_nic *natsemi, physaddr_t address ) { -enum { - MII_MCTRL = 0x15, /* mode control register */ - MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */ - MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */ -}; + /* In a 32-bit build, all addresses can be accessed */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 1; + /* A 64-bit card can access all addresses */ + if ( natsemi->flags & NATSEMI_64BIT ) + return 1; + /* A 32-bit card can access all address below 4GB */ + if ( ( address & 0xffffffffUL ) == 0 ) + return 1; -/* values we might find in the silicon revision register */ -#define SRR_DP83815_C 0x0302 -#define SRR_DP83815_D 0x0403 -#define SRR_DP83816_A4 0x0504 -#define SRR_DP83816_A5 0x0505 - -/* NATSEMI: Offsets to the device registers. - * Unlike software-only systems, device drivers interact with complex hardware. - * It's not useful to define symbolic names for every register bit in the - * device. - */ -enum register_offsets { - ChipCmd = 0x00, - ChipConfig = 0x04, - EECtrl = 0x08, - PCIBusCfg = 0x0C, - IntrStatus = 0x10, - IntrMask = 0x14, - IntrEnable = 0x18, - TxRingPtr = 0x20, - TxConfig = 0x24, - RxRingPtr = 0x30, - RxConfig = 0x34, - ClkRun = 0x3C, - WOLCmd = 0x40, - PauseCmd = 0x44, - RxFilterAddr = 0x48, - RxFilterData = 0x4C, - BootRomAddr = 0x50, - BootRomData = 0x54, - SiliconRev = 0x58, - StatsCtrl = 0x5C, - StatsData = 0x60, - RxPktErrs = 0x60, - RxMissed = 0x68, - RxCRCErrs = 0x64, - PCIPM = 0x44, - PhyStatus = 0xC0, - MIntrCtrl = 0xC4, - MIntrStatus = 0xC8, - - /* These are from the spec, around page 78... on a separate table. - */ - PGSEL = 0xCC, - PMDCSR = 0xE4, - TSTDAT = 0xFC, - DSPCFG = 0xF4, - SDCFG = 0x8C, - BasicControl = 0x80, - BasicStatus = 0x84 - -}; - -/* the values for the 'magic' registers above (PGSEL=1) */ -#define PMDCSR_VAL 0x189c /* enable preferred adaptation circuitry */ -#define TSTDAT_VAL 0x0 -#define DSPCFG_VAL 0x5040 -#define SDCFG_VAL 0x008c /* set voltage thresholds for Signal Detect */ -#define DSPCFG_LOCK 0x20 /* coefficient lock bit in DSPCFG */ -#define DSPCFG_COEF 0x1000 /* see coefficient (in TSTDAT) bit in DSPCFG */ -#define TSTDAT_FIXED 0xe8 /* magic number for bad coefficients */ - -/* Bit in ChipCmd. - */ -enum ChipCmdBits { - ChipReset = 0x100, - RxReset = 0x20, - TxReset = 0x10, - RxOff = 0x08, - RxOn = 0x04, - TxOff = 0x02, - TxOn = 0x01 -}; - -enum ChipConfig_bits { - CfgPhyDis = 0x200, - CfgPhyRst = 0x400, - CfgExtPhy = 0x1000, - CfgAnegEnable = 0x2000, - CfgAneg100 = 0x4000, - CfgAnegFull = 0x8000, - CfgAnegDone = 0x8000000, - CfgFullDuplex = 0x20000000, - CfgSpeed100 = 0x40000000, - CfgLink = 0x80000000, -}; - - -/* Bits in the RxMode register. - */ -enum rx_mode_bits { - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0xC0000000, - AcceptMulticast = 0x00200000, - AcceptAllMulticast = 0x20000000, - AcceptAllPhys = 0x10000000, - AcceptMyPhys = 0x08000000, - RxFilterEnable = 0x80000000 -}; - -/* Bits in network_desc.status - */ -enum desc_status_bits { - DescOwn = 0x80000000, - DescMore = 0x40000000, - DescIntr = 0x20000000, - DescNoCRC = 0x10000000, - DescPktOK = 0x08000000, - RxTooLong = 0x00400000 -}; - -/*Bits in Interrupt Mask register - */ -enum Intr_mask_register_bits { - RxOk = 0x001, - RxErr = 0x004, - TxOk = 0x040, - TxErr = 0x100 -}; - -enum MIntrCtrl_bits { - MICRIntEn = 0x2, -}; - -/* CFG bits [13:16] [18:23] */ -#define CFG_RESET_SAVE 0xfde000 -/* WCSR bits [0:4] [9:10] */ -#define WCSR_RESET_SAVE 0x61f -/* RFCR bits [20] [22] [27:31] */ -#define RFCR_RESET_SAVE 0xf8500000; - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need - a delay. */ -#define eeprom_delay(ee_addr) inl(ee_addr) - -enum EEPROM_Ctrl_Bits { - EE_ShiftClk = 0x04, - EE_DataIn = 0x01, - EE_ChipSelect = 0x08, - EE_DataOut = 0x02 -}; - -#define EE_Write0 (EE_ChipSelect) -#define EE_Write1 (EE_ChipSelect | EE_DataIn) - -/* The EEPROM commands include the alway-set leading bit. */ -enum EEPROM_Cmds { - EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), -}; - -/* EEPROM access , values are devices specific - */ -#define EE_CS 0x08 /* EEPROM chip select */ -#define EE_SK 0x04 /* EEPROM shift clock */ -#define EE_DI 0x01 /* Data in */ -#define EE_DO 0x02 /* Data out */ - -/* Offsets within EEPROM (these are word offsets) - */ -#define EE_MAC 7 -#define EE_REG EECtrl - -static const uint8_t natsemi_ee_bits[] = { - [SPI_BIT_SCLK] = EE_SK, - [SPI_BIT_MOSI] = EE_DI, - [SPI_BIT_MISO] = EE_DO, - [SPI_BIT_SS(0)] = EE_CS, -}; + return 0; +} +#endif /* _NATSEMI_H */ diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c deleted file mode 100644 index 0b92a91b..00000000 --- a/src/drivers/net/ns83820.c +++ /dev/null @@ -1,1007 +0,0 @@ -/************************************************************************** -* ns83820.c: Etherboot device driver for the National Semiconductor 83820 -* Written 2004 by Timothy Legge -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Portions of this code based on: -* ns83820.c by Benjamin LaHaise with contributions -* for Linux kernel 2.4.x. -* -* Linux Driver Version 0.20, 20020610 -* -* This development of this Etherboot driver was funded by: -* -* NXTV: http://www.nxtv.com/ -* -* REVISION HISTORY: -* ================ -* -* v1.0 02-16-2004 timlegge Initial port of Linux driver -* v1.1 02-19-2004 timlegge More rohbust transmit and poll -* -* Indent Options: indent -kr -i8 -***************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/* to get some global routines like printf */ -#include "etherboot.h" -/* to get the interface to the body of the program */ -#include "nic.h" -/* to get the PCI support functions, if this is a PCI NIC */ -#include - -#if ARCH == ia64 /* Support 64-bit addressing */ -#define USE_64BIT_ADDR -#endif - -#define HZ 100 - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) - -/* NIC specific static variables go here */ - -/* Global parameters. See MODULE_PARM near the bottom. */ -// static int ihr = 2; -static int reset_phy = 0; -static int lnksts = 0; /* CFG_LNKSTS bit polarity */ - -#if defined(CONFIG_HIGHMEM64G) || defined(__ia64__) -#define USE_64BIT_ADDR "+" -#endif - -#if defined(USE_64BIT_ADDR) -#define TRY_DAC 1 -#else -#define TRY_DAC 0 -#endif - -/* tunables */ -#define RX_BUF_SIZE 1500 /* 8192 */ - -/* Must not exceed ~65000. */ -#define NR_RX_DESC 64 -#define NR_TX_DESC 1 - - /* not tunable *//* Extra 6 bytes for 64 bit alignment (divisable by 8) */ -#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14 + 6) /* rx/tx mac addr + type */ - -#define MIN_TX_DESC_FREE 8 - -/* register defines */ -#define CFGCS 0x04 - -#define CR_TXE 0x00000001 -#define CR_TXD 0x00000002 -/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE - * The Receive engine skips one descriptor and moves - * onto the next one!! */ -#define CR_RXE 0x00000004 -#define CR_RXD 0x00000008 -#define CR_TXR 0x00000010 -#define CR_RXR 0x00000020 -#define CR_SWI 0x00000080 -#define CR_RST 0x00000100 - -#define PTSCR_EEBIST_FAIL 0x00000001 -#define PTSCR_EEBIST_EN 0x00000002 -#define PTSCR_EELOAD_EN 0x00000004 -#define PTSCR_RBIST_FAIL 0x000001b8 -#define PTSCR_RBIST_DONE 0x00000200 -#define PTSCR_RBIST_EN 0x00000400 -#define PTSCR_RBIST_RST 0x00002000 - -#define MEAR_EEDI 0x00000001 -#define MEAR_EEDO 0x00000002 -#define MEAR_EECLK 0x00000004 -#define MEAR_EESEL 0x00000008 -#define MEAR_MDIO 0x00000010 -#define MEAR_MDDIR 0x00000020 -#define MEAR_MDC 0x00000040 - -#define ISR_TXDESC3 0x40000000 -#define ISR_TXDESC2 0x20000000 -#define ISR_TXDESC1 0x10000000 -#define ISR_TXDESC0 0x08000000 -#define ISR_RXDESC3 0x04000000 -#define ISR_RXDESC2 0x02000000 -#define ISR_RXDESC1 0x01000000 -#define ISR_RXDESC0 0x00800000 -#define ISR_TXRCMP 0x00400000 -#define ISR_RXRCMP 0x00200000 -#define ISR_DPERR 0x00100000 -#define ISR_SSERR 0x00080000 -#define ISR_RMABT 0x00040000 -#define ISR_RTABT 0x00020000 -#define ISR_RXSOVR 0x00010000 -#define ISR_HIBINT 0x00008000 -#define ISR_PHY 0x00004000 -#define ISR_PME 0x00002000 -#define ISR_SWI 0x00001000 -#define ISR_MIB 0x00000800 -#define ISR_TXURN 0x00000400 -#define ISR_TXIDLE 0x00000200 -#define ISR_TXERR 0x00000100 -#define ISR_TXDESC 0x00000080 -#define ISR_TXOK 0x00000040 -#define ISR_RXORN 0x00000020 -#define ISR_RXIDLE 0x00000010 -#define ISR_RXEARLY 0x00000008 -#define ISR_RXERR 0x00000004 -#define ISR_RXDESC 0x00000002 -#define ISR_RXOK 0x00000001 - -#define TXCFG_CSI 0x80000000 -#define TXCFG_HBI 0x40000000 -#define TXCFG_MLB 0x20000000 -#define TXCFG_ATP 0x10000000 -#define TXCFG_ECRETRY 0x00800000 -#define TXCFG_BRST_DIS 0x00080000 -#define TXCFG_MXDMA1024 0x00000000 -#define TXCFG_MXDMA512 0x00700000 -#define TXCFG_MXDMA256 0x00600000 -#define TXCFG_MXDMA128 0x00500000 -#define TXCFG_MXDMA64 0x00400000 -#define TXCFG_MXDMA32 0x00300000 -#define TXCFG_MXDMA16 0x00200000 -#define TXCFG_MXDMA8 0x00100000 - -#define CFG_LNKSTS 0x80000000 -#define CFG_SPDSTS 0x60000000 -#define CFG_SPDSTS1 0x40000000 -#define CFG_SPDSTS0 0x20000000 -#define CFG_DUPSTS 0x10000000 -#define CFG_TBI_EN 0x01000000 -#define CFG_MODE_1000 0x00400000 -/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy. - * Read the Phy response and then configure the MAC accordingly */ -#define CFG_AUTO_1000 0x00200000 -#define CFG_PINT_CTL 0x001c0000 -#define CFG_PINT_DUPSTS 0x00100000 -#define CFG_PINT_LNKSTS 0x00080000 -#define CFG_PINT_SPDSTS 0x00040000 -#define CFG_TMRTEST 0x00020000 -#define CFG_MRM_DIS 0x00010000 -#define CFG_MWI_DIS 0x00008000 -#define CFG_T64ADDR 0x00004000 -#define CFG_PCI64_DET 0x00002000 -#define CFG_DATA64_EN 0x00001000 -#define CFG_M64ADDR 0x00000800 -#define CFG_PHY_RST 0x00000400 -#define CFG_PHY_DIS 0x00000200 -#define CFG_EXTSTS_EN 0x00000100 -#define CFG_REQALG 0x00000080 -#define CFG_SB 0x00000040 -#define CFG_POW 0x00000020 -#define CFG_EXD 0x00000010 -#define CFG_PESEL 0x00000008 -#define CFG_BROM_DIS 0x00000004 -#define CFG_EXT_125 0x00000002 -#define CFG_BEM 0x00000001 - -#define EXTSTS_UDPPKT 0x00200000 -#define EXTSTS_TCPPKT 0x00080000 -#define EXTSTS_IPPKT 0x00020000 - -#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0)) - -#define MIBC_MIBS 0x00000008 -#define MIBC_ACLR 0x00000004 -#define MIBC_FRZ 0x00000002 -#define MIBC_WRN 0x00000001 - -#define PCR_PSEN (1 << 31) -#define PCR_PS_MCAST (1 << 30) -#define PCR_PS_DA (1 << 29) -#define PCR_STHI_8 (3 << 23) -#define PCR_STLO_4 (1 << 23) -#define PCR_FFHI_8K (3 << 21) -#define PCR_FFLO_4K (1 << 21) -#define PCR_PAUSE_CNT 0xFFFE - -#define RXCFG_AEP 0x80000000 -#define RXCFG_ARP 0x40000000 -#define RXCFG_STRIPCRC 0x20000000 -#define RXCFG_RX_FD 0x10000000 -#define RXCFG_ALP 0x08000000 -#define RXCFG_AIRL 0x04000000 -#define RXCFG_MXDMA512 0x00700000 -#define RXCFG_DRTH 0x0000003e -#define RXCFG_DRTH0 0x00000002 - -#define RFCR_RFEN 0x80000000 -#define RFCR_AAB 0x40000000 -#define RFCR_AAM 0x20000000 -#define RFCR_AAU 0x10000000 -#define RFCR_APM 0x08000000 -#define RFCR_APAT 0x07800000 -#define RFCR_APAT3 0x04000000 -#define RFCR_APAT2 0x02000000 -#define RFCR_APAT1 0x01000000 -#define RFCR_APAT0 0x00800000 -#define RFCR_AARP 0x00400000 -#define RFCR_MHEN 0x00200000 -#define RFCR_UHEN 0x00100000 -#define RFCR_ULM 0x00080000 - -#define VRCR_RUDPE 0x00000080 -#define VRCR_RTCPE 0x00000040 -#define VRCR_RIPE 0x00000020 -#define VRCR_IPEN 0x00000010 -#define VRCR_DUTF 0x00000008 -#define VRCR_DVTF 0x00000004 -#define VRCR_VTREN 0x00000002 -#define VRCR_VTDEN 0x00000001 - -#define VTCR_PPCHK 0x00000008 -#define VTCR_GCHK 0x00000004 -#define VTCR_VPPTI 0x00000002 -#define VTCR_VGTI 0x00000001 - -#define CR 0x00 -#define CFG 0x04 -#define MEAR 0x08 -#define PTSCR 0x0c -#define ISR 0x10 -#define IMR 0x14 -#define IER 0x18 -#define IHR 0x1c -#define TXDP 0x20 -#define TXDP_HI 0x24 -#define TXCFG 0x28 -#define GPIOR 0x2c -#define RXDP 0x30 -#define RXDP_HI 0x34 -#define RXCFG 0x38 -#define PQCR 0x3c -#define WCSR 0x40 -#define PCR 0x44 -#define RFCR 0x48 -#define RFDR 0x4c - -#define SRR 0x58 - -#define VRCR 0xbc -#define VTCR 0xc0 -#define VDR 0xc4 -#define CCSR 0xcc - -#define TBICR 0xe0 -#define TBISR 0xe4 -#define TANAR 0xe8 -#define TANLPAR 0xec -#define TANER 0xf0 -#define TESR 0xf4 - -#define TBICR_MR_AN_ENABLE 0x00001000 -#define TBICR_MR_RESTART_AN 0x00000200 - -#define TBISR_MR_LINK_STATUS 0x00000020 -#define TBISR_MR_AN_COMPLETE 0x00000004 - -#define TANAR_PS2 0x00000100 -#define TANAR_PS1 0x00000080 -#define TANAR_HALF_DUP 0x00000040 -#define TANAR_FULL_DUP 0x00000020 - -#define GPIOR_GP5_OE 0x00000200 -#define GPIOR_GP4_OE 0x00000100 -#define GPIOR_GP3_OE 0x00000080 -#define GPIOR_GP2_OE 0x00000040 -#define GPIOR_GP1_OE 0x00000020 -#define GPIOR_GP3_OUT 0x00000004 -#define GPIOR_GP1_OUT 0x00000001 - -#define LINK_AUTONEGOTIATE 0x01 -#define LINK_DOWN 0x02 -#define LINK_UP 0x04 - - -#define __kick_rx() writel(CR_RXE, ns->base + CR) - -#define kick_rx() do { \ - DBG("kick_rx: maybe kicking\n"); \ - writel(virt_to_le32desc(&rx_ring[ns->cur_rx]), ns->base + RXDP); \ - if (ns->next_rx == ns->next_empty) \ - printf("uh-oh: next_rx == next_empty???\n"); \ - __kick_rx(); \ -} while(0) - - -#ifdef USE_64BIT_ADDR -#define HW_ADDR_LEN 8 -#else -#define HW_ADDR_LEN 4 -#endif - -#define CMDSTS_OWN 0x80000000 -#define CMDSTS_MORE 0x40000000 -#define CMDSTS_INTR 0x20000000 -#define CMDSTS_ERR 0x10000000 -#define CMDSTS_OK 0x08000000 -#define CMDSTS_LEN_MASK 0x0000ffff - -#define CMDSTS_DEST_MASK 0x01800000 -#define CMDSTS_DEST_SELF 0x00800000 -#define CMDSTS_DEST_MULTI 0x01000000 - -#define DESC_SIZE 8 /* Should be cache line sized */ - -#ifdef USE_64BIT_ADDR -struct ring_desc { - uint64_t link; - uint64_t bufptr; - u32 cmdsts; - u32 extsts; /* Extended status field */ -}; -#else -struct ring_desc { - u32 link; - u32 bufptr; - u32 cmdsts; - u32 extsts; /* Extended status field */ -}; -#endif - -/* Private Storage for the NIC */ -static struct ns83820_private { - u8 *base; - int up; - long idle; - u32 *next_rx_desc; - u16 next_rx, next_empty; - u32 cur_rx; - u32 *descs; - unsigned ihr; - u32 CFG_cache; - u32 MEAR_cache; - u32 IMR_cache; - int linkstate; - u16 tx_done_idx; - u16 tx_idx; - u16 tx_intr_idx; - u32 phy_descs; - u32 *tx_descs; - -} nsx; -static struct ns83820_private *ns; - -/* Define the TX and RX Descriptor and Buffers */ -struct { - struct ring_desc tx_ring[NR_TX_DESC] __attribute__ ((aligned(8))); - unsigned char txb[NR_TX_DESC * REAL_RX_BUF_SIZE]; - struct ring_desc rx_ring[NR_RX_DESC] __attribute__ ((aligned(8))); - unsigned char rxb[NR_RX_DESC * REAL_RX_BUF_SIZE] - __attribute__ ((aligned(8))); -} ns83820_bufs __shared; -#define tx_ring ns83820_bufs.tx_ring -#define rx_ring ns83820_bufs.rx_ring -#define txb ns83820_bufs.txb -#define rxb ns83820_bufs.rxb - -static void phy_intr(struct nic *nic __unused) -{ - static char *speeds[] = - { "10", "100", "1000", "1000(?)", "1000F" }; - u32 cfg, new_cfg; - u32 tbisr, tanar, tanlpar; - int speed, fullduplex, newlinkstate; - - cfg = readl(ns->base + CFG) ^ SPDSTS_POLARITY; - if (ns->CFG_cache & CFG_TBI_EN) { - /* we have an optical transceiver */ - tbisr = readl(ns->base + TBISR); - tanar = readl(ns->base + TANAR); - tanlpar = readl(ns->base + TANLPAR); - DBG("phy_intr: tbisr=%hX, tanar=%hX, tanlpar=%hX\n", - tbisr, tanar, tanlpar); - - if ((fullduplex = (tanlpar & TANAR_FULL_DUP) - && (tanar & TANAR_FULL_DUP))) { - - /* both of us are full duplex */ - writel(readl(ns->base + TXCFG) - | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, - ns->base + TXCFG); - writel(readl(ns->base + RXCFG) | RXCFG_RX_FD, - ns->base + RXCFG); - /* Light up full duplex LED */ - writel(readl(ns->base + GPIOR) | GPIOR_GP1_OUT, - ns->base + GPIOR); - - } else if (((tanlpar & TANAR_HALF_DUP) - && (tanar & TANAR_HALF_DUP)) - || ((tanlpar & TANAR_FULL_DUP) - && (tanar & TANAR_HALF_DUP)) - || ((tanlpar & TANAR_HALF_DUP) - && (tanar & TANAR_FULL_DUP))) { - - /* one or both of us are half duplex */ - writel((readl(ns->base + TXCFG) - & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP, - ns->base + TXCFG); - writel(readl(ns->base + RXCFG) & ~RXCFG_RX_FD, - ns->base + RXCFG); - /* Turn off full duplex LED */ - writel(readl(ns->base + GPIOR) & ~GPIOR_GP1_OUT, - ns->base + GPIOR); - } - - speed = 4; /* 1000F */ - - } else { - /* we have a copper transceiver */ - new_cfg = - ns->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); - - if (cfg & CFG_SPDSTS1) - new_cfg |= CFG_MODE_1000; - else - new_cfg &= ~CFG_MODE_1000; - - speed = ((cfg / CFG_SPDSTS0) & 3); - fullduplex = (cfg & CFG_DUPSTS); - - if (fullduplex) - new_cfg |= CFG_SB; - - if ((cfg & CFG_LNKSTS) && - ((new_cfg ^ ns->CFG_cache) & CFG_MODE_1000)) { - writel(new_cfg, ns->base + CFG); - ns->CFG_cache = new_cfg; - } - - ns->CFG_cache &= ~CFG_SPDSTS; - ns->CFG_cache |= cfg & CFG_SPDSTS; - } - - newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; - - if (newlinkstate & LINK_UP && ns->linkstate != newlinkstate) { - printf("link now %s mbps, %s duplex and up.\n", - speeds[speed], fullduplex ? "full" : "half"); - } else if (newlinkstate & LINK_DOWN - && ns->linkstate != newlinkstate) { - printf("link now down.\n"); - } - ns->linkstate = newlinkstate; -} -static void ns83820_set_multicast(struct nic *nic __unused); -static void ns83820_setup_rx(struct nic *nic) -{ - unsigned i; - ns->idle = 1; - ns->next_rx = 0; - ns->next_rx_desc = ns->descs; - ns->next_empty = 0; - ns->cur_rx = 0; - - - for (i = 0; i < NR_RX_DESC; i++) { - rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]); - rx_ring[i].bufptr = - virt_to_le32desc(&rxb[i * REAL_RX_BUF_SIZE]); - rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE); - rx_ring[i].extsts = cpu_to_le32(0); - } -// No need to wrap the ring -// rx_ring[i].link = virt_to_le32desc(&rx_ring[0]); - writel(0, ns->base + RXDP_HI); - writel(virt_to_le32desc(&rx_ring[0]), ns->base + RXDP); - - DBG("starting receiver\n"); - - writel(0x0001, ns->base + CCSR); - writel(0, ns->base + RFCR); - writel(0x7fc00000, ns->base + RFCR); - writel(0xffc00000, ns->base + RFCR); - - ns->up = 1; - - phy_intr(nic); - - /* Okay, let it rip */ - ns->IMR_cache |= ISR_PHY; - ns->IMR_cache |= ISR_RXRCMP; - //dev->IMR_cache |= ISR_RXERR; - //dev->IMR_cache |= ISR_RXOK; - ns->IMR_cache |= ISR_RXORN; - ns->IMR_cache |= ISR_RXSOVR; - ns->IMR_cache |= ISR_RXDESC; - ns->IMR_cache |= ISR_RXIDLE; - ns->IMR_cache |= ISR_TXDESC; - ns->IMR_cache |= ISR_TXIDLE; - - // No reason to enable interupts... - // writel(ns->IMR_cache, ns->base + IMR); - // writel(1, ns->base + IER); - ns83820_set_multicast(nic); - kick_rx(); -} - - -static void ns83820_do_reset(struct nic *nic __unused, u32 which) -{ - DBG("resetting chip...\n"); - writel(which, ns->base + CR); - do { - - } while (readl(ns->base + CR) & which); - DBG("okay!\n"); -} - -static void ns83820_reset(struct nic *nic) -{ - unsigned i; - DBG("ns83820_reset\n"); - - writel(0, ns->base + PQCR); - - ns83820_setup_rx(nic); - - for (i = 0; i < NR_TX_DESC; i++) { - tx_ring[i].link = 0; - tx_ring[i].bufptr = 0; - tx_ring[i].cmdsts = cpu_to_le32(0); - tx_ring[i].extsts = cpu_to_le32(0); - } - - ns->tx_idx = 0; - ns->tx_done_idx = 0; - writel(0, ns->base + TXDP_HI); - return; -} -static void ns83820_getmac(struct nic *nic __unused, u8 * mac) -{ - unsigned i; - for (i = 0; i < 3; i++) { - u32 data; - /* Read from the perfect match memory: this is loaded by - * the chip from the EEPROM via the EELOAD self test. - */ - writel(i * 2, ns->base + RFCR); - data = readl(ns->base + RFDR); - *mac++ = data; - *mac++ = data >> 8; - } -} - -static void ns83820_set_multicast(struct nic *nic __unused) -{ - u8 *rfcr = ns->base + RFCR; - u32 and_mask = 0xffffffff; - u32 or_mask = 0; - u32 val; - - /* Support Multicast */ - and_mask &= ~(RFCR_AAU | RFCR_AAM); - or_mask |= RFCR_AAM; - val = (readl(rfcr) & and_mask) | or_mask; - /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */ - writel(val & ~RFCR_RFEN, rfcr); - writel(val, rfcr); - -} -static void ns83820_run_bist(struct nic *nic __unused, const char *name, - u32 enable, u32 done, u32 fail) -{ - int timed_out = 0; - long start; - u32 status; - int loops = 0; - - DBG("start %s\n", name); - - start = currticks(); - - writel(enable, ns->base + PTSCR); - for (;;) { - loops++; - status = readl(ns->base + PTSCR); - if (!(status & enable)) - break; - if (status & done) - break; - if (status & fail) - break; - if ((currticks() - start) >= HZ) { - timed_out = 1; - break; - } - } - - if (status & fail) - printf("%s failed! (0x%hX & 0x%hX)\n", name, (unsigned int) status, - (unsigned int) fail); - else if (timed_out) - printf("run_bist %s timed out! (%hX)\n", name, (unsigned int) status); - DBG("done %s in %d loops\n", name, loops); -} - -/************************************* -Check Link -*************************************/ -static void ns83820_check_intr(struct nic *nic) { - int i; - u32 isr = readl(ns->base + ISR); - if(ISR_PHY & isr) - phy_intr(nic); - if(( ISR_RXIDLE | ISR_RXDESC | ISR_RXERR) & isr) - kick_rx(); - for (i = 0; i < NR_RX_DESC; i++) { - if (rx_ring[i].cmdsts == CMDSTS_OWN) { -// rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]); - rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE); - } - } -} -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int ns83820_poll(struct nic *nic, int retrieve) -{ - /* return true if there's an ethernet packet ready to read */ - /* nic->packet should contain data on return */ - /* nic->packetlen should contain length of data */ - u32 cmdsts; - int entry = ns->cur_rx; - - ns83820_check_intr(nic); - - cmdsts = le32_to_cpu(rx_ring[entry].cmdsts); - - if ( ! ( (CMDSTS_OWN & (cmdsts)) && (cmdsts != (CMDSTS_OWN)) ) ) - return 0; - - if ( ! retrieve ) return 1; - - if (! (CMDSTS_OK & cmdsts) ) - return 0; - - nic->packetlen = cmdsts & 0xffff; - memcpy(nic->packet, - rxb + (entry * REAL_RX_BUF_SIZE), - nic->packetlen); - // rx_ring[entry].link = 0; - rx_ring[entry].cmdsts = cpu_to_le32(CMDSTS_OWN); - - ns->cur_rx = (ns->cur_rx + 1) % NR_RX_DESC; - - if (ns->cur_rx == 0) /* We have wrapped the ring */ - kick_rx(); - - return 1; -} - -static inline void kick_tx(struct nic *nic __unused) -{ - DBG("kick_tx\n"); - writel(CR_TXE, ns->base + CR); -} - -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void ns83820_transmit(struct nic *nic, const char *d, /* Destination */ - unsigned int t, /* Type */ - unsigned int s, /* size */ - const char *p) -{ /* Packet */ - /* send the packet to destination */ - - u16 nstype; - u32 cmdsts, extsts; - int cur_tx = 0; - u32 isr = readl(ns->base + ISR); - if (ISR_TXIDLE & isr) - kick_tx(nic); - /* point to the current txb incase multiple tx_rings are used */ - memcpy(txb, d, ETH_ALEN); - memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN); - nstype = htons((u16) t); - memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2); - memcpy(txb + ETH_HLEN, p, s); - s += ETH_HLEN; - s &= 0x0FFF; - while (s < ETH_ZLEN) - txb[s++] = '\0'; - - /* Setup the transmit descriptor */ - extsts = 0; - extsts |= EXTSTS_UDPPKT; - - tx_ring[cur_tx].bufptr = virt_to_le32desc(&txb); - tx_ring[cur_tx].extsts = cpu_to_le32(extsts); - - cmdsts = cpu_to_le32(0); - cmdsts |= cpu_to_le32(CMDSTS_OWN | s); - tx_ring[cur_tx].cmdsts = cpu_to_le32(cmdsts); - - writel(virt_to_le32desc(&tx_ring[0]), ns->base + TXDP); - kick_tx(nic); -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void ns83820_disable ( struct nic *nic ) { - - /* put the card in its initial state */ - /* This function serves 3 purposes. - * This disables DMA and interrupts so we don't receive - * unexpected packets or interrupts from the card after - * etherboot has finished. - * This frees resources so etherboot may use - * this driver on another interface - * This allows etherboot to reinitialize the interface - * if something is something goes wrong. - */ - /* disable interrupts */ - writel(0, ns->base + IMR); - writel(0, ns->base + IER); - readl(ns->base + IER); - - ns->up = 0; - - ns83820_do_reset(nic, CR_RST); - - ns->IMR_cache &= - ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | - ISR_RXIDLE); - writel(ns->IMR_cache, ns->base + IMR); - - /* touch the pci bus... */ - readl(ns->base + IMR); - - /* assumes the transmitter is already disabled and reset */ - writel(0, ns->base + RXDP_HI); - writel(0, ns->base + RXDP); -} - -/************************************************************************** -IRQ - Enable, Disable, or Force interrupts -***************************************************************************/ -static void ns83820_irq(struct nic *nic __unused, irq_action_t action __unused) -{ - switch ( action ) { - case DISABLE : - break; - case ENABLE : - break; - case FORCE : - break; - } -} - -static struct nic_operations ns83820_operations = { - .connect = dummy_connect, - .poll = ns83820_poll, - .transmit = ns83820_transmit, - .irq = ns83820_irq, - -}; - -static struct pci_device_id ns83820_nics[] = { - PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820", 0), -}; - -PCI_DRIVER ( ns83820_driver, ns83820_nics, PCI_NO_CLASS ); - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -***************************************************************************/ - -#define board_found 1 -#define valid_link 0 -static int ns83820_probe ( struct nic *nic, struct pci_device *pci ) { - - long addr; - int using_dac = 0; - - if (pci->ioaddr == 0) - return 0; - - printf("ns83820.c: Found %s, vendor=0x%hX, device=0x%hX\n", - pci->id->name, pci->vendor, pci->device); - - /* point to private storage */ - ns = &nsx; - - adjust_pci_device(pci); - - addr = pci_bar_start(pci, PCI_BASE_ADDRESS_1); - - ns->base = ioremap(addr, (1UL << 12)); - - if (!ns->base) - return 0; - - nic->irqno = 0; - nic->ioaddr = pci->ioaddr & ~3; - - /* disable interrupts */ - writel(0, ns->base + IMR); - writel(0, ns->base + IER); - readl(ns->base + IER); - - ns->IMR_cache = 0; - - ns83820_do_reset(nic, CR_RST); - - /* Must reset the ram bist before running it */ - writel(PTSCR_RBIST_RST, ns->base + PTSCR); - ns83820_run_bist(nic, "sram bist", PTSCR_RBIST_EN, - PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL); - ns83820_run_bist(nic, "eeprom bist", PTSCR_EEBIST_EN, 0, - PTSCR_EEBIST_FAIL); - ns83820_run_bist(nic, "eeprom load", PTSCR_EELOAD_EN, 0, 0); - - /* I love config registers */ - ns->CFG_cache = readl(ns->base + CFG); - - if ((ns->CFG_cache & CFG_PCI64_DET)) { - printf("%s: detected 64 bit PCI data bus.\n", pci->id->name); - /*dev->CFG_cache |= CFG_DATA64_EN; */ - if (!(ns->CFG_cache & CFG_DATA64_EN)) - printf - ("%s: EEPROM did not enable 64 bit bus. Disabled.\n", - pci->id->name); - } else - ns->CFG_cache &= ~(CFG_DATA64_EN); - - ns->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | - CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | - CFG_M64ADDR); - ns->CFG_cache |= - CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS | - CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL; - ns->CFG_cache |= CFG_REQALG; - ns->CFG_cache |= CFG_POW; - ns->CFG_cache |= CFG_TMRTEST; - - /* When compiled with 64 bit addressing, we must always enable - * the 64 bit descriptor format. - */ -#ifdef USE_64BIT_ADDR - ns->CFG_cache |= CFG_M64ADDR; -#endif - -//FIXME: Enable section on dac or remove this - if (using_dac) - ns->CFG_cache |= CFG_T64ADDR; - - /* Big endian mode does not seem to do what the docs suggest */ - ns->CFG_cache &= ~CFG_BEM; - - /* setup optical transceiver if we have one */ - if (ns->CFG_cache & CFG_TBI_EN) { - DBG("%s: enabling optical transceiver\n", pci->id->name); - writel(readl(ns->base + GPIOR) | 0x3e8, ns->base + GPIOR); - - /* setup auto negotiation feature advertisement */ - writel(readl(ns->base + TANAR) - | TANAR_HALF_DUP | TANAR_FULL_DUP, - ns->base + TANAR); - - /* start auto negotiation */ - writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, - ns->base + TBICR); - writel(TBICR_MR_AN_ENABLE, ns->base + TBICR); - ns->linkstate = LINK_AUTONEGOTIATE; - - ns->CFG_cache |= CFG_MODE_1000; - } - writel(ns->CFG_cache, ns->base + CFG); - DBG("CFG: %hX\n", ns->CFG_cache); - - /* FIXME: reset_phy is defaulted to 0, should we reset anyway? */ - if (reset_phy) { - DBG("%s: resetting phy\n", pci->id->name); - writel(ns->CFG_cache | CFG_PHY_RST, ns->base + CFG); - writel(ns->CFG_cache, ns->base + CFG); - } -#if 0 /* Huh? This sets the PCI latency register. Should be done via - * the PCI layer. FIXME. - */ - if (readl(dev->base + SRR)) - writel(readl(dev->base + 0x20c) | 0xfe00, - dev->base + 0x20c); -#endif - - /* Note! The DMA burst size interacts with packet - * transmission, such that the largest packet that - * can be transmitted is 8192 - FLTH - burst size. - * If only the transmit fifo was larger... - */ - /* Ramit : 1024 DMA is not a good idea, it ends up banging - * some DELL and COMPAQ SMP systems */ - writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512 - | ((1600 / 32) * 0x100), ns->base + TXCFG); - - /* Set Rx to full duplex, don't accept runt, errored, long or length - * range errored packets. Use 512 byte DMA. - */ - /* Ramit : 1024 DMA is not a good idea, it ends up banging - * some DELL and COMPAQ SMP systems - * Turn on ALP, only we are accpeting Jumbo Packets */ - writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD - | RXCFG_STRIPCRC - //| RXCFG_ALP - | (RXCFG_MXDMA512) | 0, ns->base + RXCFG); - - /* Disable priority queueing */ - writel(0, ns->base + PQCR); - - /* Enable IP checksum validation and detetion of VLAN headers. - * Note: do not set the reject options as at least the 0x102 - * revision of the chip does not properly accept IP fragments - * at least for UDP. - */ - /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since - * the MAC it calculates the packetsize AFTER stripping the VLAN - * header, and if a VLAN Tagged packet of 64 bytes is received (like - * a ping with a VLAN header) then the card, strips the 4 byte VLAN - * tag and then checks the packet size, so if RXCFG_ARP is not enabled, - * it discrards it!. These guys...... - */ - writel(VRCR_IPEN | VRCR_VTDEN, ns->base + VRCR); - - /* Enable per-packet TCP/UDP/IP checksumming */ - writel(VTCR_PPCHK, ns->base + VTCR); - - /* Ramit : Enable async and sync pause frames */ -// writel(0, ns->base + PCR); - writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K | - PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT), - ns->base + PCR); - - /* Disable Wake On Lan */ - writel(0, ns->base + WCSR); - - ns83820_getmac(nic, nic->node_addr); - - if (using_dac) { - DBG("%s: using 64 bit addressing.\n", pci->id->name); - } - - DBG("%s: DP83820 %d.%d: io=%#04lx\n", - pci->id->name, - (unsigned) readl(ns->base + SRR) >> 8, - (unsigned) readl(ns->base + SRR) & 0xff, - pci->ioaddr); - -#ifdef PHY_CODE_IS_FINISHED - ns83820_probe_phy(dev); -#endif - - ns83820_reset(nic); - /* point to NIC specific routines */ - nic->nic_op = &ns83820_operations; - return 1; -} - -DRIVER ( "NS83820/PCI", nic_driver, pci_driver, ns83820_driver, - ns83820_probe, ns83820_disable ); - -/* - * Local variables: - * c-basic-offset: 8 - * c-indent-level: 8 - * tab-width: 8 - * End: - */ diff --git a/src/include/ipxe/threewire.h b/src/include/ipxe/threewire.h index 135ef56a..b5513ecd 100644 --- a/src/include/ipxe/threewire.h +++ b/src/include/ipxe/threewire.h @@ -61,6 +61,19 @@ init_at93cx6 ( struct spi_device *device, unsigned int organisation ) { device->nvs.write = threewire_write; } +/** + * Initialise Atmel AT93C06 serial EEPROM + * + * @v device SPI device + * @v organisation Word organisation (8 or 16) + */ +static inline __attribute__ (( always_inline )) void +init_at93c06 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.size = ( 256 / organisation ); + device->address_len = ( ( organisation == 8 ) ? 7 : 6 ); + init_at93cx6 ( device, organisation ); +} + /** * Initialise Atmel AT93C46 serial EEPROM *