diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/drivers/net/ns8390.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net/ns8390.c')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/ns8390.c | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/ns8390.c b/qemu/roms/ipxe/src/drivers/net/ns8390.c new file mode 100644 index 000000000..0ffc6216b --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/ns8390.c @@ -0,0 +1,1037 @@ +/************************************************************************** +ETHERBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: May/94 + + This code is based heavily on David Greenman's if_ed.c driver + + Copyright (C) 1993-1994, David Greenman, Martin Renters. + This software may be used, modified, copied, distributed, and sold, in + both source and binary form provided that the above copyright and these + terms are retained. Under no circumstances are the authors responsible for + the proper functioning of this software, nor do the authors assume any + responsibility for damages incurred with its use. + +Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003 +Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02 +3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94 +SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94 +3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98 +RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99 + parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker) +SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02 + based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker) + +**************************************************************************/ + +FILE_LICENCE ( BSD2 ); + +/* #warning "ns8390.c: FIXME: split ISA and PCI, clean up" */ + +#if 1 + +#if !defined(INCLUDE_NS8390) && !defined(INCLUDE_WD) && \ + !defined(INCLUDE_NE) && !defined(INCLUDE_3C503) + /* The driver named ns8390 is the PCI driver, often called + "PCI ne2000 clones". */ +# define INCLUDE_NS8390 1 +#endif + +#include "etherboot.h" +#include "nic.h" +#include "ns8390.h" +#include <ipxe/ethernet.h> +#ifdef INCLUDE_NS8390 +#include <ipxe/pci.h> +#else +#include <ipxe/isa.h> +#endif + +static unsigned char eth_vendor, eth_flags; +#ifdef INCLUDE_WD +static unsigned char eth_laar; +#endif +static unsigned short eth_nic_base, eth_asic_base; +static unsigned char eth_memsize, eth_rx_start, eth_tx_start; +static Address eth_bmem, eth_rmem; +static unsigned char eth_drain_receiver; + +#ifdef INCLUDE_WD +static struct wd_board { + const char *name; + char id; + char flags; + char memsize; +} wd_boards[] = { + {"WD8003S", TYPE_WD8003S, 0, MEM_8192}, + {"WD8003E", TYPE_WD8003E, 0, MEM_8192}, + {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384}, + {"WD8003W", TYPE_WD8003W, 0, MEM_8192}, + {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192}, + {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384}, + {"WD8003EP/WD8013EP", + TYPE_WD8013EP, 0, MEM_8192}, + {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384}, + {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384}, + {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384}, + {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384}, + {"SMC8416T", TYPE_SMC8416T, FLAG_16BIT | FLAG_790, MEM_8192}, + {"SMC8416C/BT", TYPE_SMC8416C, FLAG_16BIT | FLAG_790, MEM_8192}, + {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384}, + {NULL, 0, 0, 0} +}; +#endif + +#ifdef INCLUDE_3C503 +static unsigned char t503_output; /* AUI or internal xcvr (Thinnet) */ +#endif + +#if defined(INCLUDE_WD) +#define ASIC_PIO WD_IAR +#define eth_probe wd_probe +#if defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390) +Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390 +#endif +#endif + +#if defined(INCLUDE_3C503) +#define eth_probe t503_probe +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD) +Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390 +#endif +#endif + +#if defined(INCLUDE_NE) +#define eth_probe ne_probe +#if defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD) +Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390 +#endif +#endif + +#if defined(INCLUDE_NS8390) +#define eth_probe nepci_probe +#if defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD) +Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390 +#endif +#endif + +#if defined(INCLUDE_3C503) +#define ASIC_PIO _3COM_RFMSB +#else +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) +#define ASIC_PIO NE_DATA +#endif +#endif + +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO)) +/************************************************************************** +ETH_PIO_READ - Read a frame via Programmed I/O +**************************************************************************/ +static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) +{ +#ifdef INCLUDE_WD + outb(src & 0xff, eth_asic_base + WD_GP2); + outb(src >> 8, eth_asic_base + WD_GP2); +#else + outb(D8390_COMMAND_RD2 | + D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND); + outb(cnt, eth_nic_base + D8390_P0_RBCR0); + outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1); + outb(src, eth_nic_base + D8390_P0_RSAR0); + outb(src>>8, eth_nic_base + D8390_P0_RSAR1); + outb(D8390_COMMAND_RD0 | + D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND); + +#ifdef INCLUDE_3C503 + outb(src & 0xff, eth_asic_base + _3COM_DALSB); + outb(src >> 8, eth_asic_base + _3COM_DAMSB); + outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR); +#endif +#endif + + if (eth_flags & FLAG_16BIT) + cnt = (cnt + 1) >> 1; + + while(cnt--) { +#ifdef INCLUDE_3C503 + while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0) + ; +#endif + + if (eth_flags & FLAG_16BIT) { + *((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO); + dst += 2; + } + else + *(dst++) = inb(eth_asic_base + ASIC_PIO); + } + +#ifdef INCLUDE_3C503 + outb(t503_output, eth_asic_base + _3COM_CR); +#endif +} + +/************************************************************************** +ETH_PIO_WRITE - Write a frame via Programmed I/O +**************************************************************************/ +static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt) +{ +#ifdef COMPEX_RL2000_FIX + unsigned int x; +#endif /* COMPEX_RL2000_FIX */ +#ifdef INCLUDE_WD + outb(dst & 0xff, eth_asic_base + WD_GP2); + outb(dst >> 8, eth_asic_base + WD_GP2); +#else + outb(D8390_COMMAND_RD2 | + D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND); + outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR); + outb(cnt, eth_nic_base + D8390_P0_RBCR0); + outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1); + outb(dst, eth_nic_base + D8390_P0_RSAR0); + outb(dst>>8, eth_nic_base + D8390_P0_RSAR1); + outb(D8390_COMMAND_RD1 | + D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND); + +#ifdef INCLUDE_3C503 + outb(dst & 0xff, eth_asic_base + _3COM_DALSB); + outb(dst >> 8, eth_asic_base + _3COM_DAMSB); + + outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR); +#endif +#endif + + if (eth_flags & FLAG_16BIT) + cnt = (cnt + 1) >> 1; + + while(cnt--) + { +#ifdef INCLUDE_3C503 + while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0) + ; +#endif + + if (eth_flags & FLAG_16BIT) { + outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO); + src += 2; + } + else + outb(*(src++), eth_asic_base + ASIC_PIO); + } + +#ifdef INCLUDE_3C503 + outb(t503_output, eth_asic_base + _3COM_CR); +#else +#ifdef COMPEX_RL2000_FIX + for (x = 0; + x < COMPEX_RL2000_TRIES && + (inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) + != D8390_ISR_RDC; + ++x); + if (x >= COMPEX_RL2000_TRIES) + printf("Warning: Compex RL2000 aborted wait!\n"); +#endif /* COMPEX_RL2000_FIX */ +#ifndef INCLUDE_WD + while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) + != D8390_ISR_RDC); +#endif +#endif +} +#else +/************************************************************************** +ETH_PIO_READ - Dummy routine when NE2000 not compiled in +**************************************************************************/ +static void eth_pio_read(unsigned int src __unused, unsigned char *dst __unused, unsigned int cnt __unused) {} +#endif + + +/************************************************************************** +enable_multycast - Enable Multicast +**************************************************************************/ +static void enable_multicast(unsigned short eth_nic_base) +{ + unsigned char mcfilter[8]; + int i; + memset(mcfilter, 0xFF, 8); + outb(4, eth_nic_base+D8390_P0_RCR); + outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND); + for(i=0;i<8;i++) + { + outb(mcfilter[i], eth_nic_base + 8 + i); + if(inb(eth_nic_base + 8 + i)!=mcfilter[i]) + printf("Error SMC 83C690 Multicast filter read/write mishap %d\n",i); + } + outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND); + outb(4 | 0x08, eth_nic_base+D8390_P0_RCR); +} + +/************************************************************************** +NS8390_RESET - Reset adapter +**************************************************************************/ +static void ns8390_reset(struct nic *nic) +{ + int i; + + eth_drain_receiver = 0; +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | + D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + if (eth_flags & FLAG_16BIT) + outb(0x49, eth_nic_base+D8390_P0_DCR); + else + outb(0x48, eth_nic_base+D8390_P0_DCR); + outb(0, eth_nic_base+D8390_P0_RBCR0); + outb(0, eth_nic_base+D8390_P0_RBCR1); + outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */ + outb(2, eth_nic_base+D8390_P0_TCR); + outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR); + outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) { +#ifdef WD_790_PIO + outb(0x10, eth_asic_base + 0x06); /* disable interrupts, enable PIO */ + outb(0x01, eth_nic_base + 0x09); /* enable ring read auto-wrap */ +#else + outb(0, eth_nic_base + 0x09); +#endif + } +#endif + outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP); + outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND); + outb(0xFF, eth_nic_base+D8390_P0_ISR); + outb(0, eth_nic_base+D8390_P0_IMR); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS1 | + D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS1 | + D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + for (i=0; i<ETH_ALEN; i++) + outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i); + for (i=0; i<ETH_ALEN; i++) + outb(0xFF, eth_nic_base+D8390_P1_MAR0+i); + outb(eth_rx_start, eth_nic_base+D8390_P1_CURR); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + outb(0xFF, eth_nic_base+D8390_P0_ISR); + outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */ + outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */ + + enable_multicast(eth_nic_base); + +#ifdef INCLUDE_3C503 + /* + * No way to tell whether or not we're supposed to use + * the 3Com's transceiver unless the user tells us. + * 'flags' should have some compile time default value + * which can be changed from the command menu. + */ + t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL; + outb(t503_output, eth_asic_base + _3COM_CR); +#endif +} + +static int ns8390_poll(struct nic *nic, int retrieve); + +#ifndef INCLUDE_3C503 +/************************************************************************** +ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun +**************************************************************************/ +static void eth_rx_overrun(struct nic *nic) +{ + int start_time; + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | + D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND); + + /* wait for at least 1.6ms - we wait one timer tick */ + start_time = currticks(); + while (currticks() - start_time <= 1) + /* Nothing */; + + outb(0, eth_nic_base+D8390_P0_RBCR0); /* reset byte counter */ + outb(0, eth_nic_base+D8390_P0_RBCR1); + + /* + * Linux driver checks for interrupted TX here. This is not necessary, + * because the transmit routine waits until the frame is sent. + */ + + /* enter loopback mode and restart NIC */ + outb(2, eth_nic_base+D8390_P0_TCR); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + + /* clear the RX ring, acknowledge overrun interrupt */ + eth_drain_receiver = 1; + while (ns8390_poll(nic, 1)) + /* Nothing */; + eth_drain_receiver = 0; + outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR); + + /* leave loopback mode - no packets to be resent (see Linux driver) */ + outb(0, eth_nic_base+D8390_P0_TCR); +} +#endif /* INCLUDE_3C503 */ + +/************************************************************************** +NS8390_TRANSMIT - Transmit a frame +**************************************************************************/ +static void ns8390_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ +#if defined(INCLUDE_3C503) || (defined(INCLUDE_WD) && ! defined(WD_790_PIO)) + Address eth_vmem = bus_to_virt(eth_bmem); +#endif +#ifdef INCLUDE_3C503 + if (!(eth_flags & FLAG_PIO)) { + memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */ + memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ + *((char *)eth_vmem+12) = t>>8; /* type */ + *((char *)eth_vmem+13) = t; + memcpy((char *)eth_vmem+ETH_HLEN, p, s); + s += ETH_HLEN; + while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0; + } +#endif + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_16BIT) { + outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } +#ifndef WD_790_PIO + /* Memory interface */ + if (eth_flags & FLAG_790) { + outb(WD_MSR_MENB, eth_asic_base + WD_MSR); + inb(0x84); + } + inb(0x84); + memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */ + memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ + *((char *)eth_vmem+12) = t>>8; /* type */ + *((char *)eth_vmem+13) = t; + memcpy((char *)eth_vmem+ETH_HLEN, p, s); + s += ETH_HLEN; + while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0; + if (eth_flags & FLAG_790) { + outb(0, eth_asic_base + WD_MSR); + inb(0x84); + } +#else + inb(0x84); +#endif +#endif + +#if defined(INCLUDE_3C503) + if (eth_flags & FLAG_PIO) +#endif +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO)) + { + /* Programmed I/O */ + unsigned short type; + type = (t >> 8) | (t << 8); + eth_pio_write( (unsigned char *) d, eth_tx_start<<8, ETH_ALEN); + eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN); + /* bcc generates worse code without (const+const) below */ + eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2); + eth_pio_write( (unsigned char *) p, (eth_tx_start<<8)+ETH_HLEN, s); + s += ETH_HLEN; + if (s < ETH_ZLEN) s = ETH_ZLEN; + } +#endif +#if defined(INCLUDE_3C503) +#endif + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_16BIT) { + outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR); + outb(s, eth_nic_base+D8390_P0_TBCR0); + outb(s>>8, eth_nic_base+D8390_P0_TBCR1); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_RD2 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); +} + +/************************************************************************** +NS8390_POLL - Wait for a frame +**************************************************************************/ +static int ns8390_poll(struct nic *nic, int retrieve) +{ + int ret = 0; + unsigned char rstat, curr, next; + unsigned short len, frag; + unsigned short pktoff; + unsigned char *p; + struct ringbuffer pkthdr; + +#ifndef INCLUDE_3C503 + /* avoid infinite recursion: see eth_rx_overrun() */ + if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) { + eth_rx_overrun(nic); + return(0); + } +#endif /* INCLUDE_3C503 */ + rstat = inb(eth_nic_base+D8390_P0_RSR); + if (!(rstat & D8390_RSTAT_PRX)) return(0); + next = inb(eth_nic_base+D8390_P0_BOUND)+1; + if (next >= eth_memsize) next = eth_rx_start; + outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND); + curr = inb(eth_nic_base+D8390_P1_CURR); + outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND); + if (curr >= eth_memsize) curr=eth_rx_start; + if (curr == next) return(0); + + if ( ! retrieve ) return 1; + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_16BIT) { + outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } +#ifndef WD_790_PIO + if (eth_flags & FLAG_790) { + outb(WD_MSR_MENB, eth_asic_base + WD_MSR); + inb(0x84); + } +#endif + inb(0x84); +#endif + pktoff = next << 8; + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4); + else + memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4); + pktoff += sizeof(pkthdr); + /* incoming length includes FCS so must sub 4 */ + len = pkthdr.len - 4; + if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN + || len > ETH_FRAME_LEN) { + printf("Bogus packet, ignoring\n"); + return (0); + } + else { + p = nic->packet; + nic->packetlen = len; /* available to caller */ + frag = (eth_memsize << 8) - pktoff; + if (len > frag) { /* We have a wrap-around */ + /* read first part */ + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, frag); + else + memcpy(p, bus_to_virt(eth_rmem + pktoff), frag); + pktoff = eth_rx_start << 8; + p += frag; + len -= frag; + } + /* read second part */ + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, len); + else + memcpy(p, bus_to_virt(eth_rmem + pktoff), len); + ret = 1; + } +#ifdef INCLUDE_WD +#ifndef WD_790_PIO + if (eth_flags & FLAG_790) { + outb(0, eth_asic_base + WD_MSR); + inb(0x84); + } +#endif + if (eth_flags & FLAG_16BIT) { + outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } + inb(0x84); +#endif + next = pkthdr.next; /* frame number of next packet */ + if (next == eth_rx_start) + next = eth_memsize; + outb(next-1, eth_nic_base+D8390_P0_BOUND); + return(ret); +} + +/************************************************************************** +NS8390_DISABLE - Turn off adapter +**************************************************************************/ +static void ns8390_disable ( struct nic *nic ) { + ns8390_reset(nic); +} + +/************************************************************************** +NS8390_IRQ - Enable, Disable, or Force interrupts +**************************************************************************/ +static void ns8390_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations ns8390_operations; +static struct nic_operations ns8390_operations = { + .connect = dummy_connect, + .poll = ns8390_poll, + .transmit = ns8390_transmit, + .irq = ns8390_irq, +}; + +/************************************************************************** +ETH_PROBE - Look for an adapter +**************************************************************************/ +#ifdef INCLUDE_NS8390 +static int eth_probe (struct nic *nic, struct pci_device *pci) +#else +static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused) +#endif +{ + int i; +#ifdef INCLUDE_NS8390 + unsigned short pci_probe_addrs[] = { pci->ioaddr, 0 }; + unsigned short *probe_addrs = pci_probe_addrs; +#endif + eth_vendor = VENDOR_NONE; + eth_drain_receiver = 0; + + nic->irqno = 0; + +#ifdef INCLUDE_WD +{ + /****************************************************************** + Search for WD/SMC cards + ******************************************************************/ + struct wd_board *brd; + unsigned short chksum; + unsigned char c; + for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE; + eth_asic_base += 0x20) { + chksum = 0; + for (i=8; i<16; i++) + chksum += inb(eth_asic_base+i); + /* Extra checks to avoid soundcard */ + if ((chksum & 0xFF) == 0xFF && + inb(eth_asic_base+8) != 0xFF && + inb(eth_asic_base+9) != 0xFF) + break; + } + if (eth_asic_base > WD_HIGH_BASE) + return (0); + /* We've found a board */ + eth_vendor = VENDOR_WD; + eth_nic_base = eth_asic_base + WD_NIC_ADDR; + + nic->ioaddr = eth_nic_base; + + c = inb(eth_asic_base+WD_BID); /* Get board id */ + for (brd = wd_boards; brd->name; brd++) + if (brd->id == c) break; + if (!brd->name) { + printf("Unknown WD/SMC NIC type %hhX\n", c); + return (0); /* Unknown type */ + } + eth_flags = brd->flags; + eth_memsize = brd->memsize; + eth_tx_start = 0; + eth_rx_start = D8390_TXBUF_SIZE; + if ((c == TYPE_WD8013EP) && + (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) { + eth_flags = FLAG_16BIT; + eth_memsize = MEM_16384; + } + if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) { + eth_bmem = (0x80000 | + ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13)); + } else + eth_bmem = WD_DEFAULT_MEM; + if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) { + /* from Linux driver, 8416BT detects as 8216 sometimes */ + unsigned int addr = inb(eth_asic_base + 0xb); + if (((addr >> 4) & 3) == 0) { + brd += 2; + eth_memsize = brd->memsize; + } + } + outb(0x80, eth_asic_base + WD_MSR); /* Reset */ + for (i=0; i<ETH_ALEN; i++) { + nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR); + } + DBG ( "\n%s base %4.4x", brd->name, eth_asic_base ); + if (eth_flags & FLAG_790) { +#ifdef WD_790_PIO + DBG ( ", PIO mode, addr %s\n", eth_ntoa ( nic->node_addr ) ); + eth_bmem = 0; + eth_flags |= FLAG_PIO; /* force PIO mode */ + outb(0, eth_asic_base+WD_MSR); +#else + DBG ( ", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) ); + + outb(WD_MSR_MENB, eth_asic_base+WD_MSR); + outb((inb(eth_asic_base+0x04) | + 0x80), eth_asic_base+0x04); + outb(((unsigned)(eth_bmem >> 13) & 0x0F) | + ((unsigned)(eth_bmem >> 11) & 0x40) | + (inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B); + outb((inb(eth_asic_base+0x04) & + ~0x80), eth_asic_base+0x04); +#endif + } else { + + DBG (", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) ); + + outb(((unsigned)(eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR); + } + if (eth_flags & FLAG_16BIT) { + if (eth_flags & FLAG_790) { + eth_laar = inb(eth_asic_base + WD_LAAR); + outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + } else { + outb((eth_laar = + WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR); +/* + The previous line used to be + WD_LAAR_M16EN | WD_LAAR_L16EN | 1)); + jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made + it work for WD8013s. This seems to work for my 8013 boards. I + don't know what is really happening. I wish I had data sheets + or more time to decode the Linux driver. - Ken +*/ + } + inb(0x84); + } +} +#endif +#ifdef INCLUDE_3C503 +#ifdef T503_AUI + nic->flags = 1; /* aui */ +#else + nic->flags = 0; /* no aui */ +#endif + /****************************************************************** + Search for 3Com 3c503 if no WD/SMC cards + ******************************************************************/ + if (eth_vendor == VENDOR_NONE) { + int idx; + int iobase_reg, membase_reg; + static unsigned short base[] = { + 0x300, 0x310, 0x330, 0x350, + 0x250, 0x280, 0x2A0, 0x2E0, 0 }; + + /* Loop through possible addresses checking each one */ + + for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) { + + eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET; +/* + * Note that we use the same settings for both 8 and 16 bit cards: + * both have an 8K bank of memory at page 1 while only the 16 bit + * cards have a bank at page 0. + */ + eth_memsize = MEM_16384; + eth_tx_start = 32; + eth_rx_start = 32 + D8390_TXBUF_SIZE; + + /* Check our base address. iobase and membase should */ + /* both have a maximum of 1 bit set or be 0. */ + + iobase_reg = inb(eth_asic_base + _3COM_BCFR); + membase_reg = inb(eth_asic_base + _3COM_PCFR); + + if ((iobase_reg & (iobase_reg - 1)) || + (membase_reg & (membase_reg - 1))) + continue; /* nope */ + + /* Now get the shared memory address */ + + eth_flags = 0; + + switch (membase_reg) { + case _3COM_PCFR_DC000: + eth_bmem = 0xdc000; + break; + case _3COM_PCFR_D8000: + eth_bmem = 0xd8000; + break; + case _3COM_PCFR_CC000: + eth_bmem = 0xcc000; + break; + case _3COM_PCFR_C8000: + eth_bmem = 0xc8000; + break; + case _3COM_PCFR_PIO: + eth_flags |= FLAG_PIO; + eth_bmem = 0; + break; + default: + continue; /* nope */ + } + break; + } + + if (base[idx] == 0) /* not found */ + return (0); +#ifndef T503_SHMEM + eth_flags |= FLAG_PIO; /* force PIO mode */ + eth_bmem = 0; +#endif + eth_vendor = VENDOR_3COM; + + + /* Need this to make ns8390_poll() happy. */ + + eth_rmem = eth_bmem - 0x2000; + + /* Reset NIC and ASIC */ + + outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR ); + outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR ); + + /* Get our ethernet address */ + + outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR); + nic->ioaddr = eth_nic_base; + DBG ( "\n3Com 3c503 base %4.4x, ", eth_nic_base ); + if (eth_flags & FLAG_PIO) + DBG ( "PIO mode" ); + else + DBG ( "memory %4.4x", eth_bmem ); + for (i=0; i<ETH_ALEN; i++) { + nic->node_addr[i] = inb(eth_nic_base+i); + } + DBG ( ", %s, MAC Addr %s\n", nic->flags ? "AUI" : "internal xcvr", + eth_ntoa ( nic->node_addr ) ); + + outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR); + /* + * Initialize GA configuration register. Set bank and enable shared + * mem. We always use bank 1. Disable interrupts. + */ + outb(_3COM_GACFR_RSEL | + _3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR); + + outb(0xff, eth_asic_base + _3COM_VPTR2); + outb(0xff, eth_asic_base + _3COM_VPTR1); + outb(0x00, eth_asic_base + _3COM_VPTR0); + /* + * Clear memory and verify that it worked (we use only 8K) + */ + + if (!(eth_flags & FLAG_PIO)) { + memset(bus_to_virt(eth_bmem), 0, 0x2000); + for(i = 0; i < 0x2000; ++i) + if (*((char *)(bus_to_virt(eth_bmem+i)))) { + printf ("Failed to clear 3c503 shared mem.\n"); + return (0); + } + } + /* + * Initialize GA page/start/stop registers. + */ + outb(eth_tx_start, eth_asic_base + _3COM_PSTR); + outb(eth_memsize, eth_asic_base + _3COM_PSPR); + } +#endif +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) +{ + /****************************************************************** + Search for NE1000/2000 if no WD/SMC or 3com cards + ******************************************************************/ + unsigned char c; + if (eth_vendor == VENDOR_NONE) { + unsigned char romdata[16]; + unsigned char testbuf[32]; + int idx; + static unsigned char test[] = "NE*000 memory"; + static unsigned short base[] = { +#ifdef NE_SCAN + NE_SCAN, +#endif + 0 }; + /* if no addresses supplied, fall back on defaults */ + if (probe_addrs == NULL || probe_addrs[0] == 0) + probe_addrs = base; + eth_bmem = 0; /* No shared memory */ + for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) { + eth_flags = FLAG_PIO; + eth_asic_base = eth_nic_base + NE_ASIC_OFFSET; + eth_memsize = MEM_16384; + eth_tx_start = 32; + eth_rx_start = 32 + D8390_TXBUF_SIZE; + c = inb(eth_asic_base + NE_RESET); + outb(c, eth_asic_base + NE_RESET); + (void) inb(0x84); + outb(D8390_COMMAND_STP | + D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND); + outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR); + outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR); + outb(MEM_8192, eth_nic_base + D8390_P0_PSTART); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP); +#ifdef NS8390_FORCE_16BIT + eth_flags |= FLAG_16BIT; /* force 16-bit mode */ +#endif + + eth_pio_write( (unsigned char *) test, 8192, sizeof(test)); + eth_pio_read(8192, testbuf, sizeof(test)); + if (!memcmp(test, testbuf, sizeof(test))) + break; + eth_flags |= FLAG_16BIT; + eth_memsize = MEM_32768; + eth_tx_start = 64; + eth_rx_start = 64 + D8390_TXBUF_SIZE; + outb(D8390_DCR_WTS | + D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTART); + outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP); + eth_pio_write( (unsigned char *) test, 16384, sizeof(test)); + eth_pio_read(16384, testbuf, sizeof(test)); + if (!memcmp(testbuf, test, sizeof(test))) + break; + } + if (eth_nic_base == 0) + return (0); + if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */ + eth_flags |= FLAG_16BIT; + eth_vendor = VENDOR_NOVELL; + eth_pio_read(0, romdata, sizeof(romdata)); + for (i=0; i<ETH_ALEN; i++) { + nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)]; + } + nic->ioaddr = eth_nic_base; + DBG ( "\nNE%c000 base %4.4x, MAC Addr %s\n", + (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, + eth_ntoa ( nic->node_addr ) ); + } +} +#endif + if (eth_vendor == VENDOR_NONE) + return(0); + if (eth_vendor != VENDOR_3COM) + eth_rmem = eth_bmem; + ns8390_reset(nic); + nic->nic_op = &ns8390_operations; + + /* Based on PnP ISA map */ +#ifdef INCLUDE_WD + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x812a); +#endif +#ifdef INCLUDE_3C503 + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80f3); +#endif +#ifdef INCLUDE_NE + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80d6); +#endif + return 1; +} + +#ifdef INCLUDE_WD +struct isa_driver wd_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "WD", + .probe = wd_probe, + .ioaddrs = 0, +}; +ISA_ROM("wd","WD8003/8013, SMC8216/8416, SMC 83c790 (EtherEZ)"); +#endif + +#ifdef INCLUDE_3C503 +struct isa_driver t503_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C503", + .probe = t503_probe, + .ioaddrs = 0, +}; +ISA_ROM("3c503","3Com503, Etherlink II[/16]"); +#endif + +#ifdef INCLUDE_NE +struct isa_driver ne_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "NE*000", + .probe = ne_probe, + .ioaddrs = 0, +}; +ISA_ROM("ne","NE1000/2000 and clones"); +#endif + +#ifdef INCLUDE_NS8390 +static struct pci_device_id nepci_nics[] = { +/* A few NE2000 PCI clones, list not exhaustive */ +PCI_ROM(0x10ec, 0x8029, "rtl8029", "Realtek 8029", 0), +PCI_ROM(0x1186, 0x0300, "dlink-528", "D-Link DE-528", 0), +PCI_ROM(0x1050, 0x0940, "winbond940", "Winbond NE2000-PCI", 0), /* Winbond 86C940 / 89C940 */ +PCI_ROM(0x1050, 0x5a5a, "winbond940f", "Winbond W89c940F", 0), /* Winbond 89C940F */ +PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000", 0), +PCI_ROM(0x8e2e, 0x3000, "ktiet32p2", "KTI ET32P2", 0), +PCI_ROM(0x4a14, 0x5000, "nv5000sc", "NetVin NV5000SC", 0), +PCI_ROM(0x12c3, 0x0058, "holtek80232", "Holtek HT80232", 0), +PCI_ROM(0x12c3, 0x5598, "holtek80229", "Holtek HT80229", 0), +PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34", 0), +PCI_ROM(0x1106, 0x0926, "via86c926", "Via 86c926", 0), +}; + +PCI_DRIVER ( nepci_driver, nepci_nics, PCI_NO_CLASS ); + +DRIVER ( "NE2000/PCI", nic_driver, pci_driver, nepci_driver, + nepci_probe, ns8390_disable ); + +#endif /* INCLUDE_NS8390 */ + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ |