w89c840.c

Go to the documentation of this file.
00001 /*
00002  * Etherboot -  BOOTP/TFTP Bootstrap Program
00003  *
00004  * w89c840.c -- This file implements the winbond-840 driver for etherboot.
00005  *
00006  */
00007 
00008 /*
00009  * Adapted by Igor V. Kovalenko
00010  *  -- <garrison@mail.ru>
00011  *   OR
00012  *  -- <iko@crec.mipt.ru>
00013  * Initial adaptaion stage, including testing, completed 23 August 2000.
00014  */
00015 
00016 /*
00017  * This program is free software; you can redistribute it and/or
00018  * modify it under the terms of the GNU General Public License as
00019  * published by the Free Software Foundation; either version 2, or (at
00020  * your option) any later version.
00021  *
00022  * This program is distributed in the hope that it will be useful, but
00023  * WITHOUT ANY WARRANTY; without even the implied warranty of
00024  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00025  * General Public License for more details.
00026  *
00027  * You should have received a copy of the GNU General Public License
00028  * along with this program; if not, write to the Free Software
00029  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00030  */
00031 
00032 FILE_LICENCE ( GPL2_OR_LATER );
00033 
00034 /*
00035  *              date       version  by   what
00036  *  Written:    Aug 20 2000  V0.10  iko  Initial revision.
00037  * changes:     Aug 22 2000  V0.90  iko  Works!
00038  *              Aug 23 2000  V0.91  iko  Cleanup, posted to etherboot
00039  *                                       maintainer.
00040  *              Aug 26 2000  V0.92  iko  Fixed Rx ring handling.
00041  *                                       First Linux Kernel (TM)
00042  *                                       successfully loaded using
00043  *                                       this driver.
00044  *              Jan 07 2001  V0.93  iko  Transmitter timeouts are handled
00045  *                                       using timer2 routines. Proposed
00046  *                                       by Ken Yap to eliminate CPU speed
00047  *                                       dependency.
00048  *             Dec 12 2003  V0.94   timlegge    Fixed issues in 5.2, removed 
00049  *                                              interrupt usage, enabled
00050  *                                              multicast support
00051  *
00052  * This is the etherboot driver for cards based on Winbond W89c840F chip.
00053  *
00054  * It was written from skeleton source, with Donald Becker's winbond-840.c
00055  * kernel driver as a guideline. Mostly the w89c840 related definitions
00056  * and the lower level routines have been cut-and-pasted into this source.
00057  *
00058  * Frankly speaking, about 90% of the code was obtained using cut'n'paste
00059  * sequence :) while the remainder appeared while brainstorming
00060  * Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
00061  *
00062  * There was a demand for using this card in a rather large
00063  * remote boot environment at MSKP OVTI Lab of
00064  * Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
00065  * so you may count that for motivation.
00066  *
00067  */
00068 
00069 /*
00070  * If you want to see debugging output then define W89C840_DEBUG
00071  */
00072 
00073 /*
00074 #define W89C840_DEBUG
00075 */
00076 
00077 /*
00078  * Keep using IO_OPS for Etherboot driver!
00079  */
00080 #define USE_IO_OPS
00081 
00082 #include "etherboot.h"
00083 #include "nic.h"
00084 #include <gpxe/pci.h>
00085 #include <gpxe/ethernet.h>
00086 
00087 static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
00088 
00089 /* Linux support functions */
00090 #define virt_to_le32desc(addr)  virt_to_bus(addr)
00091 #define le32desc_to_virt(addr)  bus_to_virt(addr)
00092 
00093 /*
00094 #define cpu_to_le32(val) (val)
00095 #define le32_to_cpu(val) (val)
00096 */
00097 
00098 /* Operational parameters that are set at compile time. */
00099 
00100 /* Keep the ring sizes a power of two for compile efficiency.
00101    The compiler will convert <unsigned>'%'<2^N> into a bit mask.
00102    Making the Tx ring too large decreases the effectiveness of channel
00103    bonding and packet priority.
00104    There are no ill effects from too-large receive rings. */
00105 #define TX_RING_SIZE    2
00106 #define RX_RING_SIZE    2
00107 
00108 /* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
00109    To avoid overflowing we don't queue again until we have room for a
00110    full-size packet.
00111  */
00112 #define TX_FIFO_SIZE (2048)
00113 #define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
00114 
00115 /* Operational parameters that usually are not changed. */
00116 /* Time in jiffies before concluding the transmitter is hung. */
00117 #define TX_TIMEOUT  (10*1000)
00118 
00119 #define PKT_BUF_SZ  1536  /* Size of each temporary Rx buffer.*/
00120 
00121 /*
00122  * Used to be this much CPU loops on Celeron@400 (?),
00123  * now using real timer and TX_TIMEOUT!
00124  * #define TX_LOOP_COUNT 10000000
00125  */
00126 
00127 #if !defined(__OPTIMIZE__)
00128 #warning  You must compile this file with the correct options!
00129 #warning  See the last lines of the source file.
00130 #error You must compile this driver with "-O".
00131 #endif
00132 
00133 enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
00134 
00135 #ifdef USE_IO_OPS
00136 #define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
00137 #else
00138 #define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
00139 #endif
00140 
00141 static u32 driver_flags = CanHaveMII | HasBrokenTx;
00142 
00143 /* This driver was written to use PCI memory space, however some x86 systems
00144    work only with I/O space accesses.  Pass -DUSE_IO_OPS to use PCI I/O space
00145    accesses instead of memory space. */
00146 
00147 #ifdef USE_IO_OPS
00148 #undef readb
00149 #undef readw
00150 #undef readl
00151 #undef writeb
00152 #undef writew
00153 #undef writel
00154 #define readb inb
00155 #define readw inw
00156 #define readl inl
00157 #define writeb outb
00158 #define writew outw
00159 #define writel outl
00160 #endif
00161 
00162 /* Offsets to the Command and Status Registers, "CSRs".
00163    While similar to the Tulip, these registers are longword aligned.
00164    Note: It's not useful to define symbolic names for every register bit in
00165    the device.  The name can only partially document the semantics and make
00166    the driver longer and more difficult to read.
00167 */
00168 enum w840_offsets {
00169     PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
00170     RxRingPtr=0x0C, TxRingPtr=0x10,
00171     IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
00172     RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
00173     CurRxDescAddr=0x30, CurRxBufAddr=0x34,            /* Debug use */
00174     MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
00175     CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
00176 };
00177 
00178 /* Bits in the interrupt status/enable registers. */
00179 /* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
00180 enum intr_status_bits {
00181     NormalIntr=0x10000, AbnormalIntr=0x8000,
00182     IntrPCIErr=0x2000, TimerInt=0x800,
00183     IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
00184     TxFIFOUnderflow=0x20, RxErrIntr=0x10,
00185     TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
00186 };
00187 
00188 /* Bits in the NetworkConfig register. */
00189 enum rx_mode_bits {
00190     AcceptErr=0x80, AcceptRunt=0x40,
00191     AcceptBroadcast=0x20, AcceptMulticast=0x10,
00192     AcceptAllPhys=0x08, AcceptMyPhys=0x02,
00193 };
00194 
00195 enum mii_reg_bits {
00196     MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
00197     MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
00198 };
00199 
00200 /* The Tulip Rx and Tx buffer descriptors. */
00201 struct w840_rx_desc {
00202     s32 status;
00203     s32 length;
00204     u32 buffer1;
00205     u32 next_desc;
00206 };
00207 
00208 struct w840_tx_desc {
00209     s32 status;
00210     s32 length;
00211     u32 buffer1, buffer2;                /* We use only buffer 1.  */
00212 };
00213 
00214 /* Bits in network_desc.status */
00215 enum desc_status_bits {
00216     DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
00217     DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
00218     DescIntr=0x80000000,
00219 };
00220 #define PRIV_ALIGN    15     /* Required alignment mask */
00221 #define PRIV_ALIGN_BYTES 32
00222 
00223 static struct winbond_private
00224 {
00225     /* Descriptor rings first for alignment. */
00226     struct w840_rx_desc rx_ring[RX_RING_SIZE];
00227     struct w840_tx_desc tx_ring[TX_RING_SIZE];
00228     struct net_device *next_module;        /* Link for devices of this type. */
00229     void *priv_addr;                    /* Unaligned address for kfree */
00230     const char *product_name;
00231     /* Frequently used values: keep some adjacent for cache effect. */
00232     int chip_id, drv_flags;
00233     struct pci_dev *pci_dev;
00234     int csr6;
00235     struct w840_rx_desc *rx_head_desc;
00236     unsigned int cur_rx, dirty_rx;        /* Producer/consumer ring indices */
00237     unsigned int rx_buf_sz;                /* Based on MTU+slack. */
00238     unsigned int cur_tx, dirty_tx;
00239     int tx_q_bytes;
00240     unsigned int tx_full:1;                /* The Tx queue is full. */
00241     /* These values are keep track of the transceiver/media in use. */
00242     unsigned int full_duplex:1;            /* Full-duplex operation requested. */
00243     unsigned int duplex_lock:1;
00244     unsigned int medialock:1;            /* Do not sense media. */
00245     unsigned int default_port:4;        /* Last dev->if_port value. */
00246     /* MII transceiver section. */
00247     int mii_cnt;                        /* MII device addresses. */
00248     u16 advertising;                    /* NWay media advertisement */
00249     unsigned char phys[2];                /* MII device addresses. */
00250 } w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
00251 
00252 /* NIC specific static variables go here */
00253 
00254 static int ioaddr;
00255 static unsigned short eeprom [0x40];
00256 struct {
00257         char        rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
00258         char        tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
00259 } w89c840_buf __shared;
00260 
00261 static int  eeprom_read(long ioaddr, int location);
00262 static int  mdio_read(int base_address, int phy_id, int location);
00263 #if 0
00264 static void mdio_write(int base_address, int phy_id, int location, int value);
00265 #endif
00266 
00267 static void check_duplex(void);
00268 static void set_rx_mode(void);
00269 static void init_ring(void);
00270 
00271 #if defined(W89C840_DEBUG)
00272 static void decode_interrupt(u32 intr_status)
00273 {
00274     printf("Interrupt status: ");
00275 
00276 #define TRACE_INTR(_intr_) \
00277     if (intr_status & (_intr_)) { printf (" " #_intr_); }
00278 
00279     TRACE_INTR(NormalIntr);
00280     TRACE_INTR(AbnormalIntr);
00281     TRACE_INTR(IntrPCIErr);
00282     TRACE_INTR(TimerInt);
00283     TRACE_INTR(IntrRxDied);
00284     TRACE_INTR(RxNoBuf);
00285     TRACE_INTR(IntrRxDone);
00286     TRACE_INTR(TxFIFOUnderflow);
00287     TRACE_INTR(RxErrIntr);
00288     TRACE_INTR(TxIdle);
00289     TRACE_INTR(IntrTxStopped);
00290     TRACE_INTR(IntrTxDone);
00291 
00292     printf("\n");
00293     /*sleep(1);*/
00294 }
00295 #endif
00296 
00297 /**************************************************************************
00298 w89c840_reset - Reset adapter
00299 ***************************************************************************/
00300 static void w89c840_reset(struct nic *nic)
00301 {
00302     int i;
00303 
00304     /* Reset the chip to erase previous misconfiguration.
00305        No hold time required! */
00306     writel(0x00000001, ioaddr + PCIBusCfg);
00307 
00308     init_ring();
00309 
00310     writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
00311     writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
00312 
00313     for (i = 0; i < ETH_ALEN; i++)
00314         writeb(nic->node_addr[i], ioaddr + StationAddr + i);
00315 
00316     /* Initialize other registers. */
00317     /* Configure the PCI bus bursts and FIFO thresholds.
00318        486: Set 8 longword cache alignment, 8 longword burst.
00319        586: Set 16 longword cache alignment, no burst limit.
00320        Cache alignment bits 15:14         Burst length 13:8
00321         0000    <not allowed>         0000 align to cache    0800 8 longwords
00322         4000    8  longwords        0100 1 longword        1000 16 longwords
00323         8000    16 longwords        0200 2 longwords    2000 32 longwords
00324         C000    32  longwords        0400 4 longwords
00325        Wait the specified 50 PCI cycles after a reset by initializing
00326        Tx and Rx queues and the address filter list. */
00327 
00328     writel(0xE010, ioaddr + PCIBusCfg);
00329 
00330     writel(0, ioaddr + RxStartDemand);
00331     w840private.csr6 = 0x20022002;
00332     check_duplex();
00333     set_rx_mode();
00334 
00335     /* Do not enable the interrupts Etherboot doesn't need them */
00336 /*
00337     writel(0x1A0F5, ioaddr + IntrStatus);
00338     writel(0x1A0F5, ioaddr + IntrEnable);
00339 */
00340 #if defined(W89C840_DEBUG)
00341     printf("winbond-840 : Done reset.\n");
00342 #endif
00343 }
00344 
00345 #if 0
00346 static void handle_intr(u32 intr_stat)
00347 {
00348     if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
00349         /* we are polling, do not return now */
00350         /*return 0;*/
00351     } else {
00352         /* Acknowledge all of the current interrupt sources ASAP. */
00353         writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
00354     }
00355 
00356     if (intr_stat & AbnormalIntr) {
00357         /* There was an abnormal interrupt */
00358         printf("\n-=- Abnormal interrupt.\n");
00359 
00360 #if defined(W89C840_DEBUG)
00361         decode_interrupt(intr_stat);
00362 #endif
00363 
00364         if (intr_stat & RxNoBuf) {
00365             /* There was an interrupt */
00366             printf("-=- <=> No receive buffers available.\n");
00367             writel(0, ioaddr + RxStartDemand);
00368         }
00369     }
00370 }
00371 #endif
00372 
00373 /**************************************************************************
00374 w89c840_poll - Wait for a frame
00375 ***************************************************************************/
00376 static int w89c840_poll(struct nic *nic, int retrieve)
00377 {
00378     /* return true if there's an ethernet packet ready to read */
00379     /* nic->packet should contain data on return */
00380     /* nic->packetlen should contain length of data */
00381     int packet_received = 0;
00382 
00383 #if defined(W89C840_DEBUG)
00384     u32 intr_status = readl(ioaddr + IntrStatus);
00385 #endif
00386 
00387     do {
00388         /* Code from netdev_rx(dev) */
00389 
00390         int entry = w840private.cur_rx % RX_RING_SIZE;
00391 
00392         struct w840_rx_desc *desc = w840private.rx_head_desc;
00393         s32 status = desc->status;
00394 
00395         if (status & DescOwn) {
00396             /* DescOwn bit is still set, we should wait for RX to complete */
00397             packet_received = 0;
00398             break;
00399         }
00400 
00401         if ( !retrieve ) {
00402             packet_received = 1;
00403             break;
00404         }
00405 
00406         if ((status & 0x38008300) != 0x0300) {
00407             if ((status & 0x38000300) != 0x0300) {
00408                 /* Ingore earlier buffers. */
00409                 if ((status & 0xffff) != 0x7fff) {
00410                     printf("winbond-840 : Oversized Ethernet frame spanned "
00411                            "multiple buffers, entry %d status %X !\n",
00412                            w840private.cur_rx, (unsigned int) status);
00413                 }
00414             } else if (status & 0x8000) {
00415                 /* There was a fatal error. */
00416 #if defined(W89C840_DEBUG)
00417                 printf("winbond-840 : Receive error, Rx status %X :", status);
00418                 if (status & 0x0890) {
00419                     printf(" RXLEN_ERROR");
00420                 }
00421                 if (status & 0x004C) {
00422                     printf(", FRAME_ERROR");
00423                 }
00424                 if (status & 0x0002) {
00425                     printf(", CRC_ERROR");
00426                 }
00427                 printf("\n");
00428 #endif
00429 
00430                 /* Simpy do a reset now... */
00431                 w89c840_reset(nic);
00432 
00433                 packet_received = 0;
00434                 break;
00435             }
00436         } else {
00437             /* Omit the four octet CRC from the length. */
00438             int pkt_len = ((status >> 16) & 0x7ff) - 4;
00439 
00440 #if defined(W89C840_DEBUG)
00441             printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
00442 #endif
00443 
00444             nic->packetlen = pkt_len;
00445 
00446             /* Check if the packet is long enough to accept without copying
00447                to a minimally-sized skbuff. */
00448 
00449             memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
00450             packet_received = 1;
00451 
00452             /* Release buffer to NIC */
00453             w840private.rx_ring[entry].status = DescOwn;
00454 
00455 #if defined(W89C840_DEBUG)
00456             /* You will want this info for the initial debug. */
00457             printf("  Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
00458                    "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
00459                    "%hhX.%hhX.%hhX.%hhX.\n",
00460                    nic->packet[0],  nic->packet[1],  nic->packet[2], nic->packet[3],
00461                    nic->packet[4],  nic->packet[5],  nic->packet[6], nic->packet[7],
00462                    nic->packet[8],  nic->packet[9],  nic->packet[10],
00463                    nic->packet[11], nic->packet[12], nic->packet[13],
00464                    nic->packet[14], nic->packet[15], nic->packet[16],
00465                    nic->packet[17]);
00466 #endif
00467 
00468         }
00469 
00470         entry = (++w840private.cur_rx) % RX_RING_SIZE;
00471         w840private.rx_head_desc = &w840private.rx_ring[entry];
00472     } while (0);
00473     
00474     return packet_received;
00475 }
00476 
00477 /**************************************************************************
00478 w89c840_transmit - Transmit a frame
00479 ***************************************************************************/
00480 
00481 static void w89c840_transmit(
00482     struct nic *nic,
00483     const char *d,            /* Destination */
00484     unsigned int t,            /* Type */
00485     unsigned int s,            /* size */
00486     const char *p)            /* Packet */
00487 {
00488     /* send the packet to destination */
00489     unsigned entry;
00490     int transmit_status;
00491     unsigned long ct;
00492 
00493     /* Caution: the write order is important here, set the field
00494        with the "ownership" bits last. */
00495 
00496     /* Fill in our transmit buffer */
00497     entry = w840private.cur_tx % TX_RING_SIZE;
00498 
00499     memcpy (w89c840_buf.tx_packet, d, ETH_ALEN);    /* dst */
00500     memcpy (w89c840_buf.tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/*src*/
00501 
00502     *((char *) w89c840_buf.tx_packet + 12) = t >> 8;    /* type */
00503     *((char *) w89c840_buf.tx_packet + 13) = t;
00504 
00505     memcpy (w89c840_buf.tx_packet + ETH_HLEN, p, s);
00506     s += ETH_HLEN;
00507 
00508     while (s < ETH_ZLEN)
00509     *((char *) w89c840_buf.tx_packet + ETH_HLEN + (s++)) = 0;
00510 
00511     w840private.tx_ring[entry].buffer1
00512             = virt_to_le32desc(w89c840_buf.tx_packet);
00513 
00514     w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
00515     if (entry >= TX_RING_SIZE-1)         /* Wrap ring */
00516         w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
00517     w840private.tx_ring[entry].status = (DescOwn);
00518     w840private.cur_tx++;
00519 
00520     w840private.tx_q_bytes = (u16) s;
00521     writel(0, ioaddr + TxStartDemand);
00522 
00523     /* Work around horrible bug in the chip by marking the queue as full
00524        when we do not have FIFO room for a maximum sized packet. */
00525 
00526     if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
00527         /* Actually this is left to help finding error tails later in debugging...
00528          * See Linux kernel driver in winbond-840.c for details.
00529          */
00530         w840private.tx_full = 1;
00531     }
00532 
00533 #if defined(W89C840_DEBUG)
00534     printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
00535 #endif
00536 
00537     /* Now wait for TX to complete. */
00538     transmit_status = w840private.tx_ring[entry].status;
00539 
00540     ct = currticks();
00541     {
00542 #if defined W89C840_DEBUG
00543         u32 intr_stat = 0;
00544 #endif
00545         while (1) {
00546 
00547 #if defined(W89C840_DEBUG)
00548               decode_interrupt(intr_stat);
00549 #endif
00550 
00551                 while ( (transmit_status & DescOwn) && ct + TX_TIMEOUT < currticks()) {
00552 
00553                     transmit_status = w840private.tx_ring[entry].status;
00554                 }
00555 
00556                 break;
00557         }
00558     }
00559 
00560     if ((transmit_status & DescOwn) == 0) {
00561 
00562 #if defined(W89C840_DEBUG)
00563         printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
00564                 w840private.tx_ring[entry].status);
00565 #endif
00566 
00567         return;
00568     }
00569 
00570     /* Transmit timed out... */
00571 
00572     printf("winbond-840 : transmission TIMEOUT : status %X\n", 
00573            (unsigned int) w840private.tx_ring[entry].status);
00574 
00575     return;
00576 }
00577 
00578 /**************************************************************************
00579 w89c840_disable - Turn off ethernet interface
00580 ***************************************************************************/
00581 static void w89c840_disable ( struct nic *nic ) {
00582 
00583     w89c840_reset(nic);
00584 
00585     /* Don't know what to do to disable the board. Is this needed at all? */
00586     /* Yes, a live NIC can corrupt the loaded memory later [Ken] */
00587     /* Stop the chip's Tx and Rx processes. */
00588     writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
00589 }
00590 
00591 /**************************************************************************
00592 w89c840_irq - Enable, Disable, or Force interrupts
00593 ***************************************************************************/
00594 static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
00595 {
00596   switch ( action ) {
00597   case DISABLE :
00598     break;
00599   case ENABLE :
00600     break;
00601   case FORCE :
00602     break;
00603   }
00604 }
00605 
00606 static struct nic_operations w89c840_operations = {
00607         .connect        = dummy_connect,
00608         .poll           = w89c840_poll,
00609         .transmit       = w89c840_transmit,
00610         .irq            = w89c840_irq,
00611 
00612 };
00613 
00614 static struct pci_device_id w89c840_nics[] = {
00615 PCI_ROM(0x1050, 0x0840, "winbond840",     "Winbond W89C840F", 0),
00616 PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX", 0),
00617 };
00618 
00619 PCI_DRIVER ( w89c840_driver, w89c840_nics, PCI_NO_CLASS );
00620 
00621 /**************************************************************************
00622 w89c840_probe - Look for an adapter, this routine's visible to the outside
00623 ***************************************************************************/
00624 static int w89c840_probe ( struct nic *nic, struct pci_device *p ) {
00625 
00626 
00627     u16 sum = 0;
00628     int i, j;
00629     unsigned short value;
00630 
00631     if (p->ioaddr == 0)
00632         return 0;
00633 
00634     nic->ioaddr = p->ioaddr;
00635     nic->irqno  = 0;
00636 
00637 #if defined(W89C840_DEBUG)
00638     printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
00639 #endif
00640 
00641     ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
00642 
00643 #define PCI_DEVICE_ID_WINBOND2_89C840   0x0840
00644 #define PCI_DEVICE_ID_COMPEX_RL100ATX   0x2011
00645 
00646     /* From Matt Hortman <mbhortman@acpthinclient.com> */
00647     if (p->vendor == PCI_VENDOR_ID_WINBOND2
00648         && p->device == PCI_DEVICE_ID_WINBOND2_89C840) {
00649 
00650         /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
00651 
00652     } else if ( p->vendor == PCI_VENDOR_ID_COMPEX
00653                 && p->device == PCI_DEVICE_ID_COMPEX_RL100ATX) {
00654 
00655         /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
00656 
00657     } else {
00658         /* Gee, guess what? They missed again. */
00659         printf("device ID : %X - is not a Compex RL100ATX NIC.\n",
00660                p->device);
00661         return 0;
00662     }
00663 
00664     printf(" %s\n", w89c840_version);
00665 
00666     adjust_pci_device(p);
00667 
00668     /* Ok. Got one. Read the eeprom. */
00669     for (j = 0, i = 0; i < 0x40; i++) {
00670         value = eeprom_read(ioaddr, i);
00671         eeprom[i] = value;
00672         sum += value;
00673     }
00674 
00675     for (i=0;i<ETH_ALEN;i++) {
00676         nic->node_addr[i] =  (eeprom[i/2] >> (8*(i&1))) & 0xff;
00677     }
00678 
00679     DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) );
00680 
00681 #if defined(W89C840_DEBUG)
00682     printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
00683 #endif
00684 
00685     /* Reset the chip to erase previous misconfiguration.
00686        No hold time required! */
00687     writel(0x00000001, ioaddr + PCIBusCfg);
00688 
00689     if (driver_flags & CanHaveMII) {
00690         int phy, phy_idx = 0;
00691         for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
00692             int mii_status = mdio_read(ioaddr, phy, 1);
00693             if (mii_status != 0xffff  &&  mii_status != 0x0000) {
00694                 w840private.phys[phy_idx++] = phy;
00695                 w840private.advertising = mdio_read(ioaddr, phy, 4);
00696 
00697 #if defined(W89C840_DEBUG)
00698                 printf("winbond-840 : MII PHY found at address %d, status "
00699                        "%X advertising %hX.\n", phy, mii_status, w840private.advertising);
00700 #endif
00701 
00702             }
00703         }
00704 
00705         w840private.mii_cnt = phy_idx;
00706 
00707         if (phy_idx == 0) {
00708                 printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
00709         }
00710     }
00711 
00712     /* point to NIC specific routines */
00713     nic->nic_op = &w89c840_operations;
00714 
00715     w89c840_reset(nic);
00716 
00717     return 1;
00718 }
00719 
00720 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are
00721    often serial bit streams generated by the host processor.
00722    The example below is for the common 93c46 EEPROM, 64 16 bit words. */
00723 
00724 /* Delay between EEPROM clock transitions.
00725    No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
00726    a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
00727    made udelay() unreliable.
00728    The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
00729    depricated.
00730 */
00731 #define eeprom_delay(ee_addr)    readl(ee_addr)
00732 
00733 enum EEPROM_Ctrl_Bits {
00734     EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
00735     EE_ChipSelect=0x801, EE_DataIn=0x08,
00736 };
00737 
00738 /* The EEPROM commands include the alway-set leading bit. */
00739 enum EEPROM_Cmds {
00740     EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
00741 };
00742 
00743 static int eeprom_read(long addr, int location)
00744 {
00745     int i;
00746     int retval = 0;
00747     int ee_addr = addr + EECtrl;
00748     int read_cmd = location | EE_ReadCmd;
00749     writel(EE_ChipSelect, ee_addr);
00750 
00751     /* Shift the read command bits out. */
00752     for (i = 10; i >= 0; i--) {
00753         short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
00754         writel(dataval, ee_addr);
00755         eeprom_delay(ee_addr);
00756         writel(dataval | EE_ShiftClk, ee_addr);
00757         eeprom_delay(ee_addr);
00758     }
00759     writel(EE_ChipSelect, ee_addr);
00760 
00761     for (i = 16; i > 0; i--) {
00762         writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
00763         eeprom_delay(ee_addr);
00764         retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
00765         writel(EE_ChipSelect, ee_addr);
00766         eeprom_delay(ee_addr);
00767     }
00768 
00769     /* Terminate the EEPROM access. */
00770     writel(0, ee_addr);
00771     return retval;
00772 }
00773 
00774 /*  MII transceiver control section.
00775     Read and write the MII registers using software-generated serial
00776     MDIO protocol.  See the MII specifications or DP83840A data sheet
00777     for details.
00778 
00779     The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
00780     met by back-to-back 33Mhz PCI cycles. */
00781 #define mdio_delay(mdio_addr) readl(mdio_addr)
00782 
00783 /* Set iff a MII transceiver on any interface requires mdio preamble.
00784    This only set with older tranceivers, so the extra
00785    code size of a per-interface flag is not worthwhile. */
00786 static char mii_preamble_required = 1;
00787 
00788 #define MDIO_WRITE0 (MDIO_EnbOutput)
00789 #define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
00790 
00791 /* Generate the preamble required for initial synchronization and
00792    a few older transceivers. */
00793 static void mdio_sync(long mdio_addr)
00794 {
00795     int bits = 32;
00796 
00797     /* Establish sync by sending at least 32 logic ones. */
00798     while (--bits >= 0) {
00799         writel(MDIO_WRITE1, mdio_addr);
00800         mdio_delay(mdio_addr);
00801         writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
00802         mdio_delay(mdio_addr);
00803     }
00804 }
00805 
00806 static int mdio_read(int base_address, int phy_id, int location)
00807 {
00808     long mdio_addr = base_address + MIICtrl;
00809     int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
00810     int i, retval = 0;
00811 
00812     if (mii_preamble_required)
00813         mdio_sync(mdio_addr);
00814 
00815     /* Shift the read command bits out. */
00816     for (i = 15; i >= 0; i--) {
00817         int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
00818 
00819         writel(dataval, mdio_addr);
00820         mdio_delay(mdio_addr);
00821         writel(dataval | MDIO_ShiftClk, mdio_addr);
00822         mdio_delay(mdio_addr);
00823     }
00824     /* Read the two transition, 16 data, and wire-idle bits. */
00825     for (i = 20; i > 0; i--) {
00826         writel(MDIO_EnbIn, mdio_addr);
00827         mdio_delay(mdio_addr);
00828         retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
00829         writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
00830         mdio_delay(mdio_addr);
00831     }
00832     return (retval>>1) & 0xffff;
00833 }
00834 
00835 #if 0
00836 static void mdio_write(int base_address, int phy_id, int location, int value)
00837 {
00838     long mdio_addr = base_address + MIICtrl;
00839     int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
00840     int i;
00841 
00842     if (location == 4  &&  phy_id == w840private.phys[0])
00843         w840private.advertising = value;
00844 
00845     if (mii_preamble_required)
00846         mdio_sync(mdio_addr);
00847 
00848     /* Shift the command bits out. */
00849     for (i = 31; i >= 0; i--) {
00850         int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
00851 
00852         writel(dataval, mdio_addr);
00853         mdio_delay(mdio_addr);
00854         writel(dataval | MDIO_ShiftClk, mdio_addr);
00855         mdio_delay(mdio_addr);
00856     }
00857     /* Clear out extra bits. */
00858     for (i = 2; i > 0; i--) {
00859         writel(MDIO_EnbIn, mdio_addr);
00860         mdio_delay(mdio_addr);
00861         writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
00862         mdio_delay(mdio_addr);
00863     }
00864     return;
00865 }
00866 #endif
00867 
00868 static void check_duplex(void)
00869 {
00870     int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
00871     int negotiated =  mii_reg5 & w840private.advertising;
00872     int duplex;
00873 
00874     if (w840private.duplex_lock  ||  mii_reg5 == 0xffff)
00875         return;
00876 
00877     duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
00878     if (w840private.full_duplex != duplex) {
00879         w840private.full_duplex = duplex;       
00880 
00881 #if defined(W89C840_DEBUG)
00882         printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
00883                duplex ? "full" : "half", w840private.phys[0], negotiated);
00884 #endif
00885 
00886         w840private.csr6 &= ~0x200;
00887         w840private.csr6 |= duplex ? 0x200 : 0;
00888     }
00889 }
00890 
00891 static void set_rx_mode(void)
00892 {
00893     u32 mc_filter[2];            /* Multicast hash filter */
00894     u32 rx_mode;
00895 
00896     /* Accept all multicasts from now on. */
00897     memset(mc_filter, 0xff, sizeof(mc_filter));
00898 
00899 /*
00900  * works OK with multicast enabled. 
00901  */
00902 
00903     rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
00904 
00905     writel(mc_filter[0], ioaddr + MulticastFilter0);
00906     writel(mc_filter[1], ioaddr + MulticastFilter1);
00907     w840private.csr6 &= ~0x00F8;
00908     w840private.csr6 |= rx_mode;
00909     writel(w840private.csr6, ioaddr + NetworkConfig);
00910 
00911 #if defined(W89C840_DEBUG)
00912     printf("winbond-840 : Done setting RX mode.\n");
00913 #endif
00914 }
00915 
00916 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
00917 static void init_ring(void)
00918 {
00919     int i;
00920     char * p;
00921 
00922     w840private.tx_full = 0;
00923     w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
00924     w840private.dirty_rx = w840private.dirty_tx = 0;
00925 
00926     w840private.rx_buf_sz = PKT_BUF_SZ;
00927     w840private.rx_head_desc = &w840private.rx_ring[0];
00928 
00929     /* Initial all Rx descriptors. Fill in the Rx buffers. */
00930 
00931     p = &w89c840_buf.rx_packet[0];
00932 
00933     for (i = 0; i < RX_RING_SIZE; i++) {
00934         w840private.rx_ring[i].length = w840private.rx_buf_sz;
00935         w840private.rx_ring[i].status = 0;
00936         w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
00937 
00938         w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
00939         w840private.rx_ring[i].status = DescOwn | DescIntr;
00940     }
00941 
00942     /* Mark the last entry as wrapping the ring. */
00943     w840private.rx_ring[i-1].length |= DescEndRing;
00944     w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
00945 
00946     w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
00947 
00948     for (i = 0; i < TX_RING_SIZE; i++) {
00949         w840private.tx_ring[i].status = 0;
00950     }
00951     return;
00952 }
00953 
00954 
00955 DRIVER ( "W89C840F", nic_driver, pci_driver, w89c840_driver,
00956          w89c840_probe, w89c840_disable );
00957 
00958 /*
00959  * Local variables:
00960  *  c-basic-offset: 8
00961  *  c-indent-level: 8
00962  *  tab-width: 8
00963  * End:
00964  */

Generated on Tue Apr 6 20:01:06 2010 for gPXE by  doxygen 1.5.7.1