|
|
1.1 ! root 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: * -- <[email protected]> ! 11: * OR ! 12: * -- <[email protected]> ! 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 <ipxe/pci.h> ! 85: #include <ipxe/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; ! 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 <[email protected]> */ ! 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 (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: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.