1477 lines
40 KiB
C
1477 lines
40 KiB
C
/* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux
|
|
|
|
Written 1994-1999 by Mika Kuoppala
|
|
|
|
Copyright (C) 1994-1999 by Mika Kuoppala
|
|
Based on skeleton.c and heavily on at1700.c by Donald Becker
|
|
|
|
This software may be used and distributed according to the terms
|
|
of the GNU General Public License, incorporated herein by reference.
|
|
|
|
The author may be reached as miku@iki.fi
|
|
|
|
This driver supports following cards :
|
|
- ICL EtherTeam 16i
|
|
- ICL EtherTeam 32 EISA
|
|
(Uses true 32 bit transfers rather than 16i compability mode)
|
|
|
|
Example Module usage:
|
|
insmod eth16i.o io=0x2a0 mediatype=bnc
|
|
|
|
mediatype can be one of the following: bnc,tp,dix,auto,eprom
|
|
|
|
'auto' will try to autoprobe mediatype.
|
|
'eprom' will use whatever type defined in eprom.
|
|
|
|
I have benchmarked driver with PII/300Mhz as a ftp client
|
|
and 486/33Mhz as a ftp server. Top speed was 1128.37 kilobytes/sec.
|
|
|
|
Sources:
|
|
- skeleton.c a sample network driver core for linux,
|
|
written by Donald Becker <becker@scyld.com>
|
|
- at1700.c a driver for Allied Telesis AT1700, written
|
|
by Donald Becker.
|
|
- e16iSRV.asm a Netware 3.X Server Driver for ICL EtherTeam16i
|
|
written by Markku Viima
|
|
- The Fujitsu MB86965 databook.
|
|
|
|
Author thanks following persons due to their valueble assistance:
|
|
Markku Viima (ICL)
|
|
Ari Valve (ICL)
|
|
Donald Becker
|
|
Kurt Huwig <kurt@huwig.de>
|
|
|
|
Revision history:
|
|
|
|
Version Date Description
|
|
|
|
0.01 15.12-94 Initial version (card detection)
|
|
0.02 23.01-95 Interrupt is now hooked correctly
|
|
0.03 01.02-95 Rewrote initialization part
|
|
0.04 07.02-95 Base skeleton done...
|
|
Made a few changes to signature checking
|
|
to make it a bit reliable.
|
|
- fixed bug in tx_buf mapping
|
|
- fixed bug in initialization (DLC_EN
|
|
wasn't enabled when initialization
|
|
was done.)
|
|
0.05 08.02-95 If there were more than one packet to send,
|
|
transmit was jammed due to invalid
|
|
register write...now fixed
|
|
0.06 19.02-95 Rewrote interrupt handling
|
|
0.07 13.04-95 Wrote EEPROM read routines
|
|
Card configuration now set according to
|
|
data read from EEPROM
|
|
0.08 23.06-95 Wrote part that tries to probe used interface
|
|
port if AUTO is selected
|
|
|
|
0.09 01.09-95 Added module support
|
|
|
|
0.10 04.09-95 Fixed receive packet allocation to work
|
|
with kernels > 1.3.x
|
|
|
|
0.20 20.09-95 Added support for EtherTeam32 EISA
|
|
|
|
0.21 17.10-95 Removed the unnecessary extern
|
|
init_etherdev() declaration. Some
|
|
other cleanups.
|
|
|
|
0.22 22.02-96 Receive buffer was not flushed
|
|
correctly when faulty packet was
|
|
received. Now fixed.
|
|
|
|
0.23 26.02-96 Made resetting the adapter
|
|
more reliable.
|
|
|
|
0.24 27.02-96 Rewrote faulty packet handling in eth16i_rx
|
|
|
|
0.25 22.05-96 kfree() was missing from cleanup_module.
|
|
|
|
0.26 11.06-96 Sometimes card was not found by
|
|
check_signature(). Now made more reliable.
|
|
|
|
0.27 23.06-96 Oops. 16 consecutive collisions halted
|
|
adapter. Now will try to retransmit
|
|
MAX_COL_16 times before finally giving up.
|
|
|
|
0.28 28.10-97 Added dev_id parameter (NULL) for free_irq
|
|
|
|
0.29 29.10-97 Multiple card support for module users
|
|
|
|
0.30 30.10-97 Fixed irq allocation bug.
|
|
(request_irq moved from probe to open)
|
|
|
|
0.30a 21.08-98 Card detection made more relaxed. Driver
|
|
had problems with some TCP/IP-PROM boots
|
|
to find the card. Suggested by
|
|
Kurt Huwig <kurt@huwig.de>
|
|
|
|
0.31 28.08-98 Media interface port can now be selected
|
|
with module parameters or kernel
|
|
boot parameters.
|
|
|
|
0.32 31.08-98 IRQ was never freed if open/close
|
|
pair wasn't called. Now fixed.
|
|
|
|
0.33 10.09-98 When eth16i_open() was called after
|
|
eth16i_close() chip never recovered.
|
|
Now more shallow reset is made on
|
|
close.
|
|
|
|
0.34 29.06-99 Fixed one bad #ifdef.
|
|
Changed ioaddr -> io for consistency
|
|
|
|
0.35 01.07-99 transmit,-receive bytes were never
|
|
updated in stats.
|
|
|
|
Bugs:
|
|
In some cases the media interface autoprobing code doesn't find
|
|
the correct interface type. In this case you can
|
|
manually choose the interface type in DOS with E16IC.EXE which is
|
|
configuration software for EtherTeam16i and EtherTeam32 cards.
|
|
This is also true for IRQ setting. You cannot use module
|
|
parameter to configure IRQ of the card (yet).
|
|
|
|
To do:
|
|
- Real multicast support
|
|
- Rewrite the media interface autoprobing code. Its _horrible_ !
|
|
- Possibly merge all the MB86965 specific code to external
|
|
module for use by eth16.c and Donald's at1700.c
|
|
- IRQ configuration with module parameter. I will do
|
|
this when i will get enough info about setting
|
|
irq without configuration utility.
|
|
*/
|
|
|
|
static char *version =
|
|
"eth16i.c: v0.35 01-Jul-1999 Mika Kuoppala (miku@iki.fi)\n";
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/in.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/dma.h>
|
|
|
|
|
|
|
|
/* Few macros */
|
|
#define BITSET(ioaddr, bnum) ((outb(((inb(ioaddr)) | (bnum)), ioaddr)))
|
|
#define BITCLR(ioaddr, bnum) ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr)))
|
|
|
|
/* This is the I/O address space for Etherteam 16i adapter. */
|
|
#define ETH16I_IO_EXTENT 32
|
|
|
|
/* Ticks before deciding that transmit has timed out */
|
|
#define TX_TIMEOUT (400*HZ/1000)
|
|
|
|
/* Maximum loop count when receiving packets */
|
|
#define MAX_RX_LOOP 20
|
|
|
|
/* Some interrupt masks */
|
|
#define ETH16I_INTR_ON 0xef8a /* Higher is receive mask */
|
|
#define ETH16I_INTR_OFF 0x0000
|
|
|
|
/* Buffers header status byte meanings */
|
|
#define PKT_GOOD BIT(5)
|
|
#define PKT_GOOD_RMT BIT(4)
|
|
#define PKT_SHORT BIT(3)
|
|
#define PKT_ALIGN_ERR BIT(2)
|
|
#define PKT_CRC_ERR BIT(1)
|
|
#define PKT_RX_BUF_OVERFLOW BIT(0)
|
|
|
|
/* Transmit status register (DLCR0) */
|
|
#define TX_STATUS_REG 0
|
|
#define TX_DONE BIT(7)
|
|
#define NET_BUSY BIT(6)
|
|
#define TX_PKT_RCD BIT(5)
|
|
#define CR_LOST BIT(4)
|
|
#define TX_JABBER_ERR BIT(3)
|
|
#define COLLISION BIT(2)
|
|
#define COLLISIONS_16 BIT(1)
|
|
|
|
/* Receive status register (DLCR1) */
|
|
#define RX_STATUS_REG 1
|
|
#define RX_PKT BIT(7) /* Packet received */
|
|
#define BUS_RD_ERR BIT(6)
|
|
#define SHORT_PKT_ERR BIT(3)
|
|
#define ALIGN_ERR BIT(2)
|
|
#define CRC_ERR BIT(1)
|
|
#define RX_BUF_OVERFLOW BIT(0)
|
|
|
|
/* Transmit Interrupt Enable Register (DLCR2) */
|
|
#define TX_INTR_REG 2
|
|
#define TX_INTR_DONE BIT(7)
|
|
#define TX_INTR_COL BIT(2)
|
|
#define TX_INTR_16_COL BIT(1)
|
|
|
|
/* Receive Interrupt Enable Register (DLCR3) */
|
|
#define RX_INTR_REG 3
|
|
#define RX_INTR_RECEIVE BIT(7)
|
|
#define RX_INTR_SHORT_PKT BIT(3)
|
|
#define RX_INTR_CRC_ERR BIT(1)
|
|
#define RX_INTR_BUF_OVERFLOW BIT(0)
|
|
|
|
/* Transmit Mode Register (DLCR4) */
|
|
#define TRANSMIT_MODE_REG 4
|
|
#define LOOPBACK_CONTROL BIT(1)
|
|
#define CONTROL_OUTPUT BIT(2)
|
|
|
|
/* Receive Mode Register (DLCR5) */
|
|
#define RECEIVE_MODE_REG 5
|
|
#define RX_BUFFER_EMPTY BIT(6)
|
|
#define ACCEPT_BAD_PACKETS BIT(5)
|
|
#define RECEIVE_SHORT_ADDR BIT(4)
|
|
#define ACCEPT_SHORT_PACKETS BIT(3)
|
|
#define REMOTE_RESET BIT(2)
|
|
|
|
#define ADDRESS_FILTER_MODE BIT(1) | BIT(0)
|
|
#define REJECT_ALL 0
|
|
#define ACCEPT_ALL 3
|
|
#define MODE_1 1 /* NODE ID, BC, MC, 2-24th bit */
|
|
#define MODE_2 2 /* NODE ID, BC, MC, Hash Table */
|
|
|
|
/* Configuration Register 0 (DLCR6) */
|
|
#define CONFIG_REG_0 6
|
|
#define DLC_EN BIT(7)
|
|
#define SRAM_CYCLE_TIME_100NS BIT(6)
|
|
#define SYSTEM_BUS_WIDTH_8 BIT(5) /* 1 = 8bit, 0 = 16bit */
|
|
#define BUFFER_WIDTH_8 BIT(4) /* 1 = 8bit, 0 = 16bit */
|
|
#define TBS1 BIT(3)
|
|
#define TBS0 BIT(2)
|
|
#define SRAM_BS1 BIT(1) /* 00=8kb, 01=16kb */
|
|
#define SRAM_BS0 BIT(0) /* 10=32kb, 11=64kb */
|
|
|
|
#ifndef ETH16I_TX_BUF_SIZE /* 0 = 2kb, 1 = 4kb */
|
|
#define ETH16I_TX_BUF_SIZE 3 /* 2 = 8kb, 3 = 16kb */
|
|
#endif
|
|
#define TX_BUF_1x2048 0
|
|
#define TX_BUF_2x2048 1
|
|
#define TX_BUF_2x4098 2
|
|
#define TX_BUF_2x8192 3
|
|
|
|
/* Configuration Register 1 (DLCR7) */
|
|
#define CONFIG_REG_1 7
|
|
#define POWERUP BIT(5)
|
|
|
|
/* Transmit start register */
|
|
#define TRANSMIT_START_REG 10
|
|
#define TRANSMIT_START_RB 2
|
|
#define TX_START BIT(7) /* Rest of register bit indicate*/
|
|
/* number of packets in tx buffer*/
|
|
/* Node ID registers (DLCR8-13) */
|
|
#define NODE_ID_0 8
|
|
#define NODE_ID_RB 0
|
|
|
|
/* Hash Table registers (HT8-15) */
|
|
#define HASH_TABLE_0 8
|
|
#define HASH_TABLE_RB 1
|
|
|
|
/* Buffer memory ports */
|
|
#define BUFFER_MEM_PORT_LB 8
|
|
#define DATAPORT BUFFER_MEM_PORT_LB
|
|
#define BUFFER_MEM_PORT_HB 9
|
|
|
|
/* 16 Collision control register (BMPR11) */
|
|
#define COL_16_REG 11
|
|
#define HALT_ON_16 0x00
|
|
#define RETRANS_AND_HALT_ON_16 0x02
|
|
|
|
/* Maximum number of attempts to send after 16 concecutive collisions */
|
|
#define MAX_COL_16 10
|
|
|
|
/* DMA Burst and Transceiver Mode Register (BMPR13) */
|
|
#define TRANSCEIVER_MODE_REG 13
|
|
#define TRANSCEIVER_MODE_RB 2
|
|
#define IO_BASE_UNLOCK BIT(7)
|
|
#define LOWER_SQUELCH_TRESH BIT(6)
|
|
#define LINK_TEST_DISABLE BIT(5)
|
|
#define AUI_SELECT BIT(4)
|
|
#define DIS_AUTO_PORT_SEL BIT(3)
|
|
|
|
/* Filter Self Receive Register (BMPR14) */
|
|
#define FILTER_SELF_RX_REG 14
|
|
#define SKIP_RX_PACKET BIT(2)
|
|
#define FILTER_SELF_RECEIVE BIT(0)
|
|
|
|
/* EEPROM Control Register (BMPR 16) */
|
|
#define EEPROM_CTRL_REG 16
|
|
|
|
/* EEPROM Data Register (BMPR 17) */
|
|
#define EEPROM_DATA_REG 17
|
|
|
|
/* NMC93CSx6 EEPROM Control Bits */
|
|
#define CS_0 0x00
|
|
#define CS_1 0x20
|
|
#define SK_0 0x00
|
|
#define SK_1 0x40
|
|
#define DI_0 0x00
|
|
#define DI_1 0x80
|
|
|
|
/* NMC93CSx6 EEPROM Instructions */
|
|
#define EEPROM_READ 0x80
|
|
|
|
/* NMC93CSx6 EEPROM Addresses */
|
|
#define E_NODEID_0 0x02
|
|
#define E_NODEID_1 0x03
|
|
#define E_NODEID_2 0x04
|
|
#define E_PORT_SELECT 0x14
|
|
#define E_PORT_BNC 0x00
|
|
#define E_PORT_DIX 0x01
|
|
#define E_PORT_TP 0x02
|
|
#define E_PORT_AUTO 0x03
|
|
#define E_PORT_FROM_EPROM 0x04
|
|
#define E_PRODUCT_CFG 0x30
|
|
|
|
|
|
/* Macro to slow down io between EEPROM clock transitions */
|
|
#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { inb(0x80); }}while(0)
|
|
|
|
/* Jumperless Configuration Register (BMPR19) */
|
|
#define JUMPERLESS_CONFIG 19
|
|
|
|
/* ID ROM registers, writing to them also resets some parts of chip */
|
|
#define ID_ROM_0 24
|
|
#define ID_ROM_7 31
|
|
#define RESET ID_ROM_0
|
|
|
|
/* This is the I/O address list to be probed when seeking the card */
|
|
static unsigned int eth16i_portlist[] __initdata = {
|
|
0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0
|
|
};
|
|
|
|
static unsigned int eth32i_portlist[] __initdata = {
|
|
0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
|
|
0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0
|
|
};
|
|
|
|
/* This is the Interrupt lookup table for Eth16i card */
|
|
static unsigned int eth16i_irqmap[] __initdata = { 9, 10, 5, 15, 0 };
|
|
#define NUM_OF_ISA_IRQS 4
|
|
|
|
/* This is the Interrupt lookup table for Eth32i card */
|
|
static unsigned int eth32i_irqmap[] __initdata = { 3, 5, 7, 9, 10, 11, 12, 15, 0 };
|
|
#define EISA_IRQ_REG 0xc89
|
|
#define NUM_OF_EISA_IRQS 8
|
|
|
|
static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 };
|
|
|
|
/* Use 0 for production, 1 for verification, >2 for debug */
|
|
#ifndef ETH16I_DEBUG
|
|
#define ETH16I_DEBUG 0
|
|
#endif
|
|
static unsigned int eth16i_debug = ETH16I_DEBUG;
|
|
|
|
/* Information for each board */
|
|
|
|
struct eth16i_local {
|
|
unsigned char tx_started;
|
|
unsigned char tx_buf_busy;
|
|
unsigned short tx_queue; /* Number of packets in transmit buffer */
|
|
unsigned short tx_queue_len;
|
|
unsigned int tx_buf_size;
|
|
unsigned long open_time;
|
|
unsigned long tx_buffered_packets;
|
|
unsigned long tx_buffered_bytes;
|
|
unsigned long col_16;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
/* Function prototypes */
|
|
|
|
static int eth16i_probe1(struct net_device *dev, int ioaddr);
|
|
static int eth16i_check_signature(int ioaddr);
|
|
static int eth16i_probe_port(int ioaddr);
|
|
static void eth16i_set_port(int ioaddr, int porttype);
|
|
static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l);
|
|
static int eth16i_receive_probe_packet(int ioaddr);
|
|
static int eth16i_get_irq(int ioaddr);
|
|
static int eth16i_read_eeprom(int ioaddr, int offset);
|
|
static int eth16i_read_eeprom_word(int ioaddr);
|
|
static void eth16i_eeprom_cmd(int ioaddr, unsigned char command);
|
|
static int eth16i_open(struct net_device *dev);
|
|
static int eth16i_close(struct net_device *dev);
|
|
static int eth16i_tx(struct sk_buff *skb, struct net_device *dev);
|
|
static void eth16i_rx(struct net_device *dev);
|
|
static void eth16i_timeout(struct net_device *dev);
|
|
static irqreturn_t eth16i_interrupt(int irq, void *dev_id);
|
|
static void eth16i_reset(struct net_device *dev);
|
|
static void eth16i_timeout(struct net_device *dev);
|
|
static void eth16i_skip_packet(struct net_device *dev);
|
|
static void eth16i_multicast(struct net_device *dev);
|
|
static void eth16i_select_regbank(unsigned char regbank, int ioaddr);
|
|
static void eth16i_initialize(struct net_device *dev, int boot);
|
|
|
|
#if 0
|
|
static int eth16i_set_irq(struct net_device *dev);
|
|
#endif
|
|
|
|
#ifdef MODULE
|
|
static ushort eth16i_parse_mediatype(const char* s);
|
|
#endif
|
|
|
|
static char cardname[] __initdata = "ICL EtherTeam 16i/32";
|
|
|
|
static int __init do_eth16i_probe(struct net_device *dev)
|
|
{
|
|
int i;
|
|
int ioaddr;
|
|
int base_addr = dev->base_addr;
|
|
|
|
if(eth16i_debug > 4)
|
|
printk(KERN_DEBUG "Probing started for %s\n", cardname);
|
|
|
|
if(base_addr > 0x1ff) /* Check only single location */
|
|
return eth16i_probe1(dev, base_addr);
|
|
else if(base_addr != 0) /* Don't probe at all */
|
|
return -ENXIO;
|
|
|
|
/* Seek card from the ISA io address space */
|
|
for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++)
|
|
if(eth16i_probe1(dev, ioaddr) == 0)
|
|
return 0;
|
|
|
|
/* Seek card from the EISA io address space */
|
|
for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++)
|
|
if(eth16i_probe1(dev, ioaddr) == 0)
|
|
return 0;
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
#ifndef MODULE
|
|
struct net_device * __init eth16i_probe(int unit)
|
|
{
|
|
struct net_device *dev = alloc_etherdev(sizeof(struct eth16i_local));
|
|
int err;
|
|
|
|
if (!dev)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
sprintf(dev->name, "eth%d", unit);
|
|
netdev_boot_setup_check(dev);
|
|
|
|
err = do_eth16i_probe(dev);
|
|
if (err)
|
|
goto out;
|
|
return dev;
|
|
out:
|
|
free_netdev(dev);
|
|
return ERR_PTR(err);
|
|
}
|
|
#endif
|
|
|
|
static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
static unsigned version_printed;
|
|
int retval;
|
|
|
|
/* Let's grab the region */
|
|
if (!request_region(ioaddr, ETH16I_IO_EXTENT, cardname))
|
|
return -EBUSY;
|
|
|
|
/*
|
|
The MB86985 chip has on register which holds information in which
|
|
io address the chip lies. First read this register and compare
|
|
it to our current io address and if match then this could
|
|
be our chip.
|
|
*/
|
|
|
|
if(ioaddr < 0x1000) {
|
|
if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)]
|
|
!= ioaddr) {
|
|
retval = -ENODEV;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Now we will go a bit deeper and try to find the chip's signature */
|
|
|
|
if(eth16i_check_signature(ioaddr) != 0) {
|
|
retval = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
Now it seems that we have found a ethernet chip in this particular
|
|
ioaddr. The MB86985 chip has this feature, that when you read a
|
|
certain register it will increase it's io base address to next
|
|
configurable slot. Now when we have found the chip, first thing is
|
|
to make sure that the chip's ioaddr will hold still here.
|
|
*/
|
|
|
|
eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
|
|
outb(0x00, ioaddr + TRANSCEIVER_MODE_REG);
|
|
|
|
outb(0x00, ioaddr + RESET); /* Reset some parts of chip */
|
|
BITSET(ioaddr + CONFIG_REG_0, BIT(7)); /* Disable the data link */
|
|
|
|
if( (eth16i_debug & version_printed++) == 0)
|
|
printk(KERN_INFO "%s", version);
|
|
|
|
dev->base_addr = ioaddr;
|
|
dev->irq = eth16i_get_irq(ioaddr);
|
|
|
|
/* Try to obtain interrupt vector */
|
|
|
|
if ((retval = request_irq(dev->irq, (void *)ð16i_interrupt, 0, cardname, dev))) {
|
|
printk(KERN_WARNING "%s at %#3x, but is unusable due to conflicting IRQ %d.\n",
|
|
cardname, ioaddr, dev->irq);
|
|
goto out;
|
|
}
|
|
|
|
printk(KERN_INFO "%s: %s at %#3x, IRQ %d, ",
|
|
dev->name, cardname, ioaddr, dev->irq);
|
|
|
|
|
|
/* Now we will have to lock the chip's io address */
|
|
eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
|
|
outb(0x38, ioaddr + TRANSCEIVER_MODE_REG);
|
|
|
|
eth16i_initialize(dev, 1); /* Initialize rest of the chip's registers */
|
|
|
|
/* Now let's same some energy by shutting down the chip ;) */
|
|
BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
|
|
|
|
/* Initialize the device structure */
|
|
memset(lp, 0, sizeof(struct eth16i_local));
|
|
dev->open = eth16i_open;
|
|
dev->stop = eth16i_close;
|
|
dev->hard_start_xmit = eth16i_tx;
|
|
dev->set_multicast_list = eth16i_multicast;
|
|
dev->tx_timeout = eth16i_timeout;
|
|
dev->watchdog_timeo = TX_TIMEOUT;
|
|
spin_lock_init(&lp->lock);
|
|
|
|
retval = register_netdev(dev);
|
|
if (retval)
|
|
goto out1;
|
|
return 0;
|
|
out1:
|
|
free_irq(dev->irq, dev);
|
|
out:
|
|
release_region(ioaddr, ETH16I_IO_EXTENT);
|
|
return retval;
|
|
}
|
|
|
|
|
|
static void eth16i_initialize(struct net_device *dev, int boot)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
int i, node_w = 0;
|
|
unsigned char node_byte = 0;
|
|
|
|
/* Setup station address */
|
|
eth16i_select_regbank(NODE_ID_RB, ioaddr);
|
|
for(i = 0 ; i < 3 ; i++) {
|
|
unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i);
|
|
((unsigned short *)dev->dev_addr)[i] = ntohs(node_val);
|
|
}
|
|
|
|
for(i = 0; i < 6; i++) {
|
|
outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i);
|
|
if(boot) {
|
|
printk("%02x", inb(ioaddr + NODE_ID_0 + i));
|
|
if(i != 5)
|
|
printk(":");
|
|
}
|
|
}
|
|
|
|
/* Now we will set multicast addresses to accept none */
|
|
eth16i_select_regbank(HASH_TABLE_RB, ioaddr);
|
|
for(i = 0; i < 8; i++)
|
|
outb(0x00, ioaddr + HASH_TABLE_0 + i);
|
|
|
|
/*
|
|
Now let's disable the transmitter and receiver, set the buffer ram
|
|
cycle time, bus width and buffer data path width. Also we shall
|
|
set transmit buffer size and total buffer size.
|
|
*/
|
|
|
|
eth16i_select_regbank(2, ioaddr);
|
|
|
|
node_byte = 0;
|
|
node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG);
|
|
|
|
if( (node_w & 0xFF00) == 0x0800)
|
|
node_byte |= BUFFER_WIDTH_8;
|
|
|
|
node_byte |= SRAM_BS1;
|
|
|
|
if( (node_w & 0x00FF) == 64)
|
|
node_byte |= SRAM_BS0;
|
|
|
|
node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
|
|
|
|
outb(node_byte, ioaddr + CONFIG_REG_0);
|
|
|
|
/* We shall halt the transmitting, if 16 collisions are detected */
|
|
outb(HALT_ON_16, ioaddr + COL_16_REG);
|
|
|
|
#ifdef MODULE
|
|
/* if_port already set by init_module() */
|
|
#else
|
|
dev->if_port = (dev->mem_start < E_PORT_FROM_EPROM) ?
|
|
dev->mem_start : E_PORT_FROM_EPROM;
|
|
#endif
|
|
|
|
/* Set interface port type */
|
|
if(boot) {
|
|
char *porttype[] = {"BNC", "DIX", "TP", "AUTO", "FROM_EPROM" };
|
|
|
|
switch(dev->if_port)
|
|
{
|
|
|
|
case E_PORT_FROM_EPROM:
|
|
dev->if_port = eth16i_read_eeprom(ioaddr, E_PORT_SELECT);
|
|
break;
|
|
|
|
case E_PORT_AUTO:
|
|
dev->if_port = eth16i_probe_port(ioaddr);
|
|
break;
|
|
|
|
case E_PORT_BNC:
|
|
case E_PORT_TP:
|
|
case E_PORT_DIX:
|
|
break;
|
|
}
|
|
|
|
printk(" %s interface.\n", porttype[dev->if_port]);
|
|
|
|
eth16i_set_port(ioaddr, dev->if_port);
|
|
}
|
|
|
|
/* Set Receive Mode to normal operation */
|
|
outb(MODE_2, ioaddr + RECEIVE_MODE_REG);
|
|
}
|
|
|
|
static int eth16i_probe_port(int ioaddr)
|
|
{
|
|
int i;
|
|
int retcode;
|
|
unsigned char dummy_packet[64];
|
|
|
|
/* Powerup the chip */
|
|
outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
|
|
|
|
BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
|
|
eth16i_select_regbank(NODE_ID_RB, ioaddr);
|
|
|
|
for(i = 0; i < 6; i++) {
|
|
dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i);
|
|
dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i);
|
|
}
|
|
|
|
dummy_packet[12] = 0x00;
|
|
dummy_packet[13] = 0x04;
|
|
memset(dummy_packet + 14, 0, sizeof(dummy_packet) - 14);
|
|
|
|
eth16i_select_regbank(2, ioaddr);
|
|
|
|
for(i = 0; i < 3; i++) {
|
|
BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
eth16i_set_port(ioaddr, i);
|
|
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "Set port number %d\n", i);
|
|
|
|
retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64);
|
|
if(retcode == 0) {
|
|
retcode = eth16i_receive_probe_packet(ioaddr);
|
|
if(retcode != -1) {
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "Eth16i interface port found at %d\n", i);
|
|
return i;
|
|
}
|
|
}
|
|
else {
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "TRANSMIT_DONE timeout when probing interface port\n");
|
|
}
|
|
}
|
|
|
|
if( eth16i_debug > 1)
|
|
printk(KERN_DEBUG "Using default port\n");
|
|
|
|
return E_PORT_BNC;
|
|
}
|
|
|
|
static void eth16i_set_port(int ioaddr, int porttype)
|
|
{
|
|
unsigned short temp = 0;
|
|
|
|
eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
|
|
outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG);
|
|
|
|
temp |= DIS_AUTO_PORT_SEL;
|
|
|
|
switch(porttype) {
|
|
|
|
case E_PORT_BNC :
|
|
temp |= AUI_SELECT;
|
|
break;
|
|
|
|
case E_PORT_TP :
|
|
break;
|
|
|
|
case E_PORT_DIX :
|
|
temp |= AUI_SELECT;
|
|
BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT);
|
|
break;
|
|
}
|
|
|
|
outb(temp, ioaddr + TRANSCEIVER_MODE_REG);
|
|
|
|
if(eth16i_debug > 1) {
|
|
printk(KERN_DEBUG "TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG));
|
|
printk(KERN_DEBUG "TRANSCEIVER_MODE_REG = %x\n",
|
|
inb(ioaddr+TRANSCEIVER_MODE_REG));
|
|
}
|
|
}
|
|
|
|
static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l)
|
|
{
|
|
unsigned long starttime;
|
|
|
|
outb(0xff, ioaddr + TX_STATUS_REG);
|
|
|
|
outw(l, ioaddr + DATAPORT);
|
|
outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1);
|
|
|
|
starttime = jiffies;
|
|
outb(TX_START | 1, ioaddr + TRANSMIT_START_REG);
|
|
|
|
while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) {
|
|
if( time_after(jiffies, starttime + TX_TIMEOUT)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eth16i_receive_probe_packet(int ioaddr)
|
|
{
|
|
unsigned long starttime;
|
|
|
|
starttime = jiffies;
|
|
|
|
while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) {
|
|
if( time_after(jiffies, starttime + TX_TIMEOUT)) {
|
|
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "Timeout occurred waiting transmit packet received\n");
|
|
starttime = jiffies;
|
|
while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) {
|
|
if( time_after(jiffies, starttime + TX_TIMEOUT)) {
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "Timeout occurred waiting receive packet\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "RECEIVE_PACKET\n");
|
|
return(0); /* Found receive packet */
|
|
}
|
|
}
|
|
|
|
if(eth16i_debug > 1) {
|
|
printk(KERN_DEBUG "TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG));
|
|
printk(KERN_DEBUG "RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG));
|
|
}
|
|
|
|
return(0); /* Return success */
|
|
}
|
|
|
|
#if 0
|
|
static int eth16i_set_irq(struct net_device* dev)
|
|
{
|
|
const int ioaddr = dev->base_addr;
|
|
const int irq = dev->irq;
|
|
int i = 0;
|
|
|
|
if(ioaddr < 0x1000) {
|
|
while(eth16i_irqmap[i] && eth16i_irqmap[i] != irq)
|
|
i++;
|
|
|
|
if(i < NUM_OF_ISA_IRQS) {
|
|
u8 cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
|
|
cbyte = (cbyte & 0x3F) | (i << 6);
|
|
outb(cbyte, ioaddr + JUMPERLESS_CONFIG);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
printk(KERN_NOTICE "%s: EISA Interrupt cannot be set. Use EISA Configuration utility.\n", dev->name);
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
#endif
|
|
|
|
static int __init eth16i_get_irq(int ioaddr)
|
|
{
|
|
unsigned char cbyte;
|
|
|
|
if( ioaddr < 0x1000) {
|
|
cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
|
|
return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] );
|
|
} else { /* Oh..the card is EISA so method getting IRQ different */
|
|
unsigned short index = 0;
|
|
cbyte = inb(ioaddr + EISA_IRQ_REG);
|
|
while( (cbyte & 0x01) == 0) {
|
|
cbyte = cbyte >> 1;
|
|
index++;
|
|
}
|
|
return( eth32i_irqmap[ index ] );
|
|
}
|
|
}
|
|
|
|
static int __init eth16i_check_signature(int ioaddr)
|
|
{
|
|
int i;
|
|
unsigned char creg[4] = { 0 };
|
|
|
|
for(i = 0; i < 4 ; i++) {
|
|
|
|
creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i);
|
|
|
|
if(eth16i_debug > 1)
|
|
printk("eth16i: read signature byte %x at %x\n",
|
|
creg[i],
|
|
ioaddr + TRANSMIT_MODE_REG + i);
|
|
}
|
|
|
|
creg[0] &= 0x0F; /* Mask collision cnr */
|
|
creg[2] &= 0x7F; /* Mask DCLEN bit */
|
|
|
|
#if 0
|
|
/*
|
|
This was removed because the card was sometimes left to state
|
|
from which it couldn't be find anymore. If there is need
|
|
to more strict check still this have to be fixed.
|
|
*/
|
|
if( ! ((creg[0] == 0x06) && (creg[1] == 0x41)) ) {
|
|
if(creg[1] != 0x42)
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if( !((creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
|
|
creg[2] &= 0x40;
|
|
creg[3] &= 0x03;
|
|
|
|
if( !((creg[2] == 0x40) && (creg[3] == 0x00)) )
|
|
return -1;
|
|
}
|
|
|
|
if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0)
|
|
return -1;
|
|
|
|
if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eth16i_read_eeprom(int ioaddr, int offset)
|
|
{
|
|
int data = 0;
|
|
|
|
eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset);
|
|
outb(CS_1, ioaddr + EEPROM_CTRL_REG);
|
|
data = eth16i_read_eeprom_word(ioaddr);
|
|
outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
|
|
|
|
return(data);
|
|
}
|
|
|
|
static int eth16i_read_eeprom_word(int ioaddr)
|
|
{
|
|
int i;
|
|
int data = 0;
|
|
|
|
for(i = 16; i > 0; i--) {
|
|
outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
|
|
eeprom_slow_io();
|
|
outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
|
|
eeprom_slow_io();
|
|
data = (data << 1) |
|
|
((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0);
|
|
|
|
eeprom_slow_io();
|
|
}
|
|
|
|
return(data);
|
|
}
|
|
|
|
static void eth16i_eeprom_cmd(int ioaddr, unsigned char command)
|
|
{
|
|
int i;
|
|
|
|
outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
|
|
outb(DI_0, ioaddr + EEPROM_DATA_REG);
|
|
outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
|
|
outb(DI_1, ioaddr + EEPROM_DATA_REG);
|
|
outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
|
|
|
|
for(i = 7; i >= 0; i--) {
|
|
short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 );
|
|
outb(cmd, ioaddr + EEPROM_DATA_REG);
|
|
outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
|
|
eeprom_slow_io();
|
|
outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
|
|
eeprom_slow_io();
|
|
}
|
|
}
|
|
|
|
static int eth16i_open(struct net_device *dev)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
int ioaddr = dev->base_addr;
|
|
|
|
/* Powerup the chip */
|
|
outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
|
|
|
|
/* Initialize the chip */
|
|
eth16i_initialize(dev, 0);
|
|
|
|
/* Set the transmit buffer size */
|
|
lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03];
|
|
|
|
if(eth16i_debug > 0)
|
|
printk(KERN_DEBUG "%s: transmit buffer size %d\n",
|
|
dev->name, lp->tx_buf_size);
|
|
|
|
/* Now enable Transmitter and Receiver sections */
|
|
BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
|
|
/* Now switch to register bank 2, for run time operation */
|
|
eth16i_select_regbank(2, ioaddr);
|
|
|
|
lp->open_time = jiffies;
|
|
lp->tx_started = 0;
|
|
lp->tx_queue = 0;
|
|
lp->tx_queue_len = 0;
|
|
|
|
/* Turn on interrupts*/
|
|
outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
|
|
|
|
netif_start_queue(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int eth16i_close(struct net_device *dev)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
int ioaddr = dev->base_addr;
|
|
|
|
eth16i_reset(dev);
|
|
|
|
/* Turn off interrupts*/
|
|
outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
lp->open_time = 0;
|
|
|
|
/* Disable transmit and receive */
|
|
BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
|
|
/* Reset the chip */
|
|
/* outb(0xff, ioaddr + RESET); */
|
|
/* outw(0xffff, ioaddr + TX_STATUS_REG); */
|
|
|
|
outb(0x00, ioaddr + CONFIG_REG_1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void eth16i_timeout(struct net_device *dev)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
int ioaddr = dev->base_addr;
|
|
/*
|
|
If we get here, some higher level has decided that
|
|
we are broken. There should really be a "kick me"
|
|
function call instead.
|
|
*/
|
|
|
|
outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
|
|
printk(KERN_WARNING "%s: transmit timed out with status %04x, %s ?\n",
|
|
dev->name,
|
|
inw(ioaddr + TX_STATUS_REG), (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ?
|
|
"IRQ conflict" : "network cable problem");
|
|
|
|
dev->trans_start = jiffies;
|
|
|
|
/* Let's dump all registers */
|
|
if(eth16i_debug > 0) {
|
|
printk(KERN_DEBUG "%s: timeout: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
|
|
dev->name, inb(ioaddr + 0),
|
|
inb(ioaddr + 1), inb(ioaddr + 2),
|
|
inb(ioaddr + 3), inb(ioaddr + 4),
|
|
inb(ioaddr + 5),
|
|
inb(ioaddr + 6), inb(ioaddr + 7));
|
|
|
|
printk(KERN_DEBUG "%s: transmit start reg: %02x. collision reg %02x\n",
|
|
dev->name, inb(ioaddr + TRANSMIT_START_REG),
|
|
inb(ioaddr + COL_16_REG));
|
|
printk(KERN_DEBUG "lp->tx_queue = %d\n", lp->tx_queue);
|
|
printk(KERN_DEBUG "lp->tx_queue_len = %d\n", lp->tx_queue_len);
|
|
printk(KERN_DEBUG "lp->tx_started = %d\n", lp->tx_started);
|
|
}
|
|
dev->stats.tx_errors++;
|
|
eth16i_reset(dev);
|
|
dev->trans_start = jiffies;
|
|
outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
|
|
netif_wake_queue(dev);
|
|
}
|
|
|
|
static int eth16i_tx(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
int ioaddr = dev->base_addr;
|
|
int status = 0;
|
|
ushort length = skb->len;
|
|
unsigned char *buf;
|
|
unsigned long flags;
|
|
|
|
if (length < ETH_ZLEN) {
|
|
if (skb_padto(skb, ETH_ZLEN))
|
|
return 0;
|
|
length = ETH_ZLEN;
|
|
}
|
|
buf = skb->data;
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
/* Turn off TX interrupts */
|
|
outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
|
|
|
|
/* We would be better doing the disable_irq tricks the 3c509 does,
|
|
that would make this suck a lot less */
|
|
|
|
spin_lock_irqsave(&lp->lock, flags);
|
|
|
|
if( (length + 2) > (lp->tx_buf_size - lp->tx_queue_len)) {
|
|
if(eth16i_debug > 0)
|
|
printk(KERN_WARNING "%s: Transmit buffer full.\n", dev->name);
|
|
}
|
|
else {
|
|
outw(length, ioaddr + DATAPORT);
|
|
|
|
if( ioaddr < 0x1000 )
|
|
outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
|
|
else {
|
|
unsigned char frag = length % 4;
|
|
outsl(ioaddr + DATAPORT, buf, length >> 2);
|
|
if( frag != 0 ) {
|
|
outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1);
|
|
if( frag == 3 )
|
|
outsw(ioaddr + DATAPORT,
|
|
(buf + (length & 0xFFFC) + 2), 1);
|
|
}
|
|
}
|
|
lp->tx_buffered_packets++;
|
|
lp->tx_buffered_bytes = length;
|
|
lp->tx_queue++;
|
|
lp->tx_queue_len += length + 2;
|
|
}
|
|
lp->tx_buf_busy = 0;
|
|
|
|
if(lp->tx_started == 0) {
|
|
/* If the transmitter is idle..always trigger a transmit */
|
|
outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
|
|
lp->tx_queue = 0;
|
|
lp->tx_queue_len = 0;
|
|
dev->trans_start = jiffies;
|
|
lp->tx_started = 1;
|
|
netif_wake_queue(dev);
|
|
}
|
|
else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
|
|
/* There is still more room for one more packet in tx buffer */
|
|
netif_wake_queue(dev);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&lp->lock, flags);
|
|
|
|
outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
|
|
/* Turn TX interrupts back on */
|
|
/* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */
|
|
status = 0;
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
static void eth16i_rx(struct net_device *dev)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
int boguscount = MAX_RX_LOOP;
|
|
|
|
/* Loop until all packets have been read */
|
|
while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) {
|
|
|
|
/* Read status byte from receive buffer */
|
|
ushort status = inw(ioaddr + DATAPORT);
|
|
|
|
/* Get the size of the packet from receive buffer */
|
|
ushort pkt_len = inw(ioaddr + DATAPORT);
|
|
|
|
if(eth16i_debug > 4)
|
|
printk(KERN_DEBUG "%s: Receiving packet mode %02x status %04x.\n",
|
|
dev->name,
|
|
inb(ioaddr + RECEIVE_MODE_REG), status);
|
|
|
|
if( !(status & PKT_GOOD) ) {
|
|
dev->stats.rx_errors++;
|
|
|
|
if( (pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN) ) {
|
|
dev->stats.rx_length_errors++;
|
|
eth16i_reset(dev);
|
|
return;
|
|
}
|
|
else {
|
|
eth16i_skip_packet(dev);
|
|
dev->stats.rx_dropped++;
|
|
}
|
|
}
|
|
else { /* Ok so now we should have a good packet */
|
|
struct sk_buff *skb;
|
|
|
|
skb = dev_alloc_skb(pkt_len + 3);
|
|
if( skb == NULL ) {
|
|
printk(KERN_WARNING "%s: Could'n allocate memory for packet (len %d)\n",
|
|
dev->name, pkt_len);
|
|
eth16i_skip_packet(dev);
|
|
dev->stats.rx_dropped++;
|
|
break;
|
|
}
|
|
|
|
skb_reserve(skb,2);
|
|
|
|
/*
|
|
Now let's get the packet out of buffer.
|
|
size is (pkt_len + 1) >> 1, cause we are now reading words
|
|
and it have to be even aligned.
|
|
*/
|
|
|
|
if(ioaddr < 0x1000)
|
|
insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
|
|
(pkt_len + 1) >> 1);
|
|
else {
|
|
unsigned char *buf = skb_put(skb, pkt_len);
|
|
unsigned char frag = pkt_len % 4;
|
|
|
|
insl(ioaddr + DATAPORT, buf, pkt_len >> 2);
|
|
|
|
if(frag != 0) {
|
|
unsigned short rest[2];
|
|
rest[0] = inw( ioaddr + DATAPORT );
|
|
if(frag == 3)
|
|
rest[1] = inw( ioaddr + DATAPORT );
|
|
|
|
memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag);
|
|
}
|
|
}
|
|
|
|
skb->protocol=eth_type_trans(skb, dev);
|
|
|
|
if( eth16i_debug > 5 ) {
|
|
int i;
|
|
printk(KERN_DEBUG "%s: Received packet of length %d.\n",
|
|
dev->name, pkt_len);
|
|
for(i = 0; i < 14; i++)
|
|
printk(KERN_DEBUG " %02x", skb->data[i]);
|
|
printk(KERN_DEBUG ".\n");
|
|
}
|
|
netif_rx(skb);
|
|
dev->stats.rx_packets++;
|
|
dev->stats.rx_bytes += pkt_len;
|
|
|
|
} /* else */
|
|
|
|
if(--boguscount <= 0)
|
|
break;
|
|
|
|
} /* while */
|
|
}
|
|
|
|
static irqreturn_t eth16i_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct net_device *dev = dev_id;
|
|
struct eth16i_local *lp;
|
|
int ioaddr = 0, status;
|
|
int handled = 0;
|
|
|
|
ioaddr = dev->base_addr;
|
|
lp = netdev_priv(dev);
|
|
|
|
/* Turn off all interrupts from adapter */
|
|
outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
|
|
|
|
/* eth16i_tx won't be called */
|
|
spin_lock(&lp->lock);
|
|
|
|
status = inw(ioaddr + TX_STATUS_REG); /* Get the status */
|
|
outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */
|
|
|
|
if (status)
|
|
handled = 1;
|
|
|
|
if(eth16i_debug > 3)
|
|
printk(KERN_DEBUG "%s: Interrupt with status %04x.\n", dev->name, status);
|
|
|
|
if( status & 0x7f00 ) {
|
|
|
|
dev->stats.rx_errors++;
|
|
|
|
if(status & (BUS_RD_ERR << 8) )
|
|
printk(KERN_WARNING "%s: Bus read error.\n",dev->name);
|
|
if(status & (SHORT_PKT_ERR << 8) ) dev->stats.rx_length_errors++;
|
|
if(status & (ALIGN_ERR << 8) ) dev->stats.rx_frame_errors++;
|
|
if(status & (CRC_ERR << 8) ) dev->stats.rx_crc_errors++;
|
|
if(status & (RX_BUF_OVERFLOW << 8) ) dev->stats.rx_over_errors++;
|
|
}
|
|
if( status & 0x001a) {
|
|
|
|
dev->stats.tx_errors++;
|
|
|
|
if(status & CR_LOST) dev->stats.tx_carrier_errors++;
|
|
if(status & TX_JABBER_ERR) dev->stats.tx_window_errors++;
|
|
|
|
#if 0
|
|
if(status & COLLISION) {
|
|
dev->stats.collisions +=
|
|
((inb(ioaddr+TRANSMIT_MODE_REG) & 0xF0) >> 4);
|
|
}
|
|
#endif
|
|
if(status & COLLISIONS_16) {
|
|
if(lp->col_16 < MAX_COL_16) {
|
|
lp->col_16++;
|
|
dev->stats.collisions++;
|
|
/* Resume transmitting, skip failed packet */
|
|
outb(0x02, ioaddr + COL_16_REG);
|
|
}
|
|
else {
|
|
printk(KERN_WARNING "%s: bailing out due to many consecutive 16-in-a-row collisions. Network cable problem?\n", dev->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( status & 0x00ff ) { /* Let's check the transmit status reg */
|
|
|
|
if(status & TX_DONE) { /* The transmit has been done */
|
|
dev->stats.tx_packets = lp->tx_buffered_packets;
|
|
dev->stats.tx_bytes += lp->tx_buffered_bytes;
|
|
lp->col_16 = 0;
|
|
|
|
if(lp->tx_queue) { /* Is there still packets ? */
|
|
/* There was packet(s) so start transmitting and write also
|
|
how many packets there is to be sended */
|
|
outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
|
|
lp->tx_queue = 0;
|
|
lp->tx_queue_len = 0;
|
|
lp->tx_started = 1;
|
|
}
|
|
else {
|
|
lp->tx_started = 0;
|
|
}
|
|
netif_wake_queue(dev);
|
|
}
|
|
}
|
|
|
|
if( ( status & 0x8000 ) ||
|
|
( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) {
|
|
eth16i_rx(dev); /* We have packet in receive buffer */
|
|
}
|
|
|
|
/* Turn interrupts back on */
|
|
outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
|
|
|
|
if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
|
|
/* There is still more room for one more packet in tx buffer */
|
|
netif_wake_queue(dev);
|
|
}
|
|
|
|
spin_unlock(&lp->lock);
|
|
|
|
return IRQ_RETVAL(handled);
|
|
}
|
|
|
|
static void eth16i_skip_packet(struct net_device *dev)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
|
|
inw(ioaddr + DATAPORT);
|
|
inw(ioaddr + DATAPORT);
|
|
inw(ioaddr + DATAPORT);
|
|
|
|
outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG);
|
|
while( inb( ioaddr + FILTER_SELF_RX_REG ) != 0);
|
|
}
|
|
|
|
static void eth16i_reset(struct net_device *dev)
|
|
{
|
|
struct eth16i_local *lp = netdev_priv(dev);
|
|
int ioaddr = dev->base_addr;
|
|
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_DEBUG "%s: Resetting device.\n", dev->name);
|
|
|
|
BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
outw(0xffff, ioaddr + TX_STATUS_REG);
|
|
eth16i_select_regbank(2, ioaddr);
|
|
|
|
lp->tx_started = 0;
|
|
lp->tx_buf_busy = 0;
|
|
lp->tx_queue = 0;
|
|
lp->tx_queue_len = 0;
|
|
BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
|
|
}
|
|
|
|
static void eth16i_multicast(struct net_device *dev)
|
|
{
|
|
int ioaddr = dev->base_addr;
|
|
|
|
if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
|
|
{
|
|
outb(3, ioaddr + RECEIVE_MODE_REG);
|
|
} else {
|
|
outb(2, ioaddr + RECEIVE_MODE_REG);
|
|
}
|
|
}
|
|
|
|
static void eth16i_select_regbank(unsigned char banknbr, int ioaddr)
|
|
{
|
|
unsigned char data;
|
|
|
|
data = inb(ioaddr + CONFIG_REG_1);
|
|
outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1);
|
|
}
|
|
|
|
#ifdef MODULE
|
|
|
|
static ushort eth16i_parse_mediatype(const char* s)
|
|
{
|
|
if(!s)
|
|
return E_PORT_FROM_EPROM;
|
|
|
|
if (!strncmp(s, "bnc", 3))
|
|
return E_PORT_BNC;
|
|
else if (!strncmp(s, "tp", 2))
|
|
return E_PORT_TP;
|
|
else if (!strncmp(s, "dix", 3))
|
|
return E_PORT_DIX;
|
|
else if (!strncmp(s, "auto", 4))
|
|
return E_PORT_AUTO;
|
|
else
|
|
return E_PORT_FROM_EPROM;
|
|
}
|
|
|
|
#define MAX_ETH16I_CARDS 4 /* Max number of Eth16i cards per module */
|
|
|
|
static struct net_device *dev_eth16i[MAX_ETH16I_CARDS];
|
|
static int io[MAX_ETH16I_CARDS];
|
|
#if 0
|
|
static int irq[MAX_ETH16I_CARDS];
|
|
#endif
|
|
static char* mediatype[MAX_ETH16I_CARDS];
|
|
static int debug = -1;
|
|
|
|
MODULE_AUTHOR("Mika Kuoppala <miku@iki.fi>");
|
|
MODULE_DESCRIPTION("ICL EtherTeam 16i/32 driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
module_param_array(io, int, NULL, 0);
|
|
MODULE_PARM_DESC(io, "eth16i I/O base address(es)");
|
|
|
|
#if 0
|
|
module_param_array(irq, int, NULL, 0);
|
|
MODULE_PARM_DESC(irq, "eth16i interrupt request number");
|
|
#endif
|
|
|
|
module_param_array(mediatype, charp, NULL, 0);
|
|
MODULE_PARM_DESC(mediatype, "eth16i media type of interface(s) (bnc,tp,dix,auto,eprom)");
|
|
|
|
module_param(debug, int, 0);
|
|
MODULE_PARM_DESC(debug, "eth16i debug level (0-6)");
|
|
|
|
int __init init_module(void)
|
|
{
|
|
int this_dev, found = 0;
|
|
struct net_device *dev;
|
|
|
|
for (this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) {
|
|
dev = alloc_etherdev(sizeof(struct eth16i_local));
|
|
if (!dev)
|
|
break;
|
|
|
|
dev->base_addr = io[this_dev];
|
|
|
|
if(debug != -1)
|
|
eth16i_debug = debug;
|
|
|
|
if(eth16i_debug > 1)
|
|
printk(KERN_NOTICE "eth16i(%d): interface type %s\n", this_dev, mediatype[this_dev] ? mediatype[this_dev] : "none" );
|
|
|
|
dev->if_port = eth16i_parse_mediatype(mediatype[this_dev]);
|
|
|
|
if(io[this_dev] == 0) {
|
|
if(this_dev != 0) /* Only autoprobe 1st one */
|
|
break;
|
|
|
|
printk(KERN_NOTICE "eth16i.c: Presently autoprobing (not recommended) for a single card.\n");
|
|
}
|
|
|
|
if (do_eth16i_probe(dev) == 0) {
|
|
dev_eth16i[found++] = dev;
|
|
continue;
|
|
}
|
|
printk(KERN_WARNING "eth16i.c No Eth16i card found (i/o = 0x%x).\n",
|
|
io[this_dev]);
|
|
free_netdev(dev);
|
|
break;
|
|
}
|
|
if (found)
|
|
return 0;
|
|
return -ENXIO;
|
|
}
|
|
|
|
void __exit cleanup_module(void)
|
|
{
|
|
int this_dev;
|
|
|
|
for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) {
|
|
struct net_device *dev = dev_eth16i[this_dev];
|
|
|
|
if (netdev_priv(dev)) {
|
|
unregister_netdev(dev);
|
|
free_irq(dev->irq, dev);
|
|
release_region(dev->base_addr, ETH16I_IO_EXTENT);
|
|
free_netdev(dev);
|
|
}
|
|
}
|
|
}
|
|
#endif /* MODULE */
|