xref: /titanic_51/usr/src/grub/grub-0.97/netboot/pcnet32.c (revision 4a3b1d5b615ff6e54da1cc17f331e1ac794c5191)
11b8adde7SWilliam Kucharski /**************************************************************************
21b8adde7SWilliam Kucharski *
31b8adde7SWilliam Kucharski *    pcnet32.c -- Etherboot device driver for the AMD PCnet32
41b8adde7SWilliam Kucharski *    Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
51b8adde7SWilliam Kucharski *
61b8adde7SWilliam Kucharski *    This program is free software; you can redistribute it and/or modify
71b8adde7SWilliam Kucharski *    it under the terms of the GNU General Public License as published by
81b8adde7SWilliam Kucharski *    the Free Software Foundation; either version 2 of the License, or
91b8adde7SWilliam Kucharski *    (at your option) any later version.
101b8adde7SWilliam Kucharski *
111b8adde7SWilliam Kucharski *    This program is distributed in the hope that it will be useful,
121b8adde7SWilliam Kucharski *    but WITHOUT ANY WARRANTY; without even the implied warranty of
131b8adde7SWilliam Kucharski *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141b8adde7SWilliam Kucharski *    GNU General Public License for more details.
151b8adde7SWilliam Kucharski *
161b8adde7SWilliam Kucharski *    You should have received a copy of the GNU General Public License
171b8adde7SWilliam Kucharski *    along with this program; if not, write to the Free Software
181b8adde7SWilliam Kucharski *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
191b8adde7SWilliam Kucharski *
201b8adde7SWilliam Kucharski *    Portions of this code based on:
211b8adde7SWilliam Kucharski *		pcnet32.c: An AMD PCnet32 ethernet driver for linux:
221b8adde7SWilliam Kucharski *
231b8adde7SWilliam Kucharski *	(C) 1996-1999 Thomas Bogendoerfer
241b8adde7SWilliam Kucharski *		See Linux Driver for full information
251b8adde7SWilliam Kucharski *
261b8adde7SWilliam Kucharski *	The transmit and poll functions were written with reference to:
271b8adde7SWilliam Kucharski *	lance.c - LANCE NIC driver for Etherboot written by Ken Yap
281b8adde7SWilliam Kucharski *
291b8adde7SWilliam Kucharski *	Linux Driver Version 1.27a, 10.02.2002
301b8adde7SWilliam Kucharski *
311b8adde7SWilliam Kucharski *
321b8adde7SWilliam Kucharski *    REVISION HISTORY:
331b8adde7SWilliam Kucharski *    ================
341b8adde7SWilliam Kucharski *    v1.0	08-06-2003	timlegge	Initial port of Linux driver
351b8adde7SWilliam Kucharski *    v1.1	08-23-2003	timlegge	Add multicast support
361b8adde7SWilliam Kucharski *    v1.2	01-17-2004	timlegge	Initial driver output cleanup
371b8adde7SWilliam Kucharski *    v1.3	03-29-2004	timlegge	More driver cleanup
381b8adde7SWilliam Kucharski *
391b8adde7SWilliam Kucharski *    Indent Options: indent -kr -i8
401b8adde7SWilliam Kucharski ***************************************************************************/
411b8adde7SWilliam Kucharski 
421b8adde7SWilliam Kucharski /* to get some global routines like printf */
431b8adde7SWilliam Kucharski #include "etherboot.h"
441b8adde7SWilliam Kucharski /* to get the interface to the body of the program */
451b8adde7SWilliam Kucharski #include "nic.h"
461b8adde7SWilliam Kucharski /* to get the PCI support functions, if this is a PCI NIC */
471b8adde7SWilliam Kucharski #include "pci.h"
481b8adde7SWilliam Kucharski /* Include the time functions */
491b8adde7SWilliam Kucharski #include "timer.h"
501b8adde7SWilliam Kucharski #include "mii.h"
511b8adde7SWilliam Kucharski /* void hex_dump(const char *data, const unsigned int len); */
521b8adde7SWilliam Kucharski 
531b8adde7SWilliam Kucharski /* Etherboot Specific definations */
541b8adde7SWilliam Kucharski #define drv_version "v1.3"
551b8adde7SWilliam Kucharski #define drv_date "03-29-2004"
561b8adde7SWilliam Kucharski 
571b8adde7SWilliam Kucharski typedef unsigned char u8;
581b8adde7SWilliam Kucharski typedef signed char s8;
591b8adde7SWilliam Kucharski typedef unsigned short u16;
601b8adde7SWilliam Kucharski typedef signed short s16;
611b8adde7SWilliam Kucharski typedef unsigned int u32;
621b8adde7SWilliam Kucharski typedef signed int s32;
631b8adde7SWilliam Kucharski 
641b8adde7SWilliam Kucharski static u32 ioaddr;		/* Globally used for the card's io address */
651b8adde7SWilliam Kucharski 
661b8adde7SWilliam Kucharski #ifdef EDEBUG
671b8adde7SWilliam Kucharski #define dprintf(x) printf x
681b8adde7SWilliam Kucharski #else
691b8adde7SWilliam Kucharski #define dprintf(x)
701b8adde7SWilliam Kucharski #endif
711b8adde7SWilliam Kucharski 
721b8adde7SWilliam Kucharski /* Condensed operations for readability. */
731b8adde7SWilliam Kucharski #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
741b8adde7SWilliam Kucharski #define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
751b8adde7SWilliam Kucharski 
761b8adde7SWilliam Kucharski /* End Etherboot Specific */
771b8adde7SWilliam Kucharski 
781b8adde7SWilliam Kucharski int cards_found /* __initdata */ ;
791b8adde7SWilliam Kucharski 
801b8adde7SWilliam Kucharski #ifdef REMOVE
811b8adde7SWilliam Kucharski /* FIXME: Remove these they are probably pointless */
821b8adde7SWilliam Kucharski 
831b8adde7SWilliam Kucharski /*
841b8adde7SWilliam Kucharski  * VLB I/O addresses
851b8adde7SWilliam Kucharski  */
861b8adde7SWilliam Kucharski static unsigned int pcnet32_portlist[] /*__initdata */  =
871b8adde7SWilliam Kucharski { 0x300, 0x320, 0x340, 0x360, 0 };
881b8adde7SWilliam Kucharski 
891b8adde7SWilliam Kucharski static int pcnet32_debug = 1;
901b8adde7SWilliam Kucharski static int tx_start = 1;	/* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
911b8adde7SWilliam Kucharski static int pcnet32vlb;		/* check for VLB cards ? */
921b8adde7SWilliam Kucharski 
931b8adde7SWilliam Kucharski static struct net_device *pcnet32_dev;
941b8adde7SWilliam Kucharski 
951b8adde7SWilliam Kucharski static int max_interrupt_work = 80;
961b8adde7SWilliam Kucharski static int rx_copybreak = 200;
971b8adde7SWilliam Kucharski #endif
981b8adde7SWilliam Kucharski #define PCNET32_PORT_AUI      0x00
991b8adde7SWilliam Kucharski #define PCNET32_PORT_10BT     0x01
1001b8adde7SWilliam Kucharski #define PCNET32_PORT_GPSI     0x02
1011b8adde7SWilliam Kucharski #define PCNET32_PORT_MII      0x03
1021b8adde7SWilliam Kucharski 
1031b8adde7SWilliam Kucharski #define PCNET32_PORT_PORTSEL  0x03
1041b8adde7SWilliam Kucharski #define PCNET32_PORT_ASEL     0x04
1051b8adde7SWilliam Kucharski #define PCNET32_PORT_100      0x40
1061b8adde7SWilliam Kucharski #define PCNET32_PORT_FD	      0x80
1071b8adde7SWilliam Kucharski 
1081b8adde7SWilliam Kucharski #define PCNET32_DMA_MASK 0xffffffff
1091b8adde7SWilliam Kucharski 
1101b8adde7SWilliam Kucharski /*
1111b8adde7SWilliam Kucharski  * table to translate option values from tulip
1121b8adde7SWilliam Kucharski  * to internal options
1131b8adde7SWilliam Kucharski  */
1141b8adde7SWilliam Kucharski static unsigned char options_mapping[] = {
1151b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  0 Auto-select      */
1161b8adde7SWilliam Kucharski 	PCNET32_PORT_AUI,	/*  1 BNC/AUI          */
1171b8adde7SWilliam Kucharski 	PCNET32_PORT_AUI,	/*  2 AUI/BNC          */
1181b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  3 not supported    */
1191b8adde7SWilliam Kucharski 	PCNET32_PORT_10BT | PCNET32_PORT_FD,	/*  4 10baseT-FD       */
1201b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  5 not supported    */
1211b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  6 not supported    */
1221b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  7 not supported    */
1231b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL,	/*  8 not supported    */
1241b8adde7SWilliam Kucharski 	PCNET32_PORT_MII,	/*  9 MII 10baseT      */
1251b8adde7SWilliam Kucharski 	PCNET32_PORT_MII | PCNET32_PORT_FD,	/* 10 MII 10baseT-FD   */
1261b8adde7SWilliam Kucharski 	PCNET32_PORT_MII,	/* 11 MII (autosel)    */
1271b8adde7SWilliam Kucharski 	PCNET32_PORT_10BT,	/* 12 10BaseT          */
1281b8adde7SWilliam Kucharski 	PCNET32_PORT_MII | PCNET32_PORT_100,	/* 13 MII 100BaseTx    */
1291b8adde7SWilliam Kucharski 	PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD,	/* 14 MII 100BaseTx-FD */
1301b8adde7SWilliam Kucharski 	PCNET32_PORT_ASEL	/* 15 not supported    */
1311b8adde7SWilliam Kucharski };
1321b8adde7SWilliam Kucharski 
1331b8adde7SWilliam Kucharski #define MAX_UNITS 8		/* More are supported, limit only on options */
1341b8adde7SWilliam Kucharski static int options[MAX_UNITS];
1351b8adde7SWilliam Kucharski static int full_duplex[MAX_UNITS];
1361b8adde7SWilliam Kucharski 
1371b8adde7SWilliam Kucharski /*
1381b8adde7SWilliam Kucharski  *				Theory of Operation
1391b8adde7SWilliam Kucharski  *
1401b8adde7SWilliam Kucharski  * This driver uses the same software structure as the normal lance
1411b8adde7SWilliam Kucharski  * driver. So look for a verbose description in lance.c. The differences
1421b8adde7SWilliam Kucharski  * to the normal lance driver is the use of the 32bit mode of PCnet32
1431b8adde7SWilliam Kucharski  * and PCnetPCI chips. Because these chips are 32bit chips, there is no
1441b8adde7SWilliam Kucharski  * 16MB limitation and we don't need bounce buffers.
1451b8adde7SWilliam Kucharski  */
1461b8adde7SWilliam Kucharski 
1471b8adde7SWilliam Kucharski 
1481b8adde7SWilliam Kucharski 
1491b8adde7SWilliam Kucharski /*
1501b8adde7SWilliam Kucharski  * Set the number of Tx and Rx buffers, using Log_2(# buffers).
1511b8adde7SWilliam Kucharski  * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
1521b8adde7SWilliam Kucharski  * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
1531b8adde7SWilliam Kucharski  */
1541b8adde7SWilliam Kucharski #ifndef PCNET32_LOG_TX_BUFFERS
1551b8adde7SWilliam Kucharski #define PCNET32_LOG_TX_BUFFERS 1
1561b8adde7SWilliam Kucharski #define PCNET32_LOG_RX_BUFFERS 2
1571b8adde7SWilliam Kucharski #endif
1581b8adde7SWilliam Kucharski 
1591b8adde7SWilliam Kucharski #define TX_RING_SIZE		(1 << (PCNET32_LOG_TX_BUFFERS))
1601b8adde7SWilliam Kucharski #define TX_RING_MOD_MASK	(TX_RING_SIZE - 1)
1611b8adde7SWilliam Kucharski /* FIXME: Fix this to allow multiple tx_ring descriptors */
1621b8adde7SWilliam Kucharski #define TX_RING_LEN_BITS	0x0000	/*PCNET32_LOG_TX_BUFFERS) << 12) */
1631b8adde7SWilliam Kucharski 
1641b8adde7SWilliam Kucharski #define RX_RING_SIZE		(1 << (PCNET32_LOG_RX_BUFFERS))
1651b8adde7SWilliam Kucharski #define RX_RING_MOD_MASK	(RX_RING_SIZE - 1)
1661b8adde7SWilliam Kucharski #define RX_RING_LEN_BITS	((PCNET32_LOG_RX_BUFFERS) << 4)
1671b8adde7SWilliam Kucharski 
1681b8adde7SWilliam Kucharski #define PKT_BUF_SZ		1544
1691b8adde7SWilliam Kucharski 
1701b8adde7SWilliam Kucharski /* Offsets from base I/O address. */
1711b8adde7SWilliam Kucharski #define PCNET32_WIO_RDP		0x10
1721b8adde7SWilliam Kucharski #define PCNET32_WIO_RAP		0x12
1731b8adde7SWilliam Kucharski #define PCNET32_WIO_RESET	0x14
1741b8adde7SWilliam Kucharski #define PCNET32_WIO_BDP		0x16
1751b8adde7SWilliam Kucharski 
1761b8adde7SWilliam Kucharski #define PCNET32_DWIO_RDP	0x10
1771b8adde7SWilliam Kucharski #define PCNET32_DWIO_RAP	0x14
1781b8adde7SWilliam Kucharski #define PCNET32_DWIO_RESET	0x18
1791b8adde7SWilliam Kucharski #define PCNET32_DWIO_BDP	0x1C
1801b8adde7SWilliam Kucharski 
1811b8adde7SWilliam Kucharski #define PCNET32_TOTAL_SIZE	0x20
1821b8adde7SWilliam Kucharski 
1831b8adde7SWilliam Kucharski /* Buffers for the tx and Rx */
1841b8adde7SWilliam Kucharski 
1851b8adde7SWilliam Kucharski /* Create a static buffer of size PKT_BUF_SZ for each
1861b8adde7SWilliam Kucharski TX Descriptor.  All descriptors point to a
1871b8adde7SWilliam Kucharski part of this buffer */
1881b8adde7SWilliam Kucharski static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
1891b8adde7SWilliam Kucharski //    __attribute__ ((aligned(16)));
1901b8adde7SWilliam Kucharski 
1911b8adde7SWilliam Kucharski /* Create a static buffer of size PKT_BUF_SZ for each
1921b8adde7SWilliam Kucharski RX Descriptor   All descriptors point to a
1931b8adde7SWilliam Kucharski part of this buffer */
1941b8adde7SWilliam Kucharski static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
1951b8adde7SWilliam Kucharski //    __attribute__ ((aligned(16)));
1961b8adde7SWilliam Kucharski 
1971b8adde7SWilliam Kucharski /* The PCNET32 Rx and Tx ring descriptors. */
1981b8adde7SWilliam Kucharski struct pcnet32_rx_head {
1991b8adde7SWilliam Kucharski 	u32 base;
2001b8adde7SWilliam Kucharski 	s16 buf_length;
2011b8adde7SWilliam Kucharski 	s16 status;
2021b8adde7SWilliam Kucharski 	u32 msg_length;
2031b8adde7SWilliam Kucharski 	u32 reserved;
2041b8adde7SWilliam Kucharski };
2051b8adde7SWilliam Kucharski 
2061b8adde7SWilliam Kucharski struct pcnet32_tx_head {
2071b8adde7SWilliam Kucharski 	u32 base;
2081b8adde7SWilliam Kucharski 	s16 length;
2091b8adde7SWilliam Kucharski 	s16 status;
2101b8adde7SWilliam Kucharski 	u32 misc;
2111b8adde7SWilliam Kucharski 	u32 reserved;
2121b8adde7SWilliam Kucharski };
2131b8adde7SWilliam Kucharski 
2141b8adde7SWilliam Kucharski /* The PCNET32 32-Bit initialization block, described in databook. */
2151b8adde7SWilliam Kucharski struct pcnet32_init_block {
2161b8adde7SWilliam Kucharski 	u16 mode;
2171b8adde7SWilliam Kucharski 	u16 tlen_rlen;
2181b8adde7SWilliam Kucharski 	u8 phys_addr[6];
2191b8adde7SWilliam Kucharski 	u16 reserved;
2201b8adde7SWilliam Kucharski 	u32 filter[2];
2211b8adde7SWilliam Kucharski 	/* Receive and transmit ring base, along with extra bits. */
2221b8adde7SWilliam Kucharski 	u32 rx_ring;
2231b8adde7SWilliam Kucharski 	u32 tx_ring;
2241b8adde7SWilliam Kucharski };
2251b8adde7SWilliam Kucharski /* PCnet32 access functions */
2261b8adde7SWilliam Kucharski struct pcnet32_access {
2271b8adde7SWilliam Kucharski 	u16(*read_csr) (unsigned long, int);
2281b8adde7SWilliam Kucharski 	void (*write_csr) (unsigned long, int, u16);
2291b8adde7SWilliam Kucharski 	 u16(*read_bcr) (unsigned long, int);
2301b8adde7SWilliam Kucharski 	void (*write_bcr) (unsigned long, int, u16);
2311b8adde7SWilliam Kucharski 	 u16(*read_rap) (unsigned long);
2321b8adde7SWilliam Kucharski 	void (*write_rap) (unsigned long, u16);
2331b8adde7SWilliam Kucharski 	void (*reset) (unsigned long);
2341b8adde7SWilliam Kucharski };
2351b8adde7SWilliam Kucharski 
2361b8adde7SWilliam Kucharski /* Define the TX Descriptor */
2371b8adde7SWilliam Kucharski static struct pcnet32_tx_head tx_ring[TX_RING_SIZE]
2381b8adde7SWilliam Kucharski     __attribute__ ((aligned(16)));
2391b8adde7SWilliam Kucharski 
2401b8adde7SWilliam Kucharski 
2411b8adde7SWilliam Kucharski /* Define the RX Descriptor */
2421b8adde7SWilliam Kucharski static struct pcnet32_rx_head rx_ring[RX_RING_SIZE]
2431b8adde7SWilliam Kucharski     __attribute__ ((aligned(16)));
2441b8adde7SWilliam Kucharski 
2451b8adde7SWilliam Kucharski /* May need to be moved to mii.h */
2461b8adde7SWilliam Kucharski struct mii_if_info {
2471b8adde7SWilliam Kucharski 	int phy_id;
2481b8adde7SWilliam Kucharski 	int advertising;
2491b8adde7SWilliam Kucharski 	unsigned int full_duplex:1;	/* is full duplex? */
2501b8adde7SWilliam Kucharski };
2511b8adde7SWilliam Kucharski 
2521b8adde7SWilliam Kucharski /*
2531b8adde7SWilliam Kucharski  * The first three fields of pcnet32_private are read by the ethernet device
2541b8adde7SWilliam Kucharski  * so we allocate the structure should be allocated by pci_alloc_consistent().
2551b8adde7SWilliam Kucharski  */
2561b8adde7SWilliam Kucharski #define MII_CNT 4
2571b8adde7SWilliam Kucharski struct pcnet32_private {
2581b8adde7SWilliam Kucharski 	struct pcnet32_init_block init_block;
2591b8adde7SWilliam Kucharski 	struct pci_dev *pci_dev;	/* Pointer to the associated pci device structure */
2601b8adde7SWilliam Kucharski 	const char *name;
2611b8adde7SWilliam Kucharski 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
2621b8adde7SWilliam Kucharski 	struct sk_buff *tx_skbuff[TX_RING_SIZE];
2631b8adde7SWilliam Kucharski 	struct sk_buff *rx_skbuff[RX_RING_SIZE];
2641b8adde7SWilliam Kucharski 	struct pcnet32_access a;
2651b8adde7SWilliam Kucharski 	unsigned int cur_rx, cur_tx;	/* The next free ring entry */
2661b8adde7SWilliam Kucharski 	char tx_full;
2671b8adde7SWilliam Kucharski 	int options;
2681b8adde7SWilliam Kucharski 	int shared_irq:1,	/* shared irq possible */
2691b8adde7SWilliam Kucharski 	 ltint:1,		/* enable TxDone-intr inhibitor */
2701b8adde7SWilliam Kucharski 	 dxsuflo:1,		/* disable transmit stop on uflo */
2711b8adde7SWilliam Kucharski 	 mii:1;			/* mii port available */
2721b8adde7SWilliam Kucharski 	struct mii_if_info mii_if;
2731b8adde7SWilliam Kucharski 	unsigned char phys[MII_CNT];
2741b8adde7SWilliam Kucharski 	struct net_device *next;
2751b8adde7SWilliam Kucharski 	int full_duplex:1;
2761b8adde7SWilliam Kucharski } lpx;
2771b8adde7SWilliam Kucharski 
2781b8adde7SWilliam Kucharski static struct pcnet32_private *lp;
2791b8adde7SWilliam Kucharski 
2801b8adde7SWilliam Kucharski static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num);
2811b8adde7SWilliam Kucharski #if 0
2821b8adde7SWilliam Kucharski static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
2831b8adde7SWilliam Kucharski 		       int val);
2841b8adde7SWilliam Kucharski #endif
2851b8adde7SWilliam Kucharski enum pci_flags_bit {
2861b8adde7SWilliam Kucharski 	PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
2871b8adde7SWilliam Kucharski 	PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 =
2881b8adde7SWilliam Kucharski 	    0x10 << 2, PCI_ADDR3 = 0x10 << 3,
2891b8adde7SWilliam Kucharski };
2901b8adde7SWilliam Kucharski 
2911b8adde7SWilliam Kucharski 
2921b8adde7SWilliam Kucharski static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
2931b8adde7SWilliam Kucharski {
2941b8adde7SWilliam Kucharski 	outw(index, addr + PCNET32_WIO_RAP);
2951b8adde7SWilliam Kucharski 	return inw(addr + PCNET32_WIO_RDP);
2961b8adde7SWilliam Kucharski }
2971b8adde7SWilliam Kucharski 
2981b8adde7SWilliam Kucharski static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
2991b8adde7SWilliam Kucharski {
3001b8adde7SWilliam Kucharski 	outw(index, addr + PCNET32_WIO_RAP);
3011b8adde7SWilliam Kucharski 	outw(val, addr + PCNET32_WIO_RDP);
3021b8adde7SWilliam Kucharski }
3031b8adde7SWilliam Kucharski 
3041b8adde7SWilliam Kucharski static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
3051b8adde7SWilliam Kucharski {
3061b8adde7SWilliam Kucharski 	outw(index, addr + PCNET32_WIO_RAP);
3071b8adde7SWilliam Kucharski 	return inw(addr + PCNET32_WIO_BDP);
3081b8adde7SWilliam Kucharski }
3091b8adde7SWilliam Kucharski 
3101b8adde7SWilliam Kucharski static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
3111b8adde7SWilliam Kucharski {
3121b8adde7SWilliam Kucharski 	outw(index, addr + PCNET32_WIO_RAP);
3131b8adde7SWilliam Kucharski 	outw(val, addr + PCNET32_WIO_BDP);
3141b8adde7SWilliam Kucharski }
3151b8adde7SWilliam Kucharski 
3161b8adde7SWilliam Kucharski static u16 pcnet32_wio_read_rap(unsigned long addr)
3171b8adde7SWilliam Kucharski {
3181b8adde7SWilliam Kucharski 	return inw(addr + PCNET32_WIO_RAP);
3191b8adde7SWilliam Kucharski }
3201b8adde7SWilliam Kucharski 
3211b8adde7SWilliam Kucharski static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
3221b8adde7SWilliam Kucharski {
3231b8adde7SWilliam Kucharski 	outw(val, addr + PCNET32_WIO_RAP);
3241b8adde7SWilliam Kucharski }
3251b8adde7SWilliam Kucharski 
3261b8adde7SWilliam Kucharski static void pcnet32_wio_reset(unsigned long addr)
3271b8adde7SWilliam Kucharski {
3281b8adde7SWilliam Kucharski 	inw(addr + PCNET32_WIO_RESET);
3291b8adde7SWilliam Kucharski }
3301b8adde7SWilliam Kucharski 
3311b8adde7SWilliam Kucharski static int pcnet32_wio_check(unsigned long addr)
3321b8adde7SWilliam Kucharski {
3331b8adde7SWilliam Kucharski 	outw(88, addr + PCNET32_WIO_RAP);
3341b8adde7SWilliam Kucharski 	return (inw(addr + PCNET32_WIO_RAP) == 88);
3351b8adde7SWilliam Kucharski }
3361b8adde7SWilliam Kucharski 
3371b8adde7SWilliam Kucharski static struct pcnet32_access pcnet32_wio = {
3381b8adde7SWilliam Kucharski       read_csr:pcnet32_wio_read_csr,
3391b8adde7SWilliam Kucharski       write_csr:pcnet32_wio_write_csr,
3401b8adde7SWilliam Kucharski       read_bcr:pcnet32_wio_read_bcr,
3411b8adde7SWilliam Kucharski       write_bcr:pcnet32_wio_write_bcr,
3421b8adde7SWilliam Kucharski       read_rap:pcnet32_wio_read_rap,
3431b8adde7SWilliam Kucharski       write_rap:pcnet32_wio_write_rap,
3441b8adde7SWilliam Kucharski       reset:pcnet32_wio_reset
3451b8adde7SWilliam Kucharski };
3461b8adde7SWilliam Kucharski 
3471b8adde7SWilliam Kucharski static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
3481b8adde7SWilliam Kucharski {
3491b8adde7SWilliam Kucharski 	outl(index, addr + PCNET32_DWIO_RAP);
3501b8adde7SWilliam Kucharski 	return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);
3511b8adde7SWilliam Kucharski }
3521b8adde7SWilliam Kucharski 
3531b8adde7SWilliam Kucharski static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
3541b8adde7SWilliam Kucharski {
3551b8adde7SWilliam Kucharski 	outl(index, addr + PCNET32_DWIO_RAP);
3561b8adde7SWilliam Kucharski 	outl(val, addr + PCNET32_DWIO_RDP);
3571b8adde7SWilliam Kucharski }
3581b8adde7SWilliam Kucharski 
3591b8adde7SWilliam Kucharski static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
3601b8adde7SWilliam Kucharski {
3611b8adde7SWilliam Kucharski 	outl(index, addr + PCNET32_DWIO_RAP);
3621b8adde7SWilliam Kucharski 	return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);
3631b8adde7SWilliam Kucharski }
3641b8adde7SWilliam Kucharski 
3651b8adde7SWilliam Kucharski static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
3661b8adde7SWilliam Kucharski {
3671b8adde7SWilliam Kucharski 	outl(index, addr + PCNET32_DWIO_RAP);
3681b8adde7SWilliam Kucharski 	outl(val, addr + PCNET32_DWIO_BDP);
3691b8adde7SWilliam Kucharski }
3701b8adde7SWilliam Kucharski 
3711b8adde7SWilliam Kucharski static u16 pcnet32_dwio_read_rap(unsigned long addr)
3721b8adde7SWilliam Kucharski {
3731b8adde7SWilliam Kucharski 	return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);
3741b8adde7SWilliam Kucharski }
3751b8adde7SWilliam Kucharski 
3761b8adde7SWilliam Kucharski static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
3771b8adde7SWilliam Kucharski {
3781b8adde7SWilliam Kucharski 	outl(val, addr + PCNET32_DWIO_RAP);
3791b8adde7SWilliam Kucharski }
3801b8adde7SWilliam Kucharski 
3811b8adde7SWilliam Kucharski static void pcnet32_dwio_reset(unsigned long addr)
3821b8adde7SWilliam Kucharski {
3831b8adde7SWilliam Kucharski 	inl(addr + PCNET32_DWIO_RESET);
3841b8adde7SWilliam Kucharski }
3851b8adde7SWilliam Kucharski 
3861b8adde7SWilliam Kucharski static int pcnet32_dwio_check(unsigned long addr)
3871b8adde7SWilliam Kucharski {
3881b8adde7SWilliam Kucharski 	outl(88, addr + PCNET32_DWIO_RAP);
3891b8adde7SWilliam Kucharski 	return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);
3901b8adde7SWilliam Kucharski }
3911b8adde7SWilliam Kucharski 
3921b8adde7SWilliam Kucharski static struct pcnet32_access pcnet32_dwio = {
3931b8adde7SWilliam Kucharski       read_csr:pcnet32_dwio_read_csr,
3941b8adde7SWilliam Kucharski       write_csr:pcnet32_dwio_write_csr,
3951b8adde7SWilliam Kucharski       read_bcr:pcnet32_dwio_read_bcr,
3961b8adde7SWilliam Kucharski       write_bcr:pcnet32_dwio_write_bcr,
3971b8adde7SWilliam Kucharski       read_rap:pcnet32_dwio_read_rap,
3981b8adde7SWilliam Kucharski       write_rap:pcnet32_dwio_write_rap,
3991b8adde7SWilliam Kucharski       reset:pcnet32_dwio_reset
4001b8adde7SWilliam Kucharski };
4011b8adde7SWilliam Kucharski 
4021b8adde7SWilliam Kucharski 
4031b8adde7SWilliam Kucharski /* Initialize the PCNET32 Rx and Tx rings. */
4041b8adde7SWilliam Kucharski static int pcnet32_init_ring(struct nic *nic)
4051b8adde7SWilliam Kucharski {
4061b8adde7SWilliam Kucharski 	int i;
4071b8adde7SWilliam Kucharski 
4081b8adde7SWilliam Kucharski 	lp->tx_full = 0;
4091b8adde7SWilliam Kucharski 	lp->cur_rx = lp->cur_tx = 0;
4101b8adde7SWilliam Kucharski 
4111b8adde7SWilliam Kucharski 	for (i = 0; i < RX_RING_SIZE; i++) {
4121b8adde7SWilliam Kucharski 		rx_ring[i].base = (u32) virt_to_le32desc(&rxb[i]);
4131b8adde7SWilliam Kucharski 		rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
4141b8adde7SWilliam Kucharski 		rx_ring[i].status = le16_to_cpu(0x8000);
4151b8adde7SWilliam Kucharski 	}
4161b8adde7SWilliam Kucharski 
4171b8adde7SWilliam Kucharski 	/* The Tx buffer address is filled in as needed, but we do need to clear
4181b8adde7SWilliam Kucharski 	   the upper ownership bit. */
4191b8adde7SWilliam Kucharski 	for (i = 0; i < TX_RING_SIZE; i++) {
4201b8adde7SWilliam Kucharski 		tx_ring[i].base = 0;
4211b8adde7SWilliam Kucharski 		tx_ring[i].status = 0;
4221b8adde7SWilliam Kucharski 	}
4231b8adde7SWilliam Kucharski 
4241b8adde7SWilliam Kucharski 
4251b8adde7SWilliam Kucharski 	lp->init_block.tlen_rlen =
4261b8adde7SWilliam Kucharski 	    le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
4271b8adde7SWilliam Kucharski 	for (i = 0; i < 6; i++)
4281b8adde7SWilliam Kucharski 		lp->init_block.phys_addr[i] = nic->node_addr[i];
4291b8adde7SWilliam Kucharski 	lp->init_block.rx_ring = (u32) virt_to_le32desc(&rx_ring[0]);
4301b8adde7SWilliam Kucharski 	lp->init_block.tx_ring = (u32) virt_to_le32desc(&tx_ring[0]);
4311b8adde7SWilliam Kucharski 	return 0;
4321b8adde7SWilliam Kucharski }
4331b8adde7SWilliam Kucharski 
4341b8adde7SWilliam Kucharski /**************************************************************************
4351b8adde7SWilliam Kucharski RESET - Reset adapter
4361b8adde7SWilliam Kucharski ***************************************************************************/
4371b8adde7SWilliam Kucharski static void pcnet32_reset(struct nic *nic)
4381b8adde7SWilliam Kucharski {
4391b8adde7SWilliam Kucharski 	/* put the card in its initial state */
4401b8adde7SWilliam Kucharski 	u16 val;
4411b8adde7SWilliam Kucharski 	int i;
4421b8adde7SWilliam Kucharski 
4431b8adde7SWilliam Kucharski 	/* Reset the PCNET32 */
4441b8adde7SWilliam Kucharski 	lp->a.reset(ioaddr);
4451b8adde7SWilliam Kucharski 
4461b8adde7SWilliam Kucharski 	/* switch pcnet32 to 32bit mode */
4471b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 20, 2);
4481b8adde7SWilliam Kucharski 
4491b8adde7SWilliam Kucharski 	/* set/reset autoselect bit */
4501b8adde7SWilliam Kucharski 	val = lp->a.read_bcr(ioaddr, 2) & ~2;
4511b8adde7SWilliam Kucharski 	if (lp->options & PCNET32_PORT_ASEL)
4521b8adde7SWilliam Kucharski 		val |= 2;
4531b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 2, val);
4541b8adde7SWilliam Kucharski 	/* handle full duplex setting */
4551b8adde7SWilliam Kucharski 	if (lp->full_duplex) {
4561b8adde7SWilliam Kucharski 		val = lp->a.read_bcr(ioaddr, 9) & ~3;
4571b8adde7SWilliam Kucharski 		if (lp->options & PCNET32_PORT_FD) {
4581b8adde7SWilliam Kucharski 			val |= 1;
4591b8adde7SWilliam Kucharski 			if (lp->options ==
4601b8adde7SWilliam Kucharski 			    (PCNET32_PORT_FD | PCNET32_PORT_AUI))
4611b8adde7SWilliam Kucharski 				val |= 2;
4621b8adde7SWilliam Kucharski 		} else if (lp->options & PCNET32_PORT_ASEL) {
4631b8adde7SWilliam Kucharski 			/* workaround of xSeries250, turn on for 79C975 only */
4641b8adde7SWilliam Kucharski 			i = ((lp->a.
4651b8adde7SWilliam Kucharski 			      read_csr(ioaddr,
4661b8adde7SWilliam Kucharski 				       88) | (lp->a.read_csr(ioaddr,
4671b8adde7SWilliam Kucharski 							     89) << 16)) >>
4681b8adde7SWilliam Kucharski 			     12) & 0xffff;
4691b8adde7SWilliam Kucharski 			if (i == 0x2627)
4701b8adde7SWilliam Kucharski 				val |= 3;
4711b8adde7SWilliam Kucharski 		}
4721b8adde7SWilliam Kucharski 		lp->a.write_bcr(ioaddr, 9, val);
4731b8adde7SWilliam Kucharski 	}
4741b8adde7SWilliam Kucharski 
4751b8adde7SWilliam Kucharski 	/* set/reset GPSI bit in test register */
4761b8adde7SWilliam Kucharski 	val = lp->a.read_csr(ioaddr, 124) & ~0x10;
4771b8adde7SWilliam Kucharski 	if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
4781b8adde7SWilliam Kucharski 		val |= 0x10;
4791b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 124, val);
4801b8adde7SWilliam Kucharski 
4811b8adde7SWilliam Kucharski 	if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
482*4a3b1d5bSBart Coddens 		val = lp->a.read_bcr(ioaddr, 32) & ~0x38;	/* disable Auto Negotiation, set 10Mbps, HD */
4831b8adde7SWilliam Kucharski 		if (lp->options & PCNET32_PORT_FD)
4841b8adde7SWilliam Kucharski 			val |= 0x10;
4851b8adde7SWilliam Kucharski 		if (lp->options & PCNET32_PORT_100)
4861b8adde7SWilliam Kucharski 			val |= 0x08;
4871b8adde7SWilliam Kucharski 		lp->a.write_bcr(ioaddr, 32, val);
4881b8adde7SWilliam Kucharski 	} else {
4891b8adde7SWilliam Kucharski 		if (lp->options & PCNET32_PORT_ASEL) {	/* enable auto negotiate, setup, disable fd */
4901b8adde7SWilliam Kucharski 			val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
4911b8adde7SWilliam Kucharski 			val |= 0x20;
4921b8adde7SWilliam Kucharski 			lp->a.write_bcr(ioaddr, 32, val);
4931b8adde7SWilliam Kucharski 		}
4941b8adde7SWilliam Kucharski 	}
4951b8adde7SWilliam Kucharski 
4961b8adde7SWilliam Kucharski #ifdef DO_DXSUFLO
4971b8adde7SWilliam Kucharski 	if (lp->dxsuflo) {	/* Disable transmit stop on underflow */
4981b8adde7SWilliam Kucharski 		val = lp->a.read_csr(ioaddr, 3);
4991b8adde7SWilliam Kucharski 		val |= 0x40;
5001b8adde7SWilliam Kucharski 		lp->a.write_csr(ioaddr, 3, val);
5011b8adde7SWilliam Kucharski 	}
5021b8adde7SWilliam Kucharski #endif
5031b8adde7SWilliam Kucharski 
5041b8adde7SWilliam Kucharski 	if (lp->ltint) {	/* Enable TxDone-intr inhibitor */
5051b8adde7SWilliam Kucharski 		val = lp->a.read_csr(ioaddr, 5);
5061b8adde7SWilliam Kucharski 		val |= (1 << 14);
5071b8adde7SWilliam Kucharski 		lp->a.write_csr(ioaddr, 5, val);
5081b8adde7SWilliam Kucharski 	}
5091b8adde7SWilliam Kucharski 	lp->init_block.mode =
5101b8adde7SWilliam Kucharski 	    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
5111b8adde7SWilliam Kucharski 	lp->init_block.filter[0] = 0xffffffff;
5121b8adde7SWilliam Kucharski 	lp->init_block.filter[1] = 0xffffffff;
5131b8adde7SWilliam Kucharski 
5141b8adde7SWilliam Kucharski 	pcnet32_init_ring(nic);
5151b8adde7SWilliam Kucharski 
5161b8adde7SWilliam Kucharski 
5171b8adde7SWilliam Kucharski 	/* Re-initialize the PCNET32, and start it when done. */
5181b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 1,
5191b8adde7SWilliam Kucharski 			(virt_to_bus(&lp->init_block)) & 0xffff);
5201b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
5211b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 4, 0x0915);
5221b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 0, 0x0001);
5231b8adde7SWilliam Kucharski 
5241b8adde7SWilliam Kucharski 
5251b8adde7SWilliam Kucharski 	i = 0;
5261b8adde7SWilliam Kucharski 	while (i++ < 100)
5271b8adde7SWilliam Kucharski 		if (lp->a.read_csr(ioaddr, 0) & 0x0100)
5281b8adde7SWilliam Kucharski 			break;
5291b8adde7SWilliam Kucharski 	/*
5301b8adde7SWilliam Kucharski 	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
5311b8adde7SWilliam Kucharski 	 * reports that doing so triggers a bug in the '974.
5321b8adde7SWilliam Kucharski 	 */
5331b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 0, 0x0042);
5341b8adde7SWilliam Kucharski 
5351b8adde7SWilliam Kucharski 	dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0)));
5361b8adde7SWilliam Kucharski 
5371b8adde7SWilliam Kucharski }
5381b8adde7SWilliam Kucharski 
5391b8adde7SWilliam Kucharski /**************************************************************************
5401b8adde7SWilliam Kucharski POLL - Wait for a frame
5411b8adde7SWilliam Kucharski ***************************************************************************/
5421b8adde7SWilliam Kucharski static int pcnet32_poll(struct nic *nic __unused, int retrieve)
5431b8adde7SWilliam Kucharski {
5441b8adde7SWilliam Kucharski 	/* return true if there's an ethernet packet ready to read */
5451b8adde7SWilliam Kucharski 	/* nic->packet should contain data on return */
5461b8adde7SWilliam Kucharski 	/* nic->packetlen should contain length of data */
5471b8adde7SWilliam Kucharski 
5481b8adde7SWilliam Kucharski 	int status;
5491b8adde7SWilliam Kucharski 	int entry;
5501b8adde7SWilliam Kucharski 
5511b8adde7SWilliam Kucharski 	entry = lp->cur_rx & RX_RING_MOD_MASK;
5521b8adde7SWilliam Kucharski 	status = ((short) le16_to_cpu(rx_ring[entry].status) >> 8);
5531b8adde7SWilliam Kucharski 
5541b8adde7SWilliam Kucharski 	if (status < 0)
5551b8adde7SWilliam Kucharski 		return 0;
5561b8adde7SWilliam Kucharski 
5571b8adde7SWilliam Kucharski 	if ( ! retrieve ) return 1;
5581b8adde7SWilliam Kucharski 
5591b8adde7SWilliam Kucharski 	if (status == 0x03) {
5601b8adde7SWilliam Kucharski 		nic->packetlen =
5611b8adde7SWilliam Kucharski 		    (le32_to_cpu(rx_ring[entry].msg_length) & 0xfff) - 4;
5621b8adde7SWilliam Kucharski 		memcpy(nic->packet, &rxb[entry], nic->packetlen);
5631b8adde7SWilliam Kucharski 
5641b8adde7SWilliam Kucharski 		/* Andrew Boyd of QNX reports that some revs of the 79C765
5651b8adde7SWilliam Kucharski 		 * clear the buffer length */
5661b8adde7SWilliam Kucharski 		rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
5671b8adde7SWilliam Kucharski 		rx_ring[entry].status |= le16_to_cpu(0x8000);	/* prime for next receive */
5681b8adde7SWilliam Kucharski 		/* Switch to the next Rx ring buffer */
5691b8adde7SWilliam Kucharski 		lp->cur_rx++;
5701b8adde7SWilliam Kucharski 
5711b8adde7SWilliam Kucharski 	} else {
5721b8adde7SWilliam Kucharski 		return 0;
5731b8adde7SWilliam Kucharski 	}
5741b8adde7SWilliam Kucharski 
5751b8adde7SWilliam Kucharski 	return 1;
5761b8adde7SWilliam Kucharski }
5771b8adde7SWilliam Kucharski 
5781b8adde7SWilliam Kucharski /**************************************************************************
5791b8adde7SWilliam Kucharski TRANSMIT - Transmit a frame
5801b8adde7SWilliam Kucharski ***************************************************************************/
5811b8adde7SWilliam Kucharski static void pcnet32_transmit(struct nic *nic __unused, const char *d,	/* Destination */
5821b8adde7SWilliam Kucharski 			     unsigned int t,	/* Type */
5831b8adde7SWilliam Kucharski 			     unsigned int s,	/* size */
5841b8adde7SWilliam Kucharski 			     const char *p)
5851b8adde7SWilliam Kucharski {				/* Packet */
5861b8adde7SWilliam Kucharski 	/* send the packet to destination */
5871b8adde7SWilliam Kucharski 	unsigned long time;
5881b8adde7SWilliam Kucharski 	u8 *ptxb;
5891b8adde7SWilliam Kucharski 	u16 nstype;
5901b8adde7SWilliam Kucharski 	u16 status;
5911b8adde7SWilliam Kucharski 	int entry = 0;		/*lp->cur_tx & TX_RING_MOD_MASK; */
5921b8adde7SWilliam Kucharski 
5931b8adde7SWilliam Kucharski 	status = 0x8300;
5941b8adde7SWilliam Kucharski 	/* point to the current txb incase multiple tx_rings are used */
5951b8adde7SWilliam Kucharski 	ptxb = txb + (lp->cur_tx * PKT_BUF_SZ);
5961b8adde7SWilliam Kucharski 
5971b8adde7SWilliam Kucharski 	/* copy the packet to ring buffer */
5981b8adde7SWilliam Kucharski 	memcpy(ptxb, d, ETH_ALEN);	/* dst */
5991b8adde7SWilliam Kucharski 	memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);	/* src */
6001b8adde7SWilliam Kucharski 	nstype = htons((u16) t);	/* type */
6011b8adde7SWilliam Kucharski 	memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);	/* type */
6021b8adde7SWilliam Kucharski 	memcpy(ptxb + ETH_HLEN, p, s);
6031b8adde7SWilliam Kucharski 
6041b8adde7SWilliam Kucharski 	s += ETH_HLEN;
6051b8adde7SWilliam Kucharski 	while (s < ETH_ZLEN)	/* pad to min length */
6061b8adde7SWilliam Kucharski 		ptxb[s++] = '\0';
6071b8adde7SWilliam Kucharski 
6081b8adde7SWilliam Kucharski 	tx_ring[entry].length = le16_to_cpu(-s);
6091b8adde7SWilliam Kucharski 	tx_ring[entry].misc = 0x00000000;
6101b8adde7SWilliam Kucharski 	tx_ring[entry].base = (u32) virt_to_le32desc(ptxb);
6111b8adde7SWilliam Kucharski 
6121b8adde7SWilliam Kucharski 	/* we set the top byte as the very last thing */
6131b8adde7SWilliam Kucharski 	tx_ring[entry].status = le16_to_cpu(status);
6141b8adde7SWilliam Kucharski 
6151b8adde7SWilliam Kucharski 
6161b8adde7SWilliam Kucharski 	/* Trigger an immediate send poll */
6171b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 0, 0x0048);
6181b8adde7SWilliam Kucharski 
6191b8adde7SWilliam Kucharski 	/* wait for transmit complete */
6201b8adde7SWilliam Kucharski 	lp->cur_tx = 0;		/* (lp->cur_tx + 1); */
6211b8adde7SWilliam Kucharski 	time = currticks() + TICKS_PER_SEC;	/* wait one second */
6221b8adde7SWilliam Kucharski 	while (currticks() < time &&
6231b8adde7SWilliam Kucharski 	       ((short) le16_to_cpu(tx_ring[entry].status) < 0));
6241b8adde7SWilliam Kucharski 
6251b8adde7SWilliam Kucharski 	if ((short) le16_to_cpu(tx_ring[entry].status) < 0)
6261b8adde7SWilliam Kucharski 		printf("PCNET32 timed out on transmit\n");
6271b8adde7SWilliam Kucharski 
6281b8adde7SWilliam Kucharski 	/* Stop pointing at the current txb
6291b8adde7SWilliam Kucharski 	 * otherwise the card continues to send the packet */
6301b8adde7SWilliam Kucharski 	tx_ring[entry].base = 0;
6311b8adde7SWilliam Kucharski 
6321b8adde7SWilliam Kucharski }
6331b8adde7SWilliam Kucharski 
6341b8adde7SWilliam Kucharski /**************************************************************************
6351b8adde7SWilliam Kucharski DISABLE - Turn off ethernet interface
6361b8adde7SWilliam Kucharski ***************************************************************************/
6371b8adde7SWilliam Kucharski static void pcnet32_disable(struct dev *dev __unused)
6381b8adde7SWilliam Kucharski {
6391b8adde7SWilliam Kucharski 	/* Stop the PCNET32 here -- it ocassionally polls memory if we don't */
6401b8adde7SWilliam Kucharski 	lp->a.write_csr(ioaddr, 0, 0x0004);
6411b8adde7SWilliam Kucharski 
6421b8adde7SWilliam Kucharski 	/*
6431b8adde7SWilliam Kucharski 	 * Switch back to 16-bit mode to avoid problesm with dumb
6441b8adde7SWilliam Kucharski 	 * DOS packet driver after a warm reboot
6451b8adde7SWilliam Kucharski 	 */
6461b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 20, 4);
6471b8adde7SWilliam Kucharski }
6481b8adde7SWilliam Kucharski 
6491b8adde7SWilliam Kucharski /**************************************************************************
6501b8adde7SWilliam Kucharski IRQ - Enable, Disable, or Force interrupts
6511b8adde7SWilliam Kucharski ***************************************************************************/
6521b8adde7SWilliam Kucharski static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused)
6531b8adde7SWilliam Kucharski {
6541b8adde7SWilliam Kucharski   switch ( action ) {
6551b8adde7SWilliam Kucharski   case DISABLE :
6561b8adde7SWilliam Kucharski     break;
6571b8adde7SWilliam Kucharski   case ENABLE :
6581b8adde7SWilliam Kucharski     break;
6591b8adde7SWilliam Kucharski   case FORCE :
6601b8adde7SWilliam Kucharski     break;
6611b8adde7SWilliam Kucharski   }
6621b8adde7SWilliam Kucharski }
6631b8adde7SWilliam Kucharski 
6641b8adde7SWilliam Kucharski /**************************************************************************
6651b8adde7SWilliam Kucharski PROBE - Look for an adapter, this routine's visible to the outside
6661b8adde7SWilliam Kucharski You should omit the last argument struct pci_device * for a non-PCI NIC
6671b8adde7SWilliam Kucharski ***************************************************************************/
6681b8adde7SWilliam Kucharski static int pcnet32_probe(struct dev *dev, struct pci_device *pci)
6691b8adde7SWilliam Kucharski {
6701b8adde7SWilliam Kucharski 	struct nic *nic = (struct nic *) dev;
6711b8adde7SWilliam Kucharski 	int i, media;
6721b8adde7SWilliam Kucharski 	int fdx, mii, fset, dxsuflo, ltint;
6731b8adde7SWilliam Kucharski 	int chip_version;
6741b8adde7SWilliam Kucharski 	char *chipname;
6751b8adde7SWilliam Kucharski 	struct pcnet32_access *a = NULL;
6761b8adde7SWilliam Kucharski 	u8 promaddr[6];
6771b8adde7SWilliam Kucharski 
6781b8adde7SWilliam Kucharski 	int shared = 1;
6791b8adde7SWilliam Kucharski 	if (pci->ioaddr == 0)
6801b8adde7SWilliam Kucharski 		return 0;
6811b8adde7SWilliam Kucharski 
6821b8adde7SWilliam Kucharski 	/* BASE is used throughout to address the card */
6831b8adde7SWilliam Kucharski 	ioaddr = pci->ioaddr;
6841b8adde7SWilliam Kucharski 	printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n",
6851b8adde7SWilliam Kucharski 	       pci->name, pci->vendor, pci->dev_id);
6861b8adde7SWilliam Kucharski 
6871b8adde7SWilliam Kucharski 	nic->irqno  = 0;
6881b8adde7SWilliam Kucharski 	nic->ioaddr = pci->ioaddr & ~3;
6891b8adde7SWilliam Kucharski 
6901b8adde7SWilliam Kucharski 	/* reset the chip */
6911b8adde7SWilliam Kucharski 	pcnet32_wio_reset(ioaddr);
6921b8adde7SWilliam Kucharski 
6931b8adde7SWilliam Kucharski 	/* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
6941b8adde7SWilliam Kucharski 	if (pcnet32_wio_read_csr(ioaddr, 0) == 4
6951b8adde7SWilliam Kucharski 	    && pcnet32_wio_check(ioaddr)) {
6961b8adde7SWilliam Kucharski 		a = &pcnet32_wio;
6971b8adde7SWilliam Kucharski 	} else {
6981b8adde7SWilliam Kucharski 		pcnet32_dwio_reset(ioaddr);
6991b8adde7SWilliam Kucharski 		if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
7001b8adde7SWilliam Kucharski 		    && pcnet32_dwio_check(ioaddr)) {
7011b8adde7SWilliam Kucharski 			a = &pcnet32_dwio;
7021b8adde7SWilliam Kucharski 		} else
7031b8adde7SWilliam Kucharski 			return 0;
7041b8adde7SWilliam Kucharski 	}
7051b8adde7SWilliam Kucharski 
7061b8adde7SWilliam Kucharski 	chip_version =
7071b8adde7SWilliam Kucharski 	    a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);
7081b8adde7SWilliam Kucharski 
7091b8adde7SWilliam Kucharski 	dprintf(("PCnet chip version is %0xhX\n", chip_version));
7101b8adde7SWilliam Kucharski 	if ((chip_version & 0xfff) != 0x003)
7111b8adde7SWilliam Kucharski 		return 0;
7121b8adde7SWilliam Kucharski 
7131b8adde7SWilliam Kucharski 	/* initialize variables */
7141b8adde7SWilliam Kucharski 	fdx = mii = fset = dxsuflo = ltint = 0;
7151b8adde7SWilliam Kucharski 	chip_version = (chip_version >> 12) & 0xffff;
7161b8adde7SWilliam Kucharski 
7171b8adde7SWilliam Kucharski 	switch (chip_version) {
7181b8adde7SWilliam Kucharski 	case 0x2420:
7191b8adde7SWilliam Kucharski 		chipname = "PCnet/PCI 79C970";	/* PCI */
7201b8adde7SWilliam Kucharski 		break;
7211b8adde7SWilliam Kucharski 	case 0x2430:
7221b8adde7SWilliam Kucharski 		if (shared)
7231b8adde7SWilliam Kucharski 			chipname = "PCnet/PCI 79C970";	/* 970 gives the wrong chip id back */
7241b8adde7SWilliam Kucharski 		else
7251b8adde7SWilliam Kucharski 			chipname = "PCnet/32 79C965";	/* 486/VL bus */
7261b8adde7SWilliam Kucharski 		break;
7271b8adde7SWilliam Kucharski 	case 0x2621:
7281b8adde7SWilliam Kucharski 		chipname = "PCnet/PCI II 79C970A";	/* PCI */
7291b8adde7SWilliam Kucharski 		fdx = 1;
7301b8adde7SWilliam Kucharski 		break;
7311b8adde7SWilliam Kucharski 	case 0x2623:
7321b8adde7SWilliam Kucharski 		chipname = "PCnet/FAST 79C971";	/* PCI */
7331b8adde7SWilliam Kucharski 		fdx = 1;
7341b8adde7SWilliam Kucharski 		mii = 1;
7351b8adde7SWilliam Kucharski 		fset = 1;
7361b8adde7SWilliam Kucharski 		ltint = 1;
7371b8adde7SWilliam Kucharski 		break;
7381b8adde7SWilliam Kucharski 	case 0x2624:
7391b8adde7SWilliam Kucharski 		chipname = "PCnet/FAST+ 79C972";	/* PCI */
7401b8adde7SWilliam Kucharski 		fdx = 1;
7411b8adde7SWilliam Kucharski 		mii = 1;
7421b8adde7SWilliam Kucharski 		fset = 1;
7431b8adde7SWilliam Kucharski 		break;
7441b8adde7SWilliam Kucharski 	case 0x2625:
7451b8adde7SWilliam Kucharski 		chipname = "PCnet/FAST III 79C973";	/* PCI */
7461b8adde7SWilliam Kucharski 		fdx = 1;
7471b8adde7SWilliam Kucharski 		mii = 1;
7481b8adde7SWilliam Kucharski 		break;
7491b8adde7SWilliam Kucharski 	case 0x2626:
7501b8adde7SWilliam Kucharski 		chipname = "PCnet/Home 79C978";	/* PCI */
7511b8adde7SWilliam Kucharski 		fdx = 1;
7521b8adde7SWilliam Kucharski 		/*
7531b8adde7SWilliam Kucharski 		 * This is based on specs published at www.amd.com.  This section
7541b8adde7SWilliam Kucharski 		 * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
7551b8adde7SWilliam Kucharski 		 * mode.  The 79C978 can also go into standard ethernet, and there
7561b8adde7SWilliam Kucharski 		 * probably should be some sort of module option to select the
7571b8adde7SWilliam Kucharski 		 * mode by which the card should operate
7581b8adde7SWilliam Kucharski 		 */
7591b8adde7SWilliam Kucharski 		/* switch to home wiring mode */
7601b8adde7SWilliam Kucharski 		media = a->read_bcr(ioaddr, 49);
7611b8adde7SWilliam Kucharski 
7621b8adde7SWilliam Kucharski 		printf("media reset to %#x.\n", media);
7631b8adde7SWilliam Kucharski 		a->write_bcr(ioaddr, 49, media);
7641b8adde7SWilliam Kucharski 		break;
7651b8adde7SWilliam Kucharski 	case 0x2627:
7661b8adde7SWilliam Kucharski 		chipname = "PCnet/FAST III 79C975";	/* PCI */
7671b8adde7SWilliam Kucharski 		fdx = 1;
7681b8adde7SWilliam Kucharski 		mii = 1;
7691b8adde7SWilliam Kucharski 		break;
7701b8adde7SWilliam Kucharski 	default:
7711b8adde7SWilliam Kucharski 		printf("PCnet version %#x, no PCnet32 chip.\n",
7721b8adde7SWilliam Kucharski 		       chip_version);
7731b8adde7SWilliam Kucharski 		return 0;
7741b8adde7SWilliam Kucharski 	}
7751b8adde7SWilliam Kucharski 
7761b8adde7SWilliam Kucharski 	/*
7771b8adde7SWilliam Kucharski 	 *  On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
7781b8adde7SWilliam Kucharski 	 *  starting until the packet is loaded. Strike one for reliability, lose
7791b8adde7SWilliam Kucharski 	 *  one for latency - although on PCI this isnt a big loss. Older chips
7801b8adde7SWilliam Kucharski 	 *  have FIFO's smaller than a packet, so you can't do this.
7811b8adde7SWilliam Kucharski 	 */
7821b8adde7SWilliam Kucharski 
7831b8adde7SWilliam Kucharski 	if (fset) {
7841b8adde7SWilliam Kucharski 		a->write_bcr(ioaddr, 18,
7851b8adde7SWilliam Kucharski 			     (a->read_bcr(ioaddr, 18) | 0x0800));
7861b8adde7SWilliam Kucharski 		a->write_csr(ioaddr, 80,
7871b8adde7SWilliam Kucharski 			     (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
7881b8adde7SWilliam Kucharski 		dxsuflo = 1;
7891b8adde7SWilliam Kucharski 		ltint = 1;
7901b8adde7SWilliam Kucharski 	}
7911b8adde7SWilliam Kucharski 
7921b8adde7SWilliam Kucharski 	dprintf(("%s at %hX,", chipname, ioaddr));
7931b8adde7SWilliam Kucharski 
7941b8adde7SWilliam Kucharski 	/* read PROM address */
7951b8adde7SWilliam Kucharski 	for (i = 0; i < 6; i++)
7961b8adde7SWilliam Kucharski 		promaddr[i] = inb(ioaddr + i);
7971b8adde7SWilliam Kucharski 
7981b8adde7SWilliam Kucharski 	/* Update the nic structure with the MAC Address */
7991b8adde7SWilliam Kucharski 	for (i = 0; i < ETH_ALEN; i++) {
8001b8adde7SWilliam Kucharski 		nic->node_addr[i] = promaddr[i];
8011b8adde7SWilliam Kucharski 	}
8021b8adde7SWilliam Kucharski 	/* Print out some hardware info */
8031b8adde7SWilliam Kucharski 	printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr,
8041b8adde7SWilliam Kucharski 	       ioaddr);
8051b8adde7SWilliam Kucharski 
8061b8adde7SWilliam Kucharski 	/* Set to pci bus master */
8071b8adde7SWilliam Kucharski 	adjust_pci_device(pci);
8081b8adde7SWilliam Kucharski 
8091b8adde7SWilliam Kucharski 	/* point to private storage */
8101b8adde7SWilliam Kucharski 	lp = &lpx;
8111b8adde7SWilliam Kucharski 
8121b8adde7SWilliam Kucharski #if EBDEBUG
8131b8adde7SWilliam Kucharski 	if (((chip_version + 1) & 0xfffe) == 0x2624) {	/* Version 0x2623 or 0x2624 */
8141b8adde7SWilliam Kucharski 		i = a->read_csr(ioaddr, 80) & 0x0C00;	/* Check tx_start_pt */
8151b8adde7SWilliam Kucharski 		dprintf(("    tx_start_pt(0x%hX):", i));
8161b8adde7SWilliam Kucharski 		switch (i >> 10) {
8171b8adde7SWilliam Kucharski 		case 0:
8181b8adde7SWilliam Kucharski 			dprintf(("  20 bytes,"));
8191b8adde7SWilliam Kucharski 			break;
8201b8adde7SWilliam Kucharski 		case 1:
8211b8adde7SWilliam Kucharski 			dprintf(("  64 bytes,"));
8221b8adde7SWilliam Kucharski 			break;
8231b8adde7SWilliam Kucharski 		case 2:
8241b8adde7SWilliam Kucharski 			dprintf((" 128 bytes,"));
8251b8adde7SWilliam Kucharski 			break;
8261b8adde7SWilliam Kucharski 		case 3:
8271b8adde7SWilliam Kucharski 			dprintf(("~220 bytes,"));
8281b8adde7SWilliam Kucharski 			break;
8291b8adde7SWilliam Kucharski 		}
8301b8adde7SWilliam Kucharski 		i = a->read_bcr(ioaddr, 18);	/* Check Burst/Bus control */
8311b8adde7SWilliam Kucharski 		dprintf((" BCR18(%hX):", i & 0xffff));
8321b8adde7SWilliam Kucharski 		if (i & (1 << 5))
8331b8adde7SWilliam Kucharski 			dprintf(("BurstWrEn "));
8341b8adde7SWilliam Kucharski 		if (i & (1 << 6))
8351b8adde7SWilliam Kucharski 			dprintf(("BurstRdEn "));
8361b8adde7SWilliam Kucharski 		if (i & (1 << 7))
8371b8adde7SWilliam Kucharski 			dprintf(("DWordIO "));
8381b8adde7SWilliam Kucharski 		if (i & (1 << 11))
8391b8adde7SWilliam Kucharski 			dprintf(("NoUFlow "));
8401b8adde7SWilliam Kucharski 		i = a->read_bcr(ioaddr, 25);
8411b8adde7SWilliam Kucharski 		dprintf(("    SRAMSIZE=0x%hX,", i << 8));
8421b8adde7SWilliam Kucharski 		i = a->read_bcr(ioaddr, 26);
8431b8adde7SWilliam Kucharski 		dprintf((" SRAM_BND=0x%hX,", i << 8));
8441b8adde7SWilliam Kucharski 		i = a->read_bcr(ioaddr, 27);
8451b8adde7SWilliam Kucharski 		if (i & (1 << 14))
8461b8adde7SWilliam Kucharski 			dprintf(("LowLatRx"));
8471b8adde7SWilliam Kucharski 	}
8481b8adde7SWilliam Kucharski #endif
8491b8adde7SWilliam Kucharski 	lp->name = chipname;
8501b8adde7SWilliam Kucharski 	lp->shared_irq = shared;
8511b8adde7SWilliam Kucharski 	lp->full_duplex = fdx;
8521b8adde7SWilliam Kucharski 	lp->dxsuflo = dxsuflo;
8531b8adde7SWilliam Kucharski 	lp->ltint = ltint;
8541b8adde7SWilliam Kucharski 	lp->mii = mii;
8551b8adde7SWilliam Kucharski 	/* FIXME: Fix Options for only one card */
8561b8adde7SWilliam Kucharski 	if ((cards_found >= MAX_UNITS)
8571b8adde7SWilliam Kucharski 	    || ((unsigned int) options[cards_found] > sizeof(options_mapping)))
8581b8adde7SWilliam Kucharski 		lp->options = PCNET32_PORT_ASEL;
8591b8adde7SWilliam Kucharski 	else
8601b8adde7SWilliam Kucharski 		lp->options = options_mapping[options[cards_found]];
8611b8adde7SWilliam Kucharski 
8621b8adde7SWilliam Kucharski 	if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
8631b8adde7SWilliam Kucharski 	    ((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
8641b8adde7SWilliam Kucharski 		lp->options |= PCNET32_PORT_FD;
8651b8adde7SWilliam Kucharski 
8661b8adde7SWilliam Kucharski 	if (!a) {
8671b8adde7SWilliam Kucharski 		printf("No access methods\n");
8681b8adde7SWilliam Kucharski 		return 0;
8691b8adde7SWilliam Kucharski 	}
8701b8adde7SWilliam Kucharski 	lp->a = *a;
8711b8adde7SWilliam Kucharski 
8721b8adde7SWilliam Kucharski 	/* detect special T1/E1 WAN card by checking for MAC address */
8731b8adde7SWilliam Kucharski 	if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0
8741b8adde7SWilliam Kucharski 	    && nic->node_addr[2] == 0x75)
8751b8adde7SWilliam Kucharski 		lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
8761b8adde7SWilliam Kucharski 
8771b8adde7SWilliam Kucharski 	lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
8781b8adde7SWilliam Kucharski 	lp->init_block.tlen_rlen =
8791b8adde7SWilliam Kucharski 	    le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
8801b8adde7SWilliam Kucharski 	for (i = 0; i < 6; i++)
8811b8adde7SWilliam Kucharski 		lp->init_block.phys_addr[i] = nic->node_addr[i];
8821b8adde7SWilliam Kucharski 	lp->init_block.filter[0] = 0xffffffff;
8831b8adde7SWilliam Kucharski 	lp->init_block.filter[1] = 0xffffffff;
8841b8adde7SWilliam Kucharski 	lp->init_block.rx_ring = virt_to_bus(&rx_ring);
8851b8adde7SWilliam Kucharski 	lp->init_block.tx_ring = virt_to_bus(&tx_ring);
8861b8adde7SWilliam Kucharski 
8871b8adde7SWilliam Kucharski 	/* switch pcnet32 to 32bit mode */
8881b8adde7SWilliam Kucharski 	a->write_bcr(ioaddr, 20, 2);
8891b8adde7SWilliam Kucharski 
8901b8adde7SWilliam Kucharski 
8911b8adde7SWilliam Kucharski 	a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff);
8921b8adde7SWilliam Kucharski 	a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
8931b8adde7SWilliam Kucharski 
8941b8adde7SWilliam Kucharski 	/*
8951b8adde7SWilliam Kucharski 	 * To auto-IRQ we enable the initialization-done and DMA error
8961b8adde7SWilliam Kucharski 	 * interrupts. For ISA boards we get a DMA error, but VLB and PCI
8971b8adde7SWilliam Kucharski 	 * boards will work.
8981b8adde7SWilliam Kucharski 	 */
8991b8adde7SWilliam Kucharski 	/* Trigger an initialization just for the interrupt. */
9001b8adde7SWilliam Kucharski 
9011b8adde7SWilliam Kucharski 	a->write_csr(ioaddr, 0, 0x41);
9021b8adde7SWilliam Kucharski 	mdelay(1);
9031b8adde7SWilliam Kucharski 
9041b8adde7SWilliam Kucharski 	cards_found++;
9051b8adde7SWilliam Kucharski 
9061b8adde7SWilliam Kucharski 	/* point to NIC specific routines */
9071b8adde7SWilliam Kucharski 	pcnet32_reset(nic);
9081b8adde7SWilliam Kucharski 	if (1) {
9091b8adde7SWilliam Kucharski 	        int tmp;
9101b8adde7SWilliam Kucharski 		int phy, phy_idx = 0;
9111b8adde7SWilliam Kucharski 		u16 mii_lpa;
9121b8adde7SWilliam Kucharski 		lp->phys[0] = 1;	/* Default Setting */
9131b8adde7SWilliam Kucharski 		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
9141b8adde7SWilliam Kucharski 			int mii_status = mdio_read(nic, phy, MII_BMSR);
9151b8adde7SWilliam Kucharski 			if (mii_status != 0xffff && mii_status != 0x0000) {
9161b8adde7SWilliam Kucharski 				lp->phys[phy_idx++] = phy;
9171b8adde7SWilliam Kucharski 				lp->mii_if.advertising =
9181b8adde7SWilliam Kucharski 				    mdio_read(nic, phy, MII_ADVERTISE);
9191b8adde7SWilliam Kucharski 				if ((mii_status & 0x0040) == 0) {
9201b8adde7SWilliam Kucharski 				  tmp = phy;
9211b8adde7SWilliam Kucharski 				  dprintf (("MII PHY found at address %d, status "
9221b8adde7SWilliam Kucharski 					    "%hX advertising %hX\n", phy, mii_status,
9231b8adde7SWilliam Kucharski 					    lp->mii_if.advertising));
9241b8adde7SWilliam Kucharski 				}
9251b8adde7SWilliam Kucharski 			}
9261b8adde7SWilliam Kucharski 		}
9271b8adde7SWilliam Kucharski 		if (phy_idx == 0)
9281b8adde7SWilliam Kucharski 			printf("No MII transceiver found!\n");
9291b8adde7SWilliam Kucharski 		lp->mii_if.phy_id = lp->phys[0];
9301b8adde7SWilliam Kucharski 
9311b8adde7SWilliam Kucharski 		lp->mii_if.advertising =
9321b8adde7SWilliam Kucharski 		    mdio_read(nic, lp->phys[0], MII_ADVERTISE);
9331b8adde7SWilliam Kucharski 
9341b8adde7SWilliam Kucharski 		mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA);
9351b8adde7SWilliam Kucharski 		lp->mii_if.advertising &= mii_lpa;
9361b8adde7SWilliam Kucharski 		if (lp->mii_if.advertising & ADVERTISE_100FULL)
9371b8adde7SWilliam Kucharski 			printf("100Mbps Full-Duplex\n");
9381b8adde7SWilliam Kucharski 		else if (lp->mii_if.advertising & ADVERTISE_100HALF)
9391b8adde7SWilliam Kucharski 			printf("100Mbps Half-Duplex\n");
9401b8adde7SWilliam Kucharski 		else if (lp->mii_if.advertising & ADVERTISE_10FULL)
9411b8adde7SWilliam Kucharski 			printf("10Mbps Full-Duplex\n");
9421b8adde7SWilliam Kucharski 		else if (lp->mii_if.advertising & ADVERTISE_10HALF)
9431b8adde7SWilliam Kucharski 			printf("10Mbps Half-Duplex\n");
9441b8adde7SWilliam Kucharski 		else
9451b8adde7SWilliam Kucharski 			printf("\n");
9461b8adde7SWilliam Kucharski 	}
9471b8adde7SWilliam Kucharski 
9481b8adde7SWilliam Kucharski 	nic->poll     = pcnet32_poll;
9491b8adde7SWilliam Kucharski 	nic->transmit = pcnet32_transmit;
9501b8adde7SWilliam Kucharski 	dev->disable  = pcnet32_disable;
9511b8adde7SWilliam Kucharski 	nic->irq      = pcnet32_irq;
9521b8adde7SWilliam Kucharski 
9531b8adde7SWilliam Kucharski 	return 1;
9541b8adde7SWilliam Kucharski }
9551b8adde7SWilliam Kucharski static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num)
9561b8adde7SWilliam Kucharski {
9571b8adde7SWilliam Kucharski 	u16 val_out;
9581b8adde7SWilliam Kucharski 	int phyaddr;
9591b8adde7SWilliam Kucharski 
9601b8adde7SWilliam Kucharski 	if (!lp->mii)
9611b8adde7SWilliam Kucharski 		return 0;
9621b8adde7SWilliam Kucharski 
9631b8adde7SWilliam Kucharski 	phyaddr = lp->a.read_bcr(ioaddr, 33);
9641b8adde7SWilliam Kucharski 
9651b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 33,
9661b8adde7SWilliam Kucharski 			((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
9671b8adde7SWilliam Kucharski 	val_out = lp->a.read_bcr(ioaddr, 34);
9681b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 33, phyaddr);
9691b8adde7SWilliam Kucharski 
9701b8adde7SWilliam Kucharski 	return val_out;
9711b8adde7SWilliam Kucharski }
9721b8adde7SWilliam Kucharski 
9731b8adde7SWilliam Kucharski #if 0
9741b8adde7SWilliam Kucharski static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
9751b8adde7SWilliam Kucharski 		       int val)
9761b8adde7SWilliam Kucharski {
9771b8adde7SWilliam Kucharski 	int phyaddr;
9781b8adde7SWilliam Kucharski 
9791b8adde7SWilliam Kucharski 	if (!lp->mii)
9801b8adde7SWilliam Kucharski 		return;
9811b8adde7SWilliam Kucharski 
9821b8adde7SWilliam Kucharski 	phyaddr = lp->a.read_bcr(ioaddr, 33);
9831b8adde7SWilliam Kucharski 
9841b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 33,
9851b8adde7SWilliam Kucharski 			((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
9861b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 34, val);
9871b8adde7SWilliam Kucharski 	lp->a.write_bcr(ioaddr, 33, phyaddr);
9881b8adde7SWilliam Kucharski }
9891b8adde7SWilliam Kucharski #endif
9901b8adde7SWilliam Kucharski 
9911b8adde7SWilliam Kucharski static struct pci_id pcnet32_nics[] = {
9921b8adde7SWilliam Kucharski 	PCI_ROM(0x1022, 0x2000, "lancepci", "AMD Lance/PCI"),
9931b8adde7SWilliam Kucharski 	PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD Lance/PCI PCNet/32"),
9941b8adde7SWilliam Kucharski 	PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD Lance/HomePNA"),
9951b8adde7SWilliam Kucharski };
9961b8adde7SWilliam Kucharski 
9971b8adde7SWilliam Kucharski struct pci_driver pcnet32_driver = {
9981b8adde7SWilliam Kucharski 	.type = NIC_DRIVER,
9991b8adde7SWilliam Kucharski 	.name = "PCNET32/PCI",
10001b8adde7SWilliam Kucharski 	.probe = pcnet32_probe,
10011b8adde7SWilliam Kucharski 	.ids = pcnet32_nics,
10021b8adde7SWilliam Kucharski 	.id_count = sizeof(pcnet32_nics) / sizeof(pcnet32_nics[0]),
10031b8adde7SWilliam Kucharski 	.class = 0,
10041b8adde7SWilliam Kucharski };
1005