176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * eepro100.c -- This is a driver for Intel Fast Ethernet Controllers
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (ifec).
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Originally written for Etherboot by:
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   Copyright (C) AW Computer Systems.
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   written by R.E.Wolff -- R.E.Wolff@BitWizard.nl
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   AW Computer Systems is contributing to the free software community
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   by paying for this driver and then putting the result under GPL.
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   If you need a Linux device driver, please contact BitWizard for a
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   quote.
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2, or (at
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * your option) any later version.
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details.
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              date       version  by      what
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *  Written:    May 29 1997  V0.10  REW     Initial revision.
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * changes:     May 31 1997  V0.90  REW     Works!
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              Jun 1  1997  V0.91  REW     Cleanup
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              Jun 2  1997  V0.92  REW     Add some code documentation
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              Jul 25 1997  V1.00  REW     Tested by AW to work in a PROM
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *                                          Cleanup for publication
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              Dez 11 2004  V1.10  Kiszka  Add RX ring buffer support
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *              Jun    2008  v2.0   mdeck   Updated to gPXE. Changed much.
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Cleanups and fixes by Thomas Miletich<thomas.miletich@gmail.com>
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is the etherboot intel etherexpress Pro/100B driver.
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * It was written from scratch, with Donald Beckers eepro100.c kernel
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * driver as a guideline. Mostly the 82557 related definitions and the
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * lower level routines have been cut-and-pasted into this source.
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The driver was finished before Intel got the NDA out of the closet.
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Datasheet is now published and available from
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *    - Michael Brown
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * */
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER );
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Theory of Operation
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initialization
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ifec_pci_probe() is called by gPXE during initialization. Typical NIC
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * initialization is performed.  EEPROM data is read.
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Network Boot
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ifec_net_open() is called by gPXE before attempting to network boot from the
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * card.  Here, the Command Unit & Receive Unit are initialized.  The tx & rx
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * rings are setup.  The MAC address is programmed and the card is configured.
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ifec_net_transmit() enqueues a packet in the tx ring - active::tcbs[]  The tx
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ring is composed of TCBs linked to each other into a ring.  A tx request
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * fills out the next available TCB with a pointer to the packet data.
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The last enqueued tx is always at active::tcb_head.  Thus, a tx request fills
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * out the TCB following tcb_head.
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * active::tcb_tail points to the TCB we're awaiting completion of.
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ifec_tx_process() checks tcb_tail, and once complete,
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * blindly increments tcb_tail to the next ring TCB.
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Receive
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * priv::rfds[] is an array of Receive Frame Descriptors. The RFDs are linked
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * together to form a ring.
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ifec_net_poll() calls ifec_rx_process(), which checks the next RFD for
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * data.  If we received a packet, we allocate a new io_buffer and copy the
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * packet data into it. If alloc_iob() fails, we don't touch the RFD and try
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * again on the next poll.
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Debugging levels:
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	- DBG() is for any errors, i.e. failed alloc_iob(), malloc_dma(),
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	  TX overflow, corrupted packets, ...
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	- DBG2() is for successful events, like packet received,
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	  packet transmitted, and other general notifications.
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	- DBGP() prints the name of each called function on entry
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h>
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h>
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h>
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <unistd.h>
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/ethernet.h>
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/if_ether.h>
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/iobuf.h>
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/malloc.h>
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/pci.h>
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/spi_bit.h>
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/timer.h>
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/nvs.h>
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/threewire.h>
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/netdevice.h>
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include "eepro100.h"
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/****************************** Global data **********************************/
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is the default configuration command data. The values were copied from
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the Linux kernel initialization for the eepro100.
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct ifec_cfg ifec_cfg = {
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.status  = 0,
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.command = CmdConfigure | CmdSuspend,
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.link    = 0,        /* Filled in later */
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.byte = { 22,        /* How many bytes in this array */
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          ( TX_FIFO << 4 ) | RX_FIFO,  /* Rx & Tx FIFO limits */
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0, 0,                        /* Adaptive Interframe Spacing */
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          RX_DMA_COUNT,                /* Rx DMA max byte count */
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          TX_DMA_COUNT + 0x80,         /* Tx DMA max byte count */
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x32,      /* Many bits. */
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x03,      /* Discard short receive & Underrun retries */
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          1,         /* 1=Use MII  0=Use AUI */
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0,
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x2E,      /* NSAI, Preamble length, & Loopback*/
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0,         /* Linear priority */
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x60,      /* L PRI MODE & Interframe spacing */
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0, 0xf2,
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x48,      /* Promiscuous, Broadcast disable, CRS & CDT */
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0, 0x40,
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0xf2,      /* Stripping, Padding, Receive CRC Transfer */
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x80,      /* 0x40=Force full-duplex, 0x80=Allowfull-duplex*/
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x3f,      /* Multiple IA */
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	          0x0D }     /* Multicast all */
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct net_device_operations ifec_operations = {
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.open     = ifec_net_open,
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.close    = ifec_net_close,
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.transmit = ifec_net_transmit,
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.poll     = ifec_net_poll,
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.irq      = ifec_net_irq
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/******************* gPXE PCI Device Driver API functions ********************/
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initialize the PCI device.
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci 		The device's associated pci_device structure.
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v id  		The PCI device + vendor id.
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Returns zero if successfully initialized.
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This function is called very early on, while gPXE is initializing.
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE PCI Device Driver API function.
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_pci_probe ( struct pci_device *pci,
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                            const struct pci_device_id *id __unused )
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv;
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_pci_probe: " );
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( pci->ioaddr == 0 )
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -EINVAL;
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev = alloc_etherdev ( sizeof(*priv) );
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !netdev )
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOMEM;
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_init ( netdev, &ifec_operations );
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv = netdev->priv;
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_set_drvdata ( pci, netdev );
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->dev = &pci->dev;
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* enable bus master, etc */
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	adjust_pci_device( pci );
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "pci " );
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memset ( priv, 0, sizeof(*priv) );
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->ioaddr = pci->ioaddr;
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_reset ( netdev );
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "reset " );
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_init_eeprom ( netdev );
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* read MAC address */
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MAC_0, netdev->hw_addr,
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		   ETH_ALEN );
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* read mdio_register */
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	nvs_read ( &priv->eeprom.nvs, EEPROM_ADDR_MDIO_REGISTER,
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		   &priv->mdio_register, 2 );
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_link_update ( netdev );	/* Update link state */
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = register_netdev ( netdev ) ) != 0 )
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ints\n" );
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerror:
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_reset     ( netdev );
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_nullify ( netdev );
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_put     ( netdev );
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return rc;
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Remove a device from the PCI device list.
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci		PCI device to remove.
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a PCI Device Driver API function.
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_pci_remove ( struct pci_device *pci )
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev = pci_get_drvdata ( pci );
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_pci_remove\n" );
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unregister_netdev ( netdev );
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_reset        ( netdev );
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_nullify    ( netdev );
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_put        ( netdev );
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/****************** gPXE Network Device Driver API functions *****************/
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Close a network device.
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Device to close.
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE Network Device Driver API function.
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_net_close ( struct net_device *netdev )
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned short intr_status;
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_net_close\n" );
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* disable interrupts */
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_net_irq ( netdev, 0 );
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Ack & clear ints */
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	intr_status = inw ( ioaddr + SCBStatus );
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outw ( intr_status, ioaddr + SCBStatus );
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inw ( ioaddr + SCBStatus );
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_reset ( netdev );
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Free any resources */
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_free ( netdev );
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Interrupts to be masked */
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define INTERRUPT_MASK	( SCBMaskEarlyRx | SCBMaskFlowCtl )
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Enable or disable IRQ masking.
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Device to control.
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v enable		Zero to mask off IRQ, non-zero to enable IRQ.
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE Network Driver API function.
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_net_irq ( struct net_device *netdev, int enable )
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_net_irq\n" );
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outw ( enable ? INTERRUPT_MASK : SCBMaskAll, ioaddr + SCBCmd );
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Opens a network device.
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Device to be opened.
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc  		Non-zero if failed to open.
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This enables tx and rx on the device.
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE Network Device Driver API function.
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_net_open ( struct net_device *netdev )
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_ias *ias = NULL;
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_cfg *cfg = NULL;
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i, options;
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc = -ENOMEM;
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_net_open: " );
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Ensure interrupts are disabled. */
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_net_irq ( netdev, 0 );
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Initialize Command Unit and Receive Unit base addresses. */
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd ( netdev, 0, RUAddrLoad );
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd ( netdev, virt_to_bus ( &priv->stats ), CUStatsAddr );
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd ( netdev, 0, CUCmdBase );
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Initialize both rings */
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = ifec_rx_setup ( netdev ) ) != 0 )
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = ifec_tx_setup ( netdev ) ) != 0 )
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Initialize MDIO */
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	options = 0x00; /* 0x40 = 10mbps half duplex, 0x00 = Autosense */
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_mdio_setup ( netdev, options );
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Prepare MAC address w/ Individual Address Setup (ias) command.*/
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ias = malloc_dma ( sizeof ( *ias ), CB_ALIGN );
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !ias ) {
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		rc = -ENOMEM;
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ias->command      = CmdIASetup;
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ias->status       = 0;
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy ( ias->ia, netdev->ll_addr, ETH_ALEN );
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Prepare operating parameters w/ a configure command. */
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cfg = malloc_dma ( sizeof ( *cfg ), CB_ALIGN );
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !cfg ) {
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		rc = -ENOMEM;
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy ( cfg, &ifec_cfg, sizeof ( *cfg ) );
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cfg->link     = virt_to_bus ( priv->tcbs );
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cfg->byte[19] = ( options & 0x10 ) ? 0xC0 : 0x80;
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ias->link     = virt_to_bus ( cfg );
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Issue the ias and configure commands. */
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd ( netdev, virt_to_bus ( ias ), CUStart );
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd_wait ( netdev );
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->configured = 1;
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Wait up to 10 ms for configuration to initiate */
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 10; i && !cfg->status; i-- )
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		mdelay ( 1 );
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! cfg->status ) {
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "Failed to initiate!\n" );
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto error;
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_dma ( ias, sizeof ( *ias ) );
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_dma ( cfg, sizeof ( *cfg ) );
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG2 ( "cfg " );
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Enable rx by sending ring address to card */
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( priv->rfds[0] != NULL ) {
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd ( netdev, virt_to_bus( priv->rfds[0] ), RUStart );
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd_wait ( netdev );
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG2 ( "rx_start\n" );
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanerror:
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_dma ( cfg, sizeof ( *cfg ) );
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_dma ( ias, sizeof ( *ias ) );
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_free ( netdev );
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_reset ( netdev );
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return rc;
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This function allows a driver to process events during operation.
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Device being polled.
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is called periodically by gPXE to let the driver check the status of
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * transmitted packets and to allow the driver to check for received packets.
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE Network Device Driver API function.
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_net_poll ( struct net_device *netdev )
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	static int linkpoll = 0;
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned short intr_status;
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_net_poll\n" );
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* acknowledge interrupts ASAP */
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	intr_status = inw ( priv->ioaddr + SCBStatus );
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outw ( intr_status, priv->ioaddr + SCBStatus );
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inw ( priv->ioaddr + SCBStatus );
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG2 ( "poll - status: 0x%04X\n", intr_status );
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ++linkpoll > LINK_CHECK_PERIOD ) {
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		linkpoll = 0;
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_link_update ( netdev );	/* Update link state */
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* anything to do here? */
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( intr_status & ( ~INTERRUPT_MASK ) ) == 0 )
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* process received and transmitted packets */
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_tx_process ( netdev );
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_rx_process ( netdev );
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_check_ru_status ( netdev, intr_status );
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return;
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This transmits a packet.
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Device to transmit from.
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf 		Data to transmit.
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc  		Non-zero if failed to transmit.
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This is a gPXE Network Driver API function.
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_net_transmit ( struct net_device *netdev,
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                               struct io_buffer *iobuf )
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_tcb *tcb = priv->tcb_head->next;
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_net_transmit\n" );
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Wait for TCB to become available. */
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( tcb->status || tcb->iob ) {
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "TX overflow\n" );
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOBUFS;
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG2 ( "transmitting packet (%d bytes). status = %hX, cmd=%hX\n",
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		iob_len ( iobuf ), tcb->status, inw ( ioaddr + SCBCmd ) );
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->command   = CmdSuspend | CmdTx | CmdTxFlex;
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->count     = 0x01208000;
46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->tbd_addr0 = virt_to_bus ( iobuf->data );
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->tbd_size0 = 0x3FFF & iob_len ( iobuf );
46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->iob = iobuf;
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_tx_wake ( netdev );
46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Append to end of ring. */
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tcb_head = tcb;
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*************************** Local support functions *************************/
47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Define what each GPIO Pin does */
47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic const uint16_t ifec_ee_bits[] = {
47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SPI_BIT_SCLK]	= EE_SHIFT_CLK,
47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SPI_BIT_MOSI]	= EE_DATA_WRITE,
48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SPI_BIT_MISO]	= EE_DATA_READ,
48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	[SPI_BIT_SS(0)]	= EE_ENB,
48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Read a single bit from the GPIO pins used for SPI.
48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * should be called by SPI bitbash functions only
48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v basher		Bitbash device
48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v bit_id		Line to be read
49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_spi_read_bit ( struct bit_basher *basher,
49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       unsigned int bit_id )
49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv =
49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		container_of ( basher, struct ifec_private, spi.basher );
49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ee_addr = priv->ioaddr + CSREeprom;
49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int ret = 0;
49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t mask;
49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_spi_read_bit\n" );
50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	mask = ifec_ee_bits[bit_id];
50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ret = inw (ee_addr);
50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return ( ret & mask ) ? 1 : 0;
50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Write a single bit to the GPIO pins used for SPI.
51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * should be called by SPI bitbash functions only
51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v basher		Bitbash device
51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v bit_id		Line to write to
51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v data		Value to write
51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_spi_write_bit ( struct bit_basher *basher,
51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 unsigned int bit_id,
51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 unsigned long data )
51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv =
52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		container_of ( basher, struct ifec_private, spi.basher );
52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ee_addr = priv->ioaddr + CSREeprom;
52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	short val;
52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t mask = ifec_ee_bits[bit_id];
52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_spi_write_bit\n" );
52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	val = inw ( ee_addr );
52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	val &= ~mask;
53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	val |= data & mask;
53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outw ( val, ee_addr );
53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* set function pointer to SPI read- and write-bit functions */
53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct bit_basher_operations ifec_basher_ops = {
53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.read = ifec_spi_read_bit,
53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.write = ifec_spi_write_bit,
53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initialize the eeprom stuff
54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_init_eeprom ( struct net_device *netdev )
54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_init_eeprom\n" );
55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->spi.basher.op = &ifec_basher_ops;
55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->spi.bus.mode = SPI_MODE_THREEWIRE;
55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	init_spi_bit_basher ( &priv->spi );
55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->eeprom.bus = &priv->spi.bus;
55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* init as 93c46(93c14 compatible) first, to set the command len,
55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * block size and word len. Needs to be set for address len detection.
56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	init_at93c46 ( &priv->eeprom, 16 );
56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* detect address length, */
56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	threewire_detect_address_len ( &priv->eeprom );
56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* address len == 8 means 93c66 instead of 93c46 */
56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( priv->eeprom.address_len == 8 )
56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		init_at93c66 ( &priv->eeprom, 16 );
56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check if the network cable is plugged in.
57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev  		Network device to check.
57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret retval		greater 0 if linkup.
57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_link_check ( struct net_device *netdev )
57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned short mdio_register = priv->mdio_register;
58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_link_check\n" );
58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Read the status register once to discard stale data */
58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_mdio_read ( netdev, mdio_register & 0x1f, 1 );
58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Check to see if network cable is plugged in. */
58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! ( ifec_mdio_read ( netdev, mdio_register & 0x1f, 1 )
58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		  & ( 1 << 2 ) ) ) {
58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return 0;
59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 1;
59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check network cable link, inform gPXE as appropriate.
59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev  		Network device to check.
59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_link_update ( struct net_device *netdev )
60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_link_update\n" );
60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update link state */
60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ifec_link_check ( netdev ) )
60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_link_up ( netdev );
60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	else
60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_link_down ( netdev );
60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Support function: ifec_mdio_read
61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This probably reads a register in the "physical media interface chip".
61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * -- REW
61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_mdio_read ( struct net_device *netdev, int phy_id,
61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                                                       int location )
61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int val;
62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int boguscnt = 64*4;     /* <64 usec. to complete, typ 27 ticks */
62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_mdio_read\n" );
62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outl ( 0x08000000 | ( location << 16 ) | ( phy_id << 21 ),
62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       ioaddr + CSRCtrlMDI );
62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	do {
62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		udelay ( 16 );
63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		val = inl ( ioaddr + CSRCtrlMDI );
63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( --boguscnt < 0 ) {
63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( " ifec_mdio_read() time out with val = %X.\n",
63576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			         val );
63676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
63776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
63876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} while (! ( val & 0x10000000 ) );
63976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return val & 0xffff;
64076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
64176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
64376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initializes MDIO.
64476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
64576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev 		Network device
64676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v options		MDIO options
64776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
64876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_mdio_setup ( struct net_device *netdev, int options )
64976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
65076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
65176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned short mdio_register = priv->mdio_register;
65276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
65376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_mdio_setup\n" );
65476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
65576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (   ( (mdio_register>>8) & 0x3f ) == DP83840
65676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    || ( (mdio_register>>8) & 0x3f ) == DP83840A ) {
65776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		int mdi_reg23 = ifec_mdio_read ( netdev, mdio_register
65876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman						  & 0x1f, 23 ) | 0x0422;
65976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (CONGENB)
66076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			mdi_reg23 |= 0x0100;
66176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "DP83840 specific setup, setting register 23 to "
66276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		                                         "%hX.\n", mdi_reg23 );
66376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_mdio_write ( netdev, mdio_register & 0x1f, 23, mdi_reg23 );
66476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
66576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG2 ( "dp83840 " );
66676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( options != 0 ) {
66776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_mdio_write ( netdev, mdio_register & 0x1f, 0,
66876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		                           ( (options & 0x20) ? 0x2000 : 0 ) |
66976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		                           ( (options & 0x10) ? 0x0100 : 0 ) );
67076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "set mdio_register. " );
67176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
67276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
67376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
67476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
67576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Support function: ifec_mdio_write
67676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
67776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This probably writes to the "physical media interface chip".
67876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * -- REW
67976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
68076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_mdio_write ( struct net_device *netdev,
68176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman                             int phy_id, int location, int value )
68276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
68376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
68476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
68576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int val;
68676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int boguscnt = 64*4;     /* <64 usec. to complete, typ 27 ticks */
68776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
68876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_mdio_write\n" );
68976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
69076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outl ( 0x04000000 | ( location << 16 ) | ( phy_id << 21 ) | value,
69176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       ioaddr + CSRCtrlMDI );
69276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	do {
69376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		udelay ( 16 );
69476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
69576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		val = inl ( ioaddr + CSRCtrlMDI );
69676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( --boguscnt < 0 ) {
69776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( " ifec_mdio_write() time out with val = %X.\n",
69876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      val );
69976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
70076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
70176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} while (! ( val & 0x10000000 ) );
70276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return val & 0xffff;
70376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
70476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
70676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Resets the hardware.
70776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
70876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
70976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
71076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_reset ( struct net_device *netdev )
71176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
71276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
71376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
71476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
71576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_reset\n" );
71676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
71776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* do partial reset first */
71876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outl ( PortPartialReset, ioaddr + CSRPort );
71976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inw ( ioaddr + SCBStatus );
72076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	udelay ( 20 );
72176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
72276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* full reset */
72376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outl ( PortReset, ioaddr + CSRPort );
72476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	inw ( ioaddr + SCBStatus );
72576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	udelay ( 20 );
72676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
72776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* disable interrupts again */
72876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_net_irq ( netdev, 0 );
72976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
73076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
73176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
73276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * free()s the tx/rx rings.
73376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
73476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
73576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
73676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_free ( struct net_device *netdev )
73776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
73876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev_priv ( netdev );
73976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i;
74076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
74176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_free\n" );
74276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
74376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* free all allocated receive io_buffers */
74476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 0; i < RFD_COUNT; i++ ) {
74576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		free_iob ( priv->rx_iobs[i] );
74676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rx_iobs[i] = NULL;
74776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rfds[i] = NULL;
74876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
74976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
75076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* free TX ring buffer */
75176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_dma ( priv->tcbs, TX_RING_BYTES );
75276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
75376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tcbs = NULL;
75476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
75576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
75676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
75776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initializes an RFD.
75876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
75976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rfd    		RFD struct to initialize
76076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v command		Command word
76176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v link   		Link value
76276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
76376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_rfd_init ( struct ifec_rfd *rfd, s16 command, u32 link )
76476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
76576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_rfd_init\n" );
76676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
76776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->status      = 0;
76876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->command     = command;
76976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->rx_buf_addr = 0xFFFFFFFF;
77076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->count       = 0;
77176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->size        = RFD_PACKET_LEN;
77276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rfd->link        = link;
77376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
77476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
77576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
77676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Send address of new RFD to card
77776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
77876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
77976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
78076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_reprime_ru ( struct net_device *netdev )
78176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
78276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
78376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int cur_rx = priv->cur_rx;
78476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_reprime_ru\n" );
78676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
78776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( priv->rfds[cur_rx] != NULL ) {
78876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd ( netdev, virt_to_bus ( priv->rfds[cur_rx] ),
78976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       RUStart );
79076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd_wait ( netdev );
79176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
79276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
79376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
79476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
79576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check if reprime of RU needed
79676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
79776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
79876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
79976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_check_ru_status ( struct net_device *netdev,
80076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				   unsigned short intr_status )
80176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
80276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
80376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_check_ru_status\n" );
80576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
80676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/*
80776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	* The chip may have suspended reception for various reasons.
80876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	* Check for that, and re-prime it should this be the case.
80976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	*/
81076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	switch ( ( intr_status >> 2 ) & 0xf ) {
81176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 0:  /* Idle */
81276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 4:  /* Ready */
81376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
81476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 1:  /* Suspended */
81576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 2:  /* No resources (RFDs) */
81676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 9:  /* Suspended with no more RBDs */
81776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 10: /* No resources due to no RBDs */
81876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case 12: /* Ready with no RBDs */
81976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "ifec_net_poll: RU reprimed.\n" );
82076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ifec_reprime_ru ( netdev );
82176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
82276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		default:
82376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* reserved values */
82476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "ifec_net_poll: RU state anomaly: %i\n",
82576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      ( inw ( priv->ioaddr + SCBStatus ) >> 2 ) & 0xf );
82676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
82776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
82876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
82976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
83076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define RFD_STATUS ( RFD_OK | RFDRxCol | RFDRxErr | RFDShort | \
83176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     RFDDMAOverrun | RFDNoBufs | RFDCRCError )
83276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
83376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Looks for received packets in the rx ring, reports success or error to
83476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the core accordingly. Starts reallocation of rx ring.
83576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
83676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
83776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
83876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_rx_process ( struct net_device *netdev )
83976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
84076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv   = netdev->priv;
84176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int cur_rx = priv->cur_rx;
84276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct io_buffer *iob = priv->rx_iobs[cur_rx];
84376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_rfd *rfd = priv->rfds[cur_rx];
84476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int rx_len;
84576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	s16 status;
84676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
84776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_rx_process\n" );
84876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
84976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Process any received packets */
85076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( iob && rfd && ( status = rfd->status ) ) {
85176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		rx_len = rfd->count & RFDMaskCount;
85276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
85376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "Got a packet: Len = %d, cur_rx = %d.\n", rx_len,
85476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       cur_rx );
85576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGIO_HD ( (void*)rfd->packet, 0x30 );
85676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
85776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( ( status & RFD_STATUS ) != RFD_OK ) {
85876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "Corrupted packet received. "
85976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      "Status = %#08hx\n", status );
86076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			netdev_rx_err ( netdev, iob, -EINVAL );
86176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
86276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* Hand off the packet to the network subsystem */
86376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			iob_put ( iob, rx_len );
86476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG2 ( "Received packet: %p, len: %d\n", iob, rx_len );
86576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			netdev_rx ( netdev, iob );
86676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
86776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
86876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* make sure we don't reuse this RFD */
86976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rx_iobs[cur_rx] = NULL;
87076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rfds[cur_rx] = NULL;
87176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
87276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Next RFD */
87376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->cur_rx = ( cur_rx + 1 ) % RFD_COUNT;
87476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cur_rx = priv->cur_rx;
87576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		iob = priv->rx_iobs[cur_rx];
87676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		rfd = priv->rfds[cur_rx];
87776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
87876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
87976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_refill_rx_ring ( netdev );
88076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
88176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
88276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
88376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Allocates io_buffer, set pointers in ifec_private structure accordingly,
88476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * reserves space for RFD header in io_buffer.
88576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
88676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
88776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cur		Descriptor number to work on
88876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cmd		Value to set cmd field in RFD to
88976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v link		Pointer to ned RFD
89076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		0 on success, negative on failure
89176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
89276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_get_rx_desc ( struct net_device *netdev, int cur, int cmd,
89376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      int link )
89476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
89576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
89676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_rfd *rfd  = priv->rfds[cur];
89776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
89876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_get_rx_desc\n" );
89976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
90076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->rx_iobs[cur] = alloc_iob ( sizeof ( *rfd ) );
90176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! priv->rx_iobs[cur] ) {
90276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "alloc_iob failed. desc. nr: %d\n", cur );
90376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rfds[cur] = NULL;
90476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOMEM;
90576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
90676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
90776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Initialize new tail. */
90876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->rfds[cur] = priv->rx_iobs[cur]->data;
90976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_rfd_init ( priv->rfds[cur], cmd, link );
91076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	iob_reserve ( priv->rx_iobs[cur], RFD_HEADER_LEN );
91176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
91276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
91376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
91476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
91576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
91676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Allocate new descriptor entries and initialize them if needed
91776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
91876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
91976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
92076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_refill_rx_ring ( struct net_device *netdev )
92176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
92276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
92376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i, cur_rx;
92476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned short intr_status;
92576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
92676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_refill_rx_ring\n" );
92776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
92876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 0; i < RFD_COUNT; i++ ) {
92976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cur_rx = ( priv->cur_rx + i ) % RFD_COUNT;
93076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* only refill if empty */
93176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( priv->rfds[cur_rx] != NULL ||
93276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     priv->rx_iobs[cur_rx] != NULL )
93376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			continue;
93476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
93576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "refilling RFD %d\n", cur_rx );
93676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
93776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( ifec_get_rx_desc ( netdev, cur_rx,
93876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     CmdSuspend | CmdEndOfList, 0 ) == 0 ) {
93976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( i > 0 ) {
94076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				int prev_rx = ( ( ( cur_rx + RFD_COUNT ) - 1 )
94176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman						% RFD_COUNT );
94276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				struct ifec_rfd *rfd = priv->rfds[prev_rx];
94376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
94476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				rfd->command = 0;
94576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				rfd->link = virt_to_bus ( priv->rfds[cur_rx] );
94676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
94776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
94876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
94976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
95076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	intr_status = inw ( priv->ioaddr + SCBStatus );
95176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_check_ru_status ( netdev, intr_status );
95276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
95376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
95476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
95576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initial allocation & initialization of the rx ring.
95676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
95776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev  		Device of rx ring.
95876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc    		Non-zero if error occured
95976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
96076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_rx_setup ( struct net_device *netdev )
96176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
96276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
96376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i;
96476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
96576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_rx_setup\n" );
96676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
96776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->cur_rx = 0;
96876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
96976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* init values for ifec_refill_rx_ring() */
97076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 0; i < RFD_COUNT; i++ ) {
97176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rfds[i] = NULL;
97276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->rx_iobs[i] = NULL;
97376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
97476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_refill_rx_ring ( netdev );
97576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
97676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
97776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
97876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
97976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
98076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initiates a SCB command.
98176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
98276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
98376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ptr   		General pointer value for command.
98476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cmd   		Command to issue.
98576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc  		Non-zero if command not issued.
98676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
98776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_scb_cmd ( struct net_device *netdev, u32 ptr, u8 cmd )
98876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
98976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
99076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
99176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
99276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
99376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_scb_cmd\n" );
99476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
99576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	rc = ifec_scb_cmd_wait ( netdev );	/* Wait until ready */
99676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !rc ) {
99776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		outl ( ptr, ioaddr + SCBPointer );
99876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		outb ( cmd, ioaddr + SCBCmd );		/* Issue command */
99976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
100076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return rc;
100176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
100276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
100376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
100476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Wait for command unit to accept a command.
100576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
100676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cmd_ioaddr	I/O address of command register.
100776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc      	Non-zero if command timed out.
100876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
100976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_scb_cmd_wait ( struct net_device *netdev )
101076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
101176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
101276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long cmd_ioaddr = priv->ioaddr + SCBCmd;
101376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc, wait = CU_CMD_TIMEOUT;
101476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
101576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_scb_cmd_wait\n" );
101676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
101776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( ; wait && ( rc = inb ( cmd_ioaddr ) ); wait-- )
101876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		udelay ( 1 );
101976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
102076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !wait )
102176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "ifec_scb_cmd_wait timeout!\n" );
102276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return rc;
102376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
102476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
102576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
102676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check status of transmitted packets & perform tx completions.
102776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
102876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev    	Network device.
102976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
103076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void ifec_tx_process ( struct net_device *netdev )
103176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
103276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
103376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_tcb *tcb = priv->tcb_tail;
103476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	s16 status;
103576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
103676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_tx_process\n" );
103776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
103876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Check status of transmitted packets */
103976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ( status = tcb->status ) && tcb->iob ) {
104076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( status & TCB_U ) {
104176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* report error to gPXE */
104276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "ifec_tx_process : tx error!\n " );
104376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			netdev_tx_complete_err ( netdev, tcb->iob, -EINVAL );
104476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
104576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* report successful transmit */
104676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			netdev_tx_complete ( netdev, tcb->iob );
104776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
104876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "tx completion\n" );
104976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
105076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->iob = NULL;
105176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->status = 0;
105276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
105376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->tcb_tail = tcb->next;	/* Next TCB */
105476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb = tcb->next;
105576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
105676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
105776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
105876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
105976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Allocates & initialize tx resources.
106076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
106176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev    	Network device.
106276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc      	Non-zero if error occurred.
106376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
106476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int ifec_tx_setup ( struct net_device *netdev )
106576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
106676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
106776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_tcb *tcb;
106876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int i;
106976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
107076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_tx_setup\n" );
107176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
107276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* allocate tx ring */
107376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tcbs = malloc_dma ( TX_RING_BYTES, CB_ALIGN );
107476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( !priv->tcbs ) {
107576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "TX-ring allocation failed\n" );
107676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOMEM;
107776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
107876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
107976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb = priv->tcb_tail = priv->tcbs;
108076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tx_curr = priv->tx_tail = 0;
108176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tx_cnt = 0;
108276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
108376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 0; i < TCB_COUNT; i++, tcb++ ) {
108476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->status    = 0;
108576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->count     = 0x01208000;
108676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->iob       = NULL;
108776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->tbda_addr = virt_to_bus ( &tcb->tbd_addr0 );
108876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->link      = virt_to_bus ( tcb + 1 );
108976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		tcb->next      = tcb + 1;
109076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
109176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* We point tcb_head at the last TCB, so the first ifec_net_transmit()
109276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * will use the first (head->next) TCB to transmit. */
109376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tcb_head = --tcb;
109476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->link = virt_to_bus ( priv->tcbs );
109576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	tcb->next = priv->tcbs;
109676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
109776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
109876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
109976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
110076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
110176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Wake up the Command Unit and issue a Resume/Start.
110276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
110376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device containing Command Unit
110476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
110576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The time between clearing the S bit and issuing Resume must be as short as
110676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * possible to prevent a race condition. As noted in linux eepro100.c :
110776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   Note: Watch out for the potential race condition here: imagine
110876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	erasing the previous suspend
110976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *		the chip processes the previous command
111076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *		the chip processes the final command, and suspends
111176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *	doing the CU_RESUME
111276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *		the chip processes the next-yet-valid post-final-command.
111376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   So blindly sending a CU_RESUME is only safe if we do it immediately after
111476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   erasing the previous CmdSuspend, without the possibility of an intervening
111576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *   delay.
111676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
111776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid ifec_tx_wake ( struct net_device *netdev )
111876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
111976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_private *priv = netdev->priv;
112076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long ioaddr = priv->ioaddr;
112176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ifec_tcb *tcb = priv->tcb_head->next;
112276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
112376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGP ( "ifec_tx_wake\n" );
112476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
112576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* For the special case of the first transmit, we issue a START. The
112676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * card won't RESUME after the configure command. */
112776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( priv->configured ) {
112876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		priv->configured = 0;
112976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
113076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd_wait ( netdev );
113176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
113276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
113376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
113476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Resume if suspended. */
113576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	switch ( ( inw ( ioaddr + SCBStatus ) >> 6 ) & 0x3 ) {
113676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case 0:  /* Idle - We should not reach this state. */
113776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "ifec_tx_wake: tx idle!\n" );
113876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd ( netdev, virt_to_bus ( tcb ), CUStart );
113976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ifec_scb_cmd_wait ( netdev );
114076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
114176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case 1:  /* Suspended */
114276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "s" );
114376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
114476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	default: /* Active */
114576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG2 ( "a" );
114676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
114776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd_wait ( netdev );
114876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outl ( 0, ioaddr + SCBPointer );
114976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	priv->tcb_head->command &= ~CmdSuspend;
115076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Immediately issue Resume command */
115176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	outb ( CUResume, ioaddr + SCBCmd );
115276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ifec_scb_cmd_wait ( netdev );
115376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
115476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
115576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*********************************************************************/
115676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
115776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct pci_device_id ifec_nics[] = {
115876d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1029, "id1029",        "Intel EtherExpressPro100 ID1029", 0),
115976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1030, "id1030",        "Intel EtherExpressPro100 ID1030", 0),
116076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1031, "82801cam",      "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
116176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection", 0),
116276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection", 0),
116376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection", 0),
116476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
116576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
116676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller", 0),
116776d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1038, "id1038",        "Intel PRO/100 VM Network Connection", 0),
116876d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1039, "82562et",       "Intel PRO100 VE 82562ET", 0),
116976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x103a, "id103a",        "Intel Corporation 82559 InBusiness 10/100", 0),
117076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x103b, "82562etb",      "Intel PRO100 VE 82562ETB", 0),
117176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection", 0),
117276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection", 0),
117376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection", 0),
117476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1051, "prove",         "Intel PRO/100 VE Network Connection", 0),
117576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1059, "82551qm",       "Intel PRO/100 M Mobile Connection", 0),
117676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1209, "82559er",       "Intel EtherExpressPro100 82559ER", 0),
117776d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1227, "82865",         "Intel 82865 EtherExpress PRO/100A", 0),
117876d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1228, "82556",         "Intel 82556 EtherExpress PRO/100 Smart", 0),
117976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1229, "eepro100",      "Intel EtherExpressPro100", 0),
118076d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x2449, "82562em",       "Intel EtherExpressPro100 82562EM", 0),
118176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x2459, "82562-1",       "Intel 82562 based Fast Ethernet Connection", 0),
118276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x245d, "82562-2",       "Intel 82562 based Fast Ethernet Connection", 0),
118376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1050, "82562ez",       "Intel 82562EZ Network Connection", 0),
118476d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0),
118576d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x1065, "82562-3",       "Intel 82562 based Fast Ethernet Connection", 0),
118676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0),
118776d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanPCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0),
118876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
118976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
119076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
119176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
119276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 2003/03/17 gbaum */
119376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
119476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct pci_driver ifec_driver __pci_driver = {
119576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.ids      = ifec_nics,
119676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.id_count = ( sizeof (ifec_nics) / sizeof (ifec_nics[0]) ),
119776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.probe    = ifec_pci_probe,
119876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.remove   = ifec_pci_remove
119976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
120076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
120176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
120276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Local variables:
120376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *  c-basic-offset: 8
120476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *  c-indent-level: 8
120576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *  tab-width: 8
120676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * End:
120776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1208