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