From f4faa27dfd40a2b5af5b9a8e3b0acee3d05c751a Mon Sep 17 00:00:00 2001 From: Andrei Faur Date: Sun, 20 Jun 2010 22:23:01 +0300 Subject: [PATCH] [pcnet32] Replace pcnet32 with native driver This patch replaces the old pcnet32 driver with a new one that uses iPXE's API. Signed-off-by: Andrei Faur Signed-off-by: Stefan Hajnoczi Signed-off-by: Michael Brown --- src/drivers/net/pcnet32.c | 1842 ++++++++++++++++++++----------------- src/drivers/net/pcnet32.h | 181 ++++ 2 files changed, 1172 insertions(+), 851 deletions(-) create mode 100644 src/drivers/net/pcnet32.h diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c index b7b4c025..a481f412 100644 --- a/src/drivers/net/pcnet32.c +++ b/src/drivers/net/pcnet32.c @@ -1,1021 +1,1161 @@ -/************************************************************************** -* -* pcnet32.c -- Etherboot device driver for the AMD PCnet32 -* Written 2003-2003 by Timothy Legge -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Portions of this code based on: -* pcnet32.c: An AMD PCnet32 ethernet driver for linux: -* -* (C) 1996-1999 Thomas Bogendoerfer -* See Linux Driver for full information -* -* The transmit and poll functions were written with reference to: -* lance.c - LANCE NIC driver for Etherboot written by Ken Yap -* -* Linux Driver Version 1.27a, 10.02.2002 -* -* -* REVISION HISTORY: -* ================ -* v1.0 08-06-2003 timlegge Initial port of Linux driver -* v1.1 08-23-2003 timlegge Add multicast support -* v1.2 01-17-2004 timlegge Initial driver output cleanup -* v1.3 03-29-2004 timlegge More driver cleanup -* -* Indent Options: indent -kr -i8 -***************************************************************************/ +/* + * Copyright (c) 2010 Andrei Faur + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ FILE_LICENCE ( GPL2_OR_LATER ); -#include "etherboot.h" -#include "nic.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "mii.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcnet32.h" -/* void hex_dump(const char *data, const unsigned int len); */ - -/* Etherboot Specific definations */ -#define drv_version "v1.3" -#define drv_date "03-29-2004" - -static u32 ioaddr; /* Globally used for the card's io address */ -static struct nic_operations pcnet32_operations; - -#ifdef EDEBUG -#define dprintf(x) printf x -#else -#define dprintf(x) -#endif - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) - -/* End Etherboot Specific */ - -static int cards_found = 0 /* __initdata */ ; - -#ifdef REMOVE -/* FIXME: Remove these they are probably pointless */ - -/* - * VLB I/O addresses - */ -static unsigned int pcnet32_portlist[] /*__initdata */ = -{ 0x300, 0x320, 0x340, 0x360, 0 }; - -static int pcnet32_debug = 1; -static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ -static int pcnet32vlb; /* check for VLB cards ? */ - -static struct net_device *pcnet32_dev; - -static int max_interrupt_work = 80; -static int rx_copybreak = 200; -#endif -#define PCNET32_PORT_AUI 0x00 -#define PCNET32_PORT_10BT 0x01 -#define PCNET32_PORT_GPSI 0x02 -#define PCNET32_PORT_MII 0x03 - -#define PCNET32_PORT_PORTSEL 0x03 -#define PCNET32_PORT_ASEL 0x04 -#define PCNET32_PORT_100 0x40 -#define PCNET32_PORT_FD 0x80 - -#define PCNET32_DMA_MASK 0xffffffff - -/* - * table to translate option values from tulip - * to internal options - */ -static unsigned char options_mapping[] = { - PCNET32_PORT_ASEL, /* 0 Auto-select */ - PCNET32_PORT_AUI, /* 1 BNC/AUI */ - PCNET32_PORT_AUI, /* 2 AUI/BNC */ - PCNET32_PORT_ASEL, /* 3 not supported */ - PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ - PCNET32_PORT_ASEL, /* 5 not supported */ - PCNET32_PORT_ASEL, /* 6 not supported */ - PCNET32_PORT_ASEL, /* 7 not supported */ - PCNET32_PORT_ASEL, /* 8 not supported */ - PCNET32_PORT_MII, /* 9 MII 10baseT */ - PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ - PCNET32_PORT_MII, /* 11 MII (autosel) */ - PCNET32_PORT_10BT, /* 12 10BaseT */ - PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ - PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */ - PCNET32_PORT_ASEL /* 15 not supported */ -}; - -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS]; -static int full_duplex[MAX_UNITS]; - -/* - * Theory of Operation - * - * This driver uses the same software structure as the normal lance - * driver. So look for a verbose description in lance.c. The differences - * to the normal lance driver is the use of the 32bit mode of PCnet32 - * and PCnetPCI chips. Because these chips are 32bit chips, there is no - * 16MB limitation and we don't need bounce buffers. - */ - - - -/* - * Set the number of Tx and Rx buffers, using Log_2(# buffers). - * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. - * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). - */ -#ifndef PCNET32_LOG_TX_BUFFERS -#define PCNET32_LOG_TX_BUFFERS 1 -#define PCNET32_LOG_RX_BUFFERS 2 -#endif - -#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -/* FIXME: Fix this to allow multiple tx_ring descriptors */ -#define TX_RING_LEN_BITS 0x0000 /*PCNET32_LOG_TX_BUFFERS) << 12) */ - -#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) - -#define PKT_BUF_SZ 1544 - -/* Offsets from base I/O address. */ -#define PCNET32_WIO_RDP 0x10 -#define PCNET32_WIO_RAP 0x12 -#define PCNET32_WIO_RESET 0x14 -#define PCNET32_WIO_BDP 0x16 - -#define PCNET32_DWIO_RDP 0x10 -#define PCNET32_DWIO_RAP 0x14 -#define PCNET32_DWIO_RESET 0x18 -#define PCNET32_DWIO_BDP 0x1C - -#define PCNET32_TOTAL_SIZE 0x20 - -/* The PCNET32 Rx and Tx ring descriptors. */ -struct pcnet32_rx_head { - u32 base; - s16 buf_length; - s16 status; - u32 msg_length; - u32 reserved; -}; - -struct pcnet32_tx_head { - u32 base; - s16 length; - s16 status; - u32 misc; - u32 reserved; -}; - -/* The PCNET32 32-Bit initialization block, described in databook. */ -struct pcnet32_init_block { - u16 mode; - u16 tlen_rlen; - u8 phys_addr[6]; - u16 reserved; - u32 filter[2]; - /* Receive and transmit ring base, along with extra bits. */ - u32 rx_ring; - u32 tx_ring; -}; -/* PCnet32 access functions */ -struct pcnet32_access { - u16(*read_csr) (unsigned long, int); - void (*write_csr) (unsigned long, int, u16); - u16(*read_bcr) (unsigned long, int); - void (*write_bcr) (unsigned long, int, u16); - u16(*read_rap) (unsigned long); - void (*write_rap) (unsigned long, u16); - void (*reset) (unsigned long); -}; - -/* Define the TX and RX Descriptors and Rings */ -struct { - struct pcnet32_tx_head tx_ring[TX_RING_SIZE] - __attribute__ ((aligned(16))); - struct pcnet32_rx_head rx_ring[RX_RING_SIZE] - __attribute__ ((aligned(16))); - unsigned char txb[TX_RING_SIZE][PKT_BUF_SZ]; - unsigned char rxb[RX_RING_SIZE][PKT_BUF_SZ]; -} pcnet32_bufs __shared; - - -/* - * The first three fields of pcnet32_private are read by the ethernet device - * so we allocate the structure should be allocated by pci_alloc_consistent(). - */ -#define MII_CNT 4 -struct pcnet32_private { - struct pcnet32_init_block init_block; - struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ - const char *name; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - struct sk_buff *rx_skbuff[RX_RING_SIZE]; - struct pcnet32_access a; - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - char tx_full; - int options; - int shared_irq:1, /* shared irq possible */ - ltint:1, /* enable TxDone-intr inhibitor */ - dxsuflo:1, /* disable transmit stop on uflo */ - mii:1; /* mii port available */ - struct mii_if_info mii_if; - unsigned char phys[MII_CNT]; - struct net_device *next; - int full_duplex:1; -} lpx; - -static struct pcnet32_private *lp; - -static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num); -#if 0 -static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num, - int val); -#endif -enum pci_flags_bit { - PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4, - PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 = - 0x10 << 2, PCI_ADDR3 = 0x10 << 3, -}; - - -static u16 pcnet32_wio_read_csr(unsigned long addr, int index) +static u16 pcnet32_wio_read_csr ( unsigned long addr, int index ) { - outw(index, addr + PCNET32_WIO_RAP); - return inw(addr + PCNET32_WIO_RDP); + outw ( index, addr + PCNET32_WIO_RAP ); + return inw ( addr + PCNET32_WIO_RDP ); } -static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val) +static void pcnet32_wio_write_csr ( unsigned long addr, int index, u16 val ) { - outw(index, addr + PCNET32_WIO_RAP); - outw(val, addr + PCNET32_WIO_RDP); + outw ( index, addr + PCNET32_WIO_RAP ); + outw ( val, addr + PCNET32_WIO_RDP ); } -static u16 pcnet32_wio_read_bcr(unsigned long addr, int index) +static u16 pcnet32_wio_read_bcr ( unsigned long addr, int index ) { - outw(index, addr + PCNET32_WIO_RAP); - return inw(addr + PCNET32_WIO_BDP); + outw ( index, addr + PCNET32_WIO_RAP ); + return inw ( addr + PCNET32_WIO_BDP ); } -static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val) +static void pcnet32_wio_write_bcr ( unsigned long addr, int index, u16 val ) { - outw(index, addr + PCNET32_WIO_RAP); - outw(val, addr + PCNET32_WIO_BDP); + outw ( index, addr + PCNET32_WIO_RAP ); + outw ( val, addr + PCNET32_WIO_BDP ); } -static u16 pcnet32_wio_read_rap(unsigned long addr) +static u16 pcnet32_wio_read_rap ( unsigned long addr ) { - return inw(addr + PCNET32_WIO_RAP); + return inw ( addr + PCNET32_WIO_RAP ); } -static void pcnet32_wio_write_rap(unsigned long addr, u16 val) +static void pcnet32_wio_write_rap ( unsigned long addr , u16 val ) { - outw(val, addr + PCNET32_WIO_RAP); + outw ( val, addr + PCNET32_WIO_RAP ); } -static void pcnet32_wio_reset(unsigned long addr) +static void pcnet32_wio_reset ( unsigned long addr ) { - inw(addr + PCNET32_WIO_RESET); + inw ( addr + PCNET32_DWIO_RESET ); } -static int pcnet32_wio_check(unsigned long addr) +static int pcnet32_wio_check ( unsigned long addr ) { - outw(88, addr + PCNET32_WIO_RAP); - return (inw(addr + PCNET32_WIO_RAP) == 88); + outw ( 88, addr + PCNET32_WIO_RAP ); + return ( inw ( addr + PCNET32_WIO_RAP ) == 88 ); } static struct pcnet32_access pcnet32_wio = { - read_csr:pcnet32_wio_read_csr, - write_csr:pcnet32_wio_write_csr, - read_bcr:pcnet32_wio_read_bcr, - write_bcr:pcnet32_wio_write_bcr, - read_rap:pcnet32_wio_read_rap, - write_rap:pcnet32_wio_write_rap, - reset:pcnet32_wio_reset + .read_csr = pcnet32_wio_read_csr, + .write_csr = pcnet32_wio_write_csr, + .read_bcr = pcnet32_wio_read_bcr, + .write_bcr = pcnet32_wio_write_bcr, + .read_rap = pcnet32_wio_read_rap, + .write_rap = pcnet32_wio_write_rap, + .reset = pcnet32_wio_reset, }; -static u16 pcnet32_dwio_read_csr(unsigned long addr, int index) +static u16 pcnet32_dwio_read_csr ( unsigned long addr, int index ) { - outl(index, addr + PCNET32_DWIO_RAP); - return (inl(addr + PCNET32_DWIO_RDP) & 0xffff); + outl ( index, addr + PCNET32_DWIO_RAP ); + return ( inl ( addr + PCNET32_DWIO_RDP ) & 0xffff ); } -static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val) +static void pcnet32_dwio_write_csr ( unsigned long addr, int index, u16 val ) { - outl(index, addr + PCNET32_DWIO_RAP); - outl(val, addr + PCNET32_DWIO_RDP); + outl ( index, addr + PCNET32_DWIO_RAP ); + outl ( val, addr + PCNET32_DWIO_RDP ); } -static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index) +static u16 pcnet32_dwio_read_bcr ( unsigned long addr, int index ) { - outl(index, addr + PCNET32_DWIO_RAP); - return (inl(addr + PCNET32_DWIO_BDP) & 0xffff); + outl ( index, addr + PCNET32_DWIO_RAP ); + return ( inl ( addr + PCNET32_DWIO_BDP ) & 0xffff ); } -static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val) +static void pcnet32_dwio_write_bcr ( unsigned long addr, int index, u16 val ) { - outl(index, addr + PCNET32_DWIO_RAP); - outl(val, addr + PCNET32_DWIO_BDP); + outl ( index, addr + PCNET32_DWIO_RAP ); + outl ( val, addr + PCNET32_DWIO_BDP ); } -static u16 pcnet32_dwio_read_rap(unsigned long addr) +static u16 pcnet32_dwio_read_rap ( unsigned long addr ) { - return (inl(addr + PCNET32_DWIO_RAP) & 0xffff); + return ( inl ( addr + PCNET32_DWIO_RAP ) & 0xffff ); } -static void pcnet32_dwio_write_rap(unsigned long addr, u16 val) +static void pcnet32_dwio_write_rap ( unsigned long addr , u16 val ) { - outl(val, addr + PCNET32_DWIO_RAP); + outl ( val, addr + PCNET32_DWIO_RAP ); } -static void pcnet32_dwio_reset(unsigned long addr) +static void pcnet32_dwio_reset ( unsigned long addr ) { - inl(addr + PCNET32_DWIO_RESET); + inl ( addr + PCNET32_DWIO_RESET ); } -static int pcnet32_dwio_check(unsigned long addr) +static int pcnet32_dwio_check ( unsigned long addr ) { - outl(88, addr + PCNET32_DWIO_RAP); - return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88); + outl ( 88, addr + PCNET32_DWIO_RAP ); + return ( ( inl ( addr + PCNET32_DWIO_RAP ) & 0xffff ) == 88 ); } + static struct pcnet32_access pcnet32_dwio = { - read_csr:pcnet32_dwio_read_csr, - write_csr:pcnet32_dwio_write_csr, - read_bcr:pcnet32_dwio_read_bcr, - write_bcr:pcnet32_dwio_write_bcr, - read_rap:pcnet32_dwio_read_rap, - write_rap:pcnet32_dwio_write_rap, - reset:pcnet32_dwio_reset + .read_csr = pcnet32_dwio_read_csr, + .write_csr = pcnet32_dwio_write_csr, + .read_bcr = pcnet32_dwio_read_bcr, + .write_bcr = pcnet32_dwio_write_bcr, + .read_rap = pcnet32_dwio_read_rap, + .write_rap = pcnet32_dwio_write_rap, + .reset = pcnet32_dwio_reset, }; - -/* Initialize the PCNET32 Rx and Tx rings. */ -static int pcnet32_init_ring(struct nic *nic) +static int +pcnet32_mdio_read ( struct net_device *netdev, int phy, int reg ) { + struct pcnet32_private *priv = netdev->priv; + unsigned long ioaddr = priv->pci_dev->ioaddr; + u16 val_out; + + if ( ! priv->mii ) + return 0; + + /* First, select PHY chip and the register we want to read */ + priv->a->write_bcr ( ioaddr, 33, + ( ( phy & 0x1f ) << 5 ) | ( reg & 0x1f ) ); + + /* Read the selected register's value */ + val_out = priv->a->read_bcr ( ioaddr, 34 ); + + return val_out; +} + +static void +__unused pcnet32_mdio_write ( struct net_device *netdev, int phy, int reg, int val ) +{ + struct pcnet32_private *priv = netdev->priv; + unsigned long ioaddr = priv->pci_dev->ioaddr; + + if ( ! priv->mii ) + return; + + /* First, select PHY chip and the register we want to write to */ + priv->a->write_bcr ( ioaddr, 33, + ( ( phy & 0x1f ) << 5 ) | ( reg & 0x1f ) ); + + /* Write val to the selected register */ + priv->a->write_bcr ( ioaddr, 34, val ); +} + + +/** + * pcnet32_refill_rx_ring - Allocates iobufs for every Rx descriptor + * that doesn't have one and isn't in use by the hardware + * + * @v priv Driver private structure + */ +static void +pcnet32_refill_rx_ring ( struct pcnet32_private *priv ) +{ + struct pcnet32_rx_desc *rx_curr_desc; + u16 status; int i; - lp->tx_full = 0; - lp->cur_rx = lp->cur_tx = 0; + DBGP ( "pcnet32_refill_rx_ring\n" ); - for (i = 0; i < RX_RING_SIZE; i++) { - pcnet32_bufs.rx_ring[i].base = - virt_to_le32desc(&pcnet32_bufs.rxb[i]); - pcnet32_bufs.rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); - pcnet32_bufs.rx_ring[i].status = le16_to_cpu(0x8000); + for ( i = 0; i < RX_RING_SIZE; i++ ) { + rx_curr_desc = priv->rx_base + i; + + status = le16_to_cpu ( rx_curr_desc->status ); + + /* Don't touch descriptors owned by the hardware */ + if ( status & DescOwn ) + continue; + + /* Descriptors with iobufs still need to be processed */ + if ( priv->rx_iobuf[i] != NULL ) + continue; + + /* If alloc_iob fails, try again later (next poll) */ + if ( ! ( priv->rx_iobuf[i] = alloc_iob ( PKT_BUF_SIZE ) ) ) { + DBG ( "Refill rx ring failed\n" ); + break; + } + + rx_curr_desc->base = + cpu_to_le32 ( virt_to_bus ( priv->rx_iobuf[i]->data ) ); + rx_curr_desc->buf_length = cpu_to_le16 ( -PKT_BUF_SIZE ); + rx_curr_desc->msg_length = rx_curr_desc->reserved = 0; + + /* Owner changes after the other status fields are set */ + wmb(); + rx_curr_desc->status = cpu_to_le16 ( DescOwn ); } - /* The Tx buffer address is filled in as needed, but we do need to clear - the upper ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - pcnet32_bufs.tx_ring[i].base = 0; - pcnet32_bufs.tx_ring[i].status = 0; +} + +/** + * pcnet32_setup_rx_resources - allocate Rx resources (Descriptors) + * + * @v priv Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +pcnet32_setup_rx_resources ( struct pcnet32_private *priv ) +{ + DBGP ( "pcnet32_setup_rx_resources\n" ); + + priv->rx_base = malloc_dma ( RX_RING_BYTES, RX_RING_ALIGN ); + + DBG ( "priv->rx_base = %#08lx\n", virt_to_bus ( priv->rx_base ) ); + + if ( ! priv->rx_base ) { + return -ENOMEM; } + memset ( priv->rx_base, 0, RX_RING_BYTES ); + + pcnet32_refill_rx_ring ( priv ); + + priv->rx_curr = 0; - lp->init_block.tlen_rlen = - le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = nic->node_addr[i]; - lp->init_block.rx_ring = virt_to_le32desc(&pcnet32_bufs.rx_ring[0]); - lp->init_block.tx_ring = virt_to_le32desc(&pcnet32_bufs.tx_ring[0]); return 0; } -/************************************************************************** -RESET - Reset adapter -***************************************************************************/ -static void pcnet32_reset(struct nic *nic) +static void +pcnet32_free_rx_resources ( struct pcnet32_private *priv ) { - /* put the card in its initial state */ - u16 val; int i; - /* Reset the PCNET32 */ - lp->a.reset(ioaddr); + DBGP ( "pcnet32_free_rx_resources\n" ); - /* switch pcnet32 to 32bit mode */ - lp->a.write_bcr(ioaddr, 20, 2); + free_dma ( priv->rx_base, RX_RING_BYTES ); - /* set/reset autoselect bit */ - val = lp->a.read_bcr(ioaddr, 2) & ~2; - if (lp->options & PCNET32_PORT_ASEL) - val |= 2; - lp->a.write_bcr(ioaddr, 2, val); - - /* handle full duplex setting */ - if (lp->full_duplex) { - val = lp->a.read_bcr(ioaddr, 9) & ~3; - if (lp->options & PCNET32_PORT_FD) { - val |= 1; - if (lp->options == - (PCNET32_PORT_FD | PCNET32_PORT_AUI)) - val |= 2; - } else if (lp->options & PCNET32_PORT_ASEL) { - /* workaround of xSeries250, turn on for 79C975 only */ - i = ((lp->a. - read_csr(ioaddr, - 88) | (lp->a.read_csr(ioaddr, - 89) << 16)) >> - 12) & 0xffff; - if (i == 0x2627) - val |= 3; - } - lp->a.write_bcr(ioaddr, 9, val); + for ( i = 0; i < RX_RING_SIZE; i++ ) { + free_iob ( priv->rx_iobuf[i] ); + priv->rx_iobuf[i] = NULL; } - - /* set/reset GPSI bit in test register */ - val = lp->a.read_csr(ioaddr, 124) & ~0x10; - if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI) - val |= 0x10; - lp->a.write_csr(ioaddr, 124, val); - - if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) { - val = lp->a.read_bcr(ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */ - if (lp->options & PCNET32_PORT_FD) - val |= 0x10; - if (lp->options & PCNET32_PORT_100) - val |= 0x08; - lp->a.write_bcr(ioaddr, 32, val); - } else { - if (lp->options & PCNET32_PORT_ASEL) { /* enable auto negotiate, setup, disable fd */ - val = lp->a.read_bcr(ioaddr, 32) & ~0x98; - val |= 0x20; - lp->a.write_bcr(ioaddr, 32, val); - } - } - -#ifdef DO_DXSUFLO - if (lp->dxsuflo) { /* Disable transmit stop on underflow */ - val = lp->a.read_csr(ioaddr, 3); - val |= 0x40; - lp->a.write_csr(ioaddr, 3, val); - } -#endif - if (1) - { - //disable interrupts - val = lp->a.read_csr(ioaddr, 3); - val = val - | (1 << 14) //BABLM intr disabled - | (1 << 12) //MISSM missed frame mask intr disabled - | (1 << 10) //RINTM receive intr disabled - | (1 << 9) //TINTM transmit intr disabled - | (1 << 8) //IDONM init done intr disabled - ; - lp->a.write_csr(ioaddr, 3, val); - } - - if (lp->ltint) { /* Enable TxDone-intr inhibitor */ - val = lp->a.read_csr(ioaddr, 5); - val |= (1 << 14); - lp->a.write_csr(ioaddr, 5, val); - } - lp->init_block.mode = - le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); - lp->init_block.filter[0] = 0xffffffff; - lp->init_block.filter[1] = 0xffffffff; - - pcnet32_init_ring(nic); - - - /* Re-initialize the PCNET32, and start it when done. */ - lp->a.write_csr(ioaddr, 1, - (virt_to_bus(&lp->init_block)) & 0xffff); - lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16); - lp->a.write_csr(ioaddr, 4, 0x0915); - lp->a.write_csr(ioaddr, 0, 0x0001); - - - i = 0; - while (i++ < 100) - if (lp->a.read_csr(ioaddr, 0) & 0x0100) - break; - /* - * We used to clear the InitDone bit, 0x0100, here but Mark Stockton - * reports that doing so triggers a bug in the '974. - */ - lp->a.write_csr(ioaddr, 0, 0x0042); - - dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0))); - } -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int pcnet32_poll(struct nic *nic __unused, int retrieve) +/** + * pcnet32_setup_tx_resources - allocate Tx resources (Descriptors) + * + * @v priv Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +pcnet32_setup_tx_resources ( struct pcnet32_private *priv ) { - /* return true if there's an ethernet packet ready to read */ - /* nic->packet should contain data on return */ - /* nic->packetlen should contain length of data */ + DBGP ( "pcnet32_setup_tx_resources\n" ); - signed char status; - int entry; + priv->tx_base = malloc_dma ( TX_RING_BYTES, TX_RING_ALIGN ); - entry = lp->cur_rx & RX_RING_MOD_MASK; - status = (le16_to_cpu(pcnet32_bufs.rx_ring[entry].status) >> 8); - - if (status < 0) - return 0; - - if ( ! retrieve ) return 1; - - if (status == 0x03) { - nic->packetlen = - (le32_to_cpu(pcnet32_bufs.rx_ring[entry].msg_length) - & 0xfff) - 4; - memcpy(nic->packet, &pcnet32_bufs.rxb[entry], nic->packetlen); - - /* Andrew Boyd of QNX reports that some revs of the 79C765 - * clear the buffer length */ - pcnet32_bufs.rx_ring[entry].buf_length - = le16_to_cpu(-PKT_BUF_SZ); - /* prime for next receive */ - pcnet32_bufs.rx_ring[entry].status |= le16_to_cpu(0x8000); - /* Switch to the next Rx ring buffer */ - lp->cur_rx++; - - } else { - return 0; + if ( ! priv->tx_base ) { + return -ENOMEM; } - return 1; + memset ( priv->tx_base, 0, TX_RING_BYTES ); + + DBG ( "priv->tx_base = %#08lx\n", virt_to_bus ( priv->tx_base ) ); + + priv->tx_curr = 0; + priv->tx_fill_ctr = 0; + priv->tx_tail = 0; + + return 0; } -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void pcnet32_transmit(struct nic *nic __unused, const char *d, /* Destination */ - unsigned int t, /* Type */ - unsigned int s, /* size */ - const char *p) -{ /* Packet */ - /* send the packet to destination */ - unsigned long time; - u8 *ptxb; - u16 nstype; - u16 status; - int entry = 0; /*lp->cur_tx & TX_RING_MOD_MASK; */ - - status = 0x8300; - /* point to the current txb incase multiple tx_rings are used */ - ptxb = pcnet32_bufs.txb[lp->cur_tx]; - - /* copy the packet to ring buffer */ - memcpy(ptxb, d, ETH_ALEN); /* dst */ - memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ - nstype = htons((u16) t); /* type */ - memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */ - memcpy(ptxb + ETH_HLEN, p, s); - - s += ETH_HLEN; - while (s < ETH_ZLEN) /* pad to min length */ - ptxb[s++] = '\0'; - - pcnet32_bufs.tx_ring[entry].length = le16_to_cpu(-s); - pcnet32_bufs.tx_ring[entry].misc = 0x00000000; - pcnet32_bufs.tx_ring[entry].base = (u32) virt_to_le32desc(ptxb); - - /* we set the top byte as the very last thing */ - pcnet32_bufs.tx_ring[entry].status = le16_to_cpu(status); - - - /* Trigger an immediate send poll */ - lp->a.write_csr(ioaddr, 0, 0x0048); - - /* wait for transmit complete */ - lp->cur_tx = 0; /* (lp->cur_tx + 1); */ - time = currticks() + TICKS_PER_SEC; /* wait one second */ - while (currticks() < time && - ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0)); - - if ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0) - printf("PCNET32 timed out on transmit\n"); - - /* Stop pointing at the current txb - * otherwise the card continues to send the packet */ - pcnet32_bufs.tx_ring[entry].base = 0; - -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void pcnet32_disable ( struct nic *nic __unused ) { - /* Stop the PCNET32 here -- it ocassionally polls memory if we don't */ - lp->a.write_csr(ioaddr, 0, 0x0004); - - /* - * Switch back to 16-bit mode to avoid problems with dumb - * DOS packet driver after a warm reboot - */ - lp->a.write_bcr(ioaddr, 20, 0); -} - -/************************************************************************** -IRQ - Enable, Disable, or Force interrupts -***************************************************************************/ -static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused) +static void +pcnet32_free_tx_resources ( struct pcnet32_private *priv ) { - switch ( action ) { - case DISABLE : - break; - case ENABLE : - break; - case FORCE : - break; - } + DBGP ( "pcnet32_free_tx_resources\n" ); + + free_dma ( priv->tx_base, TX_RING_BYTES ); } - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -You should omit the last argument struct pci_device * for a non-PCI NIC -***************************************************************************/ -static int pcnet32_probe ( struct nic *nic, struct pci_device *pci ) { - - int i, media; - int fdx, mii, fset, dxsuflo, ltint; +static int +pcnet32_chip_detect ( struct pcnet32_private *priv ) +{ + int fdx, mii, fset; + int media; + int rc; + unsigned long ioaddr; + struct pcnet32_access *a; int chip_version; - struct pcnet32_access *a = NULL; char *chipname; - u8 promaddr[6]; - int shared = 1; - if (pci->ioaddr == 0) - return 0; + ioaddr = priv->pci_dev->ioaddr; + a = priv->a; - /* BASE is used throughout to address the card */ - ioaddr = pci->ioaddr; - printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n", - pci->driver_name, pci->vendor, pci->device); + chip_version = a->read_csr ( ioaddr, 88 ) + | ( a->read_csr ( ioaddr, 89 ) << 16 ); - nic->irqno = 0; - nic->ioaddr = pci->ioaddr & ~3; + rc = -ENODEV; - /* reset the chip */ - pcnet32_wio_reset(ioaddr); + DBG ( "PCnet chip version is 0x%X\n", chip_version ); + if ( ( chip_version & 0xfff ) != 0x003 ) + goto err_unsupported; - /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr(ioaddr, 0) == 4 - && pcnet32_wio_check(ioaddr)) { - a = &pcnet32_wio; - } else { - pcnet32_dwio_reset(ioaddr); - if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 - && pcnet32_dwio_check(ioaddr)) { - a = &pcnet32_dwio; - } else - return 0; - } - - chip_version = - a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16); - - dprintf(("PCnet chip version is 0x%X\n", chip_version)); - if ((chip_version & 0xfff) != 0x003) - return 0; - - /* initialize variables */ - fdx = mii = fset = dxsuflo = ltint = 0; - chip_version = (chip_version >> 12) & 0xffff; + fdx = mii = fset = 0; + chip_version = ( chip_version >> 12 ) & 0xffff; switch (chip_version) { case 0x2420: - chipname = "PCnet/PCI 79C970"; /* PCI */ + chipname = "PCnet/PCI 79C970"; break; case 0x2430: - if (shared) - chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */ - else - chipname = "PCnet/32 79C965"; /* 486/VL bus */ + /* 970 gives the wrong chip id back */ + chipname = "PCnet/PCI 79C970"; break; case 0x2621: - chipname = "PCnet/PCI II 79C970A"; /* PCI */ + chipname = "PCnet/PCI II 79C970A"; fdx = 1; break; case 0x2623: - chipname = "PCnet/FAST 79C971"; /* PCI */ + chipname = "PCnet/FAST 79C971"; fdx = 1; mii = 1; fset = 1; - ltint = 1; break; case 0x2624: - chipname = "PCnet/FAST+ 79C972"; /* PCI */ + chipname = "PCnet/FAST+ 79C972"; fdx = 1; mii = 1; fset = 1; break; case 0x2625: - chipname = "PCnet/FAST III 79C973"; /* PCI */ + chipname = "PCnet/FAST III 79C973"; fdx = 1; mii = 1; break; case 0x2626: - chipname = "PCnet/Home 79C978"; /* PCI */ + chipname = "PCnet/Home 79C978"; fdx = 1; - /* - * This is based on specs published at www.amd.com. This section - * assumes that a card with a 79C978 wants to go into 1Mb HomePNA - * mode. The 79C978 can also go into standard ethernet, and there - * probably should be some sort of module option to select the - * mode by which the card should operate + /* + * This is based on specs published at www.amd.com. This section + * assumes that a NIC with a 79C978 wants to go into 1Mb HomePNA + * mode. The 79C978 can also go into standard ethernet, and + * there probably should be some sort of module option to select + * the mode by which the card should operate */ /* switch to home wiring mode */ media = a->read_bcr(ioaddr, 49); - printf("media reset to %#x.\n", media); + DBG ( "media reset to %#x.\n", media ); a->write_bcr(ioaddr, 49, media); break; case 0x2627: - chipname = "PCnet/FAST III 79C975"; /* PCI */ + chipname = "PCnet/FAST III 79C975"; + fdx = 1; + mii = 1; + break; + case 0x2628: + chipname = "PCnet/PRO 79C976"; fdx = 1; mii = 1; break; default: chipname = "UNKNOWN"; - printf("PCnet version %#x, no PCnet32 chip.\n", - chip_version); - return 0; + DBG ( "PCnet version %#x, no PCnet32 chip.\n", chip_version ); + goto err_unsupported; } + DBG ( "PCnet chipname %s\n", chipname ); + /* - * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit - * starting until the packet is loaded. Strike one for reliability, lose - * one for latency - although on PCI this isnt a big loss. Older chips - * have FIFO's smaller than a packet, so you can't do this. + * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit + * starting until the packet is loaded. Strike one for reliability, lose + * one for latency - although on PCI this isnt a big loss. Older chips + * have FIFO's smaller than a packet, so you can't do this. + * Turn on BCR18:BurstRdEn and BCR18:BurstWrEn. */ - if (fset) { - a->write_bcr(ioaddr, 18, - (a->read_bcr(ioaddr, 18) | 0x0800)); - a->write_csr(ioaddr, 80, - (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); - dxsuflo = 1; - ltint = 1; + a->write_bcr ( ioaddr, 18, + ( a->read_bcr ( ioaddr, 18 ) | 0x0860 ) ); + a->write_csr ( ioaddr, 80, + ( a->read_csr ( ioaddr, 80 ) & 0x0C00) | 0x0C00 ); } - DBG ( "%s at %hX,", chipname, (unsigned int) ioaddr ); + priv->full_duplex = fdx; + priv->mii = mii; - /* read PROM address */ - for (i = 0; i < 6; i++) - promaddr[i] = inb(ioaddr + i); + return 0; - /* Update the nic structure with the MAC Address */ - for (i = 0; i < ETH_ALEN; i++) { - nic->node_addr[i] = promaddr[i]; - } +err_unsupported: + return rc; +} - /* Print out some hardware info */ - DBG ( "%s: IO Addr 0x%hX, MAC Addr %s\n ", chipname, (unsigned int) ioaddr, - eth_ntoa ( nic->node_addr ) ); +/** + * pcnet32_set_ops - Determines the ops used to access the registers + * + * @v priv Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +pcnet32_set_ops ( struct pcnet32_private *priv ) +{ + int rc; + unsigned long ioaddr; - /* Set to pci bus master */ - adjust_pci_device(pci); + ioaddr = priv->pci_dev->ioaddr; - /* point to private storage */ - lp = &lpx; - -#if EBDEBUG - if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ - i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ - dprintf((" tx_start_pt(0x%hX):", i)); - switch (i >> 10) { - case 0: - dprintf((" 20 bytes,")); - break; - case 1: - dprintf((" 64 bytes,")); - break; - case 2: - dprintf((" 128 bytes,")); - break; - case 3: - dprintf(("~220 bytes,")); - break; - } - i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ - dprintf((" BCR18(%hX):", i & 0xffff)); - if (i & (1 << 5)) - dprintf(("BurstWrEn ")); - if (i & (1 << 6)) - dprintf(("BurstRdEn ")); - if (i & (1 << 7)) - dprintf(("DWordIO ")); - if (i & (1 << 11)) - dprintf(("NoUFlow ")); - i = a->read_bcr(ioaddr, 25); - dprintf((" SRAMSIZE=0x%hX,", i << 8)); - i = a->read_bcr(ioaddr, 26); - dprintf((" SRAM_BND=0x%hX,", i << 8)); - i = a->read_bcr(ioaddr, 27); - if (i & (1 << 14)) - dprintf(("LowLatRx")); - } -#endif - lp->name = chipname; - lp->shared_irq = shared; - lp->full_duplex = fdx; - lp->dxsuflo = dxsuflo; - lp->ltint = ltint; - lp->mii = mii; - /* FIXME: Fix Options for only one card */ - if ((cards_found >= MAX_UNITS) - || ((unsigned int) options[cards_found] > sizeof(options_mapping))) - lp->options = PCNET32_PORT_ASEL; - else - lp->options = options_mapping[options[cards_found]]; - - if (fdx && !(lp->options & PCNET32_PORT_ASEL) && - ((cards_found >= MAX_UNITS) || full_duplex[cards_found])) - lp->options |= PCNET32_PORT_FD; - - if (!a) { - printf("No access methods\n"); - return 0; - } - - // lp->a = *a; - // Causes a loader: - // bin/blib.a(pcnet32.o)(.text+0x6b6): In function `pcnet32_probe': - // drivers/net/pcnet32.c:871: undefined reference to `memcpy' - // make: *** [bin/pcnet32.dsk.tmp] Error 1 - // So we do: - memcpy ( &lp->a, a, sizeof ( lp->a ) ); - // To explicity call memcpy. - - /* detect special T1/E1 WAN card by checking for MAC address */ - if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0 - && nic->node_addr[2] == 0x75) - lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; - - lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ - lp->init_block.tlen_rlen = - le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = nic->node_addr[i]; - lp->init_block.filter[0] = 0xffffffff; - lp->init_block.filter[1] = 0xffffffff; - lp->init_block.rx_ring = virt_to_bus(&pcnet32_bufs.rx_ring); - lp->init_block.tx_ring = virt_to_bus(&pcnet32_bufs.tx_ring); - - /* switch pcnet32 to 32bit mode */ - a->write_bcr(ioaddr, 20, 2); - - a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff); - a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16); - - /* - * To auto-IRQ we enable the initialization-done and DMA error - * interrupts. For ISA boards we get a DMA error, but VLB and PCI - * boards will work. + /* Check if CSR0 has its default value and perform a write / read + in the RAP register to see if it works. Based on these results + determine what mode the NIC is in (WIO / DWIO) */ - /* Trigger an initialization just for the interrupt. */ + rc = -ENODEV; - -// a->write_csr(ioaddr, 0, 0x41); -// mdelay(1); - - cards_found++; - - /* point to NIC specific routines */ - pcnet32_reset(nic); - if (mii) { - int tmp; - int phy, phy_idx = 0; - u16 mii_lpa; - lp->phys[0] = 1; /* Default Setting */ - for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(nic, phy, MII_BMSR); - if (mii_status != 0xffff && mii_status != 0x0000) { - lp->phys[phy_idx++] = phy; - lp->mii_if.advertising = - mdio_read(nic, phy, MII_ADVERTISE); - if ((mii_status & 0x0040) == 0) { - tmp = phy; - dprintf (("MII PHY found at address %d, status " - "%hX advertising %hX\n", phy, mii_status, - lp->mii_if.advertising)); - } - } - } - if (phy_idx == 0) - printf("No MII transceiver found!\n"); - lp->mii_if.phy_id = lp->phys[0]; - - lp->mii_if.advertising = - mdio_read(nic, lp->phys[0], MII_ADVERTISE); - - mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA); - lp->mii_if.advertising &= mii_lpa; - if (lp->mii_if.advertising & ADVERTISE_100FULL) - printf("100Mbps Full-Duplex\n"); - else if (lp->mii_if.advertising & ADVERTISE_100HALF) - printf("100Mbps Half-Duplex\n"); - else if (lp->mii_if.advertising & ADVERTISE_10FULL) - printf("10Mbps Full-Duplex\n"); - else if (lp->mii_if.advertising & ADVERTISE_10HALF) - printf("10Mbps Half-Duplex\n"); - else - printf("\n"); + if ( pcnet32_wio_read_csr ( ioaddr, 0 ) == 4 && + pcnet32_wio_check ( ioaddr ) ) { + priv->a = &pcnet32_wio; } else { - /* The older chips are fixed 10Mbps, and some support full duplex, - * although not via autonegotiation, but only via configuration. */ - if (fdx) - printf("10Mbps Full-Duplex\n"); - else - printf("10Mbps Half-Duplex\n"); + pcnet32_dwio_reset ( ioaddr ); + if ( pcnet32_dwio_read_csr ( ioaddr, 0 ) == 4 && + pcnet32_dwio_check ( ioaddr ) ) { + priv->a = &pcnet32_dwio; + } else { + goto err_unsupported; + } } - nic->nic_op = &pcnet32_operations; + return 0; - return 1; -} -static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num) -{ - u16 val_out; - int phyaddr; - - if (!lp->mii) - return 0; - - phyaddr = lp->a.read_bcr(ioaddr, 33); - - lp->a.write_bcr(ioaddr, 33, - ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); - val_out = lp->a.read_bcr(ioaddr, 34); - lp->a.write_bcr(ioaddr, 33, phyaddr); - - return val_out; +err_unsupported: + return rc; } -#if 0 -static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num, - int val) +/** + * pcnet32_setup_init_block - setup the NICs initialization block + * + * @v priv Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + */ +static void +pcnet32_setup_init_block ( struct pcnet32_private *priv ) { - int phyaddr; + int i; - if (!lp->mii) + /* Configure the network port based on what we've established so far */ + priv->init_block.mode = + cpu_to_le16 ( ( priv->options & PCNET32_PORT_PORTSEL ) << 7 ); + + /* Setup RLEN and TLEN fields */ + priv->init_block.tlen_rlen = + cpu_to_le16 ( ( PCNET32_LOG_RX_BUFFERS << 4 ) | + ( PCNET32_LOG_TX_BUFFERS << 12 ) ); + + /* Fill in physical address */ + for ( i = 0; i < ETH_ALEN; i++) + priv->init_block.phys_addr[i] = priv->netdev->hw_addr[i]; + + /* No multicasting scheme, accept everything */ + priv->init_block.filter[0] = 0xffffffff; + priv->init_block.filter[1] = 0xffffffff; + + priv->init_block.rx_ring = + cpu_to_le32 ( virt_to_bus ( priv->rx_base ) ); + priv->init_block.tx_ring = + cpu_to_le32 ( virt_to_bus ( priv->tx_base ) ); + + /* Make sure all changes are visible */ + wmb(); +} + +/** + * pcnet32_setup_probe_phy - go through all PHYs and see which one is present + * + * @v priv Driver private structure + */ +static void +pcnet32_setup_probe_phy ( struct pcnet32_private *priv ) +{ + unsigned long ioaddr = priv->pci_dev->ioaddr; + char phycount; + int phy_id; + int i; + + if ( priv->mii ) { + phy_id = ( ( priv->a->read_bcr ( ioaddr, 33 ) ) >> 5 ) & 0x1f; + for ( i = 0; i < PCNET32_MAX_PHYS; i++ ) { + unsigned short id1, id2; + id1 = pcnet32_mdio_read ( priv->netdev, i, MII_PHYSID1 ); + if ( id1 == 0xffff ) + continue; + id2 = pcnet32_mdio_read ( priv->netdev, i, MII_PHYSID2 ); + if ( id2 == 0xffff ) + continue; + if ( i == 31 && ( ( priv->chip_version + 1 ) & 0xfffe ) == 0x2624 ) + continue; + + phycount++; + phy_id = i; + } + priv->a->write_bcr ( ioaddr, 33, phy_id << 5 ); + if ( phycount > 1 ) + priv->options |= PCNET32_PORT_MII; + } +} + +/** + * pcnet32_setup_mac_addr - check for inconsistency between CSR12-14 + * and PROM addresses + * + * @v priv Driver private structure + */ +static int +pcnet32_setup_mac_addr ( struct pcnet32_private *priv ) +{ + int i; + u8 promaddr[ETH_ALEN]; + unsigned long ioaddr = priv->pci_dev->ioaddr; + + /* In most chips, after a chip reset, the ethernet address is read from + * the station address PROM at the base address and programmed into the + * "Physical Address Registers" CSR12-14. + * As a precautionary measure, we read the PROM values and complain if + * they disagree with the CSRs. If they miscompare, and the PROM addr + * is valid, then the PROM addr is used. + */ + for ( i = 0; i < 3; i++ ) { + unsigned int val; + val = priv->a->read_csr ( ioaddr, i + 12 ) & 0x0ffff; + /* There may be endianness issues here. */ + priv->netdev->hw_addr[2 * i] = val & 0x0ff; + priv->netdev->hw_addr[2 * i + 1] = ( val >> 8 ) & 0x0ff; + } + + for ( i = 0; i < ETH_ALEN; i++ ) + promaddr[i] = inb ( ioaddr + i ); + + if ( memcmp ( promaddr, priv->netdev->hw_addr, ETH_ALEN ) || + ! is_valid_ether_addr ( priv->netdev->hw_addr ) ) { + if ( is_valid_ether_addr ( promaddr ) ) { + DBG ( "CSR address is invalid, using PROM addr\n" ); + memcpy ( priv->netdev->hw_addr, promaddr, ETH_ALEN ); + } + } + + /* If ethernet address is not valid, return error */ + if ( ! is_valid_ether_addr ( priv->netdev->hw_addr ) ) + return -EADDRNOTAVAIL; + + return 0; +} + +/** + * pcnet32_setup_if_duplex - Sets the NICs used interface and duplex mode + * + * @v priv Driver private structure + */ +static void +pcnet32_setup_if_duplex ( struct pcnet32_private *priv ) +{ + unsigned long ioaddr = priv->pci_dev->ioaddr; + u16 val; + + /* Set/Reset autoselect bit */ + val = priv->a->read_bcr ( ioaddr, 2 ) & ~2; + if ( priv->options & PCNET32_PORT_ASEL ) + val |= 2; + priv->a->write_bcr ( ioaddr, 2, val ); + + /* Handle full duplex setting */ + if ( priv->full_duplex ) { + val = priv->a->read_bcr ( ioaddr, 9 ) & ~3; + if ( priv->options & PCNET32_PORT_FD ) { + val |= 1; + if ( priv->options == ( PCNET32_PORT_FD | PCNET32_PORT_AUI ) ) + val |= 2; + } else if ( priv->options & PCNET32_PORT_ASEL ) { + /* Workaround of xSeries 250, on for 79C975 only */ + if ( priv->chip_version == 0x2627 ) + val |= 3; + } + priv->a->write_bcr ( ioaddr, 9, val ); + } + + /* Set/Reset GPSI bit in test register */ + val = priv->a->read_csr ( ioaddr, 124 ) & ~0x10; + if ( ( priv->options & PCNET32_PORT_PORTSEL ) == PCNET32_PORT_GPSI ) + val |= 0x10; + priv->a->write_bcr ( ioaddr, 124, val ); + + /* Allied Telesyn AT are 100Mbit only and do not negotiate */ + u16 subsys_vend_id, subsys_dev_id; + pci_read_config_word ( priv->pci_dev, + PCI_SUBSYSTEM_VENDOR_ID, + &subsys_vend_id ); + pci_read_config_word ( priv->pci_dev, + PCI_SUBSYSTEM_ID, + &subsys_dev_id ); + if ( subsys_vend_id == PCI_VENDOR_ID_AT && + ( ( subsys_dev_id == PCI_SUBDEVICE_ID_AT_2700FX ) || + ( subsys_dev_id == PCI_SUBDEVICE_ID_AT_2701FX ) ) ) { + priv->options = PCNET32_PORT_FD | PCNET32_PORT_100; + } + + if ( priv->mii && ! ( priv->options & PCNET32_PORT_ASEL ) ) { + /* Disable Auto Negotiation, set 10Mbps, HD */ + val = priv->a->read_bcr ( ioaddr, 32 ) & ~0x38; + if ( priv->options & PCNET32_PORT_FD ) + val |= 0x10; + if ( priv->options & PCNET32_PORT_100 ) + val |= 0x08; + priv->a->write_bcr ( ioaddr, 32, val ); + } else if ( priv->options & PCNET32_PORT_ASEL ) { + /* 79C970 chips do not have the BCR32 register */ + if ( ( priv->chip_version != 0x2420 ) && + ( priv->chip_version != 0x2621 ) ) { + /* Enable Auto Negotiation, setup, disable FD */ + val = priv->a->read_bcr ( ioaddr, 32 ) & ~0x98; + val |= 0x20; + priv->a->write_bcr ( ioaddr, 32, val ); + } + } +} + +/** + * pcnet32_hw_start - Starts up the NIC + * + * @v priv Driver private structure + */ +static void +pcnet32_hw_start ( struct pcnet32_private *priv ) +{ + unsigned long ioaddr = priv->pci_dev->ioaddr; + int i; + + /* Begin initialization procedure */ + priv->a->write_csr ( ioaddr, 0, Init ); + + /* Wait for the initialization to be done */ + i = 0; + while ( i++ < 100 ) + if ( priv->a->read_csr ( ioaddr, 0 ) & InitDone ) + break; + + /* Start the chip */ + priv->a->write_csr ( ioaddr, 0, Strt ); +} + +/** + * open - Called when a network interface is made active + * + * @v netdev Network device + * @ret rc Return status code, 0 on success, negative value on failure + **/ +static int +pcnet32_open ( struct net_device *netdev ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + unsigned long ioaddr = priv->pci_dev->ioaddr; + int rc; + u16 val; + + /* Setup TX and RX descriptors */ + if ( ( rc = pcnet32_setup_tx_resources ( priv ) ) != 0 ) { + DBG ( "Error setting up TX resources\n" ); + goto err_setup_tx; + } + + if ( ( rc = pcnet32_setup_rx_resources ( priv ) ) != 0 ) { + DBG ( "Error setting up RX resources\n" ); + goto err_setup_rx; + } + + /* Reset the chip */ + priv->a->reset ( ioaddr ); + + /* Switch pcnet32 to 32bit mode */ + priv->a->write_bcr ( ioaddr, 20, PCNET32_SWSTYLE_PCNET32 ); + + /* Setup the interface and duplex mode */ + pcnet32_setup_if_duplex ( priv ); + + /* Disable interrupts */ + val = priv->a->read_csr ( ioaddr, 3 ); + val |= BablMask | MissFrameMask | RxIntMask | TxIntMask | InitDoneMask; + priv->a->write_csr ( ioaddr, 3, val ); + + /* Setup initialization block */ + pcnet32_setup_init_block ( priv ); + + /* Fill in the address of the initialization block */ + priv->a->write_csr ( ioaddr, 1, + ( virt_to_bus ( &priv->init_block ) ) & 0xffff ); + priv->a->write_csr ( ioaddr, 2, + ( virt_to_bus ( &priv->init_block ) ) >> 16 ); + + /* Enable Auto-Pad, disable interrupts */ + priv->a->write_csr ( ioaddr, 4, 0x0915 ); + + pcnet32_hw_start ( priv ); + + return 0; + +err_setup_rx: + pcnet32_free_tx_resources ( priv ); +err_setup_tx: + priv->a->reset( priv->pci_dev->ioaddr ); + return rc; +} + +/** + * transmit - Transmit a packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +pcnet32_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + unsigned long ioaddr = priv->pci_dev->ioaddr; + uint32_t tx_len = iob_len ( iobuf ); + struct pcnet32_tx_desc *tx_curr_desc; + + DBGP ( "pcnet32_transmit\n" ); + + if ( priv->tx_fill_ctr == TX_RING_SIZE ) { + DBG ( "Tx overflow\n" ); + return -ENOTSUP; + } + + priv->tx_iobuf[priv->tx_curr] = iobuf; + + tx_curr_desc = priv->tx_base + priv->tx_curr; + + /* Configure current descriptor to transmit packet */ + tx_curr_desc->length = cpu_to_le16 ( -tx_len ); + tx_curr_desc->misc = 0x00000000; + tx_curr_desc->base = cpu_to_le32 ( virt_to_bus ( iobuf->data ) ); + + /* Owner changes after the other status fields are set */ + wmb(); + tx_curr_desc->status = + cpu_to_le16 ( DescOwn | StartOfPacket | EndOfPacket ); + + /* Trigger an immediate send poll */ + priv->a->write_csr ( ioaddr, 0, + ( priv->irq_enabled ? IntEnable : 0 ) | TxDemand ); + + /* Point to the next free descriptor */ + priv->tx_curr = ( priv->tx_curr + 1 ) % TX_RING_SIZE; + + /* Increment number of tx descriptors in use */ + priv->tx_fill_ctr++; + + return 0; +} + +/** + * pcnet32_process_tx_packets - Checks for successfully sent packets, + * reports them to iPXE with netdev_tx_complete() + * + * @v netdev Network device + */ +static void +pcnet32_process_tx_packets ( struct net_device *netdev ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + struct pcnet32_tx_desc *tx_curr_desc; + + DBGP ( "pcnet32_process_tx_packets\n" ); + + while ( priv->tx_tail != priv->tx_curr ) { + tx_curr_desc = priv->tx_base + priv->tx_tail; + + u16 status = le16_to_cpu ( tx_curr_desc->status ); + + DBG ( "Before OWN bit check, status: %#08x\n", status ); + + /* Skip this descriptor if hardware still owns it */ + if ( status & DescOwn ) + break; + + DBG ( "Transmitted packet.\n" ); + DBG ( "priv->tx_fill_ctr= %d\n", priv->tx_fill_ctr ); + DBG ( "priv->tx_tail = %d\n", priv->tx_tail ); + DBG ( "priv->tx_curr = %d\n", priv->tx_curr ); + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + + /* This packet is ready for completion */ + netdev_tx_complete ( netdev, priv->tx_iobuf[priv->tx_tail]); + + /* Clear the descriptor */ + memset ( tx_curr_desc, 0, sizeof(*tx_curr_desc) ); + + /* Reduce the number of tx descriptors in use */ + priv->tx_fill_ctr--; + + /* Go to next available descriptor */ + priv->tx_tail = ( priv->tx_tail + 1 ) % TX_RING_SIZE; + } +} + +/** + * pcnet32_process_rx_packets - Checks for received packets, reports them + * to iPXE with netdev_rx() or netdev_rx_err() if there was an error receiving + * the packet + * + * @v netdev Network device + */ +static void +pcnet32_process_rx_packets ( struct net_device *netdev ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + struct pcnet32_rx_desc *rx_curr_desc; + u16 status; + u32 len; + int i; + + DBGP ( "pcnet32_process_rx_packets\n" ); + + for ( i = 0; i < RX_RING_SIZE; i++ ) { + rx_curr_desc = priv->rx_base + priv->rx_curr; + + status = le16_to_cpu ( rx_curr_desc->status ); + rmb(); + + DBG ( "Before OWN bit check, status: %#08x\n", status ); + + /* Skip this descriptor if hardware still owns it */ + if ( status & DescOwn ) + break; + + /* We own the descriptor, but it has not been refilled yet */ + if ( priv->rx_iobuf[priv->rx_curr] == NULL ) + break; + + DBG ( "Received packet.\n" ); + DBG ( "priv->rx_curr = %d\n", priv->rx_curr ); + DBG ( "rx_len = %d\n", + ( le32_to_cpu ( rx_curr_desc->msg_length ) & 0xfff ) - 4 ); + DBG ( "rx_curr_desc = %#08lx\n", + virt_to_bus ( rx_curr_desc ) ); + + /* Check ERR bit */ + if ( status & 0x4000 ) { + netdev_rx_err ( netdev, priv->rx_iobuf[priv->rx_curr], + -EINVAL ); + DBG ( "Corrupted packet received!\n"); + } else { + /* Adjust size of the iobuf to reflect received data */ + len = ( le32_to_cpu ( rx_curr_desc->msg_length ) & 0xfff ) - 4; + iob_put ( priv->rx_iobuf[priv->rx_curr], len ); + + /* Add this packet to the receive queue */ + netdev_rx ( netdev, priv->rx_iobuf[priv->rx_curr] ); + } + + /* Invalidate iobuf and descriptor */ + priv->rx_iobuf[priv->rx_curr] = NULL; + memset ( rx_curr_desc, 0, sizeof(*rx_curr_desc) ); + + /* Point to the next free descriptor */ + priv->rx_curr = ( priv->rx_curr + 1 ) % RX_RING_SIZE; + } + + /* Allocate new iobufs where needed */ + pcnet32_refill_rx_ring ( priv ); +} + +/** + * poll - Poll for received packets + * + * @v netdev Network device + */ +static void +pcnet32_poll ( struct net_device *netdev ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + unsigned long ioaddr = priv->pci_dev->ioaddr; + u16 status; + + DBGP ( "pcnet32_poll\n" ); + + status = priv->a->read_csr ( ioaddr, 0 ); + + /* Clear interrupts */ + priv->a->write_csr ( ioaddr, 0, status ); + + DBG ( "pcnet32_poll: mask = %#04x, status = %#04x\n", + priv->a->read_csr ( ioaddr, 3 ), status ); + + /* Return when RINT or TINT are not set */ + if ( ( status & 0x0500 ) == 0x0000 ) return; - phyaddr = lp->a.read_bcr(ioaddr, 33); + /* Process transmitted packets */ + pcnet32_process_tx_packets ( netdev ); - lp->a.write_bcr(ioaddr, 33, - ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); - lp->a.write_bcr(ioaddr, 34, val); - lp->a.write_bcr(ioaddr, 33, phyaddr); + /* Process received packets */ + pcnet32_process_rx_packets ( netdev ); } -#endif -static struct nic_operations pcnet32_operations = { - .connect = dummy_connect, - .poll = pcnet32_poll, +/** + * close - Disable network interface + * + * @v netdev network interface device structure + **/ +static void +pcnet32_close ( struct net_device *netdev ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + unsigned long ioaddr = priv->pci_dev->ioaddr; + + DBGP ( "pcnet32_close\n" ); + + /* Reset the chip */ + pcnet32_wio_reset ( ioaddr ); + + /* Stop the PCNET32 - it occasionally polls memory if we don't */ + priv->a->write_csr ( ioaddr, 0, Stop ); + + /* Switch back to 16bit mode to avoid problems with dumb + * DOS packet driver after a warm reboot */ + priv->a->write_bcr ( ioaddr, 20, PCNET32_SWSTYLE_LANCE ); + + pcnet32_free_rx_resources ( priv ); + pcnet32_free_tx_resources ( priv ); +} + +static void pcnet32_irq_enable ( struct pcnet32_private *priv ) +{ + unsigned long ioaddr = priv->pci_dev->ioaddr; + u16 val; + + DBGP ( "pcnet32_irq_enable\n" ); + + /* Enable TINT and RINT masks */ + val = priv->a->read_csr ( ioaddr, 3 ); + val &= ~( RxIntMask | TxIntMask ); + priv->a->write_csr ( ioaddr, 3, val ); + + /* Enable interrupts */ + priv->a->write_csr ( ioaddr, 0, IntEnable ); + + priv->irq_enabled = 1; +} + +static void pcnet32_irq_disable ( struct pcnet32_private *priv ) +{ + unsigned long ioaddr = priv->pci_dev->ioaddr; + + DBGP ( "pcnet32_irq_disable\n" ); + + priv->a->write_csr ( ioaddr, 0, 0x0000 ); + + priv->irq_enabled = 0; +} + +/** + * irq - enable or disable interrupts + * + * @v netdev network adapter + * @v action requested interrupt action + **/ +static void +pcnet32_irq ( struct net_device *netdev, int action ) +{ + struct pcnet32_private *priv = netdev_priv ( netdev ); + + DBGP ( "pcnet32_irq\n" ); + + switch ( action ) { + case 0: + pcnet32_irq_disable ( priv ); + break; + default: + pcnet32_irq_enable ( priv ); + break; + } +} + +static struct net_device_operations pcnet32_operations = { + .open = pcnet32_open, .transmit = pcnet32_transmit, + .poll = pcnet32_poll, + .close = pcnet32_close, .irq = pcnet32_irq, - }; +/** + * probe - Initial configuration of NIC + * + * @v pdev PCI device + * @v ent PCI IDs + * + * @ret rc Return status code + **/ +static int +pcnet32_probe ( struct pci_device *pdev, const struct pci_device_id *ent ) +{ + struct net_device *netdev; + struct pcnet32_private *priv; + unsigned long ioaddr; + int rc; + + DBGP ( "pcnet32_probe\n" ); + + DBG ( "Found %s, vendor = %#04x, device = %#04x\n", + pdev->driver_name, ent->vendor, ent->device ); + + /* Allocate our private data */ + netdev = alloc_etherdev ( sizeof ( *priv ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc_etherdev; + } + + /* Link our operations to the netdev struct */ + netdev_init ( netdev, &pcnet32_operations ); + + /* Link the PCI device to the netdev struct */ + pci_set_drvdata ( pdev, netdev ); + netdev->dev = &pdev->dev; + + /* Get a reference to our private data */ + priv = netdev_priv ( netdev ); + + /* We'll need these set up for the rest of the routines */ + priv->pci_dev = pdev; + priv->netdev = netdev; + + ioaddr = pdev->ioaddr; + + /* Only use irqs under UNDI */ + priv->irq_enabled = 0; + + /* Reset the chip */ + pcnet32_wio_reset ( ioaddr ); + + if ( ( rc = pcnet32_set_ops ( priv ) ) != 0 ) { + DBG ( "Setting driver operations failed\n"); + goto err_set_ops; + } + + if ( ( rc = pcnet32_chip_detect ( priv ) ) != 0 ) { + DBG ( "pcnet32_chip_detect failed\n" ); + goto err_chip_detect; + } + + /* Enter bus mastering mode */ + adjust_pci_device ( pdev ); + + /* Verify and get MAC address */ + if ( ( rc = pcnet32_setup_mac_addr ( priv ) ) != 0 ) { + DBG ( "Setting MAC address failed\n" ); + goto err_mac_addr; + } + + DBG ( "IO Addr 0x%lX, MAC Addr %s\n", ioaddr, + eth_ntoa ( netdev->hw_addr ) ); + + priv->options = PCNET32_PORT_ASEL; + + /* Detect special T1/E1 WAN card by checking for MAC address */ + if ( netdev->hw_addr[0] == 0x00 && + netdev->hw_addr[1] == 0xE0 && + netdev->hw_addr[2] == 0x75 ) + priv->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; + + /* Probe the PHY so we can check link state and speed */ + pcnet32_setup_probe_phy ( priv ); + + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBG ( "Error registering netdev\n" ); + goto err_register; + } + + netdev_link_up ( netdev ); + + return 0; + +err_register: + netdev_put ( netdev ); +err_chip_detect: +err_set_ops: +err_alloc_etherdev: +err_mac_addr: + return rc; +} + +/** + * remove - Device Removal Routine + * + * @v pdev PCI device information struct + **/ +static void +pcnet32_remove ( struct pci_device *pdev ) +{ + struct net_device *netdev = pci_get_drvdata ( pdev ); + unsigned long ioaddr = pdev->ioaddr; + + DBGP ( "pcnet32_remove\n" ); + + /* Reset the chip */ + pcnet32_wio_reset ( ioaddr ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + static struct pci_device_id pcnet32_nics[] = { PCI_ROM(0x1022, 0x2000, "pcnet32", "AMD PCnet/PCI", 0), PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD PCNet FAST III", 0), PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD PCnet/HomePNA", 0), }; -PCI_DRIVER ( pcnet32_driver, pcnet32_nics, PCI_NO_CLASS ); - -DRIVER ( "PCNET32/PCI", nic_driver, pci_driver, pcnet32_driver, - pcnet32_probe, pcnet32_disable ); - -/* - * Local variables: - * c-basic-offset: 8 - * c-indent-level: 8 - * tab-width: 8 - * End: - */ +struct pci_driver pcnet32_driver __pci_driver = { + .ids = pcnet32_nics, + .id_count = ARRAY_SIZE ( pcnet32_nics ), + .probe = pcnet32_probe, + .remove = pcnet32_remove, +}; diff --git a/src/drivers/net/pcnet32.h b/src/drivers/net/pcnet32.h new file mode 100644 index 00000000..bd03cbc0 --- /dev/null +++ b/src/drivers/net/pcnet32.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010 Andrei Faur + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifndef _PCNET32_H_ +#define _PCNET32_H_ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Set default values to 16 Tx buffers and 32 Rx buffers. + */ +#define PCNET32_LOG_TX_BUFFERS 4 +#define PCNET32_LOG_RX_BUFFERS 5 + +/* Maximum number of descriptor rings is 512 */ +#define PCNET32_LOG_MAX_TX_BUFFERS 9 +#define PCNET32_LOG_MAX_RX_BUFFERS 9 + +#define TX_RING_SIZE ( 1 << ( PCNET32_LOG_TX_BUFFERS ) ) +#define TX_MAX_RING_SIZE ( 1 << ( PCNET32_LOG_MAX_TX_BUFFERS ) ) + +#define RX_RING_SIZE ( 1 << ( PCNET32_LOG_RX_BUFFERS ) ) +#define RX_MAX_RING_SIZE ( 1 << ( PCNET32_LOG_MAX_RX_BUFFERS ) ) + +#define RX_RING_BYTES ( RX_RING_SIZE * sizeof(struct pcnet32_rx_desc ) ) +#define TX_RING_BYTES ( TX_RING_SIZE * sizeof(struct pcnet32_tx_desc ) ) + +#define PKT_BUF_SIZE 1536 + +#define RX_RING_ALIGN 16 +#define TX_RING_ALIGN 16 + +#define INIT_BLOCK_ALIGN 32 + +#define PCNET32_WIO_RDP 0x10 +#define PCNET32_WIO_RAP 0x12 +#define PCNET32_WIO_RESET 0x14 +#define PCNET32_WIO_BDP 0x16 + +#define PCNET32_DWIO_RDP 0x10 +#define PCNET32_DWIO_RAP 0x14 +#define PCNET32_DWIO_RESET 0x18 +#define PCNET32_DWIO_BDP 0x1C + +#define PCNET32_PORT_AUI 0x00 +#define PCNET32_PORT_10BT 0x01 +#define PCNET32_PORT_GPSI 0x02 +#define PCNET32_PORT_MII 0x03 + +#define PCNET32_PORT_PORTSEL 0x03 +#define PCNET32_PORT_ASEL 0x04 +#define PCNET32_PORT_100 0x40 +#define PCNET32_PORT_FD 0x80 + +#define PCNET32_SWSTYLE_LANCE 0x00 +#define PCNET32_SWSTYLE_ILACC 0x01 +#define PCNET32_SWSTYLE_PCNET32 0x02 + +#define PCNET32_MAX_PHYS 32 + +#ifndef PCI_VENDOR_ID_AT +#define PCI_VENDOR_ID_AT 0x1259 +#endif + +#ifndef PCI_SUBDEVICE_ID_AT_2700FX +#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 +#endif + +#ifndef PCI_SUBDEVICE_ID_AT_2701FX +#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 +#endif + +struct pcnet32_rx_desc { + u32 base; + s16 buf_length; + s16 status; + u32 msg_length; + u32 reserved; +}; + +struct pcnet32_tx_desc { + u32 base; + s16 length; + s16 status; + u32 misc; + u32 reserved; +}; + +struct pcnet32_init_block { + u16 mode; + u16 tlen_rlen; + u8 phys_addr[6]; + u16 reserved; + u32 filter[2]; + u32 rx_ring; + u32 tx_ring; +}; + +struct pcnet32_access { + u16 ( *read_csr ) ( unsigned long, int ); + void ( *write_csr ) ( unsigned long, int, u16 ); + u16 ( *read_bcr ) ( unsigned long, int ); + void ( *write_bcr ) ( unsigned long, int, u16 ); + u16 ( *read_rap ) ( unsigned long ); + void ( *write_rap ) ( unsigned long, u16 ); + void ( *reset ) ( unsigned long ); +}; + +struct pcnet32_private { + struct pcnet32_init_block init_block __attribute__((aligned(32))); + struct pci_device *pci_dev; + struct net_device *netdev; + + struct io_buffer *rx_iobuf[RX_RING_SIZE]; + struct io_buffer *tx_iobuf[TX_RING_SIZE]; + + struct pcnet32_rx_desc *rx_base; + struct pcnet32_tx_desc *tx_base; + uint32_t rx_curr; + uint32_t tx_curr; + uint32_t tx_tail; + uint32_t tx_fill_ctr; + + struct pcnet32_access *a; + int options; + unsigned int mii:1, + full_duplex:1; + + unsigned short chip_version; + + char irq_enabled; +}; + +enum pcnet32_desc_status_bit { + DescOwn = (1 << 15), + StartOfPacket = (1 << 9), + EndOfPacket = (1 << 8) +}; + +enum pcnet32_register_content { + /* CSR0 bits - Controller status register */ + RxInt = (1 << 10), + TxInt = (1 << 9), + InitDone = (1 << 8), + IntFlag = (1 << 7), + IntEnable = (1 << 6), + TxDemand = (1 << 3), + Stop = (1 << 2), + Strt = (1 << 1), + Init = (1 << 0), + + /* CSR3 bits - Controller status register */ + BablMask = (1 << 14), + MissFrameMask = (1 << 12), + MemErrMask = (1 << 11), + RxIntMask = (1 << 10), + TxIntMask = (1 << 9), + InitDoneMask = (1 << 8) + +}; + +#endif /* _PCNET32_H_ */