[e16e8f2] | 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 | */ |
---|