1N/A/*
1N/A * Etherboot - BOOTP/TFTP Bootstrap Program
1N/A *
1N/A * w89c840.c -- This file implements the winbond-840 driver for etherboot.
1N/A *
1N/A */
1N/A
1N/A/*
1N/A * Adapted by Igor V. Kovalenko
1N/A * -- <garrison@mail.ru>
1N/A * OR
1N/A * -- <iko@crec.mipt.ru>
1N/A * Initial adaptaion stage, including testing, completed 23 August 2000.
1N/A */
1N/A
1N/A/*
1N/A * This program is free software; you can redistribute it and/or
1N/A * modify it under the terms of the GNU General Public License as
1N/A * published by the Free Software Foundation; either version 2, or (at
1N/A * your option) any later version.
1N/A *
1N/A * This program is distributed in the hope that it will be useful, but
1N/A * WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1N/A * General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program; if not, write to the Free Software
1N/A * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1N/A */
1N/A
1N/A/*
1N/A * date version by what
1N/A * Written: Aug 20 2000 V0.10 iko Initial revision.
1N/A * changes: Aug 22 2000 V0.90 iko Works!
1N/A * Aug 23 2000 V0.91 iko Cleanup, posted to etherboot
1N/A * maintainer.
1N/A * Aug 26 2000 V0.92 iko Fixed Rx ring handling.
1N/A * First Linux Kernel (TM)
1N/A * successfully loaded using
1N/A * this driver.
1N/A * Jan 07 2001 V0.93 iko Transmitter timeouts are handled
1N/A * using timer2 routines. Proposed
1N/A * by Ken Yap to eliminate CPU speed
1N/A * dependency.
1N/A * Dec 12 2003 V0.94 timlegge Fixed issues in 5.2, removed
1N/A * interrupt usage, enabled
1N/A * multicast support
1N/A *
1N/A * This is the etherboot driver for cards based on Winbond W89c840F chip.
1N/A *
1N/A * It was written from skeleton source, with Donald Becker's winbond-840.c
1N/A * kernel driver as a guideline. Mostly the w89c840 related definitions
1N/A * and the lower level routines have been cut-and-pasted into this source.
1N/A *
1N/A * Frankly speaking, about 90% of the code was obtained using cut'n'paste
1N/A * sequence :) while the remainder appeared while brainstorming
1N/A * Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
1N/A *
1N/A * There was a demand for using this card in a rather large
1N/A * remote boot environment at MSKP OVTI Lab of
1N/A * Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
1N/A * so you may count that for motivation.
1N/A *
1N/A */
1N/A
1N/A/*
1N/A * If you want to see debugging output then define W89C840_DEBUG
1N/A */
1N/A
1N/A/*
1N/A#define W89C840_DEBUG
1N/A*/
1N/A
1N/A/*
1N/A * Keep using IO_OPS for Etherboot driver!
1N/A */
1N/A#define USE_IO_OPS
1N/A
1N/A#include "etherboot.h"
1N/A#include "nic.h"
1N/A#include "pci.h"
1N/A#include "timer.h"
1N/A
1N/Astatic const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
1N/A
1N/Atypedef unsigned char u8;
1N/Atypedef signed char s8;
1N/Atypedef unsigned short u16;
1N/Atypedef signed short s16;
1N/Atypedef unsigned int u32;
1N/Atypedef signed int s32;
1N/A
1N/A/* Linux support functions */
1N/A#define virt_to_le32desc(addr) virt_to_bus(addr)
1N/A#define le32desc_to_virt(addr) bus_to_virt(addr)
1N/A
1N/A/*
1N/A#define cpu_to_le32(val) (val)
1N/A#define le32_to_cpu(val) (val)
1N/A*/
1N/A
1N/A/* Operational parameters that are set at compile time. */
1N/A
1N/A/* Keep the ring sizes a power of two for compile efficiency.
1N/A The compiler will convert <unsigned>'%'<2^N> into a bit mask.
1N/A Making the Tx ring too large decreases the effectiveness of channel
1N/A bonding and packet priority.
1N/A There are no ill effects from too-large receive rings. */
1N/A#define TX_RING_SIZE 2
1N/A#define RX_RING_SIZE 2
1N/A
1N/A/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
1N/A To avoid overflowing we don't queue again until we have room for a
1N/A full-size packet.
1N/A */
1N/A#define TX_FIFO_SIZE (2048)
1N/A#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
1N/A
1N/A/* Operational parameters that usually are not changed. */
1N/A/* Time in jiffies before concluding the transmitter is hung. */
1N/A#define TX_TIMEOUT (10*TICKS_PER_MS)
1N/A
1N/A#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
1N/A
1N/A/*
1N/A * Used to be this much CPU loops on Celeron@400 (?),
1N/A * now using real timer and TX_TIMEOUT!
1N/A * #define TX_LOOP_COUNT 10000000
1N/A */
1N/A
1N/A#if !defined(__OPTIMIZE__)
1N/A#warning You must compile this file with the correct options!
1N/A#warning See the last lines of the source file.
1N/A#error You must compile this driver with "-O".
1N/A#endif
1N/A
1N/Aenum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
1N/A
1N/A#ifdef USE_IO_OPS
1N/A#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
1N/A#else
1N/A#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
1N/A#endif
1N/A
1N/Astatic u32 driver_flags = CanHaveMII | HasBrokenTx;
1N/A
1N/A/* This driver was written to use PCI memory space, however some x86 systems
1N/A work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space
1N/A accesses instead of memory space. */
1N/A
1N/A#ifdef USE_IO_OPS
1N/A#undef readb
1N/A#undef readw
1N/A#undef readl
1N/A#undef writeb
1N/A#undef writew
1N/A#undef writel
1N/A#define readb inb
1N/A#define readw inw
1N/A#define readl inl
1N/A#define writeb outb
1N/A#define writew outw
1N/A#define writel outl
1N/A#endif
1N/A
1N/A/* Offsets to the Command and Status Registers, "CSRs".
1N/A While similar to the Tulip, these registers are longword aligned.
1N/A Note: It's not useful to define symbolic names for every register bit in
1N/A the device. The name can only partially document the semantics and make
1N/A the driver longer and more difficult to read.
1N/A*/
1N/Aenum w840_offsets {
1N/A PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
1N/A RxRingPtr=0x0C, TxRingPtr=0x10,
1N/A IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
1N/A RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
1N/A CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */
1N/A MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
1N/A CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
1N/A};
1N/A
1N/A/* Bits in the interrupt status/enable registers. */
1N/A/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
1N/Aenum intr_status_bits {
1N/A NormalIntr=0x10000, AbnormalIntr=0x8000,
1N/A IntrPCIErr=0x2000, TimerInt=0x800,
1N/A IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
1N/A TxFIFOUnderflow=0x20, RxErrIntr=0x10,
1N/A TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
1N/A};
1N/A
1N/A/* Bits in the NetworkConfig register. */
1N/Aenum rx_mode_bits {
1N/A AcceptErr=0x80, AcceptRunt=0x40,
1N/A AcceptBroadcast=0x20, AcceptMulticast=0x10,
1N/A AcceptAllPhys=0x08, AcceptMyPhys=0x02,
1N/A};
1N/A
1N/Aenum mii_reg_bits {
1N/A MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
1N/A MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
1N/A};
1N/A
1N/A/* The Tulip Rx and Tx buffer descriptors. */
1N/Astruct w840_rx_desc {
1N/A s32 status;
1N/A s32 length;
1N/A u32 buffer1;
1N/A u32 next_desc;
1N/A};
1N/A
1N/Astruct w840_tx_desc {
1N/A s32 status;
1N/A s32 length;
1N/A u32 buffer1, buffer2; /* We use only buffer 1. */
1N/A};
1N/A
1N/A/* Bits in network_desc.status */
1N/Aenum desc_status_bits {
1N/A DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
1N/A DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
1N/A DescIntr=0x80000000,
1N/A};
1N/A#define PRIV_ALIGN 15 /* Required alignment mask */
1N/A#define PRIV_ALIGN_BYTES 32
1N/A
1N/Astatic struct winbond_private
1N/A{
1N/A /* Descriptor rings first for alignment. */
1N/A struct w840_rx_desc rx_ring[RX_RING_SIZE];
1N/A struct w840_tx_desc tx_ring[TX_RING_SIZE];
1N/A struct net_device *next_module; /* Link for devices of this type. */
1N/A void *priv_addr; /* Unaligned address for kfree */
1N/A const char *product_name;
1N/A /* Frequently used values: keep some adjacent for cache effect. */
1N/A int chip_id, drv_flags;
1N/A struct pci_dev *pci_dev;
1N/A int csr6;
1N/A struct w840_rx_desc *rx_head_desc;
1N/A unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
1N/A unsigned int rx_buf_sz; /* Based on MTU+slack. */
1N/A unsigned int cur_tx, dirty_tx;
1N/A int tx_q_bytes;
1N/A unsigned int tx_full:1; /* The Tx queue is full. */
1N/A /* These values are keep track of the transceiver/media in use. */
1N/A unsigned int full_duplex:1; /* Full-duplex operation requested. */
1N/A unsigned int duplex_lock:1;
1N/A unsigned int medialock:1; /* Do not sense media. */
1N/A unsigned int default_port:4; /* Last dev->if_port value. */
1N/A /* MII transceiver section. */
1N/A int mii_cnt; /* MII device addresses. */
1N/A u16 advertising; /* NWay media advertisement */
1N/A unsigned char phys[2]; /* MII device addresses. */
1N/A} w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
1N/A
1N/A/* NIC specific static variables go here */
1N/A
1N/Astatic int ioaddr;
1N/Astatic unsigned short eeprom [0x40];
1N/Astatic char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
1N/Astatic char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
1N/A
1N/Astatic int eeprom_read(long ioaddr, int location);
1N/Astatic int mdio_read(int base_address, int phy_id, int location);
1N/A#if 0
1N/Astatic void mdio_write(int base_address, int phy_id, int location, int value);
1N/A#endif
1N/A
1N/Astatic void check_duplex(void);
1N/Astatic void set_rx_mode(void);
1N/Astatic void init_ring(void);
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/Astatic void decode_interrupt(u32 intr_status)
1N/A{
1N/A printf("Interrupt status: ");
1N/A
1N/A#define TRACE_INTR(_intr_) \
1N/A if (intr_status & (_intr_)) { printf (" " #_intr_); }
1N/A
1N/A TRACE_INTR(NormalIntr);
1N/A TRACE_INTR(AbnormalIntr);
1N/A TRACE_INTR(IntrPCIErr);
1N/A TRACE_INTR(TimerInt);
1N/A TRACE_INTR(IntrRxDied);
1N/A TRACE_INTR(RxNoBuf);
1N/A TRACE_INTR(IntrRxDone);
1N/A TRACE_INTR(TxFIFOUnderflow);
1N/A TRACE_INTR(RxErrIntr);
1N/A TRACE_INTR(TxIdle);
1N/A TRACE_INTR(IntrTxStopped);
1N/A TRACE_INTR(IntrTxDone);
1N/A
1N/A printf("\n");
1N/A /*sleep(1);*/
1N/A}
1N/A#endif
1N/A
1N/A/**************************************************************************
1N/Aw89c840_reset - Reset adapter
1N/A***************************************************************************/
1N/Astatic void w89c840_reset(struct nic *nic)
1N/A{
1N/A int i;
1N/A
1N/A /* Reset the chip to erase previous misconfiguration.
1N/A No hold time required! */
1N/A writel(0x00000001, ioaddr + PCIBusCfg);
1N/A
1N/A init_ring();
1N/A
1N/A writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
1N/A writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
1N/A
1N/A for (i = 0; i < ETH_ALEN; i++)
1N/A writeb(nic->node_addr[i], ioaddr + StationAddr + i);
1N/A
1N/A /* Initialize other registers. */
1N/A /* Configure the PCI bus bursts and FIFO thresholds.
1N/A 486: Set 8 longword cache alignment, 8 longword burst.
1N/A 586: Set 16 longword cache alignment, no burst limit.
1N/A Cache alignment bits 15:14 Burst length 13:8
1N/A 0000 <not allowed> 0000 align to cache 0800 8 longwords
1N/A 4000 8 longwords 0100 1 longword 1000 16 longwords
1N/A 8000 16 longwords 0200 2 longwords 2000 32 longwords
1N/A C000 32 longwords 0400 4 longwords
1N/A Wait the specified 50 PCI cycles after a reset by initializing
1N/A Tx and Rx queues and the address filter list. */
1N/A
1N/A writel(0xE010, ioaddr + PCIBusCfg);
1N/A
1N/A writel(0, ioaddr + RxStartDemand);
1N/A w840private.csr6 = 0x20022002;
1N/A check_duplex();
1N/A set_rx_mode();
1N/A
1N/A /* Do not enable the interrupts Etherboot doesn't need them */
1N/A/*
1N/A writel(0x1A0F5, ioaddr + IntrStatus);
1N/A writel(0x1A0F5, ioaddr + IntrEnable);
1N/A*/
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : Done reset.\n");
1N/A#endif
1N/A}
1N/A
1N/A#if 0
1N/Astatic void handle_intr(u32 intr_stat)
1N/A{
1N/A if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
1N/A /* we are polling, do not return now */
1N/A /*return 0;*/
1N/A } else {
1N/A /* Acknowledge all of the current interrupt sources ASAP. */
1N/A writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
1N/A }
1N/A
1N/A if (intr_stat & AbnormalIntr) {
1N/A /* There was an abnormal interrupt */
1N/A printf("\n-=- Abnormal interrupt.\n");
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A decode_interrupt(intr_stat);
1N/A#endif
1N/A
1N/A if (intr_stat & RxNoBuf) {
1N/A /* There was an interrupt */
1N/A printf("-=- <=> No receive buffers available.\n");
1N/A writel(0, ioaddr + RxStartDemand);
1N/A }
1N/A }
1N/A}
1N/A#endif
1N/A
1N/A/**************************************************************************
1N/Aw89c840_poll - Wait for a frame
1N/A***************************************************************************/
1N/Astatic int w89c840_poll(struct nic *nic, int retrieve)
1N/A{
1N/A /* return true if there's an ethernet packet ready to read */
1N/A /* nic->packet should contain data on return */
1N/A /* nic->packetlen should contain length of data */
1N/A int packet_received = 0;
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A u32 intr_status = readl(ioaddr + IntrStatus);
1N/A#endif
1N/A
1N/A do {
1N/A /* Code from netdev_rx(dev) */
1N/A
1N/A int entry = w840private.cur_rx % RX_RING_SIZE;
1N/A
1N/A struct w840_rx_desc *desc = w840private.rx_head_desc;
1N/A s32 status = desc->status;
1N/A
1N/A if (status & DescOwn) {
1N/A /* DescOwn bit is still set, we should wait for RX to complete */
1N/A packet_received = 0;
1N/A break;
1N/A }
1N/A
1N/A if ( !retrieve ) {
1N/A packet_received = 1;
1N/A break;
1N/A }
1N/A
1N/A if ((status & 0x38008300) != 0x0300) {
1N/A if ((status & 0x38000300) != 0x0300) {
1N/A /* Ingore earlier buffers. */
1N/A if ((status & 0xffff) != 0x7fff) {
1N/A printf("winbond-840 : Oversized Ethernet frame spanned "
1N/A "multiple buffers, entry %d status %X !\n",
1N/A w840private.cur_rx, status);
1N/A }
1N/A } else if (status & 0x8000) {
1N/A /* There was a fatal error. */
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : Receive error, Rx status %X :", status);
1N/A if (status & 0x0890) {
1N/A printf(" RXLEN_ERROR");
1N/A }
1N/A if (status & 0x004C) {
1N/A printf(", FRAME_ERROR");
1N/A }
1N/A if (status & 0x0002) {
1N/A printf(", CRC_ERROR");
1N/A }
1N/A printf("\n");
1N/A#endif
1N/A
1N/A /* Simpy do a reset now... */
1N/A w89c840_reset(nic);
1N/A
1N/A packet_received = 0;
1N/A break;
1N/A }
1N/A } else {
1N/A /* Omit the four octet CRC from the length. */
1N/A int pkt_len = ((status >> 16) & 0x7ff) - 4;
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
1N/A#endif
1N/A
1N/A nic->packetlen = pkt_len;
1N/A
1N/A /* Check if the packet is long enough to accept without copying
1N/A to a minimally-sized skbuff. */
1N/A
1N/A memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
1N/A packet_received = 1;
1N/A
1N/A /* Release buffer to NIC */
1N/A w840private.rx_ring[entry].status = DescOwn;
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A /* You will want this info for the initial debug. */
1N/A printf(" Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
1N/A "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
1N/A "%hhX.%hhX.%hhX.%hhX.\n",
1N/A nic->packet[0], nic->packet[1], nic->packet[2], nic->packet[3],
1N/A nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7],
1N/A nic->packet[8], nic->packet[9], nic->packet[10],
1N/A nic->packet[11], nic->packet[12], nic->packet[13],
1N/A nic->packet[14], nic->packet[15], nic->packet[16],
1N/A nic->packet[17]);
1N/A#endif
1N/A
1N/A }
1N/A
1N/A entry = (++w840private.cur_rx) % RX_RING_SIZE;
1N/A w840private.rx_head_desc = &w840private.rx_ring[entry];
1N/A } while (0);
1N/A
1N/A return packet_received;
1N/A}
1N/A
1N/A/**************************************************************************
1N/Aw89c840_transmit - Transmit a frame
1N/A***************************************************************************/
1N/A
1N/Astatic void w89c840_transmit(
1N/A struct nic *nic,
1N/A const char *d, /* Destination */
1N/A unsigned int t, /* Type */
1N/A unsigned int s, /* size */
1N/A const char *p) /* Packet */
1N/A{
1N/A /* send the packet to destination */
1N/A unsigned entry;
1N/A int transmit_status;
1N/A
1N/A /* Caution: the write order is important here, set the field
1N/A with the "ownership" bits last. */
1N/A
1N/A /* Fill in our transmit buffer */
1N/A entry = w840private.cur_tx % TX_RING_SIZE;
1N/A
1N/A memcpy (tx_packet, d, ETH_ALEN); /* dst */
1N/A memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */
1N/A
1N/A *((char *) tx_packet + 12) = t >> 8; /* type */
1N/A *((char *) tx_packet + 13) = t;
1N/A
1N/A memcpy (tx_packet + ETH_HLEN, p, s);
1N/A s += ETH_HLEN;
1N/A
1N/A while (s < ETH_ZLEN)
1N/A *((char *) tx_packet + ETH_HLEN + (s++)) = 0;
1N/A
1N/A w840private.tx_ring[entry].buffer1 = virt_to_le32desc(tx_packet);
1N/A
1N/A w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
1N/A if (entry >= TX_RING_SIZE-1) /* Wrap ring */
1N/A w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
1N/A w840private.tx_ring[entry].status = (DescOwn);
1N/A w840private.cur_tx++;
1N/A
1N/A w840private.tx_q_bytes = (u16) s;
1N/A writel(0, ioaddr + TxStartDemand);
1N/A
1N/A /* Work around horrible bug in the chip by marking the queue as full
1N/A when we do not have FIFO room for a maximum sized packet. */
1N/A
1N/A if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
1N/A /* Actually this is left to help finding error tails later in debugging...
1N/A * See Linux kernel driver in winbond-840.c for details.
1N/A */
1N/A w840private.tx_full = 1;
1N/A }
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
1N/A#endif
1N/A
1N/A /* Now wait for TX to complete. */
1N/A transmit_status = w840private.tx_ring[entry].status;
1N/A
1N/A load_timer2(TX_TIMEOUT);
1N/A
1N/A {
1N/A#if defined W89C840_DEBUG
1N/A u32 intr_stat = 0;
1N/A#endif
1N/A while (1) {
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A decode_interrupt(intr_stat);
1N/A#endif
1N/A
1N/A while ( (transmit_status & DescOwn) && timer2_running()) {
1N/A
1N/A transmit_status = w840private.tx_ring[entry].status;
1N/A }
1N/A
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if ((transmit_status & DescOwn) == 0) {
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
1N/A w840private.tx_ring[entry].status);
1N/A#endif
1N/A
1N/A return;
1N/A }
1N/A
1N/A /* Transmit timed out... */
1N/A
1N/A printf("winbond-840 : transmission TIMEOUT : status %X\n", w840private.tx_ring[entry].status);
1N/A
1N/A return;
1N/A}
1N/A
1N/A/**************************************************************************
1N/Aw89c840_disable - Turn off ethernet interface
1N/A***************************************************************************/
1N/Astatic void w89c840_disable(struct dev *dev)
1N/A{
1N/A struct nic *nic = (struct nic *)dev;
1N/A /* merge reset and disable */
1N/A w89c840_reset(nic);
1N/A
1N/A /* Don't know what to do to disable the board. Is this needed at all? */
1N/A /* Yes, a live NIC can corrupt the loaded memory later [Ken] */
1N/A /* Stop the chip's Tx and Rx processes. */
1N/A writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
1N/A}
1N/A
1N/A/**************************************************************************
1N/Aw89c840_irq - Enable, Disable, or Force interrupts
1N/A***************************************************************************/
1N/Astatic void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
1N/A{
1N/A switch ( action ) {
1N/A case DISABLE :
1N/A break;
1N/A case ENABLE :
1N/A break;
1N/A case FORCE :
1N/A break;
1N/A }
1N/A}
1N/A
1N/A/**************************************************************************
1N/Aw89c840_probe - Look for an adapter, this routine's visible to the outside
1N/A***************************************************************************/
1N/Astatic int w89c840_probe(struct dev *dev, struct pci_device *p)
1N/A{
1N/A struct nic *nic = (struct nic *)dev;
1N/A u16 sum = 0;
1N/A int i, j;
1N/A unsigned short value;
1N/A
1N/A if (p->ioaddr == 0)
1N/A return 0;
1N/A
1N/A ioaddr = p->ioaddr;
1N/A nic->ioaddr = p->ioaddr & ~3;
1N/A nic->irqno = 0;
1N/A
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
1N/A#endif
1N/A
1N/A ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
1N/A
1N/A /* From Matt Hortman <mbhortman@acpthinclient.com> */
1N/A if (p->vendor == PCI_VENDOR_ID_WINBOND2
1N/A && p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) {
1N/A
1N/A /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
1N/A
1N/A } else if ( p->vendor == PCI_VENDOR_ID_COMPEX
1N/A && p->dev_id == PCI_DEVICE_ID_COMPEX_RL100ATX) {
1N/A
1N/A /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
1N/A
1N/A } else {
1N/A /* Gee, guess what? They missed again. */
1N/A printf("device ID : %X - is not a Compex RL100ATX NIC.\n", p->dev_id);
1N/A return 0;
1N/A }
1N/A
1N/A printf(" %s\n", w89c840_version);
1N/A
1N/A adjust_pci_device(p);
1N/A
1N/A /* Ok. Got one. Read the eeprom. */
1N/A for (j = 0, i = 0; i < 0x40; i++) {
1N/A value = eeprom_read(ioaddr, i);
1N/A eeprom[i] = value;
1N/A sum += value;
1N/A }
1N/A
1N/A for (i=0;i<ETH_ALEN;i++) {
1N/A nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
1N/A }
1N/A printf ("Ethernet addr: %!\n", nic->node_addr);
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
1N/A#endif
1N/A
1N/A /* Reset the chip to erase previous misconfiguration.
1N/A No hold time required! */
1N/A writel(0x00000001, ioaddr + PCIBusCfg);
1N/A
1N/A if (driver_flags & CanHaveMII) {
1N/A int phy, phy_idx = 0;
1N/A for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
1N/A int mii_status = mdio_read(ioaddr, phy, 1);
1N/A if (mii_status != 0xffff && mii_status != 0x0000) {
1N/A w840private.phys[phy_idx++] = phy;
1N/A w840private.advertising = mdio_read(ioaddr, phy, 4);
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : MII PHY found at address %d, status "
1N/A "%X advertising %hX.\n", phy, mii_status, w840private.advertising);
1N/A#endif
1N/A
1N/A }
1N/A }
1N/A
1N/A w840private.mii_cnt = phy_idx;
1N/A
1N/A if (phy_idx == 0) {
1N/A printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
1N/A }
1N/A }
1N/A
1N/A /* point to NIC specific routines */
1N/A dev->disable = w89c840_disable;
1N/A nic->poll = w89c840_poll;
1N/A nic->transmit = w89c840_transmit;
1N/A nic->irq = w89c840_irq;
1N/A
1N/A w89c840_reset(nic);
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are
1N/A often serial bit streams generated by the host processor.
1N/A The example below is for the common 93c46 EEPROM, 64 16 bit words. */
1N/A
1N/A/* Delay between EEPROM clock transitions.
1N/A No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
1N/A a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
1N/A made udelay() unreliable.
1N/A The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
1N/A depricated.
1N/A*/
1N/A#define eeprom_delay(ee_addr) readl(ee_addr)
1N/A
1N/Aenum EEPROM_Ctrl_Bits {
1N/A EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
1N/A EE_ChipSelect=0x801, EE_DataIn=0x08,
1N/A};
1N/A
1N/A/* The EEPROM commands include the alway-set leading bit. */
1N/Aenum EEPROM_Cmds {
1N/A EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
1N/A};
1N/A
1N/Astatic int eeprom_read(long addr, int location)
1N/A{
1N/A int i;
1N/A int retval = 0;
1N/A int ee_addr = addr + EECtrl;
1N/A int read_cmd = location | EE_ReadCmd;
1N/A writel(EE_ChipSelect, ee_addr);
1N/A
1N/A /* Shift the read command bits out. */
1N/A for (i = 10; i >= 0; i--) {
1N/A short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
1N/A writel(dataval, ee_addr);
1N/A eeprom_delay(ee_addr);
1N/A writel(dataval | EE_ShiftClk, ee_addr);
1N/A eeprom_delay(ee_addr);
1N/A }
1N/A writel(EE_ChipSelect, ee_addr);
1N/A
1N/A for (i = 16; i > 0; i--) {
1N/A writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
1N/A eeprom_delay(ee_addr);
1N/A retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
1N/A writel(EE_ChipSelect, ee_addr);
1N/A eeprom_delay(ee_addr);
1N/A }
1N/A
1N/A /* Terminate the EEPROM access. */
1N/A writel(0, ee_addr);
1N/A return retval;
1N/A}
1N/A
1N/A/* MII transceiver control section.
1N/A Read and write the MII registers using software-generated serial
1N/A MDIO protocol. See the MII specifications or DP83840A data sheet
1N/A for details.
1N/A
1N/A The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
1N/A met by back-to-back 33Mhz PCI cycles. */
1N/A#define mdio_delay(mdio_addr) readl(mdio_addr)
1N/A
1N/A/* Set iff a MII transceiver on any interface requires mdio preamble.
1N/A This only set with older tranceivers, so the extra
1N/A code size of a per-interface flag is not worthwhile. */
1N/Astatic char mii_preamble_required = 1;
1N/A
1N/A#define MDIO_WRITE0 (MDIO_EnbOutput)
1N/A#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
1N/A
1N/A/* Generate the preamble required for initial synchronization and
1N/A a few older transceivers. */
1N/Astatic void mdio_sync(long mdio_addr)
1N/A{
1N/A int bits = 32;
1N/A
1N/A /* Establish sync by sending at least 32 logic ones. */
1N/A while (--bits >= 0) {
1N/A writel(MDIO_WRITE1, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A }
1N/A}
1N/A
1N/Astatic int mdio_read(int base_address, int phy_id, int location)
1N/A{
1N/A long mdio_addr = base_address + MIICtrl;
1N/A int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
1N/A int i, retval = 0;
1N/A
1N/A if (mii_preamble_required)
1N/A mdio_sync(mdio_addr);
1N/A
1N/A /* Shift the read command bits out. */
1N/A for (i = 15; i >= 0; i--) {
1N/A int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
1N/A
1N/A writel(dataval, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A writel(dataval | MDIO_ShiftClk, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A }
1N/A /* Read the two transition, 16 data, and wire-idle bits. */
1N/A for (i = 20; i > 0; i--) {
1N/A writel(MDIO_EnbIn, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
1N/A writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A }
1N/A return (retval>>1) & 0xffff;
1N/A}
1N/A
1N/A#if 0
1N/Astatic void mdio_write(int base_address, int phy_id, int location, int value)
1N/A{
1N/A long mdio_addr = base_address + MIICtrl;
1N/A int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
1N/A int i;
1N/A
1N/A if (location == 4 && phy_id == w840private.phys[0])
1N/A w840private.advertising = value;
1N/A
1N/A if (mii_preamble_required)
1N/A mdio_sync(mdio_addr);
1N/A
1N/A /* Shift the command bits out. */
1N/A for (i = 31; i >= 0; i--) {
1N/A int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
1N/A
1N/A writel(dataval, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A writel(dataval | MDIO_ShiftClk, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A }
1N/A /* Clear out extra bits. */
1N/A for (i = 2; i > 0; i--) {
1N/A writel(MDIO_EnbIn, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
1N/A mdio_delay(mdio_addr);
1N/A }
1N/A return;
1N/A}
1N/A#endif
1N/A
1N/Astatic void check_duplex(void)
1N/A{
1N/A int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
1N/A int negotiated = mii_reg5 & w840private.advertising;
1N/A int duplex;
1N/A
1N/A if (w840private.duplex_lock || mii_reg5 == 0xffff)
1N/A return;
1N/A
1N/A duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
1N/A if (w840private.full_duplex != duplex) {
1N/A w840private.full_duplex = duplex;
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
1N/A duplex ? "full" : "half", w840private.phys[0], negotiated);
1N/A#endif
1N/A
1N/A w840private.csr6 &= ~0x200;
1N/A w840private.csr6 |= duplex ? 0x200 : 0;
1N/A }
1N/A}
1N/A
1N/Astatic void set_rx_mode(void)
1N/A{
1N/A u32 mc_filter[2]; /* Multicast hash filter */
1N/A u32 rx_mode;
1N/A
1N/A /* Accept all multicasts from now on. */
1N/A memset(mc_filter, 0xff, sizeof(mc_filter));
1N/A
1N/A/*
1N/A * works OK with multicast enabled.
1N/A */
1N/A
1N/A rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
1N/A
1N/A writel(mc_filter[0], ioaddr + MulticastFilter0);
1N/A writel(mc_filter[1], ioaddr + MulticastFilter1);
1N/A w840private.csr6 &= ~0x00F8;
1N/A w840private.csr6 |= rx_mode;
1N/A writel(w840private.csr6, ioaddr + NetworkConfig);
1N/A
1N/A#if defined(W89C840_DEBUG)
1N/A printf("winbond-840 : Done setting RX mode.\n");
1N/A#endif
1N/A}
1N/A
1N/A/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
1N/Astatic void init_ring(void)
1N/A{
1N/A int i;
1N/A char * p;
1N/A
1N/A w840private.tx_full = 0;
1N/A w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
1N/A w840private.dirty_rx = w840private.dirty_tx = 0;
1N/A
1N/A w840private.rx_buf_sz = PKT_BUF_SZ;
1N/A w840private.rx_head_desc = &w840private.rx_ring[0];
1N/A
1N/A /* Initial all Rx descriptors. Fill in the Rx buffers. */
1N/A
1N/A p = &rx_packet[0];
1N/A
1N/A for (i = 0; i < RX_RING_SIZE; i++) {
1N/A w840private.rx_ring[i].length = w840private.rx_buf_sz;
1N/A w840private.rx_ring[i].status = 0;
1N/A w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
1N/A
1N/A w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
1N/A w840private.rx_ring[i].status = DescOwn | DescIntr;
1N/A }
1N/A
1N/A /* Mark the last entry as wrapping the ring. */
1N/A w840private.rx_ring[i-1].length |= DescEndRing;
1N/A w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
1N/A
1N/A w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
1N/A
1N/A for (i = 0; i < TX_RING_SIZE; i++) {
1N/A w840private.tx_ring[i].status = 0;
1N/A }
1N/A return;
1N/A}
1N/A
1N/A
1N/Astatic struct pci_id w89c840_nics[] = {
1N/APCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"),
1N/APCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
1N/A};
1N/A
1N/Astruct pci_driver w89c840_driver = {
1N/A .type = NIC_DRIVER,
1N/A .name = "W89C840F",
1N/A .probe = w89c840_probe,
1N/A .ids = w89c840_nics,
1N/A .id_count = sizeof(w89c840_nics)/sizeof(w89c840_nics[0]),
1N/A .class = 0,
1N/A};