xref: /onnv-gate/usr/src/grub/grub-0.97/netboot/pcnet32.c (revision 8044:b3af80bbf173)
1*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
2*8044SWilliam.Kucharski@Sun.COM *
3*8044SWilliam.Kucharski@Sun.COM *    pcnet32.c -- Etherboot device driver for the AMD PCnet32
4*8044SWilliam.Kucharski@Sun.COM *    Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
5*8044SWilliam.Kucharski@Sun.COM *
6*8044SWilliam.Kucharski@Sun.COM *    This program is free software; you can redistribute it and/or modify
7*8044SWilliam.Kucharski@Sun.COM *    it under the terms of the GNU General Public License as published by
8*8044SWilliam.Kucharski@Sun.COM *    the Free Software Foundation; either version 2 of the License, or
9*8044SWilliam.Kucharski@Sun.COM *    (at your option) any later version.
10*8044SWilliam.Kucharski@Sun.COM *
11*8044SWilliam.Kucharski@Sun.COM *    This program is distributed in the hope that it will be useful,
12*8044SWilliam.Kucharski@Sun.COM *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*8044SWilliam.Kucharski@Sun.COM *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*8044SWilliam.Kucharski@Sun.COM *    GNU General Public License for more details.
15*8044SWilliam.Kucharski@Sun.COM *
16*8044SWilliam.Kucharski@Sun.COM *    You should have received a copy of the GNU General Public License
17*8044SWilliam.Kucharski@Sun.COM *    along with this program; if not, write to the Free Software
18*8044SWilliam.Kucharski@Sun.COM *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*8044SWilliam.Kucharski@Sun.COM *
20*8044SWilliam.Kucharski@Sun.COM *    Portions of this code based on:
21*8044SWilliam.Kucharski@Sun.COM *		pcnet32.c: An AMD PCnet32 ethernet driver for linux:
22*8044SWilliam.Kucharski@Sun.COM *
23*8044SWilliam.Kucharski@Sun.COM *	(C) 1996-1999 Thomas Bogendoerfer
24*8044SWilliam.Kucharski@Sun.COM *		See Linux Driver for full information
25*8044SWilliam.Kucharski@Sun.COM *
26*8044SWilliam.Kucharski@Sun.COM *	The transmit and poll functions were written with reference to:
27*8044SWilliam.Kucharski@Sun.COM *	lance.c - LANCE NIC driver for Etherboot written by Ken Yap
28*8044SWilliam.Kucharski@Sun.COM *
29*8044SWilliam.Kucharski@Sun.COM *	Linux Driver Version 1.27a, 10.02.2002
30*8044SWilliam.Kucharski@Sun.COM *
31*8044SWilliam.Kucharski@Sun.COM *
32*8044SWilliam.Kucharski@Sun.COM *    REVISION HISTORY:
33*8044SWilliam.Kucharski@Sun.COM *    ================
34*8044SWilliam.Kucharski@Sun.COM *    v1.0	08-06-2003	timlegge	Initial port of Linux driver
35*8044SWilliam.Kucharski@Sun.COM *    v1.1	08-23-2003	timlegge	Add multicast support
36*8044SWilliam.Kucharski@Sun.COM *    v1.2	01-17-2004	timlegge	Initial driver output cleanup
37*8044SWilliam.Kucharski@Sun.COM *    v1.3	03-29-2004	timlegge	More driver cleanup
38*8044SWilliam.Kucharski@Sun.COM *
39*8044SWilliam.Kucharski@Sun.COM *    Indent Options: indent -kr -i8
40*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
41*8044SWilliam.Kucharski@Sun.COM 
42*8044SWilliam.Kucharski@Sun.COM /* to get some global routines like printf */
43*8044SWilliam.Kucharski@Sun.COM #include "etherboot.h"
44*8044SWilliam.Kucharski@Sun.COM /* to get the interface to the body of the program */
45*8044SWilliam.Kucharski@Sun.COM #include "nic.h"
46*8044SWilliam.Kucharski@Sun.COM /* to get the PCI support functions, if this is a PCI NIC */
47*8044SWilliam.Kucharski@Sun.COM #include "pci.h"
48*8044SWilliam.Kucharski@Sun.COM /* Include the time functions */
49*8044SWilliam.Kucharski@Sun.COM #include "timer.h"
50*8044SWilliam.Kucharski@Sun.COM #include "mii.h"
51*8044SWilliam.Kucharski@Sun.COM /* void hex_dump(const char *data, const unsigned int len); */
52*8044SWilliam.Kucharski@Sun.COM 
53*8044SWilliam.Kucharski@Sun.COM /* Etherboot Specific definations */
54*8044SWilliam.Kucharski@Sun.COM #define drv_version "v1.3"
55*8044SWilliam.Kucharski@Sun.COM #define drv_date "03-29-2004"
56*8044SWilliam.Kucharski@Sun.COM 
57*8044SWilliam.Kucharski@Sun.COM typedef unsigned char u8;
58*8044SWilliam.Kucharski@Sun.COM typedef signed char s8;
59*8044SWilliam.Kucharski@Sun.COM typedef unsigned short u16;
60*8044SWilliam.Kucharski@Sun.COM typedef signed short s16;
61*8044SWilliam.Kucharski@Sun.COM typedef unsigned int u32;
62*8044SWilliam.Kucharski@Sun.COM typedef signed int s32;
63*8044SWilliam.Kucharski@Sun.COM 
64*8044SWilliam.Kucharski@Sun.COM static u32 ioaddr;		/* Globally used for the card's io address */
65*8044SWilliam.Kucharski@Sun.COM 
66*8044SWilliam.Kucharski@Sun.COM #ifdef EDEBUG
67*8044SWilliam.Kucharski@Sun.COM #define dprintf(x) printf x
68*8044SWilliam.Kucharski@Sun.COM #else
69*8044SWilliam.Kucharski@Sun.COM #define dprintf(x)
70*8044SWilliam.Kucharski@Sun.COM #endif
71*8044SWilliam.Kucharski@Sun.COM 
72*8044SWilliam.Kucharski@Sun.COM /* Condensed operations for readability. */
73*8044SWilliam.Kucharski@Sun.COM #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
74*8044SWilliam.Kucharski@Sun.COM #define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
75*8044SWilliam.Kucharski@Sun.COM 
76*8044SWilliam.Kucharski@Sun.COM /* End Etherboot Specific */
77*8044SWilliam.Kucharski@Sun.COM 
78*8044SWilliam.Kucharski@Sun.COM int cards_found /* __initdata */ ;
79*8044SWilliam.Kucharski@Sun.COM 
80*8044SWilliam.Kucharski@Sun.COM #ifdef REMOVE
81*8044SWilliam.Kucharski@Sun.COM /* FIXME: Remove these they are probably pointless */
82*8044SWilliam.Kucharski@Sun.COM 
83*8044SWilliam.Kucharski@Sun.COM /*
84*8044SWilliam.Kucharski@Sun.COM  * VLB I/O addresses
85*8044SWilliam.Kucharski@Sun.COM  */
86*8044SWilliam.Kucharski@Sun.COM static unsigned int pcnet32_portlist[] /*__initdata */  =
87*8044SWilliam.Kucharski@Sun.COM { 0x300, 0x320, 0x340, 0x360, 0 };
88*8044SWilliam.Kucharski@Sun.COM 
89*8044SWilliam.Kucharski@Sun.COM static int pcnet32_debug = 1;
90*8044SWilliam.Kucharski@Sun.COM static int tx_start = 1;	/* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
91*8044SWilliam.Kucharski@Sun.COM static int pcnet32vlb;		/* check for VLB cards ? */
92*8044SWilliam.Kucharski@Sun.COM 
93*8044SWilliam.Kucharski@Sun.COM static struct net_device *pcnet32_dev;
94*8044SWilliam.Kucharski@Sun.COM 
95*8044SWilliam.Kucharski@Sun.COM static int max_interrupt_work = 80;
96*8044SWilliam.Kucharski@Sun.COM static int rx_copybreak = 200;
97*8044SWilliam.Kucharski@Sun.COM #endif
98*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_AUI      0x00
99*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_10BT     0x01
100*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_GPSI     0x02
101*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_MII      0x03
102*8044SWilliam.Kucharski@Sun.COM 
103*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_PORTSEL  0x03
104*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_ASEL     0x04
105*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_100      0x40
106*8044SWilliam.Kucharski@Sun.COM #define PCNET32_PORT_FD	      0x80
107*8044SWilliam.Kucharski@Sun.COM 
108*8044SWilliam.Kucharski@Sun.COM #define PCNET32_DMA_MASK 0xffffffff
109*8044SWilliam.Kucharski@Sun.COM 
110*8044SWilliam.Kucharski@Sun.COM /*
111*8044SWilliam.Kucharski@Sun.COM  * table to translate option values from tulip
112*8044SWilliam.Kucharski@Sun.COM  * to internal options
113*8044SWilliam.Kucharski@Sun.COM  */
114*8044SWilliam.Kucharski@Sun.COM static unsigned char options_mapping[] = {
115*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  0 Auto-select      */
116*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_AUI,	/*  1 BNC/AUI          */
117*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_AUI,	/*  2 AUI/BNC          */
118*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  3 not supported    */
119*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_10BT | PCNET32_PORT_FD,	/*  4 10baseT-FD       */
120*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  5 not supported    */
121*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  6 not supported    */
122*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  7 not supported    */
123*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL,	/*  8 not supported    */
124*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_MII,	/*  9 MII 10baseT      */
125*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_MII | PCNET32_PORT_FD,	/* 10 MII 10baseT-FD   */
126*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_MII,	/* 11 MII (autosel)    */
127*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_10BT,	/* 12 10BaseT          */
128*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_MII | PCNET32_PORT_100,	/* 13 MII 100BaseTx    */
129*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD,	/* 14 MII 100BaseTx-FD */
130*8044SWilliam.Kucharski@Sun.COM 	PCNET32_PORT_ASEL	/* 15 not supported    */
131*8044SWilliam.Kucharski@Sun.COM };
132*8044SWilliam.Kucharski@Sun.COM 
133*8044SWilliam.Kucharski@Sun.COM #define MAX_UNITS 8		/* More are supported, limit only on options */
134*8044SWilliam.Kucharski@Sun.COM static int options[MAX_UNITS];
135*8044SWilliam.Kucharski@Sun.COM static int full_duplex[MAX_UNITS];
136*8044SWilliam.Kucharski@Sun.COM 
137*8044SWilliam.Kucharski@Sun.COM /*
138*8044SWilliam.Kucharski@Sun.COM  *				Theory of Operation
139*8044SWilliam.Kucharski@Sun.COM  *
140*8044SWilliam.Kucharski@Sun.COM  * This driver uses the same software structure as the normal lance
141*8044SWilliam.Kucharski@Sun.COM  * driver. So look for a verbose description in lance.c. The differences
142*8044SWilliam.Kucharski@Sun.COM  * to the normal lance driver is the use of the 32bit mode of PCnet32
143*8044SWilliam.Kucharski@Sun.COM  * and PCnetPCI chips. Because these chips are 32bit chips, there is no
144*8044SWilliam.Kucharski@Sun.COM  * 16MB limitation and we don't need bounce buffers.
145*8044SWilliam.Kucharski@Sun.COM  */
146*8044SWilliam.Kucharski@Sun.COM 
147*8044SWilliam.Kucharski@Sun.COM 
148*8044SWilliam.Kucharski@Sun.COM 
149*8044SWilliam.Kucharski@Sun.COM /*
150*8044SWilliam.Kucharski@Sun.COM  * Set the number of Tx and Rx buffers, using Log_2(# buffers).
151*8044SWilliam.Kucharski@Sun.COM  * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
152*8044SWilliam.Kucharski@Sun.COM  * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
153*8044SWilliam.Kucharski@Sun.COM  */
154*8044SWilliam.Kucharski@Sun.COM #ifndef PCNET32_LOG_TX_BUFFERS
155*8044SWilliam.Kucharski@Sun.COM #define PCNET32_LOG_TX_BUFFERS 1
156*8044SWilliam.Kucharski@Sun.COM #define PCNET32_LOG_RX_BUFFERS 2
157*8044SWilliam.Kucharski@Sun.COM #endif
158*8044SWilliam.Kucharski@Sun.COM 
159*8044SWilliam.Kucharski@Sun.COM #define TX_RING_SIZE		(1 << (PCNET32_LOG_TX_BUFFERS))
160*8044SWilliam.Kucharski@Sun.COM #define TX_RING_MOD_MASK	(TX_RING_SIZE - 1)
161*8044SWilliam.Kucharski@Sun.COM /* FIXME: Fix this to allow multiple tx_ring descriptors */
162*8044SWilliam.Kucharski@Sun.COM #define TX_RING_LEN_BITS	0x0000	/*PCNET32_LOG_TX_BUFFERS) << 12) */
163*8044SWilliam.Kucharski@Sun.COM 
164*8044SWilliam.Kucharski@Sun.COM #define RX_RING_SIZE		(1 << (PCNET32_LOG_RX_BUFFERS))
165*8044SWilliam.Kucharski@Sun.COM #define RX_RING_MOD_MASK	(RX_RING_SIZE - 1)
166*8044SWilliam.Kucharski@Sun.COM #define RX_RING_LEN_BITS	((PCNET32_LOG_RX_BUFFERS) << 4)
167*8044SWilliam.Kucharski@Sun.COM 
168*8044SWilliam.Kucharski@Sun.COM #define PKT_BUF_SZ		1544
169*8044SWilliam.Kucharski@Sun.COM 
170*8044SWilliam.Kucharski@Sun.COM /* Offsets from base I/O address. */
171*8044SWilliam.Kucharski@Sun.COM #define PCNET32_WIO_RDP		0x10
172*8044SWilliam.Kucharski@Sun.COM #define PCNET32_WIO_RAP		0x12
173*8044SWilliam.Kucharski@Sun.COM #define PCNET32_WIO_RESET	0x14
174*8044SWilliam.Kucharski@Sun.COM #define PCNET32_WIO_BDP		0x16
175*8044SWilliam.Kucharski@Sun.COM 
176*8044SWilliam.Kucharski@Sun.COM #define PCNET32_DWIO_RDP	0x10
177*8044SWilliam.Kucharski@Sun.COM #define PCNET32_DWIO_RAP	0x14
178*8044SWilliam.Kucharski@Sun.COM #define PCNET32_DWIO_RESET	0x18
179*8044SWilliam.Kucharski@Sun.COM #define PCNET32_DWIO_BDP	0x1C
180*8044SWilliam.Kucharski@Sun.COM 
181*8044SWilliam.Kucharski@Sun.COM #define PCNET32_TOTAL_SIZE	0x20
182*8044SWilliam.Kucharski@Sun.COM 
183*8044SWilliam.Kucharski@Sun.COM /* Buffers for the tx and Rx */
184*8044SWilliam.Kucharski@Sun.COM 
185*8044SWilliam.Kucharski@Sun.COM /* Create a static buffer of size PKT_BUF_SZ for each
186*8044SWilliam.Kucharski@Sun.COM TX Descriptor.  All descriptors point to a
187*8044SWilliam.Kucharski@Sun.COM part of this buffer */
188*8044SWilliam.Kucharski@Sun.COM static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
189*8044SWilliam.Kucharski@Sun.COM //    __attribute__ ((aligned(16)));
190*8044SWilliam.Kucharski@Sun.COM 
191*8044SWilliam.Kucharski@Sun.COM /* Create a static buffer of size PKT_BUF_SZ for each
192*8044SWilliam.Kucharski@Sun.COM RX Descriptor   All descriptors point to a
193*8044SWilliam.Kucharski@Sun.COM part of this buffer */
194*8044SWilliam.Kucharski@Sun.COM static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
195*8044SWilliam.Kucharski@Sun.COM //    __attribute__ ((aligned(16)));
196*8044SWilliam.Kucharski@Sun.COM 
197*8044SWilliam.Kucharski@Sun.COM /* The PCNET32 Rx and Tx ring descriptors. */
198*8044SWilliam.Kucharski@Sun.COM struct pcnet32_rx_head {
199*8044SWilliam.Kucharski@Sun.COM 	u32 base;
200*8044SWilliam.Kucharski@Sun.COM 	s16 buf_length;
201*8044SWilliam.Kucharski@Sun.COM 	s16 status;
202*8044SWilliam.Kucharski@Sun.COM 	u32 msg_length;
203*8044SWilliam.Kucharski@Sun.COM 	u32 reserved;
204*8044SWilliam.Kucharski@Sun.COM };
205*8044SWilliam.Kucharski@Sun.COM 
206*8044SWilliam.Kucharski@Sun.COM struct pcnet32_tx_head {
207*8044SWilliam.Kucharski@Sun.COM 	u32 base;
208*8044SWilliam.Kucharski@Sun.COM 	s16 length;
209*8044SWilliam.Kucharski@Sun.COM 	s16 status;
210*8044SWilliam.Kucharski@Sun.COM 	u32 misc;
211*8044SWilliam.Kucharski@Sun.COM 	u32 reserved;
212*8044SWilliam.Kucharski@Sun.COM };
213*8044SWilliam.Kucharski@Sun.COM 
214*8044SWilliam.Kucharski@Sun.COM /* The PCNET32 32-Bit initialization block, described in databook. */
215*8044SWilliam.Kucharski@Sun.COM struct pcnet32_init_block {
216*8044SWilliam.Kucharski@Sun.COM 	u16 mode;
217*8044SWilliam.Kucharski@Sun.COM 	u16 tlen_rlen;
218*8044SWilliam.Kucharski@Sun.COM 	u8 phys_addr[6];
219*8044SWilliam.Kucharski@Sun.COM 	u16 reserved;
220*8044SWilliam.Kucharski@Sun.COM 	u32 filter[2];
221*8044SWilliam.Kucharski@Sun.COM 	/* Receive and transmit ring base, along with extra bits. */
222*8044SWilliam.Kucharski@Sun.COM 	u32 rx_ring;
223*8044SWilliam.Kucharski@Sun.COM 	u32 tx_ring;
224*8044SWilliam.Kucharski@Sun.COM };
225*8044SWilliam.Kucharski@Sun.COM /* PCnet32 access functions */
226*8044SWilliam.Kucharski@Sun.COM struct pcnet32_access {
227*8044SWilliam.Kucharski@Sun.COM 	u16(*read_csr) (unsigned long, int);
228*8044SWilliam.Kucharski@Sun.COM 	void (*write_csr) (unsigned long, int, u16);
229*8044SWilliam.Kucharski@Sun.COM 	 u16(*read_bcr) (unsigned long, int);
230*8044SWilliam.Kucharski@Sun.COM 	void (*write_bcr) (unsigned long, int, u16);
231*8044SWilliam.Kucharski@Sun.COM 	 u16(*read_rap) (unsigned long);
232*8044SWilliam.Kucharski@Sun.COM 	void (*write_rap) (unsigned long, u16);
233*8044SWilliam.Kucharski@Sun.COM 	void (*reset) (unsigned long);
234*8044SWilliam.Kucharski@Sun.COM };
235*8044SWilliam.Kucharski@Sun.COM 
236*8044SWilliam.Kucharski@Sun.COM /* Define the TX Descriptor */
237*8044SWilliam.Kucharski@Sun.COM static struct pcnet32_tx_head tx_ring[TX_RING_SIZE]
238*8044SWilliam.Kucharski@Sun.COM     __attribute__ ((aligned(16)));
239*8044SWilliam.Kucharski@Sun.COM 
240*8044SWilliam.Kucharski@Sun.COM 
241*8044SWilliam.Kucharski@Sun.COM /* Define the RX Descriptor */
242*8044SWilliam.Kucharski@Sun.COM static struct pcnet32_rx_head rx_ring[RX_RING_SIZE]
243*8044SWilliam.Kucharski@Sun.COM     __attribute__ ((aligned(16)));
244*8044SWilliam.Kucharski@Sun.COM 
245*8044SWilliam.Kucharski@Sun.COM /* May need to be moved to mii.h */
246*8044SWilliam.Kucharski@Sun.COM struct mii_if_info {
247*8044SWilliam.Kucharski@Sun.COM 	int phy_id;
248*8044SWilliam.Kucharski@Sun.COM 	int advertising;
249*8044SWilliam.Kucharski@Sun.COM 	unsigned int full_duplex:1;	/* is full duplex? */
250*8044SWilliam.Kucharski@Sun.COM };
251*8044SWilliam.Kucharski@Sun.COM 
252*8044SWilliam.Kucharski@Sun.COM /*
253*8044SWilliam.Kucharski@Sun.COM  * The first three fields of pcnet32_private are read by the ethernet device
254*8044SWilliam.Kucharski@Sun.COM  * so we allocate the structure should be allocated by pci_alloc_consistent().
255*8044SWilliam.Kucharski@Sun.COM  */
256*8044SWilliam.Kucharski@Sun.COM #define MII_CNT 4
257*8044SWilliam.Kucharski@Sun.COM struct pcnet32_private {
258*8044SWilliam.Kucharski@Sun.COM 	struct pcnet32_init_block init_block;
259*8044SWilliam.Kucharski@Sun.COM 	struct pci_dev *pci_dev;	/* Pointer to the associated pci device structure */
260*8044SWilliam.Kucharski@Sun.COM 	const char *name;
261*8044SWilliam.Kucharski@Sun.COM 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
262*8044SWilliam.Kucharski@Sun.COM 	struct sk_buff *tx_skbuff[TX_RING_SIZE];
263*8044SWilliam.Kucharski@Sun.COM 	struct sk_buff *rx_skbuff[RX_RING_SIZE];
264*8044SWilliam.Kucharski@Sun.COM 	struct pcnet32_access a;
265*8044SWilliam.Kucharski@Sun.COM 	unsigned int cur_rx, cur_tx;	/* The next free ring entry */
266*8044SWilliam.Kucharski@Sun.COM 	char tx_full;
267*8044SWilliam.Kucharski@Sun.COM 	int options;
268*8044SWilliam.Kucharski@Sun.COM 	int shared_irq:1,	/* shared irq possible */
269*8044SWilliam.Kucharski@Sun.COM 	 ltint:1,		/* enable TxDone-intr inhibitor */
270*8044SWilliam.Kucharski@Sun.COM 	 dxsuflo:1,		/* disable transmit stop on uflo */
271*8044SWilliam.Kucharski@Sun.COM 	 mii:1;			/* mii port available */
272*8044SWilliam.Kucharski@Sun.COM 	struct mii_if_info mii_if;
273*8044SWilliam.Kucharski@Sun.COM 	unsigned char phys[MII_CNT];
274*8044SWilliam.Kucharski@Sun.COM 	struct net_device *next;
275*8044SWilliam.Kucharski@Sun.COM 	int full_duplex:1;
276*8044SWilliam.Kucharski@Sun.COM } lpx;
277*8044SWilliam.Kucharski@Sun.COM 
278*8044SWilliam.Kucharski@Sun.COM static struct pcnet32_private *lp;
279*8044SWilliam.Kucharski@Sun.COM 
280*8044SWilliam.Kucharski@Sun.COM static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num);
281*8044SWilliam.Kucharski@Sun.COM #if 0
282*8044SWilliam.Kucharski@Sun.COM static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
283*8044SWilliam.Kucharski@Sun.COM 		       int val);
284*8044SWilliam.Kucharski@Sun.COM #endif
285*8044SWilliam.Kucharski@Sun.COM enum pci_flags_bit {
286*8044SWilliam.Kucharski@Sun.COM 	PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
287*8044SWilliam.Kucharski@Sun.COM 	PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 =
288*8044SWilliam.Kucharski@Sun.COM 	    0x10 << 2, PCI_ADDR3 = 0x10 << 3,
289*8044SWilliam.Kucharski@Sun.COM };
290*8044SWilliam.Kucharski@Sun.COM 
291*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_read_csr(unsigned long addr,int index)292*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
293*8044SWilliam.Kucharski@Sun.COM {
294*8044SWilliam.Kucharski@Sun.COM 	outw(index, addr + PCNET32_WIO_RAP);
295*8044SWilliam.Kucharski@Sun.COM 	return inw(addr + PCNET32_WIO_RDP);
296*8044SWilliam.Kucharski@Sun.COM }
297*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_write_csr(unsigned long addr,int index,u16 val)298*8044SWilliam.Kucharski@Sun.COM static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
299*8044SWilliam.Kucharski@Sun.COM {
300*8044SWilliam.Kucharski@Sun.COM 	outw(index, addr + PCNET32_WIO_RAP);
301*8044SWilliam.Kucharski@Sun.COM 	outw(val, addr + PCNET32_WIO_RDP);
302*8044SWilliam.Kucharski@Sun.COM }
303*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_read_bcr(unsigned long addr,int index)304*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
305*8044SWilliam.Kucharski@Sun.COM {
306*8044SWilliam.Kucharski@Sun.COM 	outw(index, addr + PCNET32_WIO_RAP);
307*8044SWilliam.Kucharski@Sun.COM 	return inw(addr + PCNET32_WIO_BDP);
308*8044SWilliam.Kucharski@Sun.COM }
309*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_write_bcr(unsigned long addr,int index,u16 val)310*8044SWilliam.Kucharski@Sun.COM static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
311*8044SWilliam.Kucharski@Sun.COM {
312*8044SWilliam.Kucharski@Sun.COM 	outw(index, addr + PCNET32_WIO_RAP);
313*8044SWilliam.Kucharski@Sun.COM 	outw(val, addr + PCNET32_WIO_BDP);
314*8044SWilliam.Kucharski@Sun.COM }
315*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_read_rap(unsigned long addr)316*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_wio_read_rap(unsigned long addr)
317*8044SWilliam.Kucharski@Sun.COM {
318*8044SWilliam.Kucharski@Sun.COM 	return inw(addr + PCNET32_WIO_RAP);
319*8044SWilliam.Kucharski@Sun.COM }
320*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_write_rap(unsigned long addr,u16 val)321*8044SWilliam.Kucharski@Sun.COM static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
322*8044SWilliam.Kucharski@Sun.COM {
323*8044SWilliam.Kucharski@Sun.COM 	outw(val, addr + PCNET32_WIO_RAP);
324*8044SWilliam.Kucharski@Sun.COM }
325*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_reset(unsigned long addr)326*8044SWilliam.Kucharski@Sun.COM static void pcnet32_wio_reset(unsigned long addr)
327*8044SWilliam.Kucharski@Sun.COM {
328*8044SWilliam.Kucharski@Sun.COM 	inw(addr + PCNET32_WIO_RESET);
329*8044SWilliam.Kucharski@Sun.COM }
330*8044SWilliam.Kucharski@Sun.COM 
pcnet32_wio_check(unsigned long addr)331*8044SWilliam.Kucharski@Sun.COM static int pcnet32_wio_check(unsigned long addr)
332*8044SWilliam.Kucharski@Sun.COM {
333*8044SWilliam.Kucharski@Sun.COM 	outw(88, addr + PCNET32_WIO_RAP);
334*8044SWilliam.Kucharski@Sun.COM 	return (inw(addr + PCNET32_WIO_RAP) == 88);
335*8044SWilliam.Kucharski@Sun.COM }
336*8044SWilliam.Kucharski@Sun.COM 
337*8044SWilliam.Kucharski@Sun.COM static struct pcnet32_access pcnet32_wio = {
338*8044SWilliam.Kucharski@Sun.COM       read_csr:pcnet32_wio_read_csr,
339*8044SWilliam.Kucharski@Sun.COM       write_csr:pcnet32_wio_write_csr,
340*8044SWilliam.Kucharski@Sun.COM       read_bcr:pcnet32_wio_read_bcr,
341*8044SWilliam.Kucharski@Sun.COM       write_bcr:pcnet32_wio_write_bcr,
342*8044SWilliam.Kucharski@Sun.COM       read_rap:pcnet32_wio_read_rap,
343*8044SWilliam.Kucharski@Sun.COM       write_rap:pcnet32_wio_write_rap,
344*8044SWilliam.Kucharski@Sun.COM       reset:pcnet32_wio_reset
345*8044SWilliam.Kucharski@Sun.COM };
346*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_read_csr(unsigned long addr,int index)347*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
348*8044SWilliam.Kucharski@Sun.COM {
349*8044SWilliam.Kucharski@Sun.COM 	outl(index, addr + PCNET32_DWIO_RAP);
350*8044SWilliam.Kucharski@Sun.COM 	return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);
351*8044SWilliam.Kucharski@Sun.COM }
352*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_write_csr(unsigned long addr,int index,u16 val)353*8044SWilliam.Kucharski@Sun.COM static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
354*8044SWilliam.Kucharski@Sun.COM {
355*8044SWilliam.Kucharski@Sun.COM 	outl(index, addr + PCNET32_DWIO_RAP);
356*8044SWilliam.Kucharski@Sun.COM 	outl(val, addr + PCNET32_DWIO_RDP);
357*8044SWilliam.Kucharski@Sun.COM }
358*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_read_bcr(unsigned long addr,int index)359*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
360*8044SWilliam.Kucharski@Sun.COM {
361*8044SWilliam.Kucharski@Sun.COM 	outl(index, addr + PCNET32_DWIO_RAP);
362*8044SWilliam.Kucharski@Sun.COM 	return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);
363*8044SWilliam.Kucharski@Sun.COM }
364*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_write_bcr(unsigned long addr,int index,u16 val)365*8044SWilliam.Kucharski@Sun.COM static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
366*8044SWilliam.Kucharski@Sun.COM {
367*8044SWilliam.Kucharski@Sun.COM 	outl(index, addr + PCNET32_DWIO_RAP);
368*8044SWilliam.Kucharski@Sun.COM 	outl(val, addr + PCNET32_DWIO_BDP);
369*8044SWilliam.Kucharski@Sun.COM }
370*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_read_rap(unsigned long addr)371*8044SWilliam.Kucharski@Sun.COM static u16 pcnet32_dwio_read_rap(unsigned long addr)
372*8044SWilliam.Kucharski@Sun.COM {
373*8044SWilliam.Kucharski@Sun.COM 	return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);
374*8044SWilliam.Kucharski@Sun.COM }
375*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_write_rap(unsigned long addr,u16 val)376*8044SWilliam.Kucharski@Sun.COM static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
377*8044SWilliam.Kucharski@Sun.COM {
378*8044SWilliam.Kucharski@Sun.COM 	outl(val, addr + PCNET32_DWIO_RAP);
379*8044SWilliam.Kucharski@Sun.COM }
380*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_reset(unsigned long addr)381*8044SWilliam.Kucharski@Sun.COM static void pcnet32_dwio_reset(unsigned long addr)
382*8044SWilliam.Kucharski@Sun.COM {
383*8044SWilliam.Kucharski@Sun.COM 	inl(addr + PCNET32_DWIO_RESET);
384*8044SWilliam.Kucharski@Sun.COM }
385*8044SWilliam.Kucharski@Sun.COM 
pcnet32_dwio_check(unsigned long addr)386*8044SWilliam.Kucharski@Sun.COM static int pcnet32_dwio_check(unsigned long addr)
387*8044SWilliam.Kucharski@Sun.COM {
388*8044SWilliam.Kucharski@Sun.COM 	outl(88, addr + PCNET32_DWIO_RAP);
389*8044SWilliam.Kucharski@Sun.COM 	return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);
390*8044SWilliam.Kucharski@Sun.COM }
391*8044SWilliam.Kucharski@Sun.COM 
392*8044SWilliam.Kucharski@Sun.COM static struct pcnet32_access pcnet32_dwio = {
393*8044SWilliam.Kucharski@Sun.COM       read_csr:pcnet32_dwio_read_csr,
394*8044SWilliam.Kucharski@Sun.COM       write_csr:pcnet32_dwio_write_csr,
395*8044SWilliam.Kucharski@Sun.COM       read_bcr:pcnet32_dwio_read_bcr,
396*8044SWilliam.Kucharski@Sun.COM       write_bcr:pcnet32_dwio_write_bcr,
397*8044SWilliam.Kucharski@Sun.COM       read_rap:pcnet32_dwio_read_rap,
398*8044SWilliam.Kucharski@Sun.COM       write_rap:pcnet32_dwio_write_rap,
399*8044SWilliam.Kucharski@Sun.COM       reset:pcnet32_dwio_reset
400*8044SWilliam.Kucharski@Sun.COM };
401*8044SWilliam.Kucharski@Sun.COM 
402*8044SWilliam.Kucharski@Sun.COM 
403*8044SWilliam.Kucharski@Sun.COM /* Initialize the PCNET32 Rx and Tx rings. */
pcnet32_init_ring(struct nic * nic)404*8044SWilliam.Kucharski@Sun.COM static int pcnet32_init_ring(struct nic *nic)
405*8044SWilliam.Kucharski@Sun.COM {
406*8044SWilliam.Kucharski@Sun.COM 	int i;
407*8044SWilliam.Kucharski@Sun.COM 
408*8044SWilliam.Kucharski@Sun.COM 	lp->tx_full = 0;
409*8044SWilliam.Kucharski@Sun.COM 	lp->cur_rx = lp->cur_tx = 0;
410*8044SWilliam.Kucharski@Sun.COM 
411*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < RX_RING_SIZE; i++) {
412*8044SWilliam.Kucharski@Sun.COM 		rx_ring[i].base = (u32) virt_to_le32desc(&rxb[i]);
413*8044SWilliam.Kucharski@Sun.COM 		rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
414*8044SWilliam.Kucharski@Sun.COM 		rx_ring[i].status = le16_to_cpu(0x8000);
415*8044SWilliam.Kucharski@Sun.COM 	}
416*8044SWilliam.Kucharski@Sun.COM 
417*8044SWilliam.Kucharski@Sun.COM 	/* The Tx buffer address is filled in as needed, but we do need to clear
418*8044SWilliam.Kucharski@Sun.COM 	   the upper ownership bit. */
419*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < TX_RING_SIZE; i++) {
420*8044SWilliam.Kucharski@Sun.COM 		tx_ring[i].base = 0;
421*8044SWilliam.Kucharski@Sun.COM 		tx_ring[i].status = 0;
422*8044SWilliam.Kucharski@Sun.COM 	}
423*8044SWilliam.Kucharski@Sun.COM 
424*8044SWilliam.Kucharski@Sun.COM 
425*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.tlen_rlen =
426*8044SWilliam.Kucharski@Sun.COM 	    le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
427*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < 6; i++)
428*8044SWilliam.Kucharski@Sun.COM 		lp->init_block.phys_addr[i] = nic->node_addr[i];
429*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.rx_ring = (u32) virt_to_le32desc(&rx_ring[0]);
430*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.tx_ring = (u32) virt_to_le32desc(&tx_ring[0]);
431*8044SWilliam.Kucharski@Sun.COM 	return 0;
432*8044SWilliam.Kucharski@Sun.COM }
433*8044SWilliam.Kucharski@Sun.COM 
434*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
435*8044SWilliam.Kucharski@Sun.COM RESET - Reset adapter
436*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_reset(struct nic * nic)437*8044SWilliam.Kucharski@Sun.COM static void pcnet32_reset(struct nic *nic)
438*8044SWilliam.Kucharski@Sun.COM {
439*8044SWilliam.Kucharski@Sun.COM 	/* put the card in its initial state */
440*8044SWilliam.Kucharski@Sun.COM 	u16 val;
441*8044SWilliam.Kucharski@Sun.COM 	int i;
442*8044SWilliam.Kucharski@Sun.COM 
443*8044SWilliam.Kucharski@Sun.COM 	/* Reset the PCNET32 */
444*8044SWilliam.Kucharski@Sun.COM 	lp->a.reset(ioaddr);
445*8044SWilliam.Kucharski@Sun.COM 
446*8044SWilliam.Kucharski@Sun.COM 	/* switch pcnet32 to 32bit mode */
447*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 20, 2);
448*8044SWilliam.Kucharski@Sun.COM 
449*8044SWilliam.Kucharski@Sun.COM 	/* set/reset autoselect bit */
450*8044SWilliam.Kucharski@Sun.COM 	val = lp->a.read_bcr(ioaddr, 2) & ~2;
451*8044SWilliam.Kucharski@Sun.COM 	if (lp->options & PCNET32_PORT_ASEL)
452*8044SWilliam.Kucharski@Sun.COM 		val |= 2;
453*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 2, val);
454*8044SWilliam.Kucharski@Sun.COM 	/* handle full duplex setting */
455*8044SWilliam.Kucharski@Sun.COM 	if (lp->full_duplex) {
456*8044SWilliam.Kucharski@Sun.COM 		val = lp->a.read_bcr(ioaddr, 9) & ~3;
457*8044SWilliam.Kucharski@Sun.COM 		if (lp->options & PCNET32_PORT_FD) {
458*8044SWilliam.Kucharski@Sun.COM 			val |= 1;
459*8044SWilliam.Kucharski@Sun.COM 			if (lp->options ==
460*8044SWilliam.Kucharski@Sun.COM 			    (PCNET32_PORT_FD | PCNET32_PORT_AUI))
461*8044SWilliam.Kucharski@Sun.COM 				val |= 2;
462*8044SWilliam.Kucharski@Sun.COM 		} else if (lp->options & PCNET32_PORT_ASEL) {
463*8044SWilliam.Kucharski@Sun.COM 			/* workaround of xSeries250, turn on for 79C975 only */
464*8044SWilliam.Kucharski@Sun.COM 			i = ((lp->a.
465*8044SWilliam.Kucharski@Sun.COM 			      read_csr(ioaddr,
466*8044SWilliam.Kucharski@Sun.COM 				       88) | (lp->a.read_csr(ioaddr,
467*8044SWilliam.Kucharski@Sun.COM 							     89) << 16)) >>
468*8044SWilliam.Kucharski@Sun.COM 			     12) & 0xffff;
469*8044SWilliam.Kucharski@Sun.COM 			if (i == 0x2627)
470*8044SWilliam.Kucharski@Sun.COM 				val |= 3;
471*8044SWilliam.Kucharski@Sun.COM 		}
472*8044SWilliam.Kucharski@Sun.COM 		lp->a.write_bcr(ioaddr, 9, val);
473*8044SWilliam.Kucharski@Sun.COM 	}
474*8044SWilliam.Kucharski@Sun.COM 
475*8044SWilliam.Kucharski@Sun.COM 	/* set/reset GPSI bit in test register */
476*8044SWilliam.Kucharski@Sun.COM 	val = lp->a.read_csr(ioaddr, 124) & ~0x10;
477*8044SWilliam.Kucharski@Sun.COM 	if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
478*8044SWilliam.Kucharski@Sun.COM 		val |= 0x10;
479*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 124, val);
480*8044SWilliam.Kucharski@Sun.COM 
481*8044SWilliam.Kucharski@Sun.COM 	if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
482*8044SWilliam.Kucharski@Sun.COM 		val = lp->a.read_bcr(ioaddr, 32) & ~0x38;	/* disable Auto Negotiation, set 10Mpbs, HD */
483*8044SWilliam.Kucharski@Sun.COM 		if (lp->options & PCNET32_PORT_FD)
484*8044SWilliam.Kucharski@Sun.COM 			val |= 0x10;
485*8044SWilliam.Kucharski@Sun.COM 		if (lp->options & PCNET32_PORT_100)
486*8044SWilliam.Kucharski@Sun.COM 			val |= 0x08;
487*8044SWilliam.Kucharski@Sun.COM 		lp->a.write_bcr(ioaddr, 32, val);
488*8044SWilliam.Kucharski@Sun.COM 	} else {
489*8044SWilliam.Kucharski@Sun.COM 		if (lp->options & PCNET32_PORT_ASEL) {	/* enable auto negotiate, setup, disable fd */
490*8044SWilliam.Kucharski@Sun.COM 			val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
491*8044SWilliam.Kucharski@Sun.COM 			val |= 0x20;
492*8044SWilliam.Kucharski@Sun.COM 			lp->a.write_bcr(ioaddr, 32, val);
493*8044SWilliam.Kucharski@Sun.COM 		}
494*8044SWilliam.Kucharski@Sun.COM 	}
495*8044SWilliam.Kucharski@Sun.COM 
496*8044SWilliam.Kucharski@Sun.COM #ifdef DO_DXSUFLO
497*8044SWilliam.Kucharski@Sun.COM 	if (lp->dxsuflo) {	/* Disable transmit stop on underflow */
498*8044SWilliam.Kucharski@Sun.COM 		val = lp->a.read_csr(ioaddr, 3);
499*8044SWilliam.Kucharski@Sun.COM 		val |= 0x40;
500*8044SWilliam.Kucharski@Sun.COM 		lp->a.write_csr(ioaddr, 3, val);
501*8044SWilliam.Kucharski@Sun.COM 	}
502*8044SWilliam.Kucharski@Sun.COM #endif
503*8044SWilliam.Kucharski@Sun.COM 
504*8044SWilliam.Kucharski@Sun.COM 	if (lp->ltint) {	/* Enable TxDone-intr inhibitor */
505*8044SWilliam.Kucharski@Sun.COM 		val = lp->a.read_csr(ioaddr, 5);
506*8044SWilliam.Kucharski@Sun.COM 		val |= (1 << 14);
507*8044SWilliam.Kucharski@Sun.COM 		lp->a.write_csr(ioaddr, 5, val);
508*8044SWilliam.Kucharski@Sun.COM 	}
509*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.mode =
510*8044SWilliam.Kucharski@Sun.COM 	    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
511*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.filter[0] = 0xffffffff;
512*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.filter[1] = 0xffffffff;
513*8044SWilliam.Kucharski@Sun.COM 
514*8044SWilliam.Kucharski@Sun.COM 	pcnet32_init_ring(nic);
515*8044SWilliam.Kucharski@Sun.COM 
516*8044SWilliam.Kucharski@Sun.COM 
517*8044SWilliam.Kucharski@Sun.COM 	/* Re-initialize the PCNET32, and start it when done. */
518*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 1,
519*8044SWilliam.Kucharski@Sun.COM 			(virt_to_bus(&lp->init_block)) & 0xffff);
520*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
521*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 4, 0x0915);
522*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 0, 0x0001);
523*8044SWilliam.Kucharski@Sun.COM 
524*8044SWilliam.Kucharski@Sun.COM 
525*8044SWilliam.Kucharski@Sun.COM 	i = 0;
526*8044SWilliam.Kucharski@Sun.COM 	while (i++ < 100)
527*8044SWilliam.Kucharski@Sun.COM 		if (lp->a.read_csr(ioaddr, 0) & 0x0100)
528*8044SWilliam.Kucharski@Sun.COM 			break;
529*8044SWilliam.Kucharski@Sun.COM 	/*
530*8044SWilliam.Kucharski@Sun.COM 	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
531*8044SWilliam.Kucharski@Sun.COM 	 * reports that doing so triggers a bug in the '974.
532*8044SWilliam.Kucharski@Sun.COM 	 */
533*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 0, 0x0042);
534*8044SWilliam.Kucharski@Sun.COM 
535*8044SWilliam.Kucharski@Sun.COM 	dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0)));
536*8044SWilliam.Kucharski@Sun.COM 
537*8044SWilliam.Kucharski@Sun.COM }
538*8044SWilliam.Kucharski@Sun.COM 
539*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
540*8044SWilliam.Kucharski@Sun.COM POLL - Wait for a frame
541*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_poll(struct nic * nic __unused,int retrieve)542*8044SWilliam.Kucharski@Sun.COM static int pcnet32_poll(struct nic *nic __unused, int retrieve)
543*8044SWilliam.Kucharski@Sun.COM {
544*8044SWilliam.Kucharski@Sun.COM 	/* return true if there's an ethernet packet ready to read */
545*8044SWilliam.Kucharski@Sun.COM 	/* nic->packet should contain data on return */
546*8044SWilliam.Kucharski@Sun.COM 	/* nic->packetlen should contain length of data */
547*8044SWilliam.Kucharski@Sun.COM 
548*8044SWilliam.Kucharski@Sun.COM 	int status;
549*8044SWilliam.Kucharski@Sun.COM 	int entry;
550*8044SWilliam.Kucharski@Sun.COM 
551*8044SWilliam.Kucharski@Sun.COM 	entry = lp->cur_rx & RX_RING_MOD_MASK;
552*8044SWilliam.Kucharski@Sun.COM 	status = ((short) le16_to_cpu(rx_ring[entry].status) >> 8);
553*8044SWilliam.Kucharski@Sun.COM 
554*8044SWilliam.Kucharski@Sun.COM 	if (status < 0)
555*8044SWilliam.Kucharski@Sun.COM 		return 0;
556*8044SWilliam.Kucharski@Sun.COM 
557*8044SWilliam.Kucharski@Sun.COM 	if ( ! retrieve ) return 1;
558*8044SWilliam.Kucharski@Sun.COM 
559*8044SWilliam.Kucharski@Sun.COM 	if (status == 0x03) {
560*8044SWilliam.Kucharski@Sun.COM 		nic->packetlen =
561*8044SWilliam.Kucharski@Sun.COM 		    (le32_to_cpu(rx_ring[entry].msg_length) & 0xfff) - 4;
562*8044SWilliam.Kucharski@Sun.COM 		memcpy(nic->packet, &rxb[entry], nic->packetlen);
563*8044SWilliam.Kucharski@Sun.COM 
564*8044SWilliam.Kucharski@Sun.COM 		/* Andrew Boyd of QNX reports that some revs of the 79C765
565*8044SWilliam.Kucharski@Sun.COM 		 * clear the buffer length */
566*8044SWilliam.Kucharski@Sun.COM 		rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
567*8044SWilliam.Kucharski@Sun.COM 		rx_ring[entry].status |= le16_to_cpu(0x8000);	/* prime for next receive */
568*8044SWilliam.Kucharski@Sun.COM 		/* Switch to the next Rx ring buffer */
569*8044SWilliam.Kucharski@Sun.COM 		lp->cur_rx++;
570*8044SWilliam.Kucharski@Sun.COM 
571*8044SWilliam.Kucharski@Sun.COM 	} else {
572*8044SWilliam.Kucharski@Sun.COM 		return 0;
573*8044SWilliam.Kucharski@Sun.COM 	}
574*8044SWilliam.Kucharski@Sun.COM 
575*8044SWilliam.Kucharski@Sun.COM 	return 1;
576*8044SWilliam.Kucharski@Sun.COM }
577*8044SWilliam.Kucharski@Sun.COM 
578*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
579*8044SWilliam.Kucharski@Sun.COM TRANSMIT - Transmit a frame
580*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_transmit(struct nic * nic __unused,const char * d,unsigned int t,unsigned int s,const char * p)581*8044SWilliam.Kucharski@Sun.COM static void pcnet32_transmit(struct nic *nic __unused, const char *d,	/* Destination */
582*8044SWilliam.Kucharski@Sun.COM 			     unsigned int t,	/* Type */
583*8044SWilliam.Kucharski@Sun.COM 			     unsigned int s,	/* size */
584*8044SWilliam.Kucharski@Sun.COM 			     const char *p)
585*8044SWilliam.Kucharski@Sun.COM {				/* Packet */
586*8044SWilliam.Kucharski@Sun.COM 	/* send the packet to destination */
587*8044SWilliam.Kucharski@Sun.COM 	unsigned long time;
588*8044SWilliam.Kucharski@Sun.COM 	u8 *ptxb;
589*8044SWilliam.Kucharski@Sun.COM 	u16 nstype;
590*8044SWilliam.Kucharski@Sun.COM 	u16 status;
591*8044SWilliam.Kucharski@Sun.COM 	int entry = 0;		/*lp->cur_tx & TX_RING_MOD_MASK; */
592*8044SWilliam.Kucharski@Sun.COM 
593*8044SWilliam.Kucharski@Sun.COM 	status = 0x8300;
594*8044SWilliam.Kucharski@Sun.COM 	/* point to the current txb incase multiple tx_rings are used */
595*8044SWilliam.Kucharski@Sun.COM 	ptxb = txb + (lp->cur_tx * PKT_BUF_SZ);
596*8044SWilliam.Kucharski@Sun.COM 
597*8044SWilliam.Kucharski@Sun.COM 	/* copy the packet to ring buffer */
598*8044SWilliam.Kucharski@Sun.COM 	memcpy(ptxb, d, ETH_ALEN);	/* dst */
599*8044SWilliam.Kucharski@Sun.COM 	memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);	/* src */
600*8044SWilliam.Kucharski@Sun.COM 	nstype = htons((u16) t);	/* type */
601*8044SWilliam.Kucharski@Sun.COM 	memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);	/* type */
602*8044SWilliam.Kucharski@Sun.COM 	memcpy(ptxb + ETH_HLEN, p, s);
603*8044SWilliam.Kucharski@Sun.COM 
604*8044SWilliam.Kucharski@Sun.COM 	s += ETH_HLEN;
605*8044SWilliam.Kucharski@Sun.COM 	while (s < ETH_ZLEN)	/* pad to min length */
606*8044SWilliam.Kucharski@Sun.COM 		ptxb[s++] = '\0';
607*8044SWilliam.Kucharski@Sun.COM 
608*8044SWilliam.Kucharski@Sun.COM 	tx_ring[entry].length = le16_to_cpu(-s);
609*8044SWilliam.Kucharski@Sun.COM 	tx_ring[entry].misc = 0x00000000;
610*8044SWilliam.Kucharski@Sun.COM 	tx_ring[entry].base = (u32) virt_to_le32desc(ptxb);
611*8044SWilliam.Kucharski@Sun.COM 
612*8044SWilliam.Kucharski@Sun.COM 	/* we set the top byte as the very last thing */
613*8044SWilliam.Kucharski@Sun.COM 	tx_ring[entry].status = le16_to_cpu(status);
614*8044SWilliam.Kucharski@Sun.COM 
615*8044SWilliam.Kucharski@Sun.COM 
616*8044SWilliam.Kucharski@Sun.COM 	/* Trigger an immediate send poll */
617*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 0, 0x0048);
618*8044SWilliam.Kucharski@Sun.COM 
619*8044SWilliam.Kucharski@Sun.COM 	/* wait for transmit complete */
620*8044SWilliam.Kucharski@Sun.COM 	lp->cur_tx = 0;		/* (lp->cur_tx + 1); */
621*8044SWilliam.Kucharski@Sun.COM 	time = currticks() + TICKS_PER_SEC;	/* wait one second */
622*8044SWilliam.Kucharski@Sun.COM 	while (currticks() < time &&
623*8044SWilliam.Kucharski@Sun.COM 	       ((short) le16_to_cpu(tx_ring[entry].status) < 0));
624*8044SWilliam.Kucharski@Sun.COM 
625*8044SWilliam.Kucharski@Sun.COM 	if ((short) le16_to_cpu(tx_ring[entry].status) < 0)
626*8044SWilliam.Kucharski@Sun.COM 		printf("PCNET32 timed out on transmit\n");
627*8044SWilliam.Kucharski@Sun.COM 
628*8044SWilliam.Kucharski@Sun.COM 	/* Stop pointing at the current txb
629*8044SWilliam.Kucharski@Sun.COM 	 * otherwise the card continues to send the packet */
630*8044SWilliam.Kucharski@Sun.COM 	tx_ring[entry].base = 0;
631*8044SWilliam.Kucharski@Sun.COM 
632*8044SWilliam.Kucharski@Sun.COM }
633*8044SWilliam.Kucharski@Sun.COM 
634*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
635*8044SWilliam.Kucharski@Sun.COM DISABLE - Turn off ethernet interface
636*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_disable(struct dev * dev __unused)637*8044SWilliam.Kucharski@Sun.COM static void pcnet32_disable(struct dev *dev __unused)
638*8044SWilliam.Kucharski@Sun.COM {
639*8044SWilliam.Kucharski@Sun.COM 	/* Stop the PCNET32 here -- it ocassionally polls memory if we don't */
640*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_csr(ioaddr, 0, 0x0004);
641*8044SWilliam.Kucharski@Sun.COM 
642*8044SWilliam.Kucharski@Sun.COM 	/*
643*8044SWilliam.Kucharski@Sun.COM 	 * Switch back to 16-bit mode to avoid problesm with dumb
644*8044SWilliam.Kucharski@Sun.COM 	 * DOS packet driver after a warm reboot
645*8044SWilliam.Kucharski@Sun.COM 	 */
646*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 20, 4);
647*8044SWilliam.Kucharski@Sun.COM }
648*8044SWilliam.Kucharski@Sun.COM 
649*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
650*8044SWilliam.Kucharski@Sun.COM IRQ - Enable, Disable, or Force interrupts
651*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_irq(struct nic * nic __unused,irq_action_t action __unused)652*8044SWilliam.Kucharski@Sun.COM static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused)
653*8044SWilliam.Kucharski@Sun.COM {
654*8044SWilliam.Kucharski@Sun.COM   switch ( action ) {
655*8044SWilliam.Kucharski@Sun.COM   case DISABLE :
656*8044SWilliam.Kucharski@Sun.COM     break;
657*8044SWilliam.Kucharski@Sun.COM   case ENABLE :
658*8044SWilliam.Kucharski@Sun.COM     break;
659*8044SWilliam.Kucharski@Sun.COM   case FORCE :
660*8044SWilliam.Kucharski@Sun.COM     break;
661*8044SWilliam.Kucharski@Sun.COM   }
662*8044SWilliam.Kucharski@Sun.COM }
663*8044SWilliam.Kucharski@Sun.COM 
664*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
665*8044SWilliam.Kucharski@Sun.COM PROBE - Look for an adapter, this routine's visible to the outside
666*8044SWilliam.Kucharski@Sun.COM You should omit the last argument struct pci_device * for a non-PCI NIC
667*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pcnet32_probe(struct dev * dev,struct pci_device * pci)668*8044SWilliam.Kucharski@Sun.COM static int pcnet32_probe(struct dev *dev, struct pci_device *pci)
669*8044SWilliam.Kucharski@Sun.COM {
670*8044SWilliam.Kucharski@Sun.COM 	struct nic *nic = (struct nic *) dev;
671*8044SWilliam.Kucharski@Sun.COM 	int i, media;
672*8044SWilliam.Kucharski@Sun.COM 	int fdx, mii, fset, dxsuflo, ltint;
673*8044SWilliam.Kucharski@Sun.COM 	int chip_version;
674*8044SWilliam.Kucharski@Sun.COM 	char *chipname;
675*8044SWilliam.Kucharski@Sun.COM 	struct pcnet32_access *a = NULL;
676*8044SWilliam.Kucharski@Sun.COM 	u8 promaddr[6];
677*8044SWilliam.Kucharski@Sun.COM 
678*8044SWilliam.Kucharski@Sun.COM 	int shared = 1;
679*8044SWilliam.Kucharski@Sun.COM 	if (pci->ioaddr == 0)
680*8044SWilliam.Kucharski@Sun.COM 		return 0;
681*8044SWilliam.Kucharski@Sun.COM 
682*8044SWilliam.Kucharski@Sun.COM 	/* BASE is used throughout to address the card */
683*8044SWilliam.Kucharski@Sun.COM 	ioaddr = pci->ioaddr;
684*8044SWilliam.Kucharski@Sun.COM 	printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n",
685*8044SWilliam.Kucharski@Sun.COM 	       pci->name, pci->vendor, pci->dev_id);
686*8044SWilliam.Kucharski@Sun.COM 
687*8044SWilliam.Kucharski@Sun.COM 	nic->irqno  = 0;
688*8044SWilliam.Kucharski@Sun.COM 	nic->ioaddr = pci->ioaddr & ~3;
689*8044SWilliam.Kucharski@Sun.COM 
690*8044SWilliam.Kucharski@Sun.COM 	/* reset the chip */
691*8044SWilliam.Kucharski@Sun.COM 	pcnet32_wio_reset(ioaddr);
692*8044SWilliam.Kucharski@Sun.COM 
693*8044SWilliam.Kucharski@Sun.COM 	/* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
694*8044SWilliam.Kucharski@Sun.COM 	if (pcnet32_wio_read_csr(ioaddr, 0) == 4
695*8044SWilliam.Kucharski@Sun.COM 	    && pcnet32_wio_check(ioaddr)) {
696*8044SWilliam.Kucharski@Sun.COM 		a = &pcnet32_wio;
697*8044SWilliam.Kucharski@Sun.COM 	} else {
698*8044SWilliam.Kucharski@Sun.COM 		pcnet32_dwio_reset(ioaddr);
699*8044SWilliam.Kucharski@Sun.COM 		if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
700*8044SWilliam.Kucharski@Sun.COM 		    && pcnet32_dwio_check(ioaddr)) {
701*8044SWilliam.Kucharski@Sun.COM 			a = &pcnet32_dwio;
702*8044SWilliam.Kucharski@Sun.COM 		} else
703*8044SWilliam.Kucharski@Sun.COM 			return 0;
704*8044SWilliam.Kucharski@Sun.COM 	}
705*8044SWilliam.Kucharski@Sun.COM 
706*8044SWilliam.Kucharski@Sun.COM 	chip_version =
707*8044SWilliam.Kucharski@Sun.COM 	    a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);
708*8044SWilliam.Kucharski@Sun.COM 
709*8044SWilliam.Kucharski@Sun.COM 	dprintf(("PCnet chip version is %0xhX\n", chip_version));
710*8044SWilliam.Kucharski@Sun.COM 	if ((chip_version & 0xfff) != 0x003)
711*8044SWilliam.Kucharski@Sun.COM 		return 0;
712*8044SWilliam.Kucharski@Sun.COM 
713*8044SWilliam.Kucharski@Sun.COM 	/* initialize variables */
714*8044SWilliam.Kucharski@Sun.COM 	fdx = mii = fset = dxsuflo = ltint = 0;
715*8044SWilliam.Kucharski@Sun.COM 	chip_version = (chip_version >> 12) & 0xffff;
716*8044SWilliam.Kucharski@Sun.COM 
717*8044SWilliam.Kucharski@Sun.COM 	switch (chip_version) {
718*8044SWilliam.Kucharski@Sun.COM 	case 0x2420:
719*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/PCI 79C970";	/* PCI */
720*8044SWilliam.Kucharski@Sun.COM 		break;
721*8044SWilliam.Kucharski@Sun.COM 	case 0x2430:
722*8044SWilliam.Kucharski@Sun.COM 		if (shared)
723*8044SWilliam.Kucharski@Sun.COM 			chipname = "PCnet/PCI 79C970";	/* 970 gives the wrong chip id back */
724*8044SWilliam.Kucharski@Sun.COM 		else
725*8044SWilliam.Kucharski@Sun.COM 			chipname = "PCnet/32 79C965";	/* 486/VL bus */
726*8044SWilliam.Kucharski@Sun.COM 		break;
727*8044SWilliam.Kucharski@Sun.COM 	case 0x2621:
728*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/PCI II 79C970A";	/* PCI */
729*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
730*8044SWilliam.Kucharski@Sun.COM 		break;
731*8044SWilliam.Kucharski@Sun.COM 	case 0x2623:
732*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/FAST 79C971";	/* PCI */
733*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
734*8044SWilliam.Kucharski@Sun.COM 		mii = 1;
735*8044SWilliam.Kucharski@Sun.COM 		fset = 1;
736*8044SWilliam.Kucharski@Sun.COM 		ltint = 1;
737*8044SWilliam.Kucharski@Sun.COM 		break;
738*8044SWilliam.Kucharski@Sun.COM 	case 0x2624:
739*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/FAST+ 79C972";	/* PCI */
740*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
741*8044SWilliam.Kucharski@Sun.COM 		mii = 1;
742*8044SWilliam.Kucharski@Sun.COM 		fset = 1;
743*8044SWilliam.Kucharski@Sun.COM 		break;
744*8044SWilliam.Kucharski@Sun.COM 	case 0x2625:
745*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/FAST III 79C973";	/* PCI */
746*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
747*8044SWilliam.Kucharski@Sun.COM 		mii = 1;
748*8044SWilliam.Kucharski@Sun.COM 		break;
749*8044SWilliam.Kucharski@Sun.COM 	case 0x2626:
750*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/Home 79C978";	/* PCI */
751*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
752*8044SWilliam.Kucharski@Sun.COM 		/*
753*8044SWilliam.Kucharski@Sun.COM 		 * This is based on specs published at www.amd.com.  This section
754*8044SWilliam.Kucharski@Sun.COM 		 * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
755*8044SWilliam.Kucharski@Sun.COM 		 * mode.  The 79C978 can also go into standard ethernet, and there
756*8044SWilliam.Kucharski@Sun.COM 		 * probably should be some sort of module option to select the
757*8044SWilliam.Kucharski@Sun.COM 		 * mode by which the card should operate
758*8044SWilliam.Kucharski@Sun.COM 		 */
759*8044SWilliam.Kucharski@Sun.COM 		/* switch to home wiring mode */
760*8044SWilliam.Kucharski@Sun.COM 		media = a->read_bcr(ioaddr, 49);
761*8044SWilliam.Kucharski@Sun.COM 
762*8044SWilliam.Kucharski@Sun.COM 		printf("media reset to %#x.\n", media);
763*8044SWilliam.Kucharski@Sun.COM 		a->write_bcr(ioaddr, 49, media);
764*8044SWilliam.Kucharski@Sun.COM 		break;
765*8044SWilliam.Kucharski@Sun.COM 	case 0x2627:
766*8044SWilliam.Kucharski@Sun.COM 		chipname = "PCnet/FAST III 79C975";	/* PCI */
767*8044SWilliam.Kucharski@Sun.COM 		fdx = 1;
768*8044SWilliam.Kucharski@Sun.COM 		mii = 1;
769*8044SWilliam.Kucharski@Sun.COM 		break;
770*8044SWilliam.Kucharski@Sun.COM 	default:
771*8044SWilliam.Kucharski@Sun.COM 		printf("PCnet version %#x, no PCnet32 chip.\n",
772*8044SWilliam.Kucharski@Sun.COM 		       chip_version);
773*8044SWilliam.Kucharski@Sun.COM 		return 0;
774*8044SWilliam.Kucharski@Sun.COM 	}
775*8044SWilliam.Kucharski@Sun.COM 
776*8044SWilliam.Kucharski@Sun.COM 	/*
777*8044SWilliam.Kucharski@Sun.COM 	 *  On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
778*8044SWilliam.Kucharski@Sun.COM 	 *  starting until the packet is loaded. Strike one for reliability, lose
779*8044SWilliam.Kucharski@Sun.COM 	 *  one for latency - although on PCI this isnt a big loss. Older chips
780*8044SWilliam.Kucharski@Sun.COM 	 *  have FIFO's smaller than a packet, so you can't do this.
781*8044SWilliam.Kucharski@Sun.COM 	 */
782*8044SWilliam.Kucharski@Sun.COM 
783*8044SWilliam.Kucharski@Sun.COM 	if (fset) {
784*8044SWilliam.Kucharski@Sun.COM 		a->write_bcr(ioaddr, 18,
785*8044SWilliam.Kucharski@Sun.COM 			     (a->read_bcr(ioaddr, 18) | 0x0800));
786*8044SWilliam.Kucharski@Sun.COM 		a->write_csr(ioaddr, 80,
787*8044SWilliam.Kucharski@Sun.COM 			     (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
788*8044SWilliam.Kucharski@Sun.COM 		dxsuflo = 1;
789*8044SWilliam.Kucharski@Sun.COM 		ltint = 1;
790*8044SWilliam.Kucharski@Sun.COM 	}
791*8044SWilliam.Kucharski@Sun.COM 
792*8044SWilliam.Kucharski@Sun.COM 	dprintf(("%s at %hX,", chipname, ioaddr));
793*8044SWilliam.Kucharski@Sun.COM 
794*8044SWilliam.Kucharski@Sun.COM 	/* read PROM address */
795*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < 6; i++)
796*8044SWilliam.Kucharski@Sun.COM 		promaddr[i] = inb(ioaddr + i);
797*8044SWilliam.Kucharski@Sun.COM 
798*8044SWilliam.Kucharski@Sun.COM 	/* Update the nic structure with the MAC Address */
799*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < ETH_ALEN; i++) {
800*8044SWilliam.Kucharski@Sun.COM 		nic->node_addr[i] = promaddr[i];
801*8044SWilliam.Kucharski@Sun.COM 	}
802*8044SWilliam.Kucharski@Sun.COM 	/* Print out some hardware info */
803*8044SWilliam.Kucharski@Sun.COM 	printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr,
804*8044SWilliam.Kucharski@Sun.COM 	       ioaddr);
805*8044SWilliam.Kucharski@Sun.COM 
806*8044SWilliam.Kucharski@Sun.COM 	/* Set to pci bus master */
807*8044SWilliam.Kucharski@Sun.COM 	adjust_pci_device(pci);
808*8044SWilliam.Kucharski@Sun.COM 
809*8044SWilliam.Kucharski@Sun.COM 	/* point to private storage */
810*8044SWilliam.Kucharski@Sun.COM 	lp = &lpx;
811*8044SWilliam.Kucharski@Sun.COM 
812*8044SWilliam.Kucharski@Sun.COM #if EBDEBUG
813*8044SWilliam.Kucharski@Sun.COM 	if (((chip_version + 1) & 0xfffe) == 0x2624) {	/* Version 0x2623 or 0x2624 */
814*8044SWilliam.Kucharski@Sun.COM 		i = a->read_csr(ioaddr, 80) & 0x0C00;	/* Check tx_start_pt */
815*8044SWilliam.Kucharski@Sun.COM 		dprintf(("    tx_start_pt(0x%hX):", i));
816*8044SWilliam.Kucharski@Sun.COM 		switch (i >> 10) {
817*8044SWilliam.Kucharski@Sun.COM 		case 0:
818*8044SWilliam.Kucharski@Sun.COM 			dprintf(("  20 bytes,"));
819*8044SWilliam.Kucharski@Sun.COM 			break;
820*8044SWilliam.Kucharski@Sun.COM 		case 1:
821*8044SWilliam.Kucharski@Sun.COM 			dprintf(("  64 bytes,"));
822*8044SWilliam.Kucharski@Sun.COM 			break;
823*8044SWilliam.Kucharski@Sun.COM 		case 2:
824*8044SWilliam.Kucharski@Sun.COM 			dprintf((" 128 bytes,"));
825*8044SWilliam.Kucharski@Sun.COM 			break;
826*8044SWilliam.Kucharski@Sun.COM 		case 3:
827*8044SWilliam.Kucharski@Sun.COM 			dprintf(("~220 bytes,"));
828*8044SWilliam.Kucharski@Sun.COM 			break;
829*8044SWilliam.Kucharski@Sun.COM 		}
830*8044SWilliam.Kucharski@Sun.COM 		i = a->read_bcr(ioaddr, 18);	/* Check Burst/Bus control */
831*8044SWilliam.Kucharski@Sun.COM 		dprintf((" BCR18(%hX):", i & 0xffff));
832*8044SWilliam.Kucharski@Sun.COM 		if (i & (1 << 5))
833*8044SWilliam.Kucharski@Sun.COM 			dprintf(("BurstWrEn "));
834*8044SWilliam.Kucharski@Sun.COM 		if (i & (1 << 6))
835*8044SWilliam.Kucharski@Sun.COM 			dprintf(("BurstRdEn "));
836*8044SWilliam.Kucharski@Sun.COM 		if (i & (1 << 7))
837*8044SWilliam.Kucharski@Sun.COM 			dprintf(("DWordIO "));
838*8044SWilliam.Kucharski@Sun.COM 		if (i & (1 << 11))
839*8044SWilliam.Kucharski@Sun.COM 			dprintf(("NoUFlow "));
840*8044SWilliam.Kucharski@Sun.COM 		i = a->read_bcr(ioaddr, 25);
841*8044SWilliam.Kucharski@Sun.COM 		dprintf(("    SRAMSIZE=0x%hX,", i << 8));
842*8044SWilliam.Kucharski@Sun.COM 		i = a->read_bcr(ioaddr, 26);
843*8044SWilliam.Kucharski@Sun.COM 		dprintf((" SRAM_BND=0x%hX,", i << 8));
844*8044SWilliam.Kucharski@Sun.COM 		i = a->read_bcr(ioaddr, 27);
845*8044SWilliam.Kucharski@Sun.COM 		if (i & (1 << 14))
846*8044SWilliam.Kucharski@Sun.COM 			dprintf(("LowLatRx"));
847*8044SWilliam.Kucharski@Sun.COM 	}
848*8044SWilliam.Kucharski@Sun.COM #endif
849*8044SWilliam.Kucharski@Sun.COM 	lp->name = chipname;
850*8044SWilliam.Kucharski@Sun.COM 	lp->shared_irq = shared;
851*8044SWilliam.Kucharski@Sun.COM 	lp->full_duplex = fdx;
852*8044SWilliam.Kucharski@Sun.COM 	lp->dxsuflo = dxsuflo;
853*8044SWilliam.Kucharski@Sun.COM 	lp->ltint = ltint;
854*8044SWilliam.Kucharski@Sun.COM 	lp->mii = mii;
855*8044SWilliam.Kucharski@Sun.COM 	/* FIXME: Fix Options for only one card */
856*8044SWilliam.Kucharski@Sun.COM 	if ((cards_found >= MAX_UNITS)
857*8044SWilliam.Kucharski@Sun.COM 	    || ((unsigned int) options[cards_found] > sizeof(options_mapping)))
858*8044SWilliam.Kucharski@Sun.COM 		lp->options = PCNET32_PORT_ASEL;
859*8044SWilliam.Kucharski@Sun.COM 	else
860*8044SWilliam.Kucharski@Sun.COM 		lp->options = options_mapping[options[cards_found]];
861*8044SWilliam.Kucharski@Sun.COM 
862*8044SWilliam.Kucharski@Sun.COM 	if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
863*8044SWilliam.Kucharski@Sun.COM 	    ((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
864*8044SWilliam.Kucharski@Sun.COM 		lp->options |= PCNET32_PORT_FD;
865*8044SWilliam.Kucharski@Sun.COM 
866*8044SWilliam.Kucharski@Sun.COM 	if (!a) {
867*8044SWilliam.Kucharski@Sun.COM 		printf("No access methods\n");
868*8044SWilliam.Kucharski@Sun.COM 		return 0;
869*8044SWilliam.Kucharski@Sun.COM 	}
870*8044SWilliam.Kucharski@Sun.COM 	lp->a = *a;
871*8044SWilliam.Kucharski@Sun.COM 
872*8044SWilliam.Kucharski@Sun.COM 	/* detect special T1/E1 WAN card by checking for MAC address */
873*8044SWilliam.Kucharski@Sun.COM 	if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0
874*8044SWilliam.Kucharski@Sun.COM 	    && nic->node_addr[2] == 0x75)
875*8044SWilliam.Kucharski@Sun.COM 		lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
876*8044SWilliam.Kucharski@Sun.COM 
877*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
878*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.tlen_rlen =
879*8044SWilliam.Kucharski@Sun.COM 	    le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
880*8044SWilliam.Kucharski@Sun.COM 	for (i = 0; i < 6; i++)
881*8044SWilliam.Kucharski@Sun.COM 		lp->init_block.phys_addr[i] = nic->node_addr[i];
882*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.filter[0] = 0xffffffff;
883*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.filter[1] = 0xffffffff;
884*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.rx_ring = virt_to_bus(&rx_ring);
885*8044SWilliam.Kucharski@Sun.COM 	lp->init_block.tx_ring = virt_to_bus(&tx_ring);
886*8044SWilliam.Kucharski@Sun.COM 
887*8044SWilliam.Kucharski@Sun.COM 	/* switch pcnet32 to 32bit mode */
888*8044SWilliam.Kucharski@Sun.COM 	a->write_bcr(ioaddr, 20, 2);
889*8044SWilliam.Kucharski@Sun.COM 
890*8044SWilliam.Kucharski@Sun.COM 
891*8044SWilliam.Kucharski@Sun.COM 	a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff);
892*8044SWilliam.Kucharski@Sun.COM 	a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
893*8044SWilliam.Kucharski@Sun.COM 
894*8044SWilliam.Kucharski@Sun.COM 	/*
895*8044SWilliam.Kucharski@Sun.COM 	 * To auto-IRQ we enable the initialization-done and DMA error
896*8044SWilliam.Kucharski@Sun.COM 	 * interrupts. For ISA boards we get a DMA error, but VLB and PCI
897*8044SWilliam.Kucharski@Sun.COM 	 * boards will work.
898*8044SWilliam.Kucharski@Sun.COM 	 */
899*8044SWilliam.Kucharski@Sun.COM 	/* Trigger an initialization just for the interrupt. */
900*8044SWilliam.Kucharski@Sun.COM 
901*8044SWilliam.Kucharski@Sun.COM 	a->write_csr(ioaddr, 0, 0x41);
902*8044SWilliam.Kucharski@Sun.COM 	mdelay(1);
903*8044SWilliam.Kucharski@Sun.COM 
904*8044SWilliam.Kucharski@Sun.COM 	cards_found++;
905*8044SWilliam.Kucharski@Sun.COM 
906*8044SWilliam.Kucharski@Sun.COM 	/* point to NIC specific routines */
907*8044SWilliam.Kucharski@Sun.COM 	pcnet32_reset(nic);
908*8044SWilliam.Kucharski@Sun.COM 	if (1) {
909*8044SWilliam.Kucharski@Sun.COM 	        int tmp;
910*8044SWilliam.Kucharski@Sun.COM 		int phy, phy_idx = 0;
911*8044SWilliam.Kucharski@Sun.COM 		u16 mii_lpa;
912*8044SWilliam.Kucharski@Sun.COM 		lp->phys[0] = 1;	/* Default Setting */
913*8044SWilliam.Kucharski@Sun.COM 		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
914*8044SWilliam.Kucharski@Sun.COM 			int mii_status = mdio_read(nic, phy, MII_BMSR);
915*8044SWilliam.Kucharski@Sun.COM 			if (mii_status != 0xffff && mii_status != 0x0000) {
916*8044SWilliam.Kucharski@Sun.COM 				lp->phys[phy_idx++] = phy;
917*8044SWilliam.Kucharski@Sun.COM 				lp->mii_if.advertising =
918*8044SWilliam.Kucharski@Sun.COM 				    mdio_read(nic, phy, MII_ADVERTISE);
919*8044SWilliam.Kucharski@Sun.COM 				if ((mii_status & 0x0040) == 0) {
920*8044SWilliam.Kucharski@Sun.COM 				  tmp = phy;
921*8044SWilliam.Kucharski@Sun.COM 				  dprintf (("MII PHY found at address %d, status "
922*8044SWilliam.Kucharski@Sun.COM 					    "%hX advertising %hX\n", phy, mii_status,
923*8044SWilliam.Kucharski@Sun.COM 					    lp->mii_if.advertising));
924*8044SWilliam.Kucharski@Sun.COM 				}
925*8044SWilliam.Kucharski@Sun.COM 			}
926*8044SWilliam.Kucharski@Sun.COM 		}
927*8044SWilliam.Kucharski@Sun.COM 		if (phy_idx == 0)
928*8044SWilliam.Kucharski@Sun.COM 			printf("No MII transceiver found!\n");
929*8044SWilliam.Kucharski@Sun.COM 		lp->mii_if.phy_id = lp->phys[0];
930*8044SWilliam.Kucharski@Sun.COM 
931*8044SWilliam.Kucharski@Sun.COM 		lp->mii_if.advertising =
932*8044SWilliam.Kucharski@Sun.COM 		    mdio_read(nic, lp->phys[0], MII_ADVERTISE);
933*8044SWilliam.Kucharski@Sun.COM 
934*8044SWilliam.Kucharski@Sun.COM 		mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA);
935*8044SWilliam.Kucharski@Sun.COM 		lp->mii_if.advertising &= mii_lpa;
936*8044SWilliam.Kucharski@Sun.COM 		if (lp->mii_if.advertising & ADVERTISE_100FULL)
937*8044SWilliam.Kucharski@Sun.COM 			printf("100Mbps Full-Duplex\n");
938*8044SWilliam.Kucharski@Sun.COM 		else if (lp->mii_if.advertising & ADVERTISE_100HALF)
939*8044SWilliam.Kucharski@Sun.COM 			printf("100Mbps Half-Duplex\n");
940*8044SWilliam.Kucharski@Sun.COM 		else if (lp->mii_if.advertising & ADVERTISE_10FULL)
941*8044SWilliam.Kucharski@Sun.COM 			printf("10Mbps Full-Duplex\n");
942*8044SWilliam.Kucharski@Sun.COM 		else if (lp->mii_if.advertising & ADVERTISE_10HALF)
943*8044SWilliam.Kucharski@Sun.COM 			printf("10Mbps Half-Duplex\n");
944*8044SWilliam.Kucharski@Sun.COM 		else
945*8044SWilliam.Kucharski@Sun.COM 			printf("\n");
946*8044SWilliam.Kucharski@Sun.COM 	}
947*8044SWilliam.Kucharski@Sun.COM 
948*8044SWilliam.Kucharski@Sun.COM 	nic->poll     = pcnet32_poll;
949*8044SWilliam.Kucharski@Sun.COM 	nic->transmit = pcnet32_transmit;
950*8044SWilliam.Kucharski@Sun.COM 	dev->disable  = pcnet32_disable;
951*8044SWilliam.Kucharski@Sun.COM 	nic->irq      = pcnet32_irq;
952*8044SWilliam.Kucharski@Sun.COM 
953*8044SWilliam.Kucharski@Sun.COM 	return 1;
954*8044SWilliam.Kucharski@Sun.COM }
mdio_read(struct nic * nic __unused,int phy_id,int reg_num)955*8044SWilliam.Kucharski@Sun.COM static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num)
956*8044SWilliam.Kucharski@Sun.COM {
957*8044SWilliam.Kucharski@Sun.COM 	u16 val_out;
958*8044SWilliam.Kucharski@Sun.COM 	int phyaddr;
959*8044SWilliam.Kucharski@Sun.COM 
960*8044SWilliam.Kucharski@Sun.COM 	if (!lp->mii)
961*8044SWilliam.Kucharski@Sun.COM 		return 0;
962*8044SWilliam.Kucharski@Sun.COM 
963*8044SWilliam.Kucharski@Sun.COM 	phyaddr = lp->a.read_bcr(ioaddr, 33);
964*8044SWilliam.Kucharski@Sun.COM 
965*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 33,
966*8044SWilliam.Kucharski@Sun.COM 			((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
967*8044SWilliam.Kucharski@Sun.COM 	val_out = lp->a.read_bcr(ioaddr, 34);
968*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 33, phyaddr);
969*8044SWilliam.Kucharski@Sun.COM 
970*8044SWilliam.Kucharski@Sun.COM 	return val_out;
971*8044SWilliam.Kucharski@Sun.COM }
972*8044SWilliam.Kucharski@Sun.COM 
973*8044SWilliam.Kucharski@Sun.COM #if 0
974*8044SWilliam.Kucharski@Sun.COM static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
975*8044SWilliam.Kucharski@Sun.COM 		       int val)
976*8044SWilliam.Kucharski@Sun.COM {
977*8044SWilliam.Kucharski@Sun.COM 	int phyaddr;
978*8044SWilliam.Kucharski@Sun.COM 
979*8044SWilliam.Kucharski@Sun.COM 	if (!lp->mii)
980*8044SWilliam.Kucharski@Sun.COM 		return;
981*8044SWilliam.Kucharski@Sun.COM 
982*8044SWilliam.Kucharski@Sun.COM 	phyaddr = lp->a.read_bcr(ioaddr, 33);
983*8044SWilliam.Kucharski@Sun.COM 
984*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 33,
985*8044SWilliam.Kucharski@Sun.COM 			((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
986*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 34, val);
987*8044SWilliam.Kucharski@Sun.COM 	lp->a.write_bcr(ioaddr, 33, phyaddr);
988*8044SWilliam.Kucharski@Sun.COM }
989*8044SWilliam.Kucharski@Sun.COM #endif
990*8044SWilliam.Kucharski@Sun.COM 
991*8044SWilliam.Kucharski@Sun.COM static struct pci_id pcnet32_nics[] = {
992*8044SWilliam.Kucharski@Sun.COM 	PCI_ROM(0x1022, 0x2000, "lancepci", "AMD Lance/PCI"),
993*8044SWilliam.Kucharski@Sun.COM 	PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD Lance/PCI PCNet/32"),
994*8044SWilliam.Kucharski@Sun.COM 	PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD Lance/HomePNA"),
995*8044SWilliam.Kucharski@Sun.COM };
996*8044SWilliam.Kucharski@Sun.COM 
997*8044SWilliam.Kucharski@Sun.COM struct pci_driver pcnet32_driver = {
998*8044SWilliam.Kucharski@Sun.COM 	.type = NIC_DRIVER,
999*8044SWilliam.Kucharski@Sun.COM 	.name = "PCNET32/PCI",
1000*8044SWilliam.Kucharski@Sun.COM 	.probe = pcnet32_probe,
1001*8044SWilliam.Kucharski@Sun.COM 	.ids = pcnet32_nics,
1002*8044SWilliam.Kucharski@Sun.COM 	.id_count = sizeof(pcnet32_nics) / sizeof(pcnet32_nics[0]),
1003*8044SWilliam.Kucharski@Sun.COM 	.class = 0,
1004*8044SWilliam.Kucharski@Sun.COM };
1005