 4cd24eaf0c
			
		
	
	
	4cd24eaf0c
	
	
	
		
			
			This patch replaces dev->mc_count in all drivers (hopefully I didn't miss anything). Used spatch and did small tweaks and conding style changes when it was suitable. Jirka Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
		
			
				
	
	
		
			1482 lines
		
	
	
	
		
			40 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1482 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 netdev_tx_t 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 const struct net_device_ops eth16i_netdev_ops = {
 | |
| 	.ndo_open               = eth16i_open,
 | |
| 	.ndo_stop               = eth16i_close,
 | |
| 	.ndo_start_xmit    	= eth16i_tx,
 | |
| 	.ndo_set_multicast_list = eth16i_multicast,
 | |
| 	.ndo_tx_timeout 	= eth16i_timeout,
 | |
| 	.ndo_change_mtu		= eth_change_mtu,
 | |
| 	.ndo_set_mac_address 	= eth_mac_addr,
 | |
| 	.ndo_validate_addr	= eth_validate_addr,
 | |
| };
 | |
| 
 | |
| 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 */
 | |
| 	dev->netdev_ops         = ð16i_netdev_ops;
 | |
| 	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 netdev_tx_t 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 NETDEV_TX_OK;
 | |
| 		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 NETDEV_TX_OK;
 | |
| }
 | |
| 
 | |
| 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 (!netdev_mc_empty(dev) || 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 */
 |