xref: /titanic_44/usr/src/grub/grub-0.97/netboot/w89c840.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1*1b8adde7SWilliam Kucharski /*
2*1b8adde7SWilliam Kucharski  * Etherboot -  BOOTP/TFTP Bootstrap Program
3*1b8adde7SWilliam Kucharski  *
4*1b8adde7SWilliam Kucharski  * w89c840.c -- This file implements the winbond-840 driver for etherboot.
5*1b8adde7SWilliam Kucharski  *
6*1b8adde7SWilliam Kucharski  */
7*1b8adde7SWilliam Kucharski 
8*1b8adde7SWilliam Kucharski /*
9*1b8adde7SWilliam Kucharski  * Adapted by Igor V. Kovalenko
10*1b8adde7SWilliam Kucharski  *  -- <garrison@mail.ru>
11*1b8adde7SWilliam Kucharski  *   OR
12*1b8adde7SWilliam Kucharski  *  -- <iko@crec.mipt.ru>
13*1b8adde7SWilliam Kucharski  * Initial adaptaion stage, including testing, completed 23 August 2000.
14*1b8adde7SWilliam Kucharski  */
15*1b8adde7SWilliam Kucharski 
16*1b8adde7SWilliam Kucharski /*
17*1b8adde7SWilliam Kucharski  * This program is free software; you can redistribute it and/or
18*1b8adde7SWilliam Kucharski  * modify it under the terms of the GNU General Public License as
19*1b8adde7SWilliam Kucharski  * published by the Free Software Foundation; either version 2, or (at
20*1b8adde7SWilliam Kucharski  * your option) any later version.
21*1b8adde7SWilliam Kucharski  *
22*1b8adde7SWilliam Kucharski  * This program is distributed in the hope that it will be useful, but
23*1b8adde7SWilliam Kucharski  * WITHOUT ANY WARRANTY; without even the implied warranty of
24*1b8adde7SWilliam Kucharski  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25*1b8adde7SWilliam Kucharski  * General Public License for more details.
26*1b8adde7SWilliam Kucharski  *
27*1b8adde7SWilliam Kucharski  * You should have received a copy of the GNU General Public License
28*1b8adde7SWilliam Kucharski  * along with this program; if not, write to the Free Software
29*1b8adde7SWilliam Kucharski  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30*1b8adde7SWilliam Kucharski  */
31*1b8adde7SWilliam Kucharski 
32*1b8adde7SWilliam Kucharski /*
33*1b8adde7SWilliam Kucharski  *              date       version  by   what
34*1b8adde7SWilliam Kucharski  *  Written:    Aug 20 2000  V0.10  iko  Initial revision.
35*1b8adde7SWilliam Kucharski  * changes:     Aug 22 2000  V0.90  iko  Works!
36*1b8adde7SWilliam Kucharski  *              Aug 23 2000  V0.91  iko  Cleanup, posted to etherboot
37*1b8adde7SWilliam Kucharski  *                                       maintainer.
38*1b8adde7SWilliam Kucharski  *              Aug 26 2000  V0.92  iko  Fixed Rx ring handling.
39*1b8adde7SWilliam Kucharski  *                                       First Linux Kernel (TM)
40*1b8adde7SWilliam Kucharski  *                                       successfully loaded using
41*1b8adde7SWilliam Kucharski  *                                       this driver.
42*1b8adde7SWilliam Kucharski  *              Jan 07 2001  V0.93  iko  Transmitter timeouts are handled
43*1b8adde7SWilliam Kucharski  *                                       using timer2 routines. Proposed
44*1b8adde7SWilliam Kucharski  *                                       by Ken Yap to eliminate CPU speed
45*1b8adde7SWilliam Kucharski  *                                       dependency.
46*1b8adde7SWilliam Kucharski  *             Dec 12 2003  V0.94   timlegge	Fixed issues in 5.2, removed
47*1b8adde7SWilliam Kucharski  *             					interrupt usage, enabled
48*1b8adde7SWilliam Kucharski  *             					multicast support
49*1b8adde7SWilliam Kucharski  *
50*1b8adde7SWilliam Kucharski  * This is the etherboot driver for cards based on Winbond W89c840F chip.
51*1b8adde7SWilliam Kucharski  *
52*1b8adde7SWilliam Kucharski  * It was written from skeleton source, with Donald Becker's winbond-840.c
53*1b8adde7SWilliam Kucharski  * kernel driver as a guideline. Mostly the w89c840 related definitions
54*1b8adde7SWilliam Kucharski  * and the lower level routines have been cut-and-pasted into this source.
55*1b8adde7SWilliam Kucharski  *
56*1b8adde7SWilliam Kucharski  * Frankly speaking, about 90% of the code was obtained using cut'n'paste
57*1b8adde7SWilliam Kucharski  * sequence :) while the remainder appeared while brainstorming
58*1b8adde7SWilliam Kucharski  * Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
59*1b8adde7SWilliam Kucharski  *
60*1b8adde7SWilliam Kucharski  * There was a demand for using this card in a rather large
61*1b8adde7SWilliam Kucharski  * remote boot environment at MSKP OVTI Lab of
62*1b8adde7SWilliam Kucharski  * Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
63*1b8adde7SWilliam Kucharski  * so you may count that for motivation.
64*1b8adde7SWilliam Kucharski  *
65*1b8adde7SWilliam Kucharski  */
66*1b8adde7SWilliam Kucharski 
67*1b8adde7SWilliam Kucharski /*
68*1b8adde7SWilliam Kucharski  * If you want to see debugging output then define W89C840_DEBUG
69*1b8adde7SWilliam Kucharski  */
70*1b8adde7SWilliam Kucharski 
71*1b8adde7SWilliam Kucharski /*
72*1b8adde7SWilliam Kucharski #define W89C840_DEBUG
73*1b8adde7SWilliam Kucharski */
74*1b8adde7SWilliam Kucharski 
75*1b8adde7SWilliam Kucharski /*
76*1b8adde7SWilliam Kucharski  * Keep using IO_OPS for Etherboot driver!
77*1b8adde7SWilliam Kucharski  */
78*1b8adde7SWilliam Kucharski #define USE_IO_OPS
79*1b8adde7SWilliam Kucharski 
80*1b8adde7SWilliam Kucharski #include "etherboot.h"
81*1b8adde7SWilliam Kucharski #include "nic.h"
82*1b8adde7SWilliam Kucharski #include "pci.h"
83*1b8adde7SWilliam Kucharski #include "timer.h"
84*1b8adde7SWilliam Kucharski 
85*1b8adde7SWilliam Kucharski static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
86*1b8adde7SWilliam Kucharski 
87*1b8adde7SWilliam Kucharski typedef unsigned char  u8;
88*1b8adde7SWilliam Kucharski typedef   signed char  s8;
89*1b8adde7SWilliam Kucharski typedef unsigned short u16;
90*1b8adde7SWilliam Kucharski typedef   signed short s16;
91*1b8adde7SWilliam Kucharski typedef unsigned int   u32;
92*1b8adde7SWilliam Kucharski typedef   signed int   s32;
93*1b8adde7SWilliam Kucharski 
94*1b8adde7SWilliam Kucharski /* Linux support functions */
95*1b8adde7SWilliam Kucharski #define virt_to_le32desc(addr)  virt_to_bus(addr)
96*1b8adde7SWilliam Kucharski #define le32desc_to_virt(addr)  bus_to_virt(addr)
97*1b8adde7SWilliam Kucharski 
98*1b8adde7SWilliam Kucharski /*
99*1b8adde7SWilliam Kucharski #define cpu_to_le32(val) (val)
100*1b8adde7SWilliam Kucharski #define le32_to_cpu(val) (val)
101*1b8adde7SWilliam Kucharski */
102*1b8adde7SWilliam Kucharski 
103*1b8adde7SWilliam Kucharski /* Operational parameters that are set at compile time. */
104*1b8adde7SWilliam Kucharski 
105*1b8adde7SWilliam Kucharski /* Keep the ring sizes a power of two for compile efficiency.
106*1b8adde7SWilliam Kucharski    The compiler will convert <unsigned>'%'<2^N> into a bit mask.
107*1b8adde7SWilliam Kucharski    Making the Tx ring too large decreases the effectiveness of channel
108*1b8adde7SWilliam Kucharski    bonding and packet priority.
109*1b8adde7SWilliam Kucharski    There are no ill effects from too-large receive rings. */
110*1b8adde7SWilliam Kucharski #define TX_RING_SIZE    2
111*1b8adde7SWilliam Kucharski #define RX_RING_SIZE    2
112*1b8adde7SWilliam Kucharski 
113*1b8adde7SWilliam Kucharski /* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
114*1b8adde7SWilliam Kucharski    To avoid overflowing we don't queue again until we have room for a
115*1b8adde7SWilliam Kucharski    full-size packet.
116*1b8adde7SWilliam Kucharski  */
117*1b8adde7SWilliam Kucharski #define TX_FIFO_SIZE (2048)
118*1b8adde7SWilliam Kucharski #define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
119*1b8adde7SWilliam Kucharski 
120*1b8adde7SWilliam Kucharski /* Operational parameters that usually are not changed. */
121*1b8adde7SWilliam Kucharski /* Time in jiffies before concluding the transmitter is hung. */
122*1b8adde7SWilliam Kucharski #define TX_TIMEOUT  (10*TICKS_PER_MS)
123*1b8adde7SWilliam Kucharski 
124*1b8adde7SWilliam Kucharski #define PKT_BUF_SZ  1536  /* Size of each temporary Rx buffer.*/
125*1b8adde7SWilliam Kucharski 
126*1b8adde7SWilliam Kucharski /*
127*1b8adde7SWilliam Kucharski  * Used to be this much CPU loops on Celeron@400 (?),
128*1b8adde7SWilliam Kucharski  * now using real timer and TX_TIMEOUT!
129*1b8adde7SWilliam Kucharski  * #define TX_LOOP_COUNT 10000000
130*1b8adde7SWilliam Kucharski  */
131*1b8adde7SWilliam Kucharski 
132*1b8adde7SWilliam Kucharski #if !defined(__OPTIMIZE__)
133*1b8adde7SWilliam Kucharski #warning  You must compile this file with the correct options!
134*1b8adde7SWilliam Kucharski #warning  See the last lines of the source file.
135*1b8adde7SWilliam Kucharski #error You must compile this driver with "-O".
136*1b8adde7SWilliam Kucharski #endif
137*1b8adde7SWilliam Kucharski 
138*1b8adde7SWilliam Kucharski enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
139*1b8adde7SWilliam Kucharski 
140*1b8adde7SWilliam Kucharski #ifdef USE_IO_OPS
141*1b8adde7SWilliam Kucharski #define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
142*1b8adde7SWilliam Kucharski #else
143*1b8adde7SWilliam Kucharski #define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
144*1b8adde7SWilliam Kucharski #endif
145*1b8adde7SWilliam Kucharski 
146*1b8adde7SWilliam Kucharski static u32 driver_flags = CanHaveMII | HasBrokenTx;
147*1b8adde7SWilliam Kucharski 
148*1b8adde7SWilliam Kucharski /* This driver was written to use PCI memory space, however some x86 systems
149*1b8adde7SWilliam Kucharski    work only with I/O space accesses.  Pass -DUSE_IO_OPS to use PCI I/O space
150*1b8adde7SWilliam Kucharski    accesses instead of memory space. */
151*1b8adde7SWilliam Kucharski 
152*1b8adde7SWilliam Kucharski #ifdef USE_IO_OPS
153*1b8adde7SWilliam Kucharski #undef readb
154*1b8adde7SWilliam Kucharski #undef readw
155*1b8adde7SWilliam Kucharski #undef readl
156*1b8adde7SWilliam Kucharski #undef writeb
157*1b8adde7SWilliam Kucharski #undef writew
158*1b8adde7SWilliam Kucharski #undef writel
159*1b8adde7SWilliam Kucharski #define readb inb
160*1b8adde7SWilliam Kucharski #define readw inw
161*1b8adde7SWilliam Kucharski #define readl inl
162*1b8adde7SWilliam Kucharski #define writeb outb
163*1b8adde7SWilliam Kucharski #define writew outw
164*1b8adde7SWilliam Kucharski #define writel outl
165*1b8adde7SWilliam Kucharski #endif
166*1b8adde7SWilliam Kucharski 
167*1b8adde7SWilliam Kucharski /* Offsets to the Command and Status Registers, "CSRs".
168*1b8adde7SWilliam Kucharski    While similar to the Tulip, these registers are longword aligned.
169*1b8adde7SWilliam Kucharski    Note: It's not useful to define symbolic names for every register bit in
170*1b8adde7SWilliam Kucharski    the device.  The name can only partially document the semantics and make
171*1b8adde7SWilliam Kucharski    the driver longer and more difficult to read.
172*1b8adde7SWilliam Kucharski */
173*1b8adde7SWilliam Kucharski enum w840_offsets {
174*1b8adde7SWilliam Kucharski     PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
175*1b8adde7SWilliam Kucharski     RxRingPtr=0x0C, TxRingPtr=0x10,
176*1b8adde7SWilliam Kucharski     IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
177*1b8adde7SWilliam Kucharski     RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
178*1b8adde7SWilliam Kucharski     CurRxDescAddr=0x30, CurRxBufAddr=0x34,            /* Debug use */
179*1b8adde7SWilliam Kucharski     MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
180*1b8adde7SWilliam Kucharski     CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
181*1b8adde7SWilliam Kucharski };
182*1b8adde7SWilliam Kucharski 
183*1b8adde7SWilliam Kucharski /* Bits in the interrupt status/enable registers. */
184*1b8adde7SWilliam Kucharski /* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
185*1b8adde7SWilliam Kucharski enum intr_status_bits {
186*1b8adde7SWilliam Kucharski     NormalIntr=0x10000, AbnormalIntr=0x8000,
187*1b8adde7SWilliam Kucharski     IntrPCIErr=0x2000, TimerInt=0x800,
188*1b8adde7SWilliam Kucharski     IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
189*1b8adde7SWilliam Kucharski     TxFIFOUnderflow=0x20, RxErrIntr=0x10,
190*1b8adde7SWilliam Kucharski     TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
191*1b8adde7SWilliam Kucharski };
192*1b8adde7SWilliam Kucharski 
193*1b8adde7SWilliam Kucharski /* Bits in the NetworkConfig register. */
194*1b8adde7SWilliam Kucharski enum rx_mode_bits {
195*1b8adde7SWilliam Kucharski     AcceptErr=0x80, AcceptRunt=0x40,
196*1b8adde7SWilliam Kucharski     AcceptBroadcast=0x20, AcceptMulticast=0x10,
197*1b8adde7SWilliam Kucharski     AcceptAllPhys=0x08, AcceptMyPhys=0x02,
198*1b8adde7SWilliam Kucharski };
199*1b8adde7SWilliam Kucharski 
200*1b8adde7SWilliam Kucharski enum mii_reg_bits {
201*1b8adde7SWilliam Kucharski     MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
202*1b8adde7SWilliam Kucharski     MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
203*1b8adde7SWilliam Kucharski };
204*1b8adde7SWilliam Kucharski 
205*1b8adde7SWilliam Kucharski /* The Tulip Rx and Tx buffer descriptors. */
206*1b8adde7SWilliam Kucharski struct w840_rx_desc {
207*1b8adde7SWilliam Kucharski     s32 status;
208*1b8adde7SWilliam Kucharski     s32 length;
209*1b8adde7SWilliam Kucharski     u32 buffer1;
210*1b8adde7SWilliam Kucharski     u32 next_desc;
211*1b8adde7SWilliam Kucharski };
212*1b8adde7SWilliam Kucharski 
213*1b8adde7SWilliam Kucharski struct w840_tx_desc {
214*1b8adde7SWilliam Kucharski     s32 status;
215*1b8adde7SWilliam Kucharski     s32 length;
216*1b8adde7SWilliam Kucharski     u32 buffer1, buffer2;                /* We use only buffer 1.  */
217*1b8adde7SWilliam Kucharski };
218*1b8adde7SWilliam Kucharski 
219*1b8adde7SWilliam Kucharski /* Bits in network_desc.status */
220*1b8adde7SWilliam Kucharski enum desc_status_bits {
221*1b8adde7SWilliam Kucharski     DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
222*1b8adde7SWilliam Kucharski     DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
223*1b8adde7SWilliam Kucharski     DescIntr=0x80000000,
224*1b8adde7SWilliam Kucharski };
225*1b8adde7SWilliam Kucharski #define PRIV_ALIGN    15     /* Required alignment mask */
226*1b8adde7SWilliam Kucharski #define PRIV_ALIGN_BYTES 32
227*1b8adde7SWilliam Kucharski 
228*1b8adde7SWilliam Kucharski static struct winbond_private
229*1b8adde7SWilliam Kucharski {
230*1b8adde7SWilliam Kucharski     /* Descriptor rings first for alignment. */
231*1b8adde7SWilliam Kucharski     struct w840_rx_desc rx_ring[RX_RING_SIZE];
232*1b8adde7SWilliam Kucharski     struct w840_tx_desc tx_ring[TX_RING_SIZE];
233*1b8adde7SWilliam Kucharski     struct net_device *next_module;        /* Link for devices of this type. */
234*1b8adde7SWilliam Kucharski     void *priv_addr;                    /* Unaligned address for kfree */
235*1b8adde7SWilliam Kucharski     const char *product_name;
236*1b8adde7SWilliam Kucharski     /* Frequently used values: keep some adjacent for cache effect. */
237*1b8adde7SWilliam Kucharski     int chip_id, drv_flags;
238*1b8adde7SWilliam Kucharski     struct pci_dev *pci_dev;
239*1b8adde7SWilliam Kucharski     int csr6;
240*1b8adde7SWilliam Kucharski     struct w840_rx_desc *rx_head_desc;
241*1b8adde7SWilliam Kucharski     unsigned int cur_rx, dirty_rx;        /* Producer/consumer ring indices */
242*1b8adde7SWilliam Kucharski     unsigned int rx_buf_sz;                /* Based on MTU+slack. */
243*1b8adde7SWilliam Kucharski     unsigned int cur_tx, dirty_tx;
244*1b8adde7SWilliam Kucharski     int tx_q_bytes;
245*1b8adde7SWilliam Kucharski     unsigned int tx_full:1;                /* The Tx queue is full. */
246*1b8adde7SWilliam Kucharski     /* These values are keep track of the transceiver/media in use. */
247*1b8adde7SWilliam Kucharski     unsigned int full_duplex:1;            /* Full-duplex operation requested. */
248*1b8adde7SWilliam Kucharski     unsigned int duplex_lock:1;
249*1b8adde7SWilliam Kucharski     unsigned int medialock:1;            /* Do not sense media. */
250*1b8adde7SWilliam Kucharski     unsigned int default_port:4;        /* Last dev->if_port value. */
251*1b8adde7SWilliam Kucharski     /* MII transceiver section. */
252*1b8adde7SWilliam Kucharski     int mii_cnt;                        /* MII device addresses. */
253*1b8adde7SWilliam Kucharski     u16 advertising;                    /* NWay media advertisement */
254*1b8adde7SWilliam Kucharski     unsigned char phys[2];                /* MII device addresses. */
255*1b8adde7SWilliam Kucharski } w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
256*1b8adde7SWilliam Kucharski 
257*1b8adde7SWilliam Kucharski /* NIC specific static variables go here */
258*1b8adde7SWilliam Kucharski 
259*1b8adde7SWilliam Kucharski static int ioaddr;
260*1b8adde7SWilliam Kucharski static unsigned short eeprom [0x40];
261*1b8adde7SWilliam Kucharski static char        rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
262*1b8adde7SWilliam Kucharski static char        tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
263*1b8adde7SWilliam Kucharski 
264*1b8adde7SWilliam Kucharski static int  eeprom_read(long ioaddr, int location);
265*1b8adde7SWilliam Kucharski static int  mdio_read(int base_address, int phy_id, int location);
266*1b8adde7SWilliam Kucharski #if 0
267*1b8adde7SWilliam Kucharski static void mdio_write(int base_address, int phy_id, int location, int value);
268*1b8adde7SWilliam Kucharski #endif
269*1b8adde7SWilliam Kucharski 
270*1b8adde7SWilliam Kucharski static void check_duplex(void);
271*1b8adde7SWilliam Kucharski static void set_rx_mode(void);
272*1b8adde7SWilliam Kucharski static void init_ring(void);
273*1b8adde7SWilliam Kucharski 
274*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
decode_interrupt(u32 intr_status)275*1b8adde7SWilliam Kucharski static void decode_interrupt(u32 intr_status)
276*1b8adde7SWilliam Kucharski {
277*1b8adde7SWilliam Kucharski     printf("Interrupt status: ");
278*1b8adde7SWilliam Kucharski 
279*1b8adde7SWilliam Kucharski #define TRACE_INTR(_intr_) \
280*1b8adde7SWilliam Kucharski     if (intr_status & (_intr_)) { printf (" " #_intr_); }
281*1b8adde7SWilliam Kucharski 
282*1b8adde7SWilliam Kucharski     TRACE_INTR(NormalIntr);
283*1b8adde7SWilliam Kucharski     TRACE_INTR(AbnormalIntr);
284*1b8adde7SWilliam Kucharski     TRACE_INTR(IntrPCIErr);
285*1b8adde7SWilliam Kucharski     TRACE_INTR(TimerInt);
286*1b8adde7SWilliam Kucharski     TRACE_INTR(IntrRxDied);
287*1b8adde7SWilliam Kucharski     TRACE_INTR(RxNoBuf);
288*1b8adde7SWilliam Kucharski     TRACE_INTR(IntrRxDone);
289*1b8adde7SWilliam Kucharski     TRACE_INTR(TxFIFOUnderflow);
290*1b8adde7SWilliam Kucharski     TRACE_INTR(RxErrIntr);
291*1b8adde7SWilliam Kucharski     TRACE_INTR(TxIdle);
292*1b8adde7SWilliam Kucharski     TRACE_INTR(IntrTxStopped);
293*1b8adde7SWilliam Kucharski     TRACE_INTR(IntrTxDone);
294*1b8adde7SWilliam Kucharski 
295*1b8adde7SWilliam Kucharski     printf("\n");
296*1b8adde7SWilliam Kucharski     /*sleep(1);*/
297*1b8adde7SWilliam Kucharski }
298*1b8adde7SWilliam Kucharski #endif
299*1b8adde7SWilliam Kucharski 
300*1b8adde7SWilliam Kucharski /**************************************************************************
301*1b8adde7SWilliam Kucharski w89c840_reset - Reset adapter
302*1b8adde7SWilliam Kucharski ***************************************************************************/
w89c840_reset(struct nic * nic)303*1b8adde7SWilliam Kucharski static void w89c840_reset(struct nic *nic)
304*1b8adde7SWilliam Kucharski {
305*1b8adde7SWilliam Kucharski     int i;
306*1b8adde7SWilliam Kucharski 
307*1b8adde7SWilliam Kucharski     /* Reset the chip to erase previous misconfiguration.
308*1b8adde7SWilliam Kucharski        No hold time required! */
309*1b8adde7SWilliam Kucharski     writel(0x00000001, ioaddr + PCIBusCfg);
310*1b8adde7SWilliam Kucharski 
311*1b8adde7SWilliam Kucharski     init_ring();
312*1b8adde7SWilliam Kucharski 
313*1b8adde7SWilliam Kucharski     writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
314*1b8adde7SWilliam Kucharski     writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
315*1b8adde7SWilliam Kucharski 
316*1b8adde7SWilliam Kucharski     for (i = 0; i < ETH_ALEN; i++)
317*1b8adde7SWilliam Kucharski         writeb(nic->node_addr[i], ioaddr + StationAddr + i);
318*1b8adde7SWilliam Kucharski 
319*1b8adde7SWilliam Kucharski     /* Initialize other registers. */
320*1b8adde7SWilliam Kucharski     /* Configure the PCI bus bursts and FIFO thresholds.
321*1b8adde7SWilliam Kucharski        486: Set 8 longword cache alignment, 8 longword burst.
322*1b8adde7SWilliam Kucharski        586: Set 16 longword cache alignment, no burst limit.
323*1b8adde7SWilliam Kucharski        Cache alignment bits 15:14         Burst length 13:8
324*1b8adde7SWilliam Kucharski         0000    <not allowed>         0000 align to cache    0800 8 longwords
325*1b8adde7SWilliam Kucharski         4000    8  longwords        0100 1 longword        1000 16 longwords
326*1b8adde7SWilliam Kucharski         8000    16 longwords        0200 2 longwords    2000 32 longwords
327*1b8adde7SWilliam Kucharski         C000    32  longwords        0400 4 longwords
328*1b8adde7SWilliam Kucharski        Wait the specified 50 PCI cycles after a reset by initializing
329*1b8adde7SWilliam Kucharski        Tx and Rx queues and the address filter list. */
330*1b8adde7SWilliam Kucharski 
331*1b8adde7SWilliam Kucharski     writel(0xE010, ioaddr + PCIBusCfg);
332*1b8adde7SWilliam Kucharski 
333*1b8adde7SWilliam Kucharski     writel(0, ioaddr + RxStartDemand);
334*1b8adde7SWilliam Kucharski     w840private.csr6 = 0x20022002;
335*1b8adde7SWilliam Kucharski     check_duplex();
336*1b8adde7SWilliam Kucharski     set_rx_mode();
337*1b8adde7SWilliam Kucharski 
338*1b8adde7SWilliam Kucharski     /* Do not enable the interrupts Etherboot doesn't need them */
339*1b8adde7SWilliam Kucharski /*
340*1b8adde7SWilliam Kucharski     writel(0x1A0F5, ioaddr + IntrStatus);
341*1b8adde7SWilliam Kucharski     writel(0x1A0F5, ioaddr + IntrEnable);
342*1b8adde7SWilliam Kucharski */
343*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
344*1b8adde7SWilliam Kucharski     printf("winbond-840 : Done reset.\n");
345*1b8adde7SWilliam Kucharski #endif
346*1b8adde7SWilliam Kucharski }
347*1b8adde7SWilliam Kucharski 
348*1b8adde7SWilliam Kucharski #if 0
349*1b8adde7SWilliam Kucharski static void handle_intr(u32 intr_stat)
350*1b8adde7SWilliam Kucharski {
351*1b8adde7SWilliam Kucharski     if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
352*1b8adde7SWilliam Kucharski         /* we are polling, do not return now */
353*1b8adde7SWilliam Kucharski         /*return 0;*/
354*1b8adde7SWilliam Kucharski     } else {
355*1b8adde7SWilliam Kucharski         /* Acknowledge all of the current interrupt sources ASAP. */
356*1b8adde7SWilliam Kucharski         writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
357*1b8adde7SWilliam Kucharski     }
358*1b8adde7SWilliam Kucharski 
359*1b8adde7SWilliam Kucharski     if (intr_stat & AbnormalIntr) {
360*1b8adde7SWilliam Kucharski         /* There was an abnormal interrupt */
361*1b8adde7SWilliam Kucharski         printf("\n-=- Abnormal interrupt.\n");
362*1b8adde7SWilliam Kucharski 
363*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
364*1b8adde7SWilliam Kucharski         decode_interrupt(intr_stat);
365*1b8adde7SWilliam Kucharski #endif
366*1b8adde7SWilliam Kucharski 
367*1b8adde7SWilliam Kucharski         if (intr_stat & RxNoBuf) {
368*1b8adde7SWilliam Kucharski             /* There was an interrupt */
369*1b8adde7SWilliam Kucharski             printf("-=- <=> No receive buffers available.\n");
370*1b8adde7SWilliam Kucharski             writel(0, ioaddr + RxStartDemand);
371*1b8adde7SWilliam Kucharski         }
372*1b8adde7SWilliam Kucharski     }
373*1b8adde7SWilliam Kucharski }
374*1b8adde7SWilliam Kucharski #endif
375*1b8adde7SWilliam Kucharski 
376*1b8adde7SWilliam Kucharski /**************************************************************************
377*1b8adde7SWilliam Kucharski w89c840_poll - Wait for a frame
378*1b8adde7SWilliam Kucharski ***************************************************************************/
w89c840_poll(struct nic * nic,int retrieve)379*1b8adde7SWilliam Kucharski static int w89c840_poll(struct nic *nic, int retrieve)
380*1b8adde7SWilliam Kucharski {
381*1b8adde7SWilliam Kucharski     /* return true if there's an ethernet packet ready to read */
382*1b8adde7SWilliam Kucharski     /* nic->packet should contain data on return */
383*1b8adde7SWilliam Kucharski     /* nic->packetlen should contain length of data */
384*1b8adde7SWilliam Kucharski     int packet_received = 0;
385*1b8adde7SWilliam Kucharski 
386*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
387*1b8adde7SWilliam Kucharski     u32 intr_status = readl(ioaddr + IntrStatus);
388*1b8adde7SWilliam Kucharski #endif
389*1b8adde7SWilliam Kucharski 
390*1b8adde7SWilliam Kucharski     do {
391*1b8adde7SWilliam Kucharski         /* Code from netdev_rx(dev) */
392*1b8adde7SWilliam Kucharski 
393*1b8adde7SWilliam Kucharski         int entry = w840private.cur_rx % RX_RING_SIZE;
394*1b8adde7SWilliam Kucharski 
395*1b8adde7SWilliam Kucharski         struct w840_rx_desc *desc = w840private.rx_head_desc;
396*1b8adde7SWilliam Kucharski         s32 status = desc->status;
397*1b8adde7SWilliam Kucharski 
398*1b8adde7SWilliam Kucharski         if (status & DescOwn) {
399*1b8adde7SWilliam Kucharski             /* DescOwn bit is still set, we should wait for RX to complete */
400*1b8adde7SWilliam Kucharski             packet_received = 0;
401*1b8adde7SWilliam Kucharski             break;
402*1b8adde7SWilliam Kucharski         }
403*1b8adde7SWilliam Kucharski 
404*1b8adde7SWilliam Kucharski         if ( !retrieve ) {
405*1b8adde7SWilliam Kucharski             packet_received = 1;
406*1b8adde7SWilliam Kucharski             break;
407*1b8adde7SWilliam Kucharski         }
408*1b8adde7SWilliam Kucharski 
409*1b8adde7SWilliam Kucharski         if ((status & 0x38008300) != 0x0300) {
410*1b8adde7SWilliam Kucharski             if ((status & 0x38000300) != 0x0300) {
411*1b8adde7SWilliam Kucharski                 /* Ingore earlier buffers. */
412*1b8adde7SWilliam Kucharski                 if ((status & 0xffff) != 0x7fff) {
413*1b8adde7SWilliam Kucharski                     printf("winbond-840 : Oversized Ethernet frame spanned "
414*1b8adde7SWilliam Kucharski                            "multiple buffers, entry %d status %X !\n",
415*1b8adde7SWilliam Kucharski                            w840private.cur_rx, status);
416*1b8adde7SWilliam Kucharski                 }
417*1b8adde7SWilliam Kucharski             } else if (status & 0x8000) {
418*1b8adde7SWilliam Kucharski                 /* There was a fatal error. */
419*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
420*1b8adde7SWilliam Kucharski                 printf("winbond-840 : Receive error, Rx status %X :", status);
421*1b8adde7SWilliam Kucharski                 if (status & 0x0890) {
422*1b8adde7SWilliam Kucharski                     printf(" RXLEN_ERROR");
423*1b8adde7SWilliam Kucharski                 }
424*1b8adde7SWilliam Kucharski                 if (status & 0x004C) {
425*1b8adde7SWilliam Kucharski                     printf(", FRAME_ERROR");
426*1b8adde7SWilliam Kucharski                 }
427*1b8adde7SWilliam Kucharski                 if (status & 0x0002) {
428*1b8adde7SWilliam Kucharski                     printf(", CRC_ERROR");
429*1b8adde7SWilliam Kucharski                 }
430*1b8adde7SWilliam Kucharski                 printf("\n");
431*1b8adde7SWilliam Kucharski #endif
432*1b8adde7SWilliam Kucharski 
433*1b8adde7SWilliam Kucharski                 /* Simpy do a reset now... */
434*1b8adde7SWilliam Kucharski                 w89c840_reset(nic);
435*1b8adde7SWilliam Kucharski 
436*1b8adde7SWilliam Kucharski                 packet_received = 0;
437*1b8adde7SWilliam Kucharski                 break;
438*1b8adde7SWilliam Kucharski             }
439*1b8adde7SWilliam Kucharski         } else {
440*1b8adde7SWilliam Kucharski             /* Omit the four octet CRC from the length. */
441*1b8adde7SWilliam Kucharski             int pkt_len = ((status >> 16) & 0x7ff) - 4;
442*1b8adde7SWilliam Kucharski 
443*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
444*1b8adde7SWilliam Kucharski             printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
445*1b8adde7SWilliam Kucharski #endif
446*1b8adde7SWilliam Kucharski 
447*1b8adde7SWilliam Kucharski             nic->packetlen = pkt_len;
448*1b8adde7SWilliam Kucharski 
449*1b8adde7SWilliam Kucharski             /* Check if the packet is long enough to accept without copying
450*1b8adde7SWilliam Kucharski                to a minimally-sized skbuff. */
451*1b8adde7SWilliam Kucharski 
452*1b8adde7SWilliam Kucharski             memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
453*1b8adde7SWilliam Kucharski             packet_received = 1;
454*1b8adde7SWilliam Kucharski 
455*1b8adde7SWilliam Kucharski             /* Release buffer to NIC */
456*1b8adde7SWilliam Kucharski             w840private.rx_ring[entry].status = DescOwn;
457*1b8adde7SWilliam Kucharski 
458*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
459*1b8adde7SWilliam Kucharski             /* You will want this info for the initial debug. */
460*1b8adde7SWilliam Kucharski             printf("  Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
461*1b8adde7SWilliam Kucharski                    "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
462*1b8adde7SWilliam Kucharski                    "%hhX.%hhX.%hhX.%hhX.\n",
463*1b8adde7SWilliam Kucharski                    nic->packet[0],  nic->packet[1],  nic->packet[2], nic->packet[3],
464*1b8adde7SWilliam Kucharski                    nic->packet[4],  nic->packet[5],  nic->packet[6], nic->packet[7],
465*1b8adde7SWilliam Kucharski                    nic->packet[8],  nic->packet[9],  nic->packet[10],
466*1b8adde7SWilliam Kucharski                    nic->packet[11], nic->packet[12], nic->packet[13],
467*1b8adde7SWilliam Kucharski                    nic->packet[14], nic->packet[15], nic->packet[16],
468*1b8adde7SWilliam Kucharski                    nic->packet[17]);
469*1b8adde7SWilliam Kucharski #endif
470*1b8adde7SWilliam Kucharski 
471*1b8adde7SWilliam Kucharski         }
472*1b8adde7SWilliam Kucharski 
473*1b8adde7SWilliam Kucharski         entry = (++w840private.cur_rx) % RX_RING_SIZE;
474*1b8adde7SWilliam Kucharski         w840private.rx_head_desc = &w840private.rx_ring[entry];
475*1b8adde7SWilliam Kucharski     } while (0);
476*1b8adde7SWilliam Kucharski 
477*1b8adde7SWilliam Kucharski     return packet_received;
478*1b8adde7SWilliam Kucharski }
479*1b8adde7SWilliam Kucharski 
480*1b8adde7SWilliam Kucharski /**************************************************************************
481*1b8adde7SWilliam Kucharski w89c840_transmit - Transmit a frame
482*1b8adde7SWilliam Kucharski ***************************************************************************/
483*1b8adde7SWilliam Kucharski 
w89c840_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)484*1b8adde7SWilliam Kucharski static void w89c840_transmit(
485*1b8adde7SWilliam Kucharski     struct nic *nic,
486*1b8adde7SWilliam Kucharski     const char *d,            /* Destination */
487*1b8adde7SWilliam Kucharski     unsigned int t,            /* Type */
488*1b8adde7SWilliam Kucharski     unsigned int s,            /* size */
489*1b8adde7SWilliam Kucharski     const char *p)            /* Packet */
490*1b8adde7SWilliam Kucharski {
491*1b8adde7SWilliam Kucharski     /* send the packet to destination */
492*1b8adde7SWilliam Kucharski     unsigned entry;
493*1b8adde7SWilliam Kucharski     int transmit_status;
494*1b8adde7SWilliam Kucharski 
495*1b8adde7SWilliam Kucharski     /* Caution: the write order is important here, set the field
496*1b8adde7SWilliam Kucharski        with the "ownership" bits last. */
497*1b8adde7SWilliam Kucharski 
498*1b8adde7SWilliam Kucharski     /* Fill in our transmit buffer */
499*1b8adde7SWilliam Kucharski     entry = w840private.cur_tx % TX_RING_SIZE;
500*1b8adde7SWilliam Kucharski 
501*1b8adde7SWilliam Kucharski     memcpy (tx_packet, d, ETH_ALEN);    /* dst */
502*1b8adde7SWilliam Kucharski     memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */
503*1b8adde7SWilliam Kucharski 
504*1b8adde7SWilliam Kucharski     *((char *) tx_packet + 12) = t >> 8;    /* type */
505*1b8adde7SWilliam Kucharski     *((char *) tx_packet + 13) = t;
506*1b8adde7SWilliam Kucharski 
507*1b8adde7SWilliam Kucharski     memcpy (tx_packet + ETH_HLEN, p, s);
508*1b8adde7SWilliam Kucharski     s += ETH_HLEN;
509*1b8adde7SWilliam Kucharski 
510*1b8adde7SWilliam Kucharski     while (s < ETH_ZLEN)
511*1b8adde7SWilliam Kucharski     *((char *) tx_packet + ETH_HLEN + (s++)) = 0;
512*1b8adde7SWilliam Kucharski 
513*1b8adde7SWilliam Kucharski     w840private.tx_ring[entry].buffer1 = virt_to_le32desc(tx_packet);
514*1b8adde7SWilliam Kucharski 
515*1b8adde7SWilliam Kucharski     w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
516*1b8adde7SWilliam Kucharski     if (entry >= TX_RING_SIZE-1)         /* Wrap ring */
517*1b8adde7SWilliam Kucharski         w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
518*1b8adde7SWilliam Kucharski     w840private.tx_ring[entry].status = (DescOwn);
519*1b8adde7SWilliam Kucharski     w840private.cur_tx++;
520*1b8adde7SWilliam Kucharski 
521*1b8adde7SWilliam Kucharski     w840private.tx_q_bytes = (u16) s;
522*1b8adde7SWilliam Kucharski     writel(0, ioaddr + TxStartDemand);
523*1b8adde7SWilliam Kucharski 
524*1b8adde7SWilliam Kucharski     /* Work around horrible bug in the chip by marking the queue as full
525*1b8adde7SWilliam Kucharski        when we do not have FIFO room for a maximum sized packet. */
526*1b8adde7SWilliam Kucharski 
527*1b8adde7SWilliam Kucharski     if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
528*1b8adde7SWilliam Kucharski         /* Actually this is left to help finding error tails later in debugging...
529*1b8adde7SWilliam Kucharski          * See Linux kernel driver in winbond-840.c for details.
530*1b8adde7SWilliam Kucharski          */
531*1b8adde7SWilliam Kucharski         w840private.tx_full = 1;
532*1b8adde7SWilliam Kucharski     }
533*1b8adde7SWilliam Kucharski 
534*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
535*1b8adde7SWilliam Kucharski     printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
536*1b8adde7SWilliam Kucharski #endif
537*1b8adde7SWilliam Kucharski 
538*1b8adde7SWilliam Kucharski     /* Now wait for TX to complete. */
539*1b8adde7SWilliam Kucharski     transmit_status = w840private.tx_ring[entry].status;
540*1b8adde7SWilliam Kucharski 
541*1b8adde7SWilliam Kucharski     load_timer2(TX_TIMEOUT);
542*1b8adde7SWilliam Kucharski 
543*1b8adde7SWilliam Kucharski     {
544*1b8adde7SWilliam Kucharski #if defined W89C840_DEBUG
545*1b8adde7SWilliam Kucharski         u32 intr_stat = 0;
546*1b8adde7SWilliam Kucharski #endif
547*1b8adde7SWilliam Kucharski         while (1) {
548*1b8adde7SWilliam Kucharski 
549*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
550*1b8adde7SWilliam Kucharski 	      decode_interrupt(intr_stat);
551*1b8adde7SWilliam Kucharski #endif
552*1b8adde7SWilliam Kucharski 
553*1b8adde7SWilliam Kucharski                 while ( (transmit_status & DescOwn) && timer2_running()) {
554*1b8adde7SWilliam Kucharski 
555*1b8adde7SWilliam Kucharski                     transmit_status = w840private.tx_ring[entry].status;
556*1b8adde7SWilliam Kucharski                 }
557*1b8adde7SWilliam Kucharski 
558*1b8adde7SWilliam Kucharski                 break;
559*1b8adde7SWilliam Kucharski         }
560*1b8adde7SWilliam Kucharski     }
561*1b8adde7SWilliam Kucharski 
562*1b8adde7SWilliam Kucharski     if ((transmit_status & DescOwn) == 0) {
563*1b8adde7SWilliam Kucharski 
564*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
565*1b8adde7SWilliam Kucharski         printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
566*1b8adde7SWilliam Kucharski                 w840private.tx_ring[entry].status);
567*1b8adde7SWilliam Kucharski #endif
568*1b8adde7SWilliam Kucharski 
569*1b8adde7SWilliam Kucharski         return;
570*1b8adde7SWilliam Kucharski     }
571*1b8adde7SWilliam Kucharski 
572*1b8adde7SWilliam Kucharski     /* Transmit timed out... */
573*1b8adde7SWilliam Kucharski 
574*1b8adde7SWilliam Kucharski     printf("winbond-840 : transmission TIMEOUT : status %X\n", w840private.tx_ring[entry].status);
575*1b8adde7SWilliam Kucharski 
576*1b8adde7SWilliam Kucharski     return;
577*1b8adde7SWilliam Kucharski }
578*1b8adde7SWilliam Kucharski 
579*1b8adde7SWilliam Kucharski /**************************************************************************
580*1b8adde7SWilliam Kucharski w89c840_disable - Turn off ethernet interface
581*1b8adde7SWilliam Kucharski ***************************************************************************/
w89c840_disable(struct dev * dev)582*1b8adde7SWilliam Kucharski static void w89c840_disable(struct dev *dev)
583*1b8adde7SWilliam Kucharski {
584*1b8adde7SWilliam Kucharski     struct nic *nic = (struct nic *)dev;
585*1b8adde7SWilliam Kucharski     /* merge reset and disable */
586*1b8adde7SWilliam Kucharski     w89c840_reset(nic);
587*1b8adde7SWilliam Kucharski 
588*1b8adde7SWilliam Kucharski     /* Don't know what to do to disable the board. Is this needed at all? */
589*1b8adde7SWilliam Kucharski     /* Yes, a live NIC can corrupt the loaded memory later [Ken] */
590*1b8adde7SWilliam Kucharski     /* Stop the chip's Tx and Rx processes. */
591*1b8adde7SWilliam Kucharski     writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
592*1b8adde7SWilliam Kucharski }
593*1b8adde7SWilliam Kucharski 
594*1b8adde7SWilliam Kucharski /**************************************************************************
595*1b8adde7SWilliam Kucharski w89c840_irq - Enable, Disable, or Force interrupts
596*1b8adde7SWilliam Kucharski ***************************************************************************/
w89c840_irq(struct nic * nic __unused,irq_action_t action __unused)597*1b8adde7SWilliam Kucharski static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
598*1b8adde7SWilliam Kucharski {
599*1b8adde7SWilliam Kucharski   switch ( action ) {
600*1b8adde7SWilliam Kucharski   case DISABLE :
601*1b8adde7SWilliam Kucharski     break;
602*1b8adde7SWilliam Kucharski   case ENABLE :
603*1b8adde7SWilliam Kucharski     break;
604*1b8adde7SWilliam Kucharski   case FORCE :
605*1b8adde7SWilliam Kucharski     break;
606*1b8adde7SWilliam Kucharski   }
607*1b8adde7SWilliam Kucharski }
608*1b8adde7SWilliam Kucharski 
609*1b8adde7SWilliam Kucharski /**************************************************************************
610*1b8adde7SWilliam Kucharski w89c840_probe - Look for an adapter, this routine's visible to the outside
611*1b8adde7SWilliam Kucharski ***************************************************************************/
w89c840_probe(struct dev * dev,struct pci_device * p)612*1b8adde7SWilliam Kucharski static int w89c840_probe(struct dev *dev, struct pci_device *p)
613*1b8adde7SWilliam Kucharski {
614*1b8adde7SWilliam Kucharski     struct nic *nic = (struct nic *)dev;
615*1b8adde7SWilliam Kucharski     u16 sum = 0;
616*1b8adde7SWilliam Kucharski     int i, j;
617*1b8adde7SWilliam Kucharski     unsigned short value;
618*1b8adde7SWilliam Kucharski 
619*1b8adde7SWilliam Kucharski     if (p->ioaddr == 0)
620*1b8adde7SWilliam Kucharski         return 0;
621*1b8adde7SWilliam Kucharski 
622*1b8adde7SWilliam Kucharski     ioaddr      = p->ioaddr;
623*1b8adde7SWilliam Kucharski     nic->ioaddr = p->ioaddr & ~3;
624*1b8adde7SWilliam Kucharski     nic->irqno  = 0;
625*1b8adde7SWilliam Kucharski 
626*1b8adde7SWilliam Kucharski 
627*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
628*1b8adde7SWilliam Kucharski     printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
629*1b8adde7SWilliam Kucharski #endif
630*1b8adde7SWilliam Kucharski 
631*1b8adde7SWilliam Kucharski     ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
632*1b8adde7SWilliam Kucharski 
633*1b8adde7SWilliam Kucharski     /* From Matt Hortman <mbhortman@acpthinclient.com> */
634*1b8adde7SWilliam Kucharski     if (p->vendor == PCI_VENDOR_ID_WINBOND2
635*1b8adde7SWilliam Kucharski         && p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) {
636*1b8adde7SWilliam Kucharski 
637*1b8adde7SWilliam Kucharski         /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
638*1b8adde7SWilliam Kucharski 
639*1b8adde7SWilliam Kucharski     } else if ( p->vendor == PCI_VENDOR_ID_COMPEX
640*1b8adde7SWilliam Kucharski                 && p->dev_id == PCI_DEVICE_ID_COMPEX_RL100ATX) {
641*1b8adde7SWilliam Kucharski 
642*1b8adde7SWilliam Kucharski         /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
643*1b8adde7SWilliam Kucharski 
644*1b8adde7SWilliam Kucharski     } else {
645*1b8adde7SWilliam Kucharski         /* Gee, guess what? They missed again. */
646*1b8adde7SWilliam Kucharski         printf("device ID : %X - is not a Compex RL100ATX NIC.\n", p->dev_id);
647*1b8adde7SWilliam Kucharski         return 0;
648*1b8adde7SWilliam Kucharski     }
649*1b8adde7SWilliam Kucharski 
650*1b8adde7SWilliam Kucharski     printf(" %s\n", w89c840_version);
651*1b8adde7SWilliam Kucharski 
652*1b8adde7SWilliam Kucharski     adjust_pci_device(p);
653*1b8adde7SWilliam Kucharski 
654*1b8adde7SWilliam Kucharski     /* Ok. Got one. Read the eeprom. */
655*1b8adde7SWilliam Kucharski     for (j = 0, i = 0; i < 0x40; i++) {
656*1b8adde7SWilliam Kucharski         value = eeprom_read(ioaddr, i);
657*1b8adde7SWilliam Kucharski         eeprom[i] = value;
658*1b8adde7SWilliam Kucharski         sum += value;
659*1b8adde7SWilliam Kucharski     }
660*1b8adde7SWilliam Kucharski 
661*1b8adde7SWilliam Kucharski     for (i=0;i<ETH_ALEN;i++) {
662*1b8adde7SWilliam Kucharski         nic->node_addr[i] =  (eeprom[i/2] >> (8*(i&1))) & 0xff;
663*1b8adde7SWilliam Kucharski     }
664*1b8adde7SWilliam Kucharski     printf ("Ethernet addr: %!\n", nic->node_addr);
665*1b8adde7SWilliam Kucharski 
666*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
667*1b8adde7SWilliam Kucharski     printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
668*1b8adde7SWilliam Kucharski #endif
669*1b8adde7SWilliam Kucharski 
670*1b8adde7SWilliam Kucharski     /* Reset the chip to erase previous misconfiguration.
671*1b8adde7SWilliam Kucharski        No hold time required! */
672*1b8adde7SWilliam Kucharski     writel(0x00000001, ioaddr + PCIBusCfg);
673*1b8adde7SWilliam Kucharski 
674*1b8adde7SWilliam Kucharski     if (driver_flags & CanHaveMII) {
675*1b8adde7SWilliam Kucharski         int phy, phy_idx = 0;
676*1b8adde7SWilliam Kucharski         for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
677*1b8adde7SWilliam Kucharski             int mii_status = mdio_read(ioaddr, phy, 1);
678*1b8adde7SWilliam Kucharski             if (mii_status != 0xffff  &&  mii_status != 0x0000) {
679*1b8adde7SWilliam Kucharski                 w840private.phys[phy_idx++] = phy;
680*1b8adde7SWilliam Kucharski                 w840private.advertising = mdio_read(ioaddr, phy, 4);
681*1b8adde7SWilliam Kucharski 
682*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
683*1b8adde7SWilliam Kucharski                 printf("winbond-840 : MII PHY found at address %d, status "
684*1b8adde7SWilliam Kucharski                        "%X advertising %hX.\n", phy, mii_status, w840private.advertising);
685*1b8adde7SWilliam Kucharski #endif
686*1b8adde7SWilliam Kucharski 
687*1b8adde7SWilliam Kucharski             }
688*1b8adde7SWilliam Kucharski         }
689*1b8adde7SWilliam Kucharski 
690*1b8adde7SWilliam Kucharski         w840private.mii_cnt = phy_idx;
691*1b8adde7SWilliam Kucharski 
692*1b8adde7SWilliam Kucharski         if (phy_idx == 0) {
693*1b8adde7SWilliam Kucharski                 printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
694*1b8adde7SWilliam Kucharski         }
695*1b8adde7SWilliam Kucharski     }
696*1b8adde7SWilliam Kucharski 
697*1b8adde7SWilliam Kucharski     /* point to NIC specific routines */
698*1b8adde7SWilliam Kucharski     dev->disable  = w89c840_disable;
699*1b8adde7SWilliam Kucharski     nic->poll     = w89c840_poll;
700*1b8adde7SWilliam Kucharski     nic->transmit = w89c840_transmit;
701*1b8adde7SWilliam Kucharski     nic->irq      = w89c840_irq;
702*1b8adde7SWilliam Kucharski 
703*1b8adde7SWilliam Kucharski     w89c840_reset(nic);
704*1b8adde7SWilliam Kucharski 
705*1b8adde7SWilliam Kucharski     return 1;
706*1b8adde7SWilliam Kucharski }
707*1b8adde7SWilliam Kucharski 
708*1b8adde7SWilliam Kucharski /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are
709*1b8adde7SWilliam Kucharski    often serial bit streams generated by the host processor.
710*1b8adde7SWilliam Kucharski    The example below is for the common 93c46 EEPROM, 64 16 bit words. */
711*1b8adde7SWilliam Kucharski 
712*1b8adde7SWilliam Kucharski /* Delay between EEPROM clock transitions.
713*1b8adde7SWilliam Kucharski    No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
714*1b8adde7SWilliam Kucharski    a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
715*1b8adde7SWilliam Kucharski    made udelay() unreliable.
716*1b8adde7SWilliam Kucharski    The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
717*1b8adde7SWilliam Kucharski    depricated.
718*1b8adde7SWilliam Kucharski */
719*1b8adde7SWilliam Kucharski #define eeprom_delay(ee_addr)    readl(ee_addr)
720*1b8adde7SWilliam Kucharski 
721*1b8adde7SWilliam Kucharski enum EEPROM_Ctrl_Bits {
722*1b8adde7SWilliam Kucharski     EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
723*1b8adde7SWilliam Kucharski     EE_ChipSelect=0x801, EE_DataIn=0x08,
724*1b8adde7SWilliam Kucharski };
725*1b8adde7SWilliam Kucharski 
726*1b8adde7SWilliam Kucharski /* The EEPROM commands include the alway-set leading bit. */
727*1b8adde7SWilliam Kucharski enum EEPROM_Cmds {
728*1b8adde7SWilliam Kucharski     EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
729*1b8adde7SWilliam Kucharski };
730*1b8adde7SWilliam Kucharski 
eeprom_read(long addr,int location)731*1b8adde7SWilliam Kucharski static int eeprom_read(long addr, int location)
732*1b8adde7SWilliam Kucharski {
733*1b8adde7SWilliam Kucharski     int i;
734*1b8adde7SWilliam Kucharski     int retval = 0;
735*1b8adde7SWilliam Kucharski     int ee_addr = addr + EECtrl;
736*1b8adde7SWilliam Kucharski     int read_cmd = location | EE_ReadCmd;
737*1b8adde7SWilliam Kucharski     writel(EE_ChipSelect, ee_addr);
738*1b8adde7SWilliam Kucharski 
739*1b8adde7SWilliam Kucharski     /* Shift the read command bits out. */
740*1b8adde7SWilliam Kucharski     for (i = 10; i >= 0; i--) {
741*1b8adde7SWilliam Kucharski         short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
742*1b8adde7SWilliam Kucharski         writel(dataval, ee_addr);
743*1b8adde7SWilliam Kucharski         eeprom_delay(ee_addr);
744*1b8adde7SWilliam Kucharski         writel(dataval | EE_ShiftClk, ee_addr);
745*1b8adde7SWilliam Kucharski         eeprom_delay(ee_addr);
746*1b8adde7SWilliam Kucharski     }
747*1b8adde7SWilliam Kucharski     writel(EE_ChipSelect, ee_addr);
748*1b8adde7SWilliam Kucharski 
749*1b8adde7SWilliam Kucharski     for (i = 16; i > 0; i--) {
750*1b8adde7SWilliam Kucharski         writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
751*1b8adde7SWilliam Kucharski         eeprom_delay(ee_addr);
752*1b8adde7SWilliam Kucharski         retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
753*1b8adde7SWilliam Kucharski         writel(EE_ChipSelect, ee_addr);
754*1b8adde7SWilliam Kucharski         eeprom_delay(ee_addr);
755*1b8adde7SWilliam Kucharski     }
756*1b8adde7SWilliam Kucharski 
757*1b8adde7SWilliam Kucharski     /* Terminate the EEPROM access. */
758*1b8adde7SWilliam Kucharski     writel(0, ee_addr);
759*1b8adde7SWilliam Kucharski     return retval;
760*1b8adde7SWilliam Kucharski }
761*1b8adde7SWilliam Kucharski 
762*1b8adde7SWilliam Kucharski /*  MII transceiver control section.
763*1b8adde7SWilliam Kucharski     Read and write the MII registers using software-generated serial
764*1b8adde7SWilliam Kucharski     MDIO protocol.  See the MII specifications or DP83840A data sheet
765*1b8adde7SWilliam Kucharski     for details.
766*1b8adde7SWilliam Kucharski 
767*1b8adde7SWilliam Kucharski     The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
768*1b8adde7SWilliam Kucharski     met by back-to-back 33Mhz PCI cycles. */
769*1b8adde7SWilliam Kucharski #define mdio_delay(mdio_addr) readl(mdio_addr)
770*1b8adde7SWilliam Kucharski 
771*1b8adde7SWilliam Kucharski /* Set iff a MII transceiver on any interface requires mdio preamble.
772*1b8adde7SWilliam Kucharski    This only set with older tranceivers, so the extra
773*1b8adde7SWilliam Kucharski    code size of a per-interface flag is not worthwhile. */
774*1b8adde7SWilliam Kucharski static char mii_preamble_required = 1;
775*1b8adde7SWilliam Kucharski 
776*1b8adde7SWilliam Kucharski #define MDIO_WRITE0 (MDIO_EnbOutput)
777*1b8adde7SWilliam Kucharski #define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
778*1b8adde7SWilliam Kucharski 
779*1b8adde7SWilliam Kucharski /* Generate the preamble required for initial synchronization and
780*1b8adde7SWilliam Kucharski    a few older transceivers. */
mdio_sync(long mdio_addr)781*1b8adde7SWilliam Kucharski static void mdio_sync(long mdio_addr)
782*1b8adde7SWilliam Kucharski {
783*1b8adde7SWilliam Kucharski     int bits = 32;
784*1b8adde7SWilliam Kucharski 
785*1b8adde7SWilliam Kucharski     /* Establish sync by sending at least 32 logic ones. */
786*1b8adde7SWilliam Kucharski     while (--bits >= 0) {
787*1b8adde7SWilliam Kucharski         writel(MDIO_WRITE1, mdio_addr);
788*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
789*1b8adde7SWilliam Kucharski         writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
790*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
791*1b8adde7SWilliam Kucharski     }
792*1b8adde7SWilliam Kucharski }
793*1b8adde7SWilliam Kucharski 
mdio_read(int base_address,int phy_id,int location)794*1b8adde7SWilliam Kucharski static int mdio_read(int base_address, int phy_id, int location)
795*1b8adde7SWilliam Kucharski {
796*1b8adde7SWilliam Kucharski     long mdio_addr = base_address + MIICtrl;
797*1b8adde7SWilliam Kucharski     int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
798*1b8adde7SWilliam Kucharski     int i, retval = 0;
799*1b8adde7SWilliam Kucharski 
800*1b8adde7SWilliam Kucharski     if (mii_preamble_required)
801*1b8adde7SWilliam Kucharski         mdio_sync(mdio_addr);
802*1b8adde7SWilliam Kucharski 
803*1b8adde7SWilliam Kucharski     /* Shift the read command bits out. */
804*1b8adde7SWilliam Kucharski     for (i = 15; i >= 0; i--) {
805*1b8adde7SWilliam Kucharski         int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
806*1b8adde7SWilliam Kucharski 
807*1b8adde7SWilliam Kucharski         writel(dataval, mdio_addr);
808*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
809*1b8adde7SWilliam Kucharski         writel(dataval | MDIO_ShiftClk, mdio_addr);
810*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
811*1b8adde7SWilliam Kucharski     }
812*1b8adde7SWilliam Kucharski     /* Read the two transition, 16 data, and wire-idle bits. */
813*1b8adde7SWilliam Kucharski     for (i = 20; i > 0; i--) {
814*1b8adde7SWilliam Kucharski         writel(MDIO_EnbIn, mdio_addr);
815*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
816*1b8adde7SWilliam Kucharski         retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
817*1b8adde7SWilliam Kucharski         writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
818*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
819*1b8adde7SWilliam Kucharski     }
820*1b8adde7SWilliam Kucharski     return (retval>>1) & 0xffff;
821*1b8adde7SWilliam Kucharski }
822*1b8adde7SWilliam Kucharski 
823*1b8adde7SWilliam Kucharski #if 0
824*1b8adde7SWilliam Kucharski static void mdio_write(int base_address, int phy_id, int location, int value)
825*1b8adde7SWilliam Kucharski {
826*1b8adde7SWilliam Kucharski     long mdio_addr = base_address + MIICtrl;
827*1b8adde7SWilliam Kucharski     int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
828*1b8adde7SWilliam Kucharski     int i;
829*1b8adde7SWilliam Kucharski 
830*1b8adde7SWilliam Kucharski     if (location == 4  &&  phy_id == w840private.phys[0])
831*1b8adde7SWilliam Kucharski         w840private.advertising = value;
832*1b8adde7SWilliam Kucharski 
833*1b8adde7SWilliam Kucharski     if (mii_preamble_required)
834*1b8adde7SWilliam Kucharski         mdio_sync(mdio_addr);
835*1b8adde7SWilliam Kucharski 
836*1b8adde7SWilliam Kucharski     /* Shift the command bits out. */
837*1b8adde7SWilliam Kucharski     for (i = 31; i >= 0; i--) {
838*1b8adde7SWilliam Kucharski         int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
839*1b8adde7SWilliam Kucharski 
840*1b8adde7SWilliam Kucharski         writel(dataval, mdio_addr);
841*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
842*1b8adde7SWilliam Kucharski         writel(dataval | MDIO_ShiftClk, mdio_addr);
843*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
844*1b8adde7SWilliam Kucharski     }
845*1b8adde7SWilliam Kucharski     /* Clear out extra bits. */
846*1b8adde7SWilliam Kucharski     for (i = 2; i > 0; i--) {
847*1b8adde7SWilliam Kucharski         writel(MDIO_EnbIn, mdio_addr);
848*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
849*1b8adde7SWilliam Kucharski         writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
850*1b8adde7SWilliam Kucharski         mdio_delay(mdio_addr);
851*1b8adde7SWilliam Kucharski     }
852*1b8adde7SWilliam Kucharski     return;
853*1b8adde7SWilliam Kucharski }
854*1b8adde7SWilliam Kucharski #endif
855*1b8adde7SWilliam Kucharski 
check_duplex(void)856*1b8adde7SWilliam Kucharski static void check_duplex(void)
857*1b8adde7SWilliam Kucharski {
858*1b8adde7SWilliam Kucharski     int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
859*1b8adde7SWilliam Kucharski     int negotiated =  mii_reg5 & w840private.advertising;
860*1b8adde7SWilliam Kucharski     int duplex;
861*1b8adde7SWilliam Kucharski 
862*1b8adde7SWilliam Kucharski     if (w840private.duplex_lock  ||  mii_reg5 == 0xffff)
863*1b8adde7SWilliam Kucharski         return;
864*1b8adde7SWilliam Kucharski 
865*1b8adde7SWilliam Kucharski     duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
866*1b8adde7SWilliam Kucharski     if (w840private.full_duplex != duplex) {
867*1b8adde7SWilliam Kucharski         w840private.full_duplex = duplex;
868*1b8adde7SWilliam Kucharski 
869*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
870*1b8adde7SWilliam Kucharski         printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
871*1b8adde7SWilliam Kucharski                duplex ? "full" : "half", w840private.phys[0], negotiated);
872*1b8adde7SWilliam Kucharski #endif
873*1b8adde7SWilliam Kucharski 
874*1b8adde7SWilliam Kucharski         w840private.csr6 &= ~0x200;
875*1b8adde7SWilliam Kucharski         w840private.csr6 |= duplex ? 0x200 : 0;
876*1b8adde7SWilliam Kucharski     }
877*1b8adde7SWilliam Kucharski }
878*1b8adde7SWilliam Kucharski 
set_rx_mode(void)879*1b8adde7SWilliam Kucharski static void set_rx_mode(void)
880*1b8adde7SWilliam Kucharski {
881*1b8adde7SWilliam Kucharski     u32 mc_filter[2];            /* Multicast hash filter */
882*1b8adde7SWilliam Kucharski     u32 rx_mode;
883*1b8adde7SWilliam Kucharski 
884*1b8adde7SWilliam Kucharski     /* Accept all multicasts from now on. */
885*1b8adde7SWilliam Kucharski     memset(mc_filter, 0xff, sizeof(mc_filter));
886*1b8adde7SWilliam Kucharski 
887*1b8adde7SWilliam Kucharski /*
888*1b8adde7SWilliam Kucharski  * works OK with multicast enabled.
889*1b8adde7SWilliam Kucharski  */
890*1b8adde7SWilliam Kucharski 
891*1b8adde7SWilliam Kucharski     rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
892*1b8adde7SWilliam Kucharski 
893*1b8adde7SWilliam Kucharski     writel(mc_filter[0], ioaddr + MulticastFilter0);
894*1b8adde7SWilliam Kucharski     writel(mc_filter[1], ioaddr + MulticastFilter1);
895*1b8adde7SWilliam Kucharski     w840private.csr6 &= ~0x00F8;
896*1b8adde7SWilliam Kucharski     w840private.csr6 |= rx_mode;
897*1b8adde7SWilliam Kucharski     writel(w840private.csr6, ioaddr + NetworkConfig);
898*1b8adde7SWilliam Kucharski 
899*1b8adde7SWilliam Kucharski #if defined(W89C840_DEBUG)
900*1b8adde7SWilliam Kucharski     printf("winbond-840 : Done setting RX mode.\n");
901*1b8adde7SWilliam Kucharski #endif
902*1b8adde7SWilliam Kucharski }
903*1b8adde7SWilliam Kucharski 
904*1b8adde7SWilliam Kucharski /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
init_ring(void)905*1b8adde7SWilliam Kucharski static void init_ring(void)
906*1b8adde7SWilliam Kucharski {
907*1b8adde7SWilliam Kucharski     int i;
908*1b8adde7SWilliam Kucharski     char * p;
909*1b8adde7SWilliam Kucharski 
910*1b8adde7SWilliam Kucharski     w840private.tx_full = 0;
911*1b8adde7SWilliam Kucharski     w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
912*1b8adde7SWilliam Kucharski     w840private.dirty_rx = w840private.dirty_tx = 0;
913*1b8adde7SWilliam Kucharski 
914*1b8adde7SWilliam Kucharski     w840private.rx_buf_sz = PKT_BUF_SZ;
915*1b8adde7SWilliam Kucharski     w840private.rx_head_desc = &w840private.rx_ring[0];
916*1b8adde7SWilliam Kucharski 
917*1b8adde7SWilliam Kucharski     /* Initial all Rx descriptors. Fill in the Rx buffers. */
918*1b8adde7SWilliam Kucharski 
919*1b8adde7SWilliam Kucharski     p = &rx_packet[0];
920*1b8adde7SWilliam Kucharski 
921*1b8adde7SWilliam Kucharski     for (i = 0; i < RX_RING_SIZE; i++) {
922*1b8adde7SWilliam Kucharski         w840private.rx_ring[i].length = w840private.rx_buf_sz;
923*1b8adde7SWilliam Kucharski         w840private.rx_ring[i].status = 0;
924*1b8adde7SWilliam Kucharski         w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
925*1b8adde7SWilliam Kucharski 
926*1b8adde7SWilliam Kucharski         w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
927*1b8adde7SWilliam Kucharski         w840private.rx_ring[i].status = DescOwn | DescIntr;
928*1b8adde7SWilliam Kucharski     }
929*1b8adde7SWilliam Kucharski 
930*1b8adde7SWilliam Kucharski     /* Mark the last entry as wrapping the ring. */
931*1b8adde7SWilliam Kucharski     w840private.rx_ring[i-1].length |= DescEndRing;
932*1b8adde7SWilliam Kucharski     w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
933*1b8adde7SWilliam Kucharski 
934*1b8adde7SWilliam Kucharski     w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
935*1b8adde7SWilliam Kucharski 
936*1b8adde7SWilliam Kucharski     for (i = 0; i < TX_RING_SIZE; i++) {
937*1b8adde7SWilliam Kucharski         w840private.tx_ring[i].status = 0;
938*1b8adde7SWilliam Kucharski     }
939*1b8adde7SWilliam Kucharski     return;
940*1b8adde7SWilliam Kucharski }
941*1b8adde7SWilliam Kucharski 
942*1b8adde7SWilliam Kucharski 
943*1b8adde7SWilliam Kucharski static struct pci_id w89c840_nics[] = {
944*1b8adde7SWilliam Kucharski PCI_ROM(0x1050, 0x0840, "winbond840",     "Winbond W89C840F"),
945*1b8adde7SWilliam Kucharski PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
946*1b8adde7SWilliam Kucharski };
947*1b8adde7SWilliam Kucharski 
948*1b8adde7SWilliam Kucharski struct pci_driver w89c840_driver = {
949*1b8adde7SWilliam Kucharski 	.type     = NIC_DRIVER,
950*1b8adde7SWilliam Kucharski 	.name     = "W89C840F",
951*1b8adde7SWilliam Kucharski 	.probe    = w89c840_probe,
952*1b8adde7SWilliam Kucharski 	.ids      = w89c840_nics,
953*1b8adde7SWilliam Kucharski 	.id_count = sizeof(w89c840_nics)/sizeof(w89c840_nics[0]),
954*1b8adde7SWilliam Kucharski 	.class    = 0,
955*1b8adde7SWilliam Kucharski };
956