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