1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165 |
- /*
- * eepro100.c -- This is a driver for Intel Fast Ethernet Controllers
- * (ifec).
- *
- * Originally written for Etherboot by:
- *
- * Copyright (C) AW Computer Systems.
- * written by R.E.Wolff -- R.E.Wolff@BitWizard.nl
- *
- * AW Computer Systems is contributing to the free software community
- * by paying for this driver and then putting the result under GPL.
- *
- * If you need a Linux device driver, please contact BitWizard for a
- * quote.
- *
- * 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, 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.
- *
- *
- * date version by what
- * Written: May 29 1997 V0.10 REW Initial revision.
- * changes: May 31 1997 V0.90 REW Works!
- * Jun 1 1997 V0.91 REW Cleanup
- * Jun 2 1997 V0.92 REW Add some code documentation
- * Jul 25 1997 V1.00 REW Tested by AW to work in a PROM
- * Cleanup for publication
- * Dez 11 2004 V1.10 Kiszka Add RX ring buffer support
- * Jun 2008 v2.0 mdeck Updated to iPXE. Changed much.
- *
- * Cleanups and fixes by Thomas Miletich<thomas.miletich@gmail.com>
- *
- * This is the etherboot intel etherexpress Pro/100B driver.
- *
- * It was written from scratch, with Donald Beckers eepro100.c kernel
- * driver as a guideline. Mostly the 82557 related definitions and the
- * lower level routines have been cut-and-pasted into this source.
- *
- * The driver was finished before Intel got the NDA out of the closet.
- *
- * Datasheet is now published and available from
- * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
- * - Michael Brown
- * */
-
- FILE_LICENCE ( GPL2_OR_LATER );
-
- /*
- * General Theory of Operation
- *
- * Initialization
- *
- * ifec_pci_probe() is called by iPXE during initialization. Typical NIC
- * initialization is performed. EEPROM data is read.
- *
- * Network Boot
- *
- * ifec_net_open() is called by iPXE before attempting to network boot from the
- * card. Here, the Command Unit & Receive Unit are initialized. The tx & rx
- * rings are setup. The MAC address is programmed and the card is configured.
- *
- * Transmit
- *
- * ifec_net_transmit() enqueues a packet in the tx ring - active::tcbs[] The tx
- * ring is composed of TCBs linked to each other into a ring. A tx request
- * fills out the next available TCB with a pointer to the packet data.
- * The last enqueued tx is always at active::tcb_head. Thus, a tx request fills
- * out the TCB following tcb_head.
- * active::tcb_tail points to the TCB we're awaiting completion of.
- * ifec_tx_process() checks tcb_tail, and once complete,
- * blindly increments tcb_tail to the next ring TCB.
- *
- * Receive
- *
- * priv::rfds[] is an array of Receive Frame Descriptors. The RFDs are linked
- * together to form a ring.
- * ifec_net_poll() calls ifec_rx_process(), which checks the next RFD for
- * data. If we received a packet, we allocate a new io_buffer and copy the
- * packet data into it. If alloc_iob() fails, we don't touch the RFD and try
- * again on the next poll.
- */
-
- /*
- * Debugging levels:
- * - DBG() is for any errors, i.e. failed alloc_iob(), malloc_dma(),
- * TX overflow, corrupted packets, ...
- * - DBG2() is for successful events, like packet received,
- * packet transmitted, and other general notifications.
- * - DBGP() prints the name of each called function on entry
- */
-
- #include <stdint.h>
- #include <byteswap.h>
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <ipxe/ethernet.h>
- #include <ipxe/if_ether.h>
- #include <ipxe/iobuf.h>
- #include <ipxe/malloc.h>
- #include <ipxe/pci.h>
- #include <ipxe/spi_bit.h>
- #include <ipxe/timer.h>
- #include <ipxe/nvs.h>
- #include <ipxe/threewire.h>
- #include <ipxe/netdevice.h>
- #include "eepro100.h"
-
- /****************************** Global data **********************************/
-
- /*
- * This is the default configuration command data. The values were copied from
- * the Linux kernel initialization for the eepro100.
- */
- static struct ifec_cfg ifec_cfg = {
- .status = 0,
- .command = CmdConfigure | CmdSuspend,
- .link = 0, /* Filled in later */
- .byte = { 22, /* How many bytes in this array */
- ( TX_FIFO << 4 ) | RX_FIFO, /* Rx & Tx FIFO limits */
- 0, 0, /* Adaptive Interframe Spacing */
- RX_DMA_COUNT, /* Rx DMA max byte count */
- TX_DMA_COUNT + 0x80, /* Tx DMA max byte count */
- 0x32, /* Many bits. */
- 0x03, /* Discard short receive & Underrun retries */
- 1, /* 1=Use MII 0=Use AUI */
- 0,
- 0x2E, /* NSAI, Preamble length, & Loopback*/
- 0, /* Linear priority */
- 0x60, /* L PRI MODE & Interframe spacing */
- 0, 0xf2,
- 0x48, /* Promiscuous, Broadcast disable, CRS & CDT */
- 0, 0x40,
- 0xf2, /* Stripping, Padding, Receive CRC Transfer */
- 0x80, /* 0x40=Force full-duplex, 0x80=Allowfull-duplex*/
- 0x3f, /* Multiple IA */
- 0x0D } /* Multicast all */
- };
-
- static struct net_device_operations ifec_operations = {
- .open = ifec_net_open,
- .close = ifec_net_close,
- .transmit = ifec_net_transmit,
- .poll = ifec_net_poll,
- .irq = ifec_net_irq
- };
-
- /******************* iPXE PCI Device Driver API functions ********************/
-
- /*
- * Initialize the PCI device.
- *
- * @v pci The device's associated pci_device structure.
- * @v id The PCI device + vendor id.
- * @ret rc Returns zero if successfully initialized.
- *
- * This function is called very early on, while iPXE is initializing.
- * This is a iPXE PCI Device Driver API function.
- */
- static int ifec_pci_probe ( struct pci_device *pci )
- {
- struct net_device *netdev;
- struct ifec_private *priv;
- int rc;
-
- DBGP ( "ifec_pci_probe: " );
-
- if ( pci->ioaddr == 0 )
- return -EINVAL;
-
- netdev = alloc_etherdev ( sizeof(*priv) );
- if ( !netdev )
- return -ENOMEM;
-
- netdev_init ( netdev, &ifec_operations );
- priv = netdev->priv;
-
- pci_set_drvdata ( pci, netdev );
- netdev->dev = &pci->dev;
-
- /* enable bus master, etc */
- adjust_pci_device( pci );
-
- DBGP ( "pci " );
-
- memset ( priv, 0, sizeof(*priv) );
- priv->ioaddr = pci->ioaddr;
-
- ifec_reset ( netdev );
- DBGP ( "reset " );
-
- ifec_init_eeprom ( netdev );
-
- /* read MAC address */
- nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MAC_0, netdev->hw_addr,
- ETH_ALEN );
- /* read mdio_register */
- nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MDIO_REGISTER,
- &priv->mdio_register, 2 );
-
- if ( ( rc = register_netdev ( netdev ) ) != 0 )
- goto error;
-
- netdev_link_up ( netdev );
-
- DBGP ( "ints\n" );
-
- return 0;
-
- error:
- ifec_reset ( netdev );
- netdev_nullify ( netdev );
- netdev_put ( netdev );
-
- return rc;
- }
-
- /*
- * Remove a device from the PCI device list.
- *
- * @v pci PCI device to remove.
- *
- * This is a PCI Device Driver API function.
- */
- static void ifec_pci_remove ( struct pci_device *pci )
- {
- struct net_device *netdev = pci_get_drvdata ( pci );
-
- DBGP ( "ifec_pci_remove\n" );
-
- unregister_netdev ( netdev );
- ifec_reset ( netdev );
- netdev_nullify ( netdev );
- netdev_put ( netdev );
- }
-
- /****************** iPXE Network Device Driver API functions *****************/
-
- /*
- * Close a network device.
- *
- * @v netdev Device to close.
- *
- * This is a iPXE Network Device Driver API function.
- */
- static void ifec_net_close ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
- unsigned short intr_status;
-
- DBGP ( "ifec_net_close\n" );
-
- /* disable interrupts */
- ifec_net_irq ( netdev, 0 );
-
- /* Ack & clear ints */
- intr_status = inw ( ioaddr + SCBStatus );
- outw ( intr_status, ioaddr + SCBStatus );
- inw ( ioaddr + SCBStatus );
-
- ifec_reset ( netdev );
-
- /* Free any resources */
- ifec_free ( netdev );
- }
-
- /* Interrupts to be masked */
- #define INTERRUPT_MASK ( SCBMaskEarlyRx | SCBMaskFlowCtl )
-
- /*
- * Enable or disable IRQ masking.
- *
- * @v netdev Device to control.
- * @v enable Zero to mask off IRQ, non-zero to enable IRQ.
- *
- * This is a iPXE Network Driver API function.
- */
- static void ifec_net_irq ( struct net_device *netdev, int enable )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
-
- DBGP ( "ifec_net_irq\n" );
-
- outw ( enable ? INTERRUPT_MASK : SCBMaskAll, ioaddr + SCBCmd );
- }
-
- /*
- * Opens a network device.
- *
- * @v netdev Device to be opened.
- * @ret rc Non-zero if failed to open.
- *
- * This enables tx and rx on the device.
- * This is a iPXE Network Device Driver API function.
- */
- static int ifec_net_open ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- struct ifec_ias *ias = NULL;
- struct ifec_cfg *cfg = NULL;
- int i, options;
- int rc = -ENOMEM;
-
- DBGP ( "ifec_net_open: " );
-
- /* Ensure interrupts are disabled. */
- ifec_net_irq ( netdev, 0 );
-
- /* Initialize Command Unit and Receive Unit base addresses. */
- ifec_scb_cmd ( netdev, 0, RUAddrLoad );
- ifec_scb_cmd ( netdev, virt_to_bus ( &priv->stats ), CUStatsAddr );
- ifec_scb_cmd ( netdev, 0, CUCmdBase );
-
- /* Initialize both rings */
- if ( ( rc = ifec_rx_setup ( netdev ) ) != 0 )
- goto error;
- if ( ( rc = ifec_tx_setup ( netdev ) ) != 0 )
- goto error;
-
- /* Initialize MDIO */
- options = 0x00; /* 0x40 = 10mbps half duplex, 0x00 = Autosense */
- ifec_mdio_setup ( netdev, options );
-
- /* Prepare MAC address w/ Individual Address Setup (ias) command.*/
- ias = malloc_dma ( sizeof ( *ias ), CB_ALIGN );
- if ( !ias ) {
- rc = -ENOMEM;
- goto error;
- }
- ias->command = CmdIASetup;
- ias->status = 0;
- memcpy ( ias->ia, netdev->ll_addr, ETH_ALEN );
-
- /* Prepare operating parameters w/ a configure command. */
- cfg = malloc_dma ( sizeof ( *cfg ), CB_ALIGN );
- if ( !cfg ) {
- rc = -ENOMEM;
- goto error;
- }
- memcpy ( cfg, &ifec_cfg, sizeof ( *cfg ) );
- cfg->link = virt_to_bus ( priv->tcbs );
- cfg->byte[19] = ( options & 0x10 ) ? 0xC0 : 0x80;
- ias->link = virt_to_bus ( cfg );
-
- /* Issue the ias and configure commands. */
- ifec_scb_cmd ( netdev, virt_to_bus ( ias ), CUStart );
- ifec_scb_cmd_wait ( netdev );
- priv->configured = 1;
-
- /* Wait up to 10 ms for configuration to initiate */
- for ( i = 10; i && !cfg->status; i-- )
- mdelay ( 1 );
- if ( ! cfg->status ) {
- DBG ( "Failed to initiate!\n" );
- goto error;
- }
- free_dma ( ias, sizeof ( *ias ) );
- free_dma ( cfg, sizeof ( *cfg ) );
- DBG2 ( "cfg " );
-
- /* Enable rx by sending ring address to card */
- if ( priv->rfds[0] != NULL ) {
- ifec_scb_cmd ( netdev, virt_to_bus( priv->rfds[0] ), RUStart );
- ifec_scb_cmd_wait ( netdev );
- }
- DBG2 ( "rx_start\n" );
-
- return 0;
-
- error:
- free_dma ( cfg, sizeof ( *cfg ) );
- free_dma ( ias, sizeof ( *ias ) );
- ifec_free ( netdev );
- ifec_reset ( netdev );
- return rc;
- }
-
- /*
- * This function allows a driver to process events during operation.
- *
- * @v netdev Device being polled.
- *
- * This is called periodically by iPXE to let the driver check the status of
- * transmitted packets and to allow the driver to check for received packets.
- * This is a iPXE Network Device Driver API function.
- */
- static void ifec_net_poll ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned short intr_status;
-
- DBGP ( "ifec_net_poll\n" );
-
- /* acknowledge interrupts ASAP */
- intr_status = inw ( priv->ioaddr + SCBStatus );
- outw ( intr_status, priv->ioaddr + SCBStatus );
- inw ( priv->ioaddr + SCBStatus );
-
- DBG2 ( "poll - status: 0x%04X\n", intr_status );
-
- /* anything to do here? */
- if ( ( intr_status & ( ~INTERRUPT_MASK ) ) == 0 )
- return;
-
- /* process received and transmitted packets */
- ifec_tx_process ( netdev );
- ifec_rx_process ( netdev );
-
- ifec_check_ru_status ( netdev, intr_status );
-
- return;
- }
-
- /*
- * This transmits a packet.
- *
- * @v netdev Device to transmit from.
- * @v iobuf Data to transmit.
- * @ret rc Non-zero if failed to transmit.
- *
- * This is a iPXE Network Driver API function.
- */
- static int ifec_net_transmit ( struct net_device *netdev,
- struct io_buffer *iobuf )
- {
- struct ifec_private *priv = netdev->priv;
- struct ifec_tcb *tcb = priv->tcb_head->next;
- unsigned long ioaddr = priv->ioaddr;
-
- DBGP ( "ifec_net_transmit\n" );
-
- /* Wait for TCB to become available. */
- if ( tcb->status || tcb->iob ) {
- DBG ( "TX overflow\n" );
- return -ENOBUFS;
- }
-
- DBG2 ( "transmitting packet (%zd bytes). status = %hX, cmd=%hX\n",
- iob_len ( iobuf ), tcb->status, inw ( ioaddr + SCBCmd ) );
-
- tcb->command = CmdSuspend | CmdTx | CmdTxFlex;
- tcb->count = 0x01208000;
- tcb->tbd_addr0 = virt_to_bus ( iobuf->data );
- tcb->tbd_size0 = 0x3FFF & iob_len ( iobuf );
- tcb->iob = iobuf;
-
- ifec_tx_wake ( netdev );
-
- /* Append to end of ring. */
- priv->tcb_head = tcb;
-
- return 0;
- }
-
- /*************************** Local support functions *************************/
-
- /* Define what each GPIO Pin does */
- static const uint16_t ifec_ee_bits[] = {
- [SPI_BIT_SCLK] = EE_SHIFT_CLK,
- [SPI_BIT_MOSI] = EE_DATA_WRITE,
- [SPI_BIT_MISO] = EE_DATA_READ,
- [SPI_BIT_SS(0)] = EE_ENB,
- };
-
- /*
- * Read a single bit from the GPIO pins used for SPI.
- * should be called by SPI bitbash functions only
- *
- * @v basher Bitbash device
- * @v bit_id Line to be read
- */
- static int ifec_spi_read_bit ( struct bit_basher *basher,
- unsigned int bit_id )
- {
- struct ifec_private *priv =
- container_of ( basher, struct ifec_private, spi.basher );
- unsigned long ee_addr = priv->ioaddr + CSREeprom;
- unsigned int ret = 0;
- uint16_t mask;
-
- DBGP ( "ifec_spi_read_bit\n" );
-
- mask = ifec_ee_bits[bit_id];
- ret = inw (ee_addr);
-
- return ( ret & mask ) ? 1 : 0;
- }
-
- /*
- * Write a single bit to the GPIO pins used for SPI.
- * should be called by SPI bitbash functions only
- *
- * @v basher Bitbash device
- * @v bit_id Line to write to
- * @v data Value to write
- */
- static void ifec_spi_write_bit ( struct bit_basher *basher,
- unsigned int bit_id,
- unsigned long data )
- {
- struct ifec_private *priv =
- container_of ( basher, struct ifec_private, spi.basher );
- unsigned long ee_addr = priv->ioaddr + CSREeprom;
- short val;
- uint16_t mask = ifec_ee_bits[bit_id];
-
- DBGP ( "ifec_spi_write_bit\n" );
-
- val = inw ( ee_addr );
- val &= ~mask;
- val |= data & mask;
-
- outw ( val, ee_addr );
- }
-
- /* set function pointer to SPI read- and write-bit functions */
- static struct bit_basher_operations ifec_basher_ops = {
- .read = ifec_spi_read_bit,
- .write = ifec_spi_write_bit,
- };
-
- /*
- * Initialize the eeprom stuff
- *
- * @v netdev Network device
- */
- static void ifec_init_eeprom ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
-
- DBGP ( "ifec_init_eeprom\n" );
-
- priv->spi.basher.op = &ifec_basher_ops;
- priv->spi.bus.mode = SPI_MODE_THREEWIRE;
- init_spi_bit_basher ( &priv->spi );
-
- priv->eeprom.bus = &priv->spi.bus;
-
- /* init as 93c46(93c14 compatible) first, to set the command len,
- * block size and word len. Needs to be set for address len detection.
- */
- init_at93c46 ( &priv->eeprom, 16 );
-
- /* detect address length, */
- threewire_detect_address_len ( &priv->eeprom );
-
- /* address len == 8 means 93c66 instead of 93c46 */
- if ( priv->eeprom.address_len == 8 )
- init_at93c66 ( &priv->eeprom, 16 );
- }
-
- /*
- * Support function: ifec_mdio_read
- *
- * This probably reads a register in the "physical media interface chip".
- * -- REW
- */
- static int ifec_mdio_read ( struct net_device *netdev, int phy_id,
- int location )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
- int val;
- int boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
-
- DBGP ( "ifec_mdio_read\n" );
-
- outl ( 0x08000000 | ( location << 16 ) | ( phy_id << 21 ),
- ioaddr + CSRCtrlMDI );
- do {
- udelay ( 16 );
-
- val = inl ( ioaddr + CSRCtrlMDI );
-
- if ( --boguscnt < 0 ) {
- DBG ( " ifec_mdio_read() time out with val = %X.\n",
- val );
- break;
- }
- } while (! ( val & 0x10000000 ) );
- return val & 0xffff;
- }
-
- /*
- * Initializes MDIO.
- *
- * @v netdev Network device
- * @v options MDIO options
- */
- static void ifec_mdio_setup ( struct net_device *netdev, int options )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned short mdio_register = priv->mdio_register;
-
- DBGP ( "ifec_mdio_setup\n" );
-
- if ( ( (mdio_register>>8) & 0x3f ) == DP83840
- || ( (mdio_register>>8) & 0x3f ) == DP83840A ) {
- int mdi_reg23 = ifec_mdio_read ( netdev, mdio_register
- & 0x1f, 23 ) | 0x0422;
- if (CONGENB)
- mdi_reg23 |= 0x0100;
- DBG2 ( "DP83840 specific setup, setting register 23 to "
- "%hX.\n", mdi_reg23 );
- ifec_mdio_write ( netdev, mdio_register & 0x1f, 23, mdi_reg23 );
- }
- DBG2 ( "dp83840 " );
- if ( options != 0 ) {
- ifec_mdio_write ( netdev, mdio_register & 0x1f, 0,
- ( (options & 0x20) ? 0x2000 : 0 ) |
- ( (options & 0x10) ? 0x0100 : 0 ) );
- DBG2 ( "set mdio_register. " );
- }
- }
-
- /*
- * Support function: ifec_mdio_write
- *
- * This probably writes to the "physical media interface chip".
- * -- REW
- */
- static int ifec_mdio_write ( struct net_device *netdev,
- int phy_id, int location, int value )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
- int val;
- int boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
-
- DBGP ( "ifec_mdio_write\n" );
-
- outl ( 0x04000000 | ( location << 16 ) | ( phy_id << 21 ) | value,
- ioaddr + CSRCtrlMDI );
- do {
- udelay ( 16 );
-
- val = inl ( ioaddr + CSRCtrlMDI );
- if ( --boguscnt < 0 ) {
- DBG ( " ifec_mdio_write() time out with val = %X.\n",
- val );
- break;
- }
- } while (! ( val & 0x10000000 ) );
- return val & 0xffff;
- }
-
- /*
- * Resets the hardware.
- *
- * @v netdev Network device
- */
- static void ifec_reset ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
-
- DBGP ( "ifec_reset\n" );
-
- /* do partial reset first */
- outl ( PortPartialReset, ioaddr + CSRPort );
- inw ( ioaddr + SCBStatus );
- udelay ( 20 );
-
- /* full reset */
- outl ( PortReset, ioaddr + CSRPort );
- inw ( ioaddr + SCBStatus );
- udelay ( 20 );
-
- /* disable interrupts again */
- ifec_net_irq ( netdev, 0 );
- }
-
- /*
- * free()s the tx/rx rings.
- *
- * @v netdev Network device
- */
- static void ifec_free ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev_priv ( netdev );
- int i;
-
- DBGP ( "ifec_free\n" );
-
- /* free all allocated receive io_buffers */
- for ( i = 0; i < RFD_COUNT; i++ ) {
- free_iob ( priv->rx_iobs[i] );
- priv->rx_iobs[i] = NULL;
- priv->rfds[i] = NULL;
- }
-
- /* free TX ring buffer */
- free_dma ( priv->tcbs, TX_RING_BYTES );
-
- priv->tcbs = NULL;
- }
-
- /*
- * Initializes an RFD.
- *
- * @v rfd RFD struct to initialize
- * @v command Command word
- * @v link Link value
- */
- static void ifec_rfd_init ( struct ifec_rfd *rfd, s16 command, u32 link )
- {
- DBGP ( "ifec_rfd_init\n" );
-
- rfd->status = 0;
- rfd->command = command;
- rfd->rx_buf_addr = 0xFFFFFFFF;
- rfd->count = 0;
- rfd->size = RFD_PACKET_LEN;
- rfd->link = link;
- }
-
- /*
- * Send address of new RFD to card
- *
- * @v netdev Network device
- */
- static void ifec_reprime_ru ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- int cur_rx = priv->cur_rx;
-
- DBGP ( "ifec_reprime_ru\n" );
-
- if ( priv->rfds[cur_rx] != NULL ) {
- ifec_scb_cmd ( netdev, virt_to_bus ( priv->rfds[cur_rx] ),
- RUStart );
- ifec_scb_cmd_wait ( netdev );
- }
- }
-
- /*
- * Check if reprime of RU needed
- *
- * @v netdev Network device
- */
- static void ifec_check_ru_status ( struct net_device *netdev,
- unsigned short intr_status )
- {
- struct ifec_private *priv = netdev->priv;
-
- DBGP ( "ifec_check_ru_status\n" );
-
- /*
- * The chip may have suspended reception for various reasons.
- * Check for that, and re-prime it should this be the case.
- */
- switch ( ( intr_status >> 2 ) & 0xf ) {
- case 0: /* Idle */
- case 4: /* Ready */
- break;
- case 1: /* Suspended */
- case 2: /* No resources (RFDs) */
- case 9: /* Suspended with no more RBDs */
- case 10: /* No resources due to no RBDs */
- case 12: /* Ready with no RBDs */
- DBG ( "ifec_net_poll: RU reprimed.\n" );
- ifec_reprime_ru ( netdev );
- break;
- default:
- /* reserved values */
- DBG ( "ifec_net_poll: RU state anomaly: %i\n",
- ( inw ( priv->ioaddr + SCBStatus ) >> 2 ) & 0xf );
- break;
- }
- }
-
- #define RFD_STATUS ( RFD_OK | RFDRxCol | RFDRxErr | RFDShort | \
- RFDDMAOverrun | RFDNoBufs | RFDCRCError )
- /*
- * Looks for received packets in the rx ring, reports success or error to
- * the core accordingly. Starts reallocation of rx ring.
- *
- * @v netdev Network device
- */
- static void ifec_rx_process ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- int cur_rx = priv->cur_rx;
- struct io_buffer *iob = priv->rx_iobs[cur_rx];
- struct ifec_rfd *rfd = priv->rfds[cur_rx];
- unsigned int rx_len;
- s16 status;
-
- DBGP ( "ifec_rx_process\n" );
-
- /* Process any received packets */
- while ( iob && rfd && ( status = rfd->status ) ) {
- rx_len = rfd->count & RFDMaskCount;
-
- DBG2 ( "Got a packet: Len = %d, cur_rx = %d.\n", rx_len,
- cur_rx );
- DBGIO_HD ( (void*)rfd->packet, 0x30 );
-
- if ( ( status & ( RFD_STATUS & ~RFDShort ) ) != RFD_OK ) {
- DBG ( "Corrupted packet received. "
- "Status = %#08hx\n", status );
- netdev_rx_err ( netdev, iob, -EINVAL );
- } else {
- /* Hand off the packet to the network subsystem */
- iob_put ( iob, rx_len );
- DBG2 ( "Received packet: %p, len: %d\n", iob, rx_len );
- netdev_rx ( netdev, iob );
- }
-
- /* make sure we don't reuse this RFD */
- priv->rx_iobs[cur_rx] = NULL;
- priv->rfds[cur_rx] = NULL;
-
- /* Next RFD */
- priv->cur_rx = ( cur_rx + 1 ) % RFD_COUNT;
- cur_rx = priv->cur_rx;
- iob = priv->rx_iobs[cur_rx];
- rfd = priv->rfds[cur_rx];
- }
-
- ifec_refill_rx_ring ( netdev );
- }
-
- /*
- * Allocates io_buffer, set pointers in ifec_private structure accordingly,
- * reserves space for RFD header in io_buffer.
- *
- * @v netdev Network device
- * @v cur Descriptor number to work on
- * @v cmd Value to set cmd field in RFD to
- * @v link Pointer to ned RFD
- * @ret rc 0 on success, negative on failure
- */
- static int ifec_get_rx_desc ( struct net_device *netdev, int cur, int cmd,
- int link )
- {
- struct ifec_private *priv = netdev->priv;
- struct ifec_rfd *rfd = priv->rfds[cur];
-
- DBGP ( "ifec_get_rx_desc\n" );
-
- priv->rx_iobs[cur] = alloc_iob ( sizeof ( *rfd ) );
- if ( ! priv->rx_iobs[cur] ) {
- DBG ( "alloc_iob failed. desc. nr: %d\n", cur );
- priv->rfds[cur] = NULL;
- return -ENOMEM;
- }
-
- /* Initialize new tail. */
- priv->rfds[cur] = priv->rx_iobs[cur]->data;
- ifec_rfd_init ( priv->rfds[cur], cmd, link );
- iob_reserve ( priv->rx_iobs[cur], RFD_HEADER_LEN );
-
- return 0;
- }
-
- /*
- * Allocate new descriptor entries and initialize them if needed
- *
- * @v netdev Network device
- */
- static void ifec_refill_rx_ring ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- int i, cur_rx;
- unsigned short intr_status;
-
- DBGP ( "ifec_refill_rx_ring\n" );
-
- for ( i = 0; i < RFD_COUNT; i++ ) {
- cur_rx = ( priv->cur_rx + i ) % RFD_COUNT;
- /* only refill if empty */
- if ( priv->rfds[cur_rx] != NULL ||
- priv->rx_iobs[cur_rx] != NULL )
- continue;
-
- DBG2 ( "refilling RFD %d\n", cur_rx );
-
- if ( ifec_get_rx_desc ( netdev, cur_rx,
- CmdSuspend | CmdEndOfList, 0 ) == 0 ) {
- if ( i > 0 ) {
- int prev_rx = ( ( ( cur_rx + RFD_COUNT ) - 1 )
- % RFD_COUNT );
- struct ifec_rfd *rfd = priv->rfds[prev_rx];
-
- rfd->command = 0;
- rfd->link = virt_to_bus ( priv->rfds[cur_rx] );
- }
- }
- }
-
- intr_status = inw ( priv->ioaddr + SCBStatus );
- ifec_check_ru_status ( netdev, intr_status );
- }
-
- /*
- * Initial allocation & initialization of the rx ring.
- *
- * @v netdev Device of rx ring.
- * @ret rc Non-zero if error occurred
- */
- static int ifec_rx_setup ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- int i;
-
- DBGP ( "ifec_rx_setup\n" );
-
- priv->cur_rx = 0;
-
- /* init values for ifec_refill_rx_ring() */
- for ( i = 0; i < RFD_COUNT; i++ ) {
- priv->rfds[i] = NULL;
- priv->rx_iobs[i] = NULL;
- }
- ifec_refill_rx_ring ( netdev );
-
- return 0;
- }
-
- /*
- * Initiates a SCB command.
- *
- * @v netdev Network device
- * @v ptr General pointer value for command.
- * @v cmd Command to issue.
- * @ret rc Non-zero if command not issued.
- */
- static int ifec_scb_cmd ( struct net_device *netdev, u32 ptr, u8 cmd )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
- int rc;
-
- DBGP ( "ifec_scb_cmd\n" );
-
- rc = ifec_scb_cmd_wait ( netdev ); /* Wait until ready */
- if ( !rc ) {
- outl ( ptr, ioaddr + SCBPointer );
- outb ( cmd, ioaddr + SCBCmd ); /* Issue command */
- }
- return rc;
- }
-
- /*
- * Wait for command unit to accept a command.
- *
- * @v cmd_ioaddr I/O address of command register.
- * @ret rc Non-zero if command timed out.
- */
- static int ifec_scb_cmd_wait ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long cmd_ioaddr = priv->ioaddr + SCBCmd;
- int rc, wait = CU_CMD_TIMEOUT;
-
- DBGP ( "ifec_scb_cmd_wait\n" );
-
- for ( ; wait && ( rc = inb ( cmd_ioaddr ) ); wait-- )
- udelay ( 1 );
-
- if ( !wait )
- DBG ( "ifec_scb_cmd_wait timeout!\n" );
- return rc;
- }
-
- /*
- * Check status of transmitted packets & perform tx completions.
- *
- * @v netdev Network device.
- */
- static void ifec_tx_process ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- struct ifec_tcb *tcb = priv->tcb_tail;
- s16 status;
-
- DBGP ( "ifec_tx_process\n" );
-
- /* Check status of transmitted packets */
- while ( ( status = tcb->status ) && tcb->iob ) {
- if ( status & TCB_U ) {
- /* report error to iPXE */
- DBG ( "ifec_tx_process : tx error!\n " );
- netdev_tx_complete_err ( netdev, tcb->iob, -EINVAL );
- } else {
- /* report successful transmit */
- netdev_tx_complete ( netdev, tcb->iob );
- }
- DBG2 ( "tx completion\n" );
-
- tcb->iob = NULL;
- tcb->status = 0;
-
- priv->tcb_tail = tcb->next; /* Next TCB */
- tcb = tcb->next;
- }
- }
-
- /*
- * Allocates & initialize tx resources.
- *
- * @v netdev Network device.
- * @ret rc Non-zero if error occurred.
- */
- static int ifec_tx_setup ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- struct ifec_tcb *tcb;
- int i;
-
- DBGP ( "ifec_tx_setup\n" );
-
- /* allocate tx ring */
- priv->tcbs = malloc_dma ( TX_RING_BYTES, CB_ALIGN );
- if ( !priv->tcbs ) {
- DBG ( "TX-ring allocation failed\n" );
- return -ENOMEM;
- }
-
- tcb = priv->tcb_tail = priv->tcbs;
- priv->tx_curr = priv->tx_tail = 0;
- priv->tx_cnt = 0;
-
- for ( i = 0; i < TCB_COUNT; i++, tcb++ ) {
- tcb->status = 0;
- tcb->count = 0x01208000;
- tcb->iob = NULL;
- tcb->tbda_addr = virt_to_bus ( &tcb->tbd_addr0 );
- tcb->link = virt_to_bus ( tcb + 1 );
- tcb->next = tcb + 1;
- }
- /* We point tcb_head at the last TCB, so the first ifec_net_transmit()
- * will use the first (head->next) TCB to transmit. */
- priv->tcb_head = --tcb;
- tcb->link = virt_to_bus ( priv->tcbs );
- tcb->next = priv->tcbs;
-
- return 0;
- }
-
- /*
- * Wake up the Command Unit and issue a Resume/Start.
- *
- * @v netdev Network device containing Command Unit
- *
- * The time between clearing the S bit and issuing Resume must be as short as
- * possible to prevent a race condition. As noted in linux eepro100.c :
- * Note: Watch out for the potential race condition here: imagine
- * erasing the previous suspend
- * the chip processes the previous command
- * the chip processes the final command, and suspends
- * doing the CU_RESUME
- * the chip processes the next-yet-valid post-final-command.
- * So blindly sending a CU_RESUME is only safe if we do it immediately after
- * erasing the previous CmdSuspend, without the possibility of an intervening
- * delay.
- */
- void ifec_tx_wake ( struct net_device *netdev )
- {
- struct ifec_private *priv = netdev->priv;
- unsigned long ioaddr = priv->ioaddr;
- struct ifec_tcb *tcb = priv->tcb_head->next;
-
- DBGP ( "ifec_tx_wake\n" );
-
- /* For the special case of the first transmit, we issue a START. The
- * card won't RESUME after the configure command. */
- if ( priv->configured ) {
- priv->configured = 0;
- ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
- ifec_scb_cmd_wait ( netdev );
- return;
- }
-
- /* Resume if suspended. */
- switch ( ( inw ( ioaddr + SCBStatus ) >> 6 ) & 0x3 ) {
- case 0: /* Idle - We should not reach this state. */
- DBG2 ( "ifec_tx_wake: tx idle!\n" );
- ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
- ifec_scb_cmd_wait ( netdev );
- return;
- case 1: /* Suspended */
- DBG2 ( "s" );
- break;
- default: /* Active */
- DBG2 ( "a" );
- }
- ifec_scb_cmd_wait ( netdev );
- outl ( 0, ioaddr + SCBPointer );
- priv->tcb_head->command &= ~CmdSuspend;
- /* Immediately issue Resume command */
- outb ( CUResume, ioaddr + SCBCmd );
- ifec_scb_cmd_wait ( netdev );
- }
-
- /*********************************************************************/
-
- static struct pci_device_id ifec_nics[] = {
- PCI_ROM(0x8086, 0x1029, "id1029", "Intel EtherExpressPro100 ID1029", 0),
- PCI_ROM(0x8086, 0x1030, "id1030", "Intel EtherExpressPro100 ID1030", 0),
- PCI_ROM(0x8086, 0x1031, "82801cam", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection", 0),
- PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection", 0),
- PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection", 0),
- PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x1038, "id1038", "Intel PRO/100 VM Network Connection", 0),
- PCI_ROM(0x8086, 0x1039, "82562et", "Intel PRO100 VE 82562ET", 0),
- PCI_ROM(0x8086, 0x103a, "id103a", "Intel Corporation 82559 InBusiness 10/100", 0),
- PCI_ROM(0x8086, 0x103b, "82562etb", "Intel PRO100 VE 82562ETB", 0),
- PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection", 0),
- PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection", 0),
- PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection", 0),
- PCI_ROM(0x8086, 0x1051, "prove", "Intel PRO/100 VE Network Connection", 0),
- PCI_ROM(0x8086, 0x1059, "82551qm", "Intel PRO/100 M Mobile Connection", 0),
- PCI_ROM(0x8086, 0x1209, "82559er", "Intel EtherExpressPro100 82559ER", 0),
- PCI_ROM(0x8086, 0x1227, "82865", "Intel 82865 EtherExpress PRO/100A", 0),
- PCI_ROM(0x8086, 0x1228, "82556", "Intel 82556 EtherExpress PRO/100 Smart", 0),
- PCI_ROM(0x8086, 0x1229, "eepro100", "Intel EtherExpressPro100", 0),
- PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM", 0),
- PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection", 0),
- PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection", 0),
- PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection", 0),
- PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection", 0),
- PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0),
- PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0),
- PCI_ROM(0x8086, 0x1092, "82562-3", "Intel Pro/100 VE Network", 0),
- PCI_ROM(0x8086, 0x27dc, "eepro100-27dc", "Intel 82801G (ICH7) Chipset Ethernet Controller", 0),
- PCI_ROM(0x8086, 0x10fe, "82552", "Intel 82552 10/100 Network Connection", 0),
- };
-
- /* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
- * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
- * 2003/03/17 gbaum */
-
- struct pci_driver ifec_driver __pci_driver = {
- .ids = ifec_nics,
- .id_count = ( sizeof (ifec_nics) / sizeof (ifec_nics[0]) ),
- .probe = ifec_pci_probe,
- .remove = ifec_pci_remove
- };
-
- /*
- * Local variables:
- * c-basic-offset: 8
- * c-indent-level: 8
- * tab-width: 8
- * End:
- */
|