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