mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-30 23:58:51 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										103
									
								
								drivers/net/ethernet/8390/8390.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								drivers/net/ethernet/8390/8390.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | |||
| /* 8390 core for usual drivers */ | ||||
| 
 | ||||
| static const char version[] = | ||||
|     "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| int ei_open(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_open(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_open); | ||||
| 
 | ||||
| int ei_close(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_close(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_close); | ||||
| 
 | ||||
| netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||||
| { | ||||
| 	return __ei_start_xmit(skb, dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_start_xmit); | ||||
| 
 | ||||
| struct net_device_stats *ei_get_stats(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_get_stats(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_get_stats); | ||||
| 
 | ||||
| void ei_set_multicast_list(struct net_device *dev) | ||||
| { | ||||
| 	__ei_set_multicast_list(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_set_multicast_list); | ||||
| 
 | ||||
| void ei_tx_timeout(struct net_device *dev) | ||||
| { | ||||
| 	__ei_tx_timeout(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_tx_timeout); | ||||
| 
 | ||||
| irqreturn_t ei_interrupt(int irq, void *dev_id) | ||||
| { | ||||
| 	return __ei_interrupt(irq, dev_id); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_interrupt); | ||||
| 
 | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| void ei_poll(struct net_device *dev) | ||||
| { | ||||
| 	__ei_poll(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(ei_poll); | ||||
| #endif | ||||
| 
 | ||||
| const struct net_device_ops ei_netdev_ops = { | ||||
| 	.ndo_open		= ei_open, | ||||
| 	.ndo_stop		= ei_close, | ||||
| 	.ndo_start_xmit		= ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= ei_tx_timeout, | ||||
| 	.ndo_get_stats		= ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= ei_poll, | ||||
| #endif | ||||
| }; | ||||
| EXPORT_SYMBOL(ei_netdev_ops); | ||||
| 
 | ||||
| struct net_device *__alloc_ei_netdev(int size) | ||||
| { | ||||
| 	struct net_device *dev = ____alloc_ei_netdev(size); | ||||
| 	if (dev) | ||||
| 		dev->netdev_ops = &ei_netdev_ops; | ||||
| 	return dev; | ||||
| } | ||||
| EXPORT_SYMBOL(__alloc_ei_netdev); | ||||
| 
 | ||||
| void NS8390_init(struct net_device *dev, int startp) | ||||
| { | ||||
| 	__NS8390_init(dev, startp); | ||||
| } | ||||
| EXPORT_SYMBOL(NS8390_init); | ||||
| 
 | ||||
| #if defined(MODULE) | ||||
| 
 | ||||
| static int __init ns8390_module_init(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void __exit ns8390_module_exit(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| module_init(ns8390_module_init); | ||||
| module_exit(ns8390_module_exit); | ||||
| #endif /* MODULE */ | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										225
									
								
								drivers/net/ethernet/8390/8390.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								drivers/net/ethernet/8390/8390.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,225 @@ | |||
| /* Generic NS8390 register definitions. */ | ||||
| /* This file is part of Donald Becker's 8390 drivers, and is distributed
 | ||||
|    under the same license. Auto-loading of 8390.o only in v2.2 - Paul G. | ||||
|    Some of these names and comments originated from the Crynwr | ||||
|    packet drivers, which are distributed under the GPL. */ | ||||
| 
 | ||||
| #ifndef _8390_h | ||||
| #define _8390_h | ||||
| 
 | ||||
| #include <linux/if_ether.h> | ||||
| #include <linux/ioport.h> | ||||
| #include <linux/irqreturn.h> | ||||
| #include <linux/skbuff.h> | ||||
| 
 | ||||
| #define TX_PAGES 12	/* Two Tx slots */ | ||||
| 
 | ||||
| /* The 8390 specific per-packet-header format. */ | ||||
| struct e8390_pkt_hdr { | ||||
|   unsigned char status; /* status */ | ||||
|   unsigned char next;   /* pointer to next packet. */ | ||||
|   unsigned short count; /* header + packet length in bytes */ | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| void ei_poll(struct net_device *dev); | ||||
| void eip_poll(struct net_device *dev); | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /* Without I/O delay - non ISA or later chips */ | ||||
| void NS8390_init(struct net_device *dev, int startp); | ||||
| int ei_open(struct net_device *dev); | ||||
| int ei_close(struct net_device *dev); | ||||
| irqreturn_t ei_interrupt(int irq, void *dev_id); | ||||
| void ei_tx_timeout(struct net_device *dev); | ||||
| netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev); | ||||
| void ei_set_multicast_list(struct net_device *dev); | ||||
| struct net_device_stats *ei_get_stats(struct net_device *dev); | ||||
| 
 | ||||
| extern const struct net_device_ops ei_netdev_ops; | ||||
| 
 | ||||
| struct net_device *__alloc_ei_netdev(int size); | ||||
| static inline struct net_device *alloc_ei_netdev(void) | ||||
| { | ||||
| 	return __alloc_ei_netdev(0); | ||||
| } | ||||
| 
 | ||||
| /* With I/O delay form */ | ||||
| void NS8390p_init(struct net_device *dev, int startp); | ||||
| int eip_open(struct net_device *dev); | ||||
| int eip_close(struct net_device *dev); | ||||
| irqreturn_t eip_interrupt(int irq, void *dev_id); | ||||
| void eip_tx_timeout(struct net_device *dev); | ||||
| netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev); | ||||
| void eip_set_multicast_list(struct net_device *dev); | ||||
| struct net_device_stats *eip_get_stats(struct net_device *dev); | ||||
| 
 | ||||
| extern const struct net_device_ops eip_netdev_ops; | ||||
| 
 | ||||
| struct net_device *__alloc_eip_netdev(int size); | ||||
| static inline struct net_device *alloc_eip_netdev(void) | ||||
| { | ||||
| 	return __alloc_eip_netdev(0); | ||||
| } | ||||
| 
 | ||||
| /* You have one of these per-board */ | ||||
| struct ei_device { | ||||
| 	const char *name; | ||||
| 	void (*reset_8390)(struct net_device *); | ||||
| 	void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int); | ||||
| 	void (*block_output)(struct net_device *, int, const unsigned char *, int); | ||||
| 	void (*block_input)(struct net_device *, int, struct sk_buff *, int); | ||||
| 	unsigned long rmem_start; | ||||
| 	unsigned long rmem_end; | ||||
| 	void __iomem *mem; | ||||
| 	unsigned char mcfilter[8]; | ||||
| 	unsigned open:1; | ||||
| 	unsigned word16:1;  		/* We have the 16-bit (vs 8-bit) version of the card. */ | ||||
| 	unsigned bigendian:1;		/* 16-bit big endian mode. Do NOT */ | ||||
| 					/* set this on random 8390 clones! */ | ||||
| 	unsigned txing:1;		/* Transmit Active */ | ||||
| 	unsigned irqlock:1;		/* 8390's intrs disabled when '1'. */ | ||||
| 	unsigned dmaing:1;		/* Remote DMA Active */ | ||||
| 	unsigned char tx_start_page, rx_start_page, stop_page; | ||||
| 	unsigned char current_page;	/* Read pointer in buffer  */ | ||||
| 	unsigned char interface_num;	/* Net port (AUI, 10bT.) to use. */ | ||||
| 	unsigned char txqueue;		/* Tx Packet buffer queue length. */ | ||||
| 	short tx1, tx2;			/* Packet lengths for ping-pong tx. */ | ||||
| 	short lasttx;			/* Alpha version consistency check. */ | ||||
| 	unsigned char reg0;		/* Register '0' in a WD8013 */ | ||||
| 	unsigned char reg5;		/* Register '5' in a WD8013 */ | ||||
| 	unsigned char saved_irq;	/* Original dev->irq value. */ | ||||
| 	u32 *reg_offset;		/* Register mapping table */ | ||||
| 	spinlock_t page_lock;		/* Page register locks */ | ||||
| 	unsigned long priv;		/* Private field to store bus IDs etc. */ | ||||
| 	u32 msg_enable;			/* debug message level */ | ||||
| #ifdef AX88796_PLATFORM | ||||
| 	unsigned char rxcr_base;	/* default value for RXCR */ | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| /* The maximum number of 8390 interrupt service routines called per IRQ. */ | ||||
| #define MAX_SERVICE 12 | ||||
| 
 | ||||
| /* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */ | ||||
| #define TX_TIMEOUT (20*HZ/100) | ||||
| 
 | ||||
| #define ei_status (*(struct ei_device *)netdev_priv(dev)) | ||||
| 
 | ||||
| /* Some generic ethernet register configurations. */ | ||||
| #define E8390_TX_IRQ_MASK	0xa	/* For register EN0_ISR */ | ||||
| #define E8390_RX_IRQ_MASK	0x5 | ||||
| 
 | ||||
| #ifdef AX88796_PLATFORM | ||||
| #define E8390_RXCONFIG		(ei_status.rxcr_base | 0x04) | ||||
| #define E8390_RXOFF		(ei_status.rxcr_base | 0x20) | ||||
| #else | ||||
| #define E8390_RXCONFIG		0x4	/* EN0_RXCR: broadcasts, no multicast,errors */ | ||||
| #define E8390_RXOFF		0x20	/* EN0_RXCR: Accept no packets */ | ||||
| #endif | ||||
| 
 | ||||
| #define E8390_TXCONFIG		0x00	/* EN0_TXCR: Normal transmit mode */ | ||||
| #define E8390_TXOFF		0x02	/* EN0_TXCR: Transmitter off */ | ||||
| 
 | ||||
| 
 | ||||
| /*  Register accessed at EN_CMD, the 8390 base addr.  */ | ||||
| #define E8390_STOP	0x01	/* Stop and reset the chip */ | ||||
| #define E8390_START	0x02	/* Start the chip, clear reset */ | ||||
| #define E8390_TRANS	0x04	/* Transmit a frame */ | ||||
| #define E8390_RREAD	0x08	/* Remote read */ | ||||
| #define E8390_RWRITE	0x10	/* Remote write  */ | ||||
| #define E8390_NODMA	0x20	/* Remote DMA */ | ||||
| #define E8390_PAGE0	0x00	/* Select page chip registers */ | ||||
| #define E8390_PAGE1	0x40	/* using the two high-order bits */ | ||||
| #define E8390_PAGE2	0x80	/* Page 3 is invalid. */ | ||||
| 
 | ||||
| /*
 | ||||
|  *	Only generate indirect loads given a machine that needs them. | ||||
|  *      - removed AMIGA_PCMCIA from this list, handled as ISA io now | ||||
|  *	- the _p for generates no delay by default 8390p.c overrides this. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef ei_inb | ||||
| #define ei_inb(_p)	inb(_p) | ||||
| #define ei_outb(_v,_p)	outb(_v,_p) | ||||
| #define ei_inb_p(_p)	inb(_p) | ||||
| #define ei_outb_p(_v,_p) outb(_v,_p) | ||||
| #endif | ||||
| 
 | ||||
| #ifndef EI_SHIFT | ||||
| #define EI_SHIFT(x)	(x) | ||||
| #endif | ||||
| 
 | ||||
| #define E8390_CMD	EI_SHIFT(0x00)  /* The command register (for all pages) */ | ||||
| /* Page 0 register offsets. */ | ||||
| #define EN0_CLDALO	EI_SHIFT(0x01)	/* Low byte of current local dma addr  RD */ | ||||
| #define EN0_STARTPG	EI_SHIFT(0x01)	/* Starting page of ring bfr WR */ | ||||
| #define EN0_CLDAHI	EI_SHIFT(0x02)	/* High byte of current local dma addr  RD */ | ||||
| #define EN0_STOPPG	EI_SHIFT(0x02)	/* Ending page +1 of ring bfr WR */ | ||||
| #define EN0_BOUNDARY	EI_SHIFT(0x03)	/* Boundary page of ring bfr RD WR */ | ||||
| #define EN0_TSR		EI_SHIFT(0x04)	/* Transmit status reg RD */ | ||||
| #define EN0_TPSR	EI_SHIFT(0x04)	/* Transmit starting page WR */ | ||||
| #define EN0_NCR		EI_SHIFT(0x05)	/* Number of collision reg RD */ | ||||
| #define EN0_TCNTLO	EI_SHIFT(0x05)	/* Low  byte of tx byte count WR */ | ||||
| #define EN0_FIFO	EI_SHIFT(0x06)	/* FIFO RD */ | ||||
| #define EN0_TCNTHI	EI_SHIFT(0x06)	/* High byte of tx byte count WR */ | ||||
| #define EN0_ISR		EI_SHIFT(0x07)	/* Interrupt status reg RD WR */ | ||||
| #define EN0_CRDALO	EI_SHIFT(0x08)	/* low byte of current remote dma address RD */ | ||||
| #define EN0_RSARLO	EI_SHIFT(0x08)	/* Remote start address reg 0 */ | ||||
| #define EN0_CRDAHI	EI_SHIFT(0x09)	/* high byte, current remote dma address RD */ | ||||
| #define EN0_RSARHI	EI_SHIFT(0x09)	/* Remote start address reg 1 */ | ||||
| #define EN0_RCNTLO	EI_SHIFT(0x0a)	/* Remote byte count reg WR */ | ||||
| #define EN0_RCNTHI	EI_SHIFT(0x0b)	/* Remote byte count reg WR */ | ||||
| #define EN0_RSR		EI_SHIFT(0x0c)	/* rx status reg RD */ | ||||
| #define EN0_RXCR	EI_SHIFT(0x0c)	/* RX configuration reg WR */ | ||||
| #define EN0_TXCR	EI_SHIFT(0x0d)	/* TX configuration reg WR */ | ||||
| #define EN0_COUNTER0	EI_SHIFT(0x0d)	/* Rcv alignment error counter RD */ | ||||
| #define EN0_DCFG	EI_SHIFT(0x0e)	/* Data configuration reg WR */ | ||||
| #define EN0_COUNTER1	EI_SHIFT(0x0e)	/* Rcv CRC error counter RD */ | ||||
| #define EN0_IMR		EI_SHIFT(0x0f)	/* Interrupt mask reg WR */ | ||||
| #define EN0_COUNTER2	EI_SHIFT(0x0f)	/* Rcv missed frame error counter RD */ | ||||
| 
 | ||||
| /* Bits in EN0_ISR - Interrupt status register */ | ||||
| #define ENISR_RX	0x01	/* Receiver, no error */ | ||||
| #define ENISR_TX	0x02	/* Transmitter, no error */ | ||||
| #define ENISR_RX_ERR	0x04	/* Receiver, with error */ | ||||
| #define ENISR_TX_ERR	0x08	/* Transmitter, with error */ | ||||
| #define ENISR_OVER	0x10	/* Receiver overwrote the ring */ | ||||
| #define ENISR_COUNTERS	0x20	/* Counters need emptying */ | ||||
| #define ENISR_RDC	0x40	/* remote dma complete */ | ||||
| #define ENISR_RESET	0x80	/* Reset completed */ | ||||
| #define ENISR_ALL	0x3f	/* Interrupts we will enable */ | ||||
| 
 | ||||
| /* Bits in EN0_DCFG - Data config register */ | ||||
| #define ENDCFG_WTS	0x01	/* word transfer mode selection */ | ||||
| #define ENDCFG_BOS	0x02	/* byte order selection */ | ||||
| 
 | ||||
| /* Page 1 register offsets. */ | ||||
| #define EN1_PHYS   EI_SHIFT(0x01)	/* This board's physical enet addr RD WR */ | ||||
| #define EN1_PHYS_SHIFT(i)  EI_SHIFT(i+1) /* Get and set mac address */ | ||||
| #define EN1_CURPAG EI_SHIFT(0x07)	/* Current memory page RD WR */ | ||||
| #define EN1_MULT   EI_SHIFT(0x08)	/* Multicast filter mask array (8 bytes) RD WR */ | ||||
| #define EN1_MULT_SHIFT(i)  EI_SHIFT(8+i) /* Get and set multicast filter */ | ||||
| 
 | ||||
| /* Bits in received packet status byte and EN0_RSR*/ | ||||
| #define ENRSR_RXOK	0x01	/* Received a good packet */ | ||||
| #define ENRSR_CRC	0x02	/* CRC error */ | ||||
| #define ENRSR_FAE	0x04	/* frame alignment error */ | ||||
| #define ENRSR_FO	0x08	/* FIFO overrun */ | ||||
| #define ENRSR_MPA	0x10	/* missed pkt */ | ||||
| #define ENRSR_PHY	0x20	/* physical/multicast address */ | ||||
| #define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */ | ||||
| #define ENRSR_DEF	0x80	/* deferring */ | ||||
| 
 | ||||
| /* Transmitted packet status, EN0_TSR. */ | ||||
| #define ENTSR_PTX 0x01	/* Packet transmitted without error */ | ||||
| #define ENTSR_ND  0x02	/* The transmit wasn't deferred. */ | ||||
| #define ENTSR_COL 0x04	/* The transmit collided at least once. */ | ||||
| #define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */ | ||||
| #define ENTSR_CRS 0x10	/* The carrier sense was lost. */ | ||||
| #define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */ | ||||
| #define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */ | ||||
| #define ENTSR_OWC 0x80  /* There was an out-of-window collision. */ | ||||
| 
 | ||||
| #endif /* _8390_h */ | ||||
							
								
								
									
										105
									
								
								drivers/net/ethernet/8390/8390p.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								drivers/net/ethernet/8390/8390p.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | |||
| /* 8390 core for ISA devices needing bus delays */ | ||||
| 
 | ||||
| static const char version[] = | ||||
|     "8390p.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| #define ei_inb(_p)	inb(_p) | ||||
| #define ei_outb(_v, _p)	outb(_v, _p) | ||||
| #define ei_inb_p(_p)	inb_p(_p) | ||||
| #define ei_outb_p(_v, _p) outb_p(_v, _p) | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| int eip_open(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_open(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_open); | ||||
| 
 | ||||
| int eip_close(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_close(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_close); | ||||
| 
 | ||||
| netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||||
| { | ||||
| 	return __ei_start_xmit(skb, dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_start_xmit); | ||||
| 
 | ||||
| struct net_device_stats *eip_get_stats(struct net_device *dev) | ||||
| { | ||||
| 	return __ei_get_stats(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_get_stats); | ||||
| 
 | ||||
| void eip_set_multicast_list(struct net_device *dev) | ||||
| { | ||||
| 	__ei_set_multicast_list(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_set_multicast_list); | ||||
| 
 | ||||
| void eip_tx_timeout(struct net_device *dev) | ||||
| { | ||||
| 	__ei_tx_timeout(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_tx_timeout); | ||||
| 
 | ||||
| irqreturn_t eip_interrupt(int irq, void *dev_id) | ||||
| { | ||||
| 	return __ei_interrupt(irq, dev_id); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_interrupt); | ||||
| 
 | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| void eip_poll(struct net_device *dev) | ||||
| { | ||||
| 	__ei_poll(dev); | ||||
| } | ||||
| EXPORT_SYMBOL(eip_poll); | ||||
| #endif | ||||
| 
 | ||||
| const struct net_device_ops eip_netdev_ops = { | ||||
| 	.ndo_open		= eip_open, | ||||
| 	.ndo_stop		= eip_close, | ||||
| 	.ndo_start_xmit		= eip_start_xmit, | ||||
| 	.ndo_tx_timeout		= eip_tx_timeout, | ||||
| 	.ndo_get_stats		= eip_get_stats, | ||||
| 	.ndo_set_rx_mode	= eip_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= eip_poll, | ||||
| #endif | ||||
| }; | ||||
| EXPORT_SYMBOL(eip_netdev_ops); | ||||
| 
 | ||||
| struct net_device *__alloc_eip_netdev(int size) | ||||
| { | ||||
| 	struct net_device *dev = ____alloc_ei_netdev(size); | ||||
| 	if (dev) | ||||
| 		dev->netdev_ops = &eip_netdev_ops; | ||||
| 	return dev; | ||||
| } | ||||
| EXPORT_SYMBOL(__alloc_eip_netdev); | ||||
| 
 | ||||
| void NS8390p_init(struct net_device *dev, int startp) | ||||
| { | ||||
| 	__NS8390_init(dev, startp); | ||||
| } | ||||
| EXPORT_SYMBOL(NS8390p_init); | ||||
| 
 | ||||
| static int __init NS8390p_init_module(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void __exit NS8390p_cleanup_module(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| module_init(NS8390p_init_module); | ||||
| module_exit(NS8390p_cleanup_module); | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										206
									
								
								drivers/net/ethernet/8390/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								drivers/net/ethernet/8390/Kconfig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,206 @@ | |||
| # | ||||
| # 8390 device configuration | ||||
| # | ||||
| 
 | ||||
| config NET_VENDOR_8390 | ||||
| 	bool "National Semi-conductor 8390 devices" | ||||
| 	default y | ||||
| 	depends on NET_VENDOR_NATSEMI | ||||
| 	---help--- | ||||
| 	  If you have a network (Ethernet) card belonging to this class, say Y | ||||
| 	  and read the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>. | ||||
| 
 | ||||
| 	  Note that the answer to this question doesn't directly affect the | ||||
| 	  kernel: saying N will just cause the configurator to skip all | ||||
| 	  the questions about Western Digital cards. If you say Y, you will be | ||||
| 	  asked for your specific card in the following questions. | ||||
| 
 | ||||
| if NET_VENDOR_8390 | ||||
| 
 | ||||
| config PCMCIA_AXNET | ||||
| 	tristate "Asix AX88190 PCMCIA support" | ||||
| 	depends on PCMCIA | ||||
| 	---help--- | ||||
| 	  Say Y here if you intend to attach an Asix AX88190-based PCMCIA | ||||
| 	  (PC-card) Fast Ethernet card to your computer.  These cards are | ||||
| 	  nearly NE2000 compatible but need a separate driver due to a few | ||||
| 	  misfeatures. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module will be | ||||
| 	  called axnet_cs.  If unsure, say N. | ||||
| 
 | ||||
| config AX88796 | ||||
| 	tristate "ASIX AX88796 NE2000 clone support" | ||||
| 	depends on (ARM || MIPS || SUPERH) | ||||
| 	select CRC32 | ||||
| 	select PHYLIB | ||||
| 	select MDIO_BITBANG | ||||
| 	---help--- | ||||
| 	  AX88796 driver, using platform bus to provide | ||||
| 	  chip detection and resources | ||||
| 
 | ||||
| config AX88796_93CX6 | ||||
| 	bool "ASIX AX88796 external 93CX6 eeprom support" | ||||
| 	depends on AX88796 | ||||
| 	select EEPROM_93CX6 | ||||
| 	---help--- | ||||
| 	  Select this if your platform comes with an external 93CX6 eeprom. | ||||
| 
 | ||||
| config HYDRA | ||||
| 	tristate "Hydra support" | ||||
| 	depends on ZORRO | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have a Hydra Ethernet adapter, say Y. Otherwise, say N. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module | ||||
| 	  will be called hydra. | ||||
| 
 | ||||
| config ARM_ETHERH | ||||
| 	tristate "I-cubed EtherH/ANT EtherM support" | ||||
| 	depends on ARM && ARCH_ACORN | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have an Acorn system with one of these network cards, you | ||||
| 	  should say Y to this option if you wish to use it with Linux. | ||||
| 
 | ||||
| config MAC8390 | ||||
| 	bool "Macintosh NS 8390 based ethernet cards" | ||||
| 	depends on MAC | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you want to include a driver to support Nubus or LC-PDS | ||||
| 	  Ethernet cards using an NS8390 chipset or its equivalent, say Y | ||||
| 	  and read the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>. | ||||
| 
 | ||||
| config MCF8390 | ||||
| 	tristate "ColdFire NS8390 based Ethernet support" | ||||
| 	depends on COLDFIRE | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  This driver is for Ethernet devices using an NS8390-compatible | ||||
| 	  chipset on many common ColdFire CPU based boards. Many of the older | ||||
| 	  Freescale dev boards use this, and some other common boards like | ||||
| 	  some SnapGear routers do as well. | ||||
| 
 | ||||
| 	  If you have one of these boards and want to use the network interface | ||||
| 	  on them then choose Y. To compile this driver as a module, choose M | ||||
| 	  here, the module will be called mcf8390. | ||||
| 
 | ||||
| config NE2000 | ||||
| 	tristate "NE2000/NE1000 support" | ||||
| 	depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX || \ | ||||
| 		    ATARI_ETHERNEC) | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have a network (Ethernet) card of this type, say Y and read | ||||
| 	  the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>.  Many Ethernet cards | ||||
| 	  without a specific driver are compatible with NE2000. | ||||
| 
 | ||||
| 	  If you have a PCI NE2000 card however, say N here and Y to "PCI | ||||
| 	  NE2000 and clone support" below. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. The module | ||||
| 	  will be called ne. | ||||
| 
 | ||||
| config NE2K_PCI | ||||
| 	tristate "PCI NE2000 and clones support (see help)" | ||||
| 	depends on PCI | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  This driver is for NE2000 compatible PCI cards. It will not work | ||||
| 	  with ISA NE2000 cards (they have their own driver, "NE2000/NE1000 | ||||
| 	  support" below). If you have a PCI NE2000 network (Ethernet) card, | ||||
| 	  say Y and read the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>. | ||||
| 
 | ||||
| 	  This driver also works for the following NE2000 clone cards: | ||||
| 	  RealTek RTL-8029  Winbond 89C940  Compex RL2000  KTI ET32P2 | ||||
| 	  NetVin NV5000SC   Via 86C926      SureCom NE34   Winbond | ||||
| 	  Holtek HT80232    Holtek HT80229 | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. The module | ||||
| 	  will be called ne2k-pci. | ||||
| 
 | ||||
| config APNE | ||||
| 	tristate "PCMCIA NE2000 support" | ||||
| 	depends on AMIGA_PCMCIA | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have a PCMCIA NE2000 compatible adapter, say Y.  Otherwise, | ||||
| 	  say N. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module | ||||
| 	  will be called apne. | ||||
| 
 | ||||
| config PCMCIA_PCNET | ||||
| 	tristate "NE2000 compatible PCMCIA support" | ||||
| 	depends on PCMCIA | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  Say Y here if you intend to attach an NE2000 compatible PCMCIA | ||||
| 	  (PC-card) Ethernet or Fast Ethernet card to your computer. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module will be | ||||
| 	  called pcnet_cs.  If unsure, say N. | ||||
| 
 | ||||
| config STNIC | ||||
| 	tristate "National DP83902AV  support" | ||||
| 	depends on SUPERH | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  Support for cards based on the National Semiconductor DP83902AV | ||||
| 	  ST-NIC Serial Network Interface Controller for Twisted Pair.  This | ||||
| 	  is a 10Mbit/sec Ethernet controller.  Product overview and specs at | ||||
| 	  <http://www.national.com/pf/DP/DP83902A.html>. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config ULTRA | ||||
| 	tristate "SMC Ultra support" | ||||
| 	depends on ISA | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have a network (Ethernet) card of this type, say Y and read | ||||
| 	  the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>. | ||||
| 
 | ||||
| 	  Important: There have been many reports that, with some motherboards | ||||
| 	  mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, | ||||
| 	  such as some BusLogic models) causes corruption problems with many | ||||
| 	  operating systems. The Linux smc-ultra driver has a work-around for | ||||
| 	  this but keep it in mind if you have such a SCSI card and have | ||||
| 	  problems. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. The module | ||||
| 	  will be called smc-ultra. | ||||
| 
 | ||||
| config WD80x3 | ||||
| 	tristate "WD80*3 support" | ||||
| 	depends on ISA | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  If you have a network (Ethernet) card of this type, say Y and read | ||||
| 	  the Ethernet-HOWTO, available from | ||||
| 	  <http://www.tldp.org/docs.html#howto>. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here. The module | ||||
| 	  will be called wd. | ||||
| 
 | ||||
| config ZORRO8390 | ||||
| 	tristate "Zorro NS8390-based Ethernet support" | ||||
| 	depends on ZORRO | ||||
| 	select CRC32 | ||||
| 	---help--- | ||||
| 	  This driver is for Zorro Ethernet cards using an NS8390-compatible | ||||
| 	  chipset, like the Village Tronic Ariadne II and the Individual | ||||
| 	  Computers X-Surf Ethernet cards. If you have such a card, say Y. | ||||
| 	  Otherwise, say N. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module | ||||
| 	  will be called zorro8390. | ||||
| 
 | ||||
| endif # NET_VENDOR_8390 | ||||
							
								
								
									
										18
									
								
								drivers/net/ethernet/8390/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								drivers/net/ethernet/8390/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #
 | ||||
| # Makefile for the 8390 network device drivers.
 | ||||
| #
 | ||||
| 
 | ||||
| obj-$(CONFIG_MAC8390) += mac8390.o | ||||
| obj-$(CONFIG_APNE) += apne.o 8390.o | ||||
| obj-$(CONFIG_ARM_ETHERH) += etherh.o | ||||
| obj-$(CONFIG_AX88796) += ax88796.o | ||||
| obj-$(CONFIG_HYDRA) += hydra.o 8390.o | ||||
| obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o | ||||
| obj-$(CONFIG_NE2000) += ne.o 8390p.o | ||||
| obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o | ||||
| obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o | ||||
| obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o | ||||
| obj-$(CONFIG_STNIC) += stnic.o 8390.o | ||||
| obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o | ||||
| obj-$(CONFIG_WD80x3) += wd.o 8390.o | ||||
| obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o | ||||
							
								
								
									
										626
									
								
								drivers/net/ethernet/8390/apne.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										626
									
								
								drivers/net/ethernet/8390/apne.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,626 @@ | |||
| /*
 | ||||
|  * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200 | ||||
|  * | ||||
|  * (C) Copyright 1997 Alain Malek | ||||
|  *                    (Alain.Malek@cryogen.com) | ||||
|  * | ||||
|  * ---------------------------------------------------------------------------- | ||||
|  * | ||||
|  * This program is based on | ||||
|  * | ||||
|  * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux | ||||
|  *             Written 1992-94 by Donald Becker. | ||||
|  * | ||||
|  * 8390.c:     A general NS8390 ethernet driver core for linux. | ||||
|  *             Written 1992-94 by Donald Becker. | ||||
|  * | ||||
|  * cnetdevice: A Sana-II ethernet driver for AmigaOS | ||||
|  *             Written by Bruce Abbott (bhabbott@inhb.co.nz) | ||||
|  * | ||||
|  * ---------------------------------------------------------------------------- | ||||
|  * | ||||
|  * This file is subject to the terms and conditions of the GNU General Public | ||||
|  * License.  See the file COPYING in the main directory of the Linux | ||||
|  * distribution for more details. | ||||
|  * | ||||
|  * ---------------------------------------------------------------------------- | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/pci.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/jiffies.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <asm/setup.h> | ||||
| #include <asm/amigaints.h> | ||||
| #include <asm/amigahw.h> | ||||
| #include <asm/amigayle.h> | ||||
| #include <asm/amipcmcia.h> | ||||
| 
 | ||||
| #include "8390.h" | ||||
| 
 | ||||
| /* ---- No user-serviceable parts below ---- */ | ||||
| 
 | ||||
| #define DRV_NAME "apne" | ||||
| 
 | ||||
| #define NE_BASE	 (dev->base_addr) | ||||
| #define NE_CMD	 		0x00 | ||||
| #define NE_DATAPORT		0x10            /* NatSemi-defined port window offset. */ | ||||
| #define NE_RESET		0x1f            /* Issue a read to reset, a write to clear. */ | ||||
| #define NE_IO_EXTENT	        0x20 | ||||
| 
 | ||||
| #define NE_EN0_ISR		0x07 | ||||
| #define NE_EN0_DCFG		0x0e | ||||
| 
 | ||||
| #define NE_EN0_RSARLO	        0x08 | ||||
| #define NE_EN0_RSARHI	        0x09 | ||||
| #define NE_EN0_RCNTLO	        0x0a | ||||
| #define NE_EN0_RXCR		0x0c | ||||
| #define NE_EN0_TXCR		0x0d | ||||
| #define NE_EN0_RCNTHI	        0x0b | ||||
| #define NE_EN0_IMR		0x0f | ||||
| 
 | ||||
| #define NE1SM_START_PG	0x20	/* First page of TX buffer */ | ||||
| #define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */ | ||||
| #define NESM_START_PG	0x40	/* First page of TX buffer */ | ||||
| #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ | ||||
| 
 | ||||
| 
 | ||||
| struct net_device * __init apne_probe(int unit); | ||||
| static int apne_probe1(struct net_device *dev, int ioaddr); | ||||
| 
 | ||||
| static void apne_reset_8390(struct net_device *dev); | ||||
| static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 			  int ring_page); | ||||
| static void apne_block_input(struct net_device *dev, int count, | ||||
| 								struct sk_buff *skb, int ring_offset); | ||||
| static void apne_block_output(struct net_device *dev, const int count, | ||||
| 							const unsigned char *buf, const int start_page); | ||||
| static irqreturn_t apne_interrupt(int irq, void *dev_id); | ||||
| 
 | ||||
| static int init_pcmcia(void); | ||||
| 
 | ||||
| /* IO base address used for nic */ | ||||
| 
 | ||||
| #define IOBASE 0x300 | ||||
| 
 | ||||
| /*
 | ||||
|    use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand | ||||
|    you can find the values to use by looking at the cnet.device | ||||
|    config file example (the default values are for the CNET40BC card) | ||||
| */ | ||||
| 
 | ||||
| /*
 | ||||
| #define MANUAL_CONFIG 0x20 | ||||
| #define MANUAL_OFFSET 0x3f8 | ||||
| 
 | ||||
| #define MANUAL_HWADDR0 0x00 | ||||
| #define MANUAL_HWADDR1 0x12 | ||||
| #define MANUAL_HWADDR2 0x34 | ||||
| #define MANUAL_HWADDR3 0x56 | ||||
| #define MANUAL_HWADDR4 0x78 | ||||
| #define MANUAL_HWADDR5 0x9a | ||||
| */ | ||||
| 
 | ||||
| static const char version[] = | ||||
|     "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n"; | ||||
| 
 | ||||
| static int apne_owned;	/* signal if card already owned */ | ||||
| 
 | ||||
| static u32 apne_msg_enable; | ||||
| module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH)); | ||||
| MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); | ||||
| 
 | ||||
| struct net_device * __init apne_probe(int unit) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	struct ei_device *ei_local; | ||||
| 
 | ||||
| #ifndef MANUAL_CONFIG | ||||
| 	char tuple[8]; | ||||
| #endif | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!MACH_IS_AMIGA) | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 
 | ||||
| 	if (apne_owned) | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 
 | ||||
| 	if ( !(AMIGAHW_PRESENT(PCMCIA)) ) | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 
 | ||||
| 	pr_info("Looking for PCMCIA ethernet card : "); | ||||
| 
 | ||||
| 	/* check if a card is inserted */ | ||||
| 	if (!(PCMCIA_INSERTED)) { | ||||
| 		pr_cont("NO PCMCIA card inserted\n"); | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 	} | ||||
| 
 | ||||
| 	dev = alloc_ei_netdev(); | ||||
| 	if (!dev) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	if (unit >= 0) { | ||||
| 		sprintf(dev->name, "eth%d", unit); | ||||
| 		netdev_boot_setup_check(dev); | ||||
| 	} | ||||
| 	ei_local = netdev_priv(dev); | ||||
| 	ei_local->msg_enable = apne_msg_enable; | ||||
| 
 | ||||
| 	/* disable pcmcia irq for readtuple */ | ||||
| 	pcmcia_disable_irq(); | ||||
| 
 | ||||
| #ifndef MANUAL_CONFIG | ||||
| 	if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) || | ||||
| 		(tuple[2] != CISTPL_FUNCID_NETWORK)) { | ||||
| 		pr_cont("not an ethernet card\n"); | ||||
| 		/* XXX: shouldn't we re-enable irq here? */ | ||||
| 		free_netdev(dev); | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	pr_cont("ethernet PCMCIA card inserted\n"); | ||||
| 
 | ||||
| 	if (!init_pcmcia()) { | ||||
| 		/* XXX: shouldn't we re-enable irq here? */ | ||||
| 		free_netdev(dev); | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!request_region(IOBASE, 0x20, DRV_NAME)) { | ||||
| 		free_netdev(dev); | ||||
| 		return ERR_PTR(-EBUSY); | ||||
| 	} | ||||
| 
 | ||||
| 	err = apne_probe1(dev, IOBASE); | ||||
| 	if (err) { | ||||
| 		release_region(IOBASE, 0x20); | ||||
| 		free_netdev(dev); | ||||
| 		return ERR_PTR(err); | ||||
| 	} | ||||
| 	err = register_netdev(dev); | ||||
| 	if (!err) | ||||
| 		return dev; | ||||
| 
 | ||||
| 	pcmcia_disable_irq(); | ||||
| 	free_irq(IRQ_AMIGA_PORTS, dev); | ||||
| 	pcmcia_reset(); | ||||
| 	release_region(IOBASE, 0x20); | ||||
| 	free_netdev(dev); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| 
 | ||||
| static int __init apne_probe1(struct net_device *dev, int ioaddr) | ||||
| { | ||||
|     int i; | ||||
|     unsigned char SA_prom[32]; | ||||
|     int wordlength = 2; | ||||
|     const char *name = NULL; | ||||
|     int start_page, stop_page; | ||||
| #ifndef MANUAL_HWADDR0 | ||||
|     int neX000, ctron; | ||||
| #endif | ||||
|     static unsigned version_printed; | ||||
| 
 | ||||
|     if ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) | ||||
| 		netdev_info(dev, version); | ||||
| 
 | ||||
|     netdev_info(dev, "PCMCIA NE*000 ethercard probe"); | ||||
| 
 | ||||
|     /* Reset card. Who knows what dain-bramaged state it was left in. */ | ||||
|     {	unsigned long reset_start_time = jiffies; | ||||
| 
 | ||||
| 	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); | ||||
| 
 | ||||
| 	while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0) | ||||
| 		if (time_after(jiffies, reset_start_time + 2*HZ/100)) { | ||||
| 			pr_cont(" not found (no reset ack).\n"); | ||||
| 			return -ENODEV; | ||||
| 		} | ||||
| 
 | ||||
| 	outb(0xff, ioaddr + NE_EN0_ISR);		/* Ack all intr. */ | ||||
|     } | ||||
| 
 | ||||
| #ifndef MANUAL_HWADDR0 | ||||
| 
 | ||||
|     /* Read the 16 bytes of station address PROM.
 | ||||
|        We must first initialize registers, similar to NS8390_init(eifdev, 0). | ||||
|        We can't reliably read the SAPROM address without this. | ||||
|        (I learned the hard way!). */ | ||||
|     { | ||||
| 	struct {unsigned long value, offset; } program_seq[] = { | ||||
| 	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/ | ||||
| 	    {0x48,	NE_EN0_DCFG},	/* Set byte-wide (0x48) access. */ | ||||
| 	    {0x00,	NE_EN0_RCNTLO},	/* Clear the count regs. */ | ||||
| 	    {0x00,	NE_EN0_RCNTHI}, | ||||
| 	    {0x00,	NE_EN0_IMR},	/* Mask completion irq. */ | ||||
| 	    {0xFF,	NE_EN0_ISR}, | ||||
| 	    {E8390_RXOFF, NE_EN0_RXCR},	/* 0x20  Set to monitor */ | ||||
| 	    {E8390_TXOFF, NE_EN0_TXCR},	/* 0x02  and loopback mode. */ | ||||
| 	    {32,	NE_EN0_RCNTLO}, | ||||
| 	    {0x00,	NE_EN0_RCNTHI}, | ||||
| 	    {0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000. */ | ||||
| 	    {0x00,	NE_EN0_RSARHI}, | ||||
| 	    {E8390_RREAD+E8390_START, NE_CMD}, | ||||
| 	}; | ||||
| 	for (i = 0; i < ARRAY_SIZE(program_seq); i++) { | ||||
| 	    outb(program_seq[i].value, ioaddr + program_seq[i].offset); | ||||
| 	} | ||||
| 
 | ||||
|     } | ||||
|     for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { | ||||
| 	SA_prom[i] = inb(ioaddr + NE_DATAPORT); | ||||
| 	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); | ||||
| 	if (SA_prom[i] != SA_prom[i+1]) | ||||
| 	    wordlength = 1; | ||||
|     } | ||||
| 
 | ||||
|     /*	At this point, wordlength *only* tells us if the SA_prom is doubled
 | ||||
| 	up or not because some broken PCI cards don't respect the byte-wide | ||||
| 	request in program_seq above, and hence don't have doubled up values. | ||||
| 	These broken cards would otherwise be detected as an ne1000.  */ | ||||
| 
 | ||||
|     if (wordlength == 2) | ||||
| 	for (i = 0; i < 16; i++) | ||||
| 		SA_prom[i] = SA_prom[i+i]; | ||||
| 
 | ||||
|     if (wordlength == 2) { | ||||
| 	/* We must set the 8390 for word mode. */ | ||||
| 	outb(0x49, ioaddr + NE_EN0_DCFG); | ||||
| 	start_page = NESM_START_PG; | ||||
| 	stop_page = NESM_STOP_PG; | ||||
|     } else { | ||||
| 	start_page = NE1SM_START_PG; | ||||
| 	stop_page = NE1SM_STOP_PG; | ||||
|     } | ||||
| 
 | ||||
|     neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57); | ||||
|     ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); | ||||
| 
 | ||||
|     /* Set up the rest of the parameters. */ | ||||
|     if (neX000) { | ||||
| 	name = (wordlength == 2) ? "NE2000" : "NE1000"; | ||||
|     } else if (ctron) { | ||||
| 	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16"; | ||||
| 	start_page = 0x01; | ||||
| 	stop_page = (wordlength == 2) ? 0x40 : 0x20; | ||||
|     } else { | ||||
| 	pr_cont(" not found.\n"); | ||||
| 	return -ENXIO; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| #else | ||||
|     wordlength = 2; | ||||
|     /* We must set the 8390 for word mode. */ | ||||
|     outb(0x49, ioaddr + NE_EN0_DCFG); | ||||
|     start_page = NESM_START_PG; | ||||
|     stop_page = NESM_STOP_PG; | ||||
| 
 | ||||
|     SA_prom[0] = MANUAL_HWADDR0; | ||||
|     SA_prom[1] = MANUAL_HWADDR1; | ||||
|     SA_prom[2] = MANUAL_HWADDR2; | ||||
|     SA_prom[3] = MANUAL_HWADDR3; | ||||
|     SA_prom[4] = MANUAL_HWADDR4; | ||||
|     SA_prom[5] = MANUAL_HWADDR5; | ||||
|     name = "NE2000"; | ||||
| #endif | ||||
| 
 | ||||
|     dev->base_addr = ioaddr; | ||||
|     dev->irq = IRQ_AMIGA_PORTS; | ||||
|     dev->netdev_ops = &ei_netdev_ops; | ||||
| 
 | ||||
|     /* Install the Interrupt handler */ | ||||
|     i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev); | ||||
|     if (i) return i; | ||||
| 
 | ||||
|     for (i = 0; i < ETH_ALEN; i++) | ||||
| 	dev->dev_addr[i] = SA_prom[i]; | ||||
| 
 | ||||
|     pr_cont(" %pM\n", dev->dev_addr); | ||||
| 
 | ||||
|     netdev_info(dev, "%s found.\n", name); | ||||
| 
 | ||||
|     ei_status.name = name; | ||||
|     ei_status.tx_start_page = start_page; | ||||
|     ei_status.stop_page = stop_page; | ||||
|     ei_status.word16 = (wordlength == 2); | ||||
| 
 | ||||
|     ei_status.rx_start_page = start_page + TX_PAGES; | ||||
| 
 | ||||
|     ei_status.reset_8390 = &apne_reset_8390; | ||||
|     ei_status.block_input = &apne_block_input; | ||||
|     ei_status.block_output = &apne_block_output; | ||||
|     ei_status.get_8390_hdr = &apne_get_8390_hdr; | ||||
| 
 | ||||
|     NS8390_init(dev, 0); | ||||
| 
 | ||||
|     pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */ | ||||
|     pcmcia_enable_irq(); | ||||
| 
 | ||||
|     apne_owned = 1; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* Hard reset the card.  This used to pause for the same period that a
 | ||||
|    8390 reset command required, but that shouldn't be necessary. */ | ||||
| static void | ||||
| apne_reset_8390(struct net_device *dev) | ||||
| { | ||||
|     unsigned long reset_start_time = jiffies; | ||||
|     struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
|     init_pcmcia(); | ||||
| 
 | ||||
|     netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies); | ||||
| 
 | ||||
|     outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); | ||||
| 
 | ||||
|     ei_status.txing = 0; | ||||
|     ei_status.dmaing = 0; | ||||
| 
 | ||||
|     /* This check _should_not_ be necessary, omit eventually. */ | ||||
|     while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0) | ||||
| 	if (time_after(jiffies, reset_start_time + 2*HZ/100)) { | ||||
| 		netdev_err(dev, "ne_reset_8390() did not complete.\n"); | ||||
| 		break; | ||||
| 	} | ||||
|     outb(ENISR_RESET, NE_BASE + NE_EN0_ISR);	/* Ack intr. */ | ||||
| } | ||||
| 
 | ||||
| /* Grab the 8390 specific header. Similar to the block_input routine, but
 | ||||
|    we don't need to be concerned with ring wrap as the header will be at | ||||
|    the start of a page, so we optimize accordingly. */ | ||||
| 
 | ||||
| static void | ||||
| apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 
 | ||||
|     int nic_base = dev->base_addr; | ||||
|     int cnt; | ||||
|     char *ptrc; | ||||
|     short *ptrs; | ||||
| 
 | ||||
|     /* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
|     if (ei_status.dmaing) { | ||||
| 	netdev_err(dev, "DMAing conflict in ne_get_8390_hdr " | ||||
| 		   "[DMAstat:%d][irqlock:%d][intr:%d].\n", | ||||
| 		   ei_status.dmaing, ei_status.irqlock, dev->irq); | ||||
| 	return; | ||||
|     } | ||||
| 
 | ||||
|     ei_status.dmaing |= 0x01; | ||||
|     outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
|     outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO); | ||||
|     outb(0, nic_base + NE_EN0_RCNTHI); | ||||
|     outb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */ | ||||
|     outb(ring_page, nic_base + NE_EN0_RSARHI); | ||||
|     outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| 
 | ||||
|     if (ei_status.word16) { | ||||
|         ptrs = (short*)hdr; | ||||
|         for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++) | ||||
|             *ptrs++ = inw(NE_BASE + NE_DATAPORT); | ||||
|     } else { | ||||
|         ptrc = (char*)hdr; | ||||
|         for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++) | ||||
|             *ptrc++ = inb(NE_BASE + NE_DATAPORT); | ||||
|     } | ||||
| 
 | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */ | ||||
|     ei_status.dmaing &= ~0x01; | ||||
| 
 | ||||
|     le16_to_cpus(&hdr->count); | ||||
| } | ||||
| 
 | ||||
| /* Block input and output, similar to the Crynwr packet driver.  If you
 | ||||
|    are porting to a new ethercard, look at the packet driver source for hints. | ||||
|    The NEx000 doesn't share the on-board packet memory -- you have to put | ||||
|    the packet out through the "remote DMA" dataport using outb. */ | ||||
| 
 | ||||
| static void | ||||
| apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
|     int nic_base = dev->base_addr; | ||||
|     char *buf = skb->data; | ||||
|     char *ptrc; | ||||
|     short *ptrs; | ||||
|     int cnt; | ||||
| 
 | ||||
|     /* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
|     if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in ne_block_input " | ||||
| 			   "[DMAstat:%d][irqlock:%d][intr:%d].\n", | ||||
| 			   ei_status.dmaing, ei_status.irqlock, dev->irq); | ||||
| 	return; | ||||
|     } | ||||
|     ei_status.dmaing |= 0x01; | ||||
|     outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
|     outb(count & 0xff, nic_base + NE_EN0_RCNTLO); | ||||
|     outb(count >> 8, nic_base + NE_EN0_RCNTHI); | ||||
|     outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO); | ||||
|     outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI); | ||||
|     outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
|     if (ei_status.word16) { | ||||
|       ptrs = (short*)buf; | ||||
|       for (cnt = 0; cnt < (count>>1); cnt++) | ||||
|         *ptrs++ = inw(NE_BASE + NE_DATAPORT); | ||||
|       if (count & 0x01) { | ||||
| 	buf[count-1] = inb(NE_BASE + NE_DATAPORT); | ||||
|       } | ||||
|     } else { | ||||
|       ptrc = buf; | ||||
|       for (cnt = 0; cnt < count; cnt++) | ||||
|         *ptrc++ = inb(NE_BASE + NE_DATAPORT); | ||||
|     } | ||||
| 
 | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */ | ||||
|     ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| apne_block_output(struct net_device *dev, int count, | ||||
| 		const unsigned char *buf, const int start_page) | ||||
| { | ||||
|     int nic_base = NE_BASE; | ||||
|     unsigned long dma_start; | ||||
|     char *ptrc; | ||||
|     short *ptrs; | ||||
|     int cnt; | ||||
| 
 | ||||
|     /* Round the count up for word writes.  Do we need to do this?
 | ||||
|        What effect will an odd byte count have on the 8390? | ||||
|        I should check someday. */ | ||||
|     if (ei_status.word16 && (count & 0x01)) | ||||
|       count++; | ||||
| 
 | ||||
|     /* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
|     if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in ne_block_output." | ||||
| 			   "[DMAstat:%d][irqlock:%d][intr:%d]\n", | ||||
| 			   ei_status.dmaing, ei_status.irqlock, dev->irq); | ||||
| 	return; | ||||
|     } | ||||
|     ei_status.dmaing |= 0x01; | ||||
|     /* We should already be in page 0, but to be safe... */ | ||||
|     outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); | ||||
| 
 | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
| 
 | ||||
|    /* Now the normal output. */ | ||||
|     outb(count & 0xff, nic_base + NE_EN0_RCNTLO); | ||||
|     outb(count >> 8,   nic_base + NE_EN0_RCNTHI); | ||||
|     outb(0x00, nic_base + NE_EN0_RSARLO); | ||||
|     outb(start_page, nic_base + NE_EN0_RSARHI); | ||||
| 
 | ||||
|     outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); | ||||
|     if (ei_status.word16) { | ||||
|         ptrs = (short*)buf; | ||||
|         for (cnt = 0; cnt < count>>1; cnt++) | ||||
|             outw(*ptrs++, NE_BASE+NE_DATAPORT); | ||||
|     } else { | ||||
|         ptrc = (char*)buf; | ||||
|         for (cnt = 0; cnt < count; cnt++) | ||||
| 	    outb(*ptrc++, NE_BASE + NE_DATAPORT); | ||||
|     } | ||||
| 
 | ||||
|     dma_start = jiffies; | ||||
| 
 | ||||
|     while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0) | ||||
| 	if (time_after(jiffies, dma_start + 2*HZ/100)) {	/* 20ms */ | ||||
| 		netdev_warn(dev, "timeout waiting for Tx RDC.\n"); | ||||
| 		apne_reset_8390(dev); | ||||
| 		NS8390_init(dev,1); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
|     outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */ | ||||
|     ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t apne_interrupt(int irq, void *dev_id) | ||||
| { | ||||
|     unsigned char pcmcia_intreq; | ||||
| 
 | ||||
|     if (!(gayle.inten & GAYLE_IRQ_IRQ)) | ||||
|         return IRQ_NONE; | ||||
| 
 | ||||
|     pcmcia_intreq = pcmcia_get_intreq(); | ||||
| 
 | ||||
|     if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) { | ||||
|         pcmcia_ack_int(pcmcia_intreq); | ||||
|         return IRQ_NONE; | ||||
|     } | ||||
|     if (apne_msg_enable & NETIF_MSG_INTR) | ||||
| 	pr_debug("pcmcia intreq = %x\n", pcmcia_intreq); | ||||
|     pcmcia_disable_irq();			/* to get rid of the sti() within ei_interrupt */ | ||||
|     ei_interrupt(irq, dev_id); | ||||
|     pcmcia_ack_int(pcmcia_get_intreq()); | ||||
|     pcmcia_enable_irq(); | ||||
|     return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| #ifdef MODULE | ||||
| static struct net_device *apne_dev; | ||||
| 
 | ||||
| static int __init apne_module_init(void) | ||||
| { | ||||
| 	apne_dev = apne_probe(-1); | ||||
| 	return PTR_ERR_OR_ZERO(apne_dev); | ||||
| } | ||||
| 
 | ||||
| static void __exit apne_module_exit(void) | ||||
| { | ||||
| 	unregister_netdev(apne_dev); | ||||
| 
 | ||||
| 	pcmcia_disable_irq(); | ||||
| 
 | ||||
| 	free_irq(IRQ_AMIGA_PORTS, apne_dev); | ||||
| 
 | ||||
| 	pcmcia_reset(); | ||||
| 
 | ||||
| 	release_region(IOBASE, 0x20); | ||||
| 
 | ||||
| 	free_netdev(apne_dev); | ||||
| } | ||||
| module_init(apne_module_init); | ||||
| module_exit(apne_module_exit); | ||||
| #endif | ||||
| 
 | ||||
| static int init_pcmcia(void) | ||||
| { | ||||
| 	u_char config; | ||||
| #ifndef MANUAL_CONFIG | ||||
| 	u_char tuple[32]; | ||||
| 	int offset_len; | ||||
| #endif | ||||
| 	u_long offset; | ||||
| 
 | ||||
| 	pcmcia_reset(); | ||||
| 	pcmcia_program_voltage(PCMCIA_0V); | ||||
| 	pcmcia_access_speed(PCMCIA_SPEED_250NS); | ||||
| 	pcmcia_write_enable(); | ||||
| 
 | ||||
| #ifdef MANUAL_CONFIG | ||||
| 	config = MANUAL_CONFIG; | ||||
| #else | ||||
| 	/* get and write config byte to enable IO port */ | ||||
| 
 | ||||
| 	if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	config = tuple[2] & 0x3f; | ||||
| #endif | ||||
| #ifdef MANUAL_OFFSET | ||||
| 	offset = MANUAL_OFFSET; | ||||
| #else | ||||
| 	if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	offset_len = (tuple[2] & 0x3) + 1; | ||||
| 	offset = 0; | ||||
| 	while(offset_len--) { | ||||
| 		offset = (offset << 8) | tuple[4+offset_len]; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	out_8(GAYLE_ATTRIBUTE+offset, config); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										1016
									
								
								drivers/net/ethernet/8390/ax88796.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1016
									
								
								drivers/net/ethernet/8390/ax88796.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1706
									
								
								drivers/net/ethernet/8390/axnet_cs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1706
									
								
								drivers/net/ethernet/8390/axnet_cs.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										874
									
								
								drivers/net/ethernet/8390/etherh.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								drivers/net/ethernet/8390/etherh.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,874 @@ | |||
| /*
 | ||||
|  *  linux/drivers/acorn/net/etherh.c | ||||
|  * | ||||
|  *  Copyright (C) 2000-2002 Russell King | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * NS8390 I-cubed EtherH and ANT EtherM specific driver | ||||
|  * Thanks to I-Cubed for information on their cards. | ||||
|  * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton | ||||
|  * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) | ||||
|  * EtherM integration re-engineered by Russell King. | ||||
|  * | ||||
|  * Changelog: | ||||
|  *  08-12-1996	RMK	1.00	Created | ||||
|  *		RMK	1.03	Added support for EtherLan500 cards | ||||
|  *  23-11-1997	RMK	1.04	Added media autodetection | ||||
|  *  16-04-1998	RMK	1.05	Improved media autodetection | ||||
|  *  10-02-2000	RMK	1.06	Updated for 2.3.43 | ||||
|  *  13-05-2000	RMK	1.07	Updated for 2.3.99-pre8 | ||||
|  *  12-10-1999  CK/TEW		EtherM driver first release | ||||
|  *  21-12-2000	TTC		EtherH/EtherM integration | ||||
|  *  25-12-2000	RMK	1.08	Clean integration of EtherM into this driver. | ||||
|  *  03-01-2002	RMK	1.09	Always enable IRQs if we're in the nic slot. | ||||
|  */ | ||||
| 
 | ||||
| #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/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/ethtool.h> | ||||
| #include <linux/skbuff.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/jiffies.h> | ||||
| 
 | ||||
| #include <asm/ecard.h> | ||||
| #include <asm/io.h> | ||||
| #include <asm/system_info.h> | ||||
| 
 | ||||
| #define EI_SHIFT(x)	(ei_local->reg_offset[x]) | ||||
| 
 | ||||
| #define ei_inb(_p)	 readb((void __iomem *)_p) | ||||
| #define ei_outb(_v,_p)	 writeb(_v,(void __iomem *)_p) | ||||
| #define ei_inb_p(_p)	 readb((void __iomem *)_p) | ||||
| #define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p) | ||||
| 
 | ||||
| #define DRV_NAME	"etherh" | ||||
| #define DRV_VERSION	"1.11" | ||||
| 
 | ||||
| static char version[] = | ||||
| 	"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n"; | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| static u32 etherh_msg_enable; | ||||
| 
 | ||||
| struct etherh_priv { | ||||
| 	void __iomem	*ioc_fast; | ||||
| 	void __iomem	*memc; | ||||
| 	void __iomem	*dma_base; | ||||
| 	unsigned int	id; | ||||
| 	void __iomem	*ctrl_port; | ||||
| 	unsigned char	ctrl; | ||||
| 	u32		supported; | ||||
| }; | ||||
| 
 | ||||
| struct etherh_data { | ||||
| 	unsigned long	ns8390_offset; | ||||
| 	unsigned long	dataport_offset; | ||||
| 	unsigned long	ctrlport_offset; | ||||
| 	int		ctrl_ioc; | ||||
| 	const char	name[16]; | ||||
| 	u32		supported; | ||||
| 	unsigned char	tx_start_page; | ||||
| 	unsigned char	stop_page; | ||||
| }; | ||||
| 
 | ||||
| MODULE_AUTHOR("Russell King"); | ||||
| MODULE_DESCRIPTION("EtherH/EtherM driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| #define ETHERH500_DATAPORT	0x800	/* MEMC */ | ||||
| #define ETHERH500_NS8390	0x000	/* MEMC */ | ||||
| #define ETHERH500_CTRLPORT	0x800	/* IOC  */ | ||||
| 
 | ||||
| #define ETHERH600_DATAPORT	0x040	/* MEMC */ | ||||
| #define ETHERH600_NS8390	0x800	/* MEMC */ | ||||
| #define ETHERH600_CTRLPORT	0x200	/* MEMC */ | ||||
| 
 | ||||
| #define ETHERH_CP_IE		1 | ||||
| #define ETHERH_CP_IF		2 | ||||
| #define ETHERH_CP_HEARTBEAT	2 | ||||
| 
 | ||||
| #define ETHERH_TX_START_PAGE	1 | ||||
| #define ETHERH_STOP_PAGE	127 | ||||
| 
 | ||||
| /*
 | ||||
|  * These came from CK/TEW | ||||
|  */ | ||||
| #define ETHERM_DATAPORT		0x200	/* MEMC */ | ||||
| #define ETHERM_NS8390		0x800	/* MEMC */ | ||||
| #define ETHERM_CTRLPORT		0x23c	/* MEMC */ | ||||
| 
 | ||||
| #define ETHERM_TX_START_PAGE	64 | ||||
| #define ETHERM_STOP_PAGE	127 | ||||
| 
 | ||||
| /* ------------------------------------------------------------------------ */ | ||||
| 
 | ||||
| #define etherh_priv(dev) \ | ||||
|  ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device))) | ||||
| 
 | ||||
| static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask) | ||||
| { | ||||
| 	unsigned char ctrl = eh->ctrl | mask; | ||||
| 	eh->ctrl = ctrl; | ||||
| 	writeb(ctrl, eh->ctrl_port); | ||||
| } | ||||
| 
 | ||||
| static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask) | ||||
| { | ||||
| 	unsigned char ctrl = eh->ctrl & ~mask; | ||||
| 	eh->ctrl = ctrl; | ||||
| 	writeb(ctrl, eh->ctrl_port); | ||||
| } | ||||
| 
 | ||||
| static inline unsigned int etherh_get_stat(struct etherh_priv *eh) | ||||
| { | ||||
| 	return readb(eh->ctrl_port); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static void etherh_irq_enable(ecard_t *ec, int irqnr) | ||||
| { | ||||
| 	struct etherh_priv *eh = ec->irq_data; | ||||
| 
 | ||||
| 	etherh_set_ctrl(eh, ETHERH_CP_IE); | ||||
| } | ||||
| 
 | ||||
| static void etherh_irq_disable(ecard_t *ec, int irqnr) | ||||
| { | ||||
| 	struct etherh_priv *eh = ec->irq_data; | ||||
| 
 | ||||
| 	etherh_clr_ctrl(eh, ETHERH_CP_IE); | ||||
| } | ||||
| 
 | ||||
| static expansioncard_ops_t etherh_ops = { | ||||
| 	.irqenable	= etherh_irq_enable, | ||||
| 	.irqdisable	= etherh_irq_disable, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static void | ||||
| etherh_setif(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	unsigned long flags; | ||||
| 	void __iomem *addr; | ||||
| 
 | ||||
| 	local_irq_save(flags); | ||||
| 
 | ||||
| 	/* set the interface type */ | ||||
| 	switch (etherh_priv(dev)->id) { | ||||
| 	case PROD_I3_ETHERLAN600: | ||||
| 	case PROD_I3_ETHERLAN600A: | ||||
| 		addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; | ||||
| 
 | ||||
| 		switch (dev->if_port) { | ||||
| 		case IF_PORT_10BASE2: | ||||
| 			writeb((readb(addr) & 0xf8) | 1, addr); | ||||
| 			break; | ||||
| 		case IF_PORT_10BASET: | ||||
| 			writeb((readb(addr) & 0xf8), addr); | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case PROD_I3_ETHERLAN500: | ||||
| 		switch (dev->if_port) { | ||||
| 		case IF_PORT_10BASE2: | ||||
| 			etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF); | ||||
| 			break; | ||||
| 
 | ||||
| 		case IF_PORT_10BASET: | ||||
| 			etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF); | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	local_irq_restore(flags); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| etherh_getifstat(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	void __iomem *addr; | ||||
| 	int stat = 0; | ||||
| 
 | ||||
| 	switch (etherh_priv(dev)->id) { | ||||
| 	case PROD_I3_ETHERLAN600: | ||||
| 	case PROD_I3_ETHERLAN600A: | ||||
| 		addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; | ||||
| 		switch (dev->if_port) { | ||||
| 		case IF_PORT_10BASE2: | ||||
| 			stat = 1; | ||||
| 			break; | ||||
| 		case IF_PORT_10BASET: | ||||
| 			stat = readb(addr) & 4; | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case PROD_I3_ETHERLAN500: | ||||
| 		switch (dev->if_port) { | ||||
| 		case IF_PORT_10BASE2: | ||||
| 			stat = 1; | ||||
| 			break; | ||||
| 		case IF_PORT_10BASET: | ||||
| 			stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT; | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		stat = 0; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return stat != 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Configure the interface.  Note that we ignore the other | ||||
|  * parts of ifmap, since its mostly meaningless for this driver. | ||||
|  */ | ||||
| static int etherh_set_config(struct net_device *dev, struct ifmap *map) | ||||
| { | ||||
| 	switch (map->port) { | ||||
| 	case IF_PORT_10BASE2: | ||||
| 	case IF_PORT_10BASET: | ||||
| 		/*
 | ||||
| 		 * If the user explicitly sets the interface | ||||
| 		 * media type, turn off automedia detection. | ||||
| 		 */ | ||||
| 		dev->flags &= ~IFF_AUTOMEDIA; | ||||
| 		dev->if_port = map->port; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	etherh_setif(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Reset the 8390 (hard reset).  Note that we can't actually do this. | ||||
|  */ | ||||
| static void | ||||
| etherh_reset(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	void __iomem *addr = (void __iomem *)dev->base_addr; | ||||
| 
 | ||||
| 	writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * See if we need to change the interface type. | ||||
| 	 * Note that we use 'interface_num' as a flag | ||||
| 	 * to indicate that we need to change the media. | ||||
| 	 */ | ||||
| 	if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { | ||||
| 		ei_local->interface_num = 0; | ||||
| 
 | ||||
| 		if (dev->if_port == IF_PORT_10BASET) | ||||
| 			dev->if_port = IF_PORT_10BASE2; | ||||
| 		else | ||||
| 			dev->if_port = IF_PORT_10BASET; | ||||
| 
 | ||||
| 		etherh_setif(dev); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Write a block of data out to the 8390 | ||||
|  */ | ||||
| static void | ||||
| etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	unsigned long dma_start; | ||||
| 	void __iomem *dma_base, *addr; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in etherh_block_input: " | ||||
| 			   " DMAstat %d irqlock %d\n", | ||||
| 			   ei_local->dmaing, ei_local->irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make sure we have a round number of bytes if we're in word mode. | ||||
| 	 */ | ||||
| 	if (count & 1 && ei_local->word16) | ||||
| 		count++; | ||||
| 
 | ||||
| 	ei_local->dmaing = 1; | ||||
| 
 | ||||
| 	addr = (void __iomem *)dev->base_addr; | ||||
| 	dma_base = etherh_priv(dev)->dma_base; | ||||
| 
 | ||||
| 	count = (count + 1) & ~1; | ||||
| 	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); | ||||
| 
 | ||||
| 	writeb (0x42, addr + EN0_RCNTLO); | ||||
| 	writeb (0x00, addr + EN0_RCNTHI); | ||||
| 	writeb (0x42, addr + EN0_RSARLO); | ||||
| 	writeb (0x00, addr + EN0_RSARHI); | ||||
| 	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); | ||||
| 
 | ||||
| 	udelay (1); | ||||
| 
 | ||||
| 	writeb (ENISR_RDC, addr + EN0_ISR); | ||||
| 	writeb (count, addr + EN0_RCNTLO); | ||||
| 	writeb (count >> 8, addr + EN0_RCNTHI); | ||||
| 	writeb (0, addr + EN0_RSARLO); | ||||
| 	writeb (start_page, addr + EN0_RSARHI); | ||||
| 	writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD); | ||||
| 
 | ||||
| 	if (ei_local->word16) | ||||
| 		writesw (dma_base, buf, count >> 1); | ||||
| 	else | ||||
| 		writesb (dma_base, buf, count); | ||||
| 
 | ||||
| 	dma_start = jiffies; | ||||
| 
 | ||||
| 	while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0) | ||||
| 		if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */ | ||||
| 			netdev_warn(dev, "timeout waiting for TX RDC\n"); | ||||
| 			etherh_reset (dev); | ||||
| 			__NS8390_init (dev, 1); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 	writeb (ENISR_RDC, addr + EN0_ISR); | ||||
| 	ei_local->dmaing = 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Read a block of data from the 8390 | ||||
|  */ | ||||
| static void | ||||
| etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	unsigned char *buf; | ||||
| 	void __iomem *dma_base, *addr; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in etherh_block_input: " | ||||
| 			   " DMAstat %d irqlock %d\n", | ||||
| 			   ei_local->dmaing, ei_local->irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->dmaing = 1; | ||||
| 
 | ||||
| 	addr = (void __iomem *)dev->base_addr; | ||||
| 	dma_base = etherh_priv(dev)->dma_base; | ||||
| 
 | ||||
| 	buf = skb->data; | ||||
| 	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); | ||||
| 	writeb (count, addr + EN0_RCNTLO); | ||||
| 	writeb (count >> 8, addr + EN0_RCNTHI); | ||||
| 	writeb (ring_offset, addr + EN0_RSARLO); | ||||
| 	writeb (ring_offset >> 8, addr + EN0_RSARHI); | ||||
| 	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); | ||||
| 
 | ||||
| 	if (ei_local->word16) { | ||||
| 		readsw (dma_base, buf, count >> 1); | ||||
| 		if (count & 1) | ||||
| 			buf[count - 1] = readb (dma_base); | ||||
| 	} else | ||||
| 		readsb (dma_base, buf, count); | ||||
| 
 | ||||
| 	writeb (ENISR_RDC, addr + EN0_ISR); | ||||
| 	ei_local->dmaing = 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Read a header from the 8390 | ||||
|  */ | ||||
| static void | ||||
| etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	void __iomem *dma_base, *addr; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in etherh_get_header: " | ||||
| 			   " DMAstat %d irqlock %d\n", | ||||
| 			   ei_local->dmaing, ei_local->irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->dmaing = 1; | ||||
| 
 | ||||
| 	addr = (void __iomem *)dev->base_addr; | ||||
| 	dma_base = etherh_priv(dev)->dma_base; | ||||
| 
 | ||||
| 	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); | ||||
| 	writeb (sizeof (*hdr), addr + EN0_RCNTLO); | ||||
| 	writeb (0, addr + EN0_RCNTHI); | ||||
| 	writeb (0, addr + EN0_RSARLO); | ||||
| 	writeb (ring_page, addr + EN0_RSARHI); | ||||
| 	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); | ||||
| 
 | ||||
| 	if (ei_local->word16) | ||||
| 		readsw (dma_base, hdr, sizeof (*hdr) >> 1); | ||||
| 	else | ||||
| 		readsb (dma_base, hdr, sizeof (*hdr)); | ||||
| 
 | ||||
| 	writeb (ENISR_RDC, addr + EN0_ISR); | ||||
| 	ei_local->dmaing = 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Open/initialize the board.  This is called (in the current kernel) | ||||
|  * sometime after booting when the 'ifconfig' program is run. | ||||
|  * | ||||
|  * This routine should set everything up anew at each open, even | ||||
|  * registers that "should" only need to be set once at boot, so that | ||||
|  * there is non-reboot way to recover if something goes wrong. | ||||
|  */ | ||||
| static int | ||||
| etherh_open(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev)) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make sure that we aren't going to change the | ||||
| 	 * media type on the next reset - we are about to | ||||
| 	 * do automedia manually now. | ||||
| 	 */ | ||||
| 	ei_local->interface_num = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If we are doing automedia detection, do it now. | ||||
| 	 * This is more reliable than the 8390's detection. | ||||
| 	 */ | ||||
| 	if (dev->flags & IFF_AUTOMEDIA) { | ||||
| 		dev->if_port = IF_PORT_10BASET; | ||||
| 		etherh_setif(dev); | ||||
| 		mdelay(1); | ||||
| 		if (!etherh_getifstat(dev)) { | ||||
| 			dev->if_port = IF_PORT_10BASE2; | ||||
| 			etherh_setif(dev); | ||||
| 		} | ||||
| 	} else | ||||
| 		etherh_setif(dev); | ||||
| 
 | ||||
| 	etherh_reset(dev); | ||||
| 	__ei_open(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The inverse routine to etherh_open(). | ||||
|  */ | ||||
| static int | ||||
| etherh_close(struct net_device *dev) | ||||
| { | ||||
| 	__ei_close (dev); | ||||
| 	free_irq (dev->irq, dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialisation | ||||
|  */ | ||||
| 
 | ||||
| static void __init etherh_banner(void) | ||||
| { | ||||
| 	static int version_printed; | ||||
| 
 | ||||
| 	if ((etherh_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) | ||||
| 		pr_info("%s", version); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Read the ethernet address string from the on board rom. | ||||
|  * This is an ascii string... | ||||
|  */ | ||||
| static int etherh_addr(char *addr, struct expansion_card *ec) | ||||
| { | ||||
| 	struct in_chunk_dir cd; | ||||
| 	char *s; | ||||
| 	 | ||||
| 	if (!ecard_readchunk(&cd, ec, 0xf5, 0)) { | ||||
| 		printk(KERN_ERR "%s: unable to read podule description string\n", | ||||
| 		       dev_name(&ec->dev)); | ||||
| 		goto no_addr; | ||||
| 	} | ||||
| 
 | ||||
| 	s = strchr(cd.d.string, '('); | ||||
| 	if (s) { | ||||
| 		int i; | ||||
| 
 | ||||
| 		for (i = 0; i < 6; i++) { | ||||
| 			addr[i] = simple_strtoul(s + 1, &s, 0x10); | ||||
| 			if (*s != (i == 5? ')' : ':')) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (i == 6) | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	printk(KERN_ERR "%s: unable to parse MAC address: %s\n", | ||||
| 	       dev_name(&ec->dev), cd.d.string); | ||||
| 
 | ||||
|  no_addr: | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Create an ethernet address from the system serial number. | ||||
|  */ | ||||
| static int __init etherm_addr(char *addr) | ||||
| { | ||||
| 	unsigned int serial; | ||||
| 
 | ||||
| 	if (system_serial_low == 0 && system_serial_high == 0) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	serial = system_serial_low | system_serial_high; | ||||
| 
 | ||||
| 	addr[0] = 0; | ||||
| 	addr[1] = 0; | ||||
| 	addr[2] = 0xa4; | ||||
| 	addr[3] = 0x10 + (serial >> 24); | ||||
| 	addr[4] = serial >> 16; | ||||
| 	addr[5] = serial >> 8; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) | ||||
| { | ||||
| 	strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); | ||||
| 	strlcpy(info->version, DRV_VERSION, sizeof(info->version)); | ||||
| 	strlcpy(info->bus_info, dev_name(dev->dev.parent), | ||||
| 		sizeof(info->bus_info)); | ||||
| } | ||||
| 
 | ||||
| static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||||
| { | ||||
| 	cmd->supported	= etherh_priv(dev)->supported; | ||||
| 	ethtool_cmd_speed_set(cmd, SPEED_10); | ||||
| 	cmd->duplex	= DUPLEX_HALF; | ||||
| 	cmd->port	= dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC; | ||||
| 	cmd->autoneg	= (dev->flags & IFF_AUTOMEDIA ? | ||||
| 			   AUTONEG_ENABLE : AUTONEG_DISABLE); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||||
| { | ||||
| 	switch (cmd->autoneg) { | ||||
| 	case AUTONEG_ENABLE: | ||||
| 		dev->flags |= IFF_AUTOMEDIA; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUTONEG_DISABLE: | ||||
| 		switch (cmd->port) { | ||||
| 		case PORT_TP: | ||||
| 			dev->if_port = IF_PORT_10BASET; | ||||
| 			break; | ||||
| 
 | ||||
| 		case PORT_BNC: | ||||
| 			dev->if_port = IF_PORT_10BASE2; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		dev->flags &= ~IFF_AUTOMEDIA; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	etherh_setif(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static u32 etherh_get_msglevel(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	return ei_local->msg_enable; | ||||
| } | ||||
| 
 | ||||
| static void etherh_set_msglevel(struct net_device *dev, u32 v) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	ei_local->msg_enable = v; | ||||
| } | ||||
| 
 | ||||
| static const struct ethtool_ops etherh_ethtool_ops = { | ||||
| 	.get_settings	= etherh_get_settings, | ||||
| 	.set_settings	= etherh_set_settings, | ||||
| 	.get_drvinfo	= etherh_get_drvinfo, | ||||
| 	.get_ts_info	= ethtool_op_get_ts_info, | ||||
| 	.get_msglevel	= etherh_get_msglevel, | ||||
| 	.set_msglevel	= etherh_set_msglevel, | ||||
| }; | ||||
| 
 | ||||
| static const struct net_device_ops etherh_netdev_ops = { | ||||
| 	.ndo_open		= etherh_open, | ||||
| 	.ndo_stop		= etherh_close, | ||||
| 	.ndo_set_config		= etherh_set_config, | ||||
| 	.ndo_start_xmit		= __ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= __ei_tx_timeout, | ||||
| 	.ndo_get_stats		= __ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= __ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= __ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static u32 etherh_regoffsets[16]; | ||||
| static u32 etherm_regoffsets[16]; | ||||
| 
 | ||||
| static int | ||||
| etherh_probe(struct expansion_card *ec, const struct ecard_id *id) | ||||
| { | ||||
| 	const struct etherh_data *data = id->data; | ||||
| 	struct ei_device *ei_local; | ||||
| 	struct net_device *dev; | ||||
| 	struct etherh_priv *eh; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	etherh_banner(); | ||||
| 
 | ||||
| 	ret = ecard_request_resources(ec); | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	dev = ____alloc_ei_netdev(sizeof(struct etherh_priv)); | ||||
| 	if (!dev) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto release; | ||||
| 	} | ||||
| 
 | ||||
| 	SET_NETDEV_DEV(dev, &ec->dev); | ||||
| 
 | ||||
| 	dev->netdev_ops		= ðerh_netdev_ops; | ||||
| 	dev->irq		= ec->irq; | ||||
| 	dev->ethtool_ops	= ðerh_ethtool_ops; | ||||
| 
 | ||||
| 	if (data->supported & SUPPORTED_Autoneg) | ||||
| 		dev->flags |= IFF_AUTOMEDIA; | ||||
| 	if (data->supported & SUPPORTED_TP) { | ||||
| 		dev->flags |= IFF_PORTSEL; | ||||
| 		dev->if_port = IF_PORT_10BASET; | ||||
| 	} else if (data->supported & SUPPORTED_BNC) { | ||||
| 		dev->flags |= IFF_PORTSEL; | ||||
| 		dev->if_port = IF_PORT_10BASE2; | ||||
| 	} else | ||||
| 		dev->if_port = IF_PORT_UNKNOWN; | ||||
| 
 | ||||
| 	eh = etherh_priv(dev); | ||||
| 	eh->supported		= data->supported; | ||||
| 	eh->ctrl		= 0; | ||||
| 	eh->id			= ec->cid.product; | ||||
| 	eh->memc		= ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE); | ||||
| 	if (!eh->memc) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto free; | ||||
| 	} | ||||
| 
 | ||||
| 	eh->ctrl_port = eh->memc; | ||||
| 	if (data->ctrl_ioc) { | ||||
| 		eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE); | ||||
| 		if (!eh->ioc_fast) { | ||||
| 			ret = -ENOMEM; | ||||
| 			goto free; | ||||
| 		} | ||||
| 		eh->ctrl_port = eh->ioc_fast; | ||||
| 	} | ||||
| 
 | ||||
| 	dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset; | ||||
| 	eh->dma_base = eh->memc + data->dataport_offset; | ||||
| 	eh->ctrl_port += data->ctrlport_offset; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * IRQ and control port handling - only for non-NIC slot cards. | ||||
| 	 */ | ||||
| 	if (ec->slot_no != 8) { | ||||
| 		ecard_setirq(ec, ðerh_ops, eh); | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * If we're in the NIC slot, make sure the IRQ is enabled | ||||
| 		 */ | ||||
| 		etherh_set_ctrl(eh, ETHERH_CP_IE); | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local = netdev_priv(dev); | ||||
| 	spin_lock_init(&ei_local->page_lock); | ||||
| 
 | ||||
| 	if (ec->cid.product == PROD_ANT_ETHERM) { | ||||
| 		etherm_addr(dev->dev_addr); | ||||
| 		ei_local->reg_offset = etherm_regoffsets; | ||||
| 	} else { | ||||
| 		etherh_addr(dev->dev_addr, ec); | ||||
| 		ei_local->reg_offset = etherh_regoffsets; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->name          = dev->name; | ||||
| 	ei_local->word16        = 1; | ||||
| 	ei_local->tx_start_page = data->tx_start_page; | ||||
| 	ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; | ||||
| 	ei_local->stop_page     = data->stop_page; | ||||
| 	ei_local->reset_8390    = etherh_reset; | ||||
| 	ei_local->block_input   = etherh_block_input; | ||||
| 	ei_local->block_output  = etherh_block_output; | ||||
| 	ei_local->get_8390_hdr  = etherh_get_header; | ||||
| 	ei_local->interface_num = 0; | ||||
| 	ei_local->msg_enable = etherh_msg_enable; | ||||
| 
 | ||||
| 	etherh_reset(dev); | ||||
| 	__NS8390_init(dev, 0); | ||||
| 
 | ||||
| 	ret = register_netdev(dev); | ||||
| 	if (ret) | ||||
| 		goto free; | ||||
| 
 | ||||
| 	netdev_info(dev, "%s in slot %d, %pM\n", | ||||
| 		    data->name, ec->slot_no, dev->dev_addr); | ||||
| 
 | ||||
| 	ecard_set_drvdata(ec, dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  free: | ||||
| 	free_netdev(dev); | ||||
|  release: | ||||
| 	ecard_release_resources(ec); | ||||
|  out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void etherh_remove(struct expansion_card *ec) | ||||
| { | ||||
| 	struct net_device *dev = ecard_get_drvdata(ec); | ||||
| 
 | ||||
| 	ecard_set_drvdata(ec, NULL); | ||||
| 
 | ||||
| 	unregister_netdev(dev); | ||||
| 
 | ||||
| 	free_netdev(dev); | ||||
| 
 | ||||
| 	ecard_release_resources(ec); | ||||
| } | ||||
| 
 | ||||
| static struct etherh_data etherm_data = { | ||||
| 	.ns8390_offset		= ETHERM_NS8390, | ||||
| 	.dataport_offset	= ETHERM_NS8390 + ETHERM_DATAPORT, | ||||
| 	.ctrlport_offset	= ETHERM_NS8390 + ETHERM_CTRLPORT, | ||||
| 	.name			= "ANT EtherM", | ||||
| 	.supported		= SUPPORTED_10baseT_Half, | ||||
| 	.tx_start_page		= ETHERM_TX_START_PAGE, | ||||
| 	.stop_page		= ETHERM_STOP_PAGE, | ||||
| }; | ||||
| 
 | ||||
| static struct etherh_data etherlan500_data = { | ||||
| 	.ns8390_offset		= ETHERH500_NS8390, | ||||
| 	.dataport_offset	= ETHERH500_NS8390 + ETHERH500_DATAPORT, | ||||
| 	.ctrlport_offset	= ETHERH500_CTRLPORT, | ||||
| 	.ctrl_ioc		= 1, | ||||
| 	.name			= "i3 EtherH 500", | ||||
| 	.supported		= SUPPORTED_10baseT_Half, | ||||
| 	.tx_start_page		= ETHERH_TX_START_PAGE, | ||||
| 	.stop_page		= ETHERH_STOP_PAGE, | ||||
| }; | ||||
| 
 | ||||
| static struct etherh_data etherlan600_data = { | ||||
| 	.ns8390_offset		= ETHERH600_NS8390, | ||||
| 	.dataport_offset	= ETHERH600_NS8390 + ETHERH600_DATAPORT, | ||||
| 	.ctrlport_offset	= ETHERH600_NS8390 + ETHERH600_CTRLPORT, | ||||
| 	.name			= "i3 EtherH 600", | ||||
| 	.supported		= SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, | ||||
| 	.tx_start_page		= ETHERH_TX_START_PAGE, | ||||
| 	.stop_page		= ETHERH_STOP_PAGE, | ||||
| }; | ||||
| 
 | ||||
| static struct etherh_data etherlan600a_data = { | ||||
| 	.ns8390_offset		= ETHERH600_NS8390, | ||||
| 	.dataport_offset	= ETHERH600_NS8390 + ETHERH600_DATAPORT, | ||||
| 	.ctrlport_offset	= ETHERH600_NS8390 + ETHERH600_CTRLPORT, | ||||
| 	.name			= "i3 EtherH 600A", | ||||
| 	.supported		= SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, | ||||
| 	.tx_start_page		= ETHERH_TX_START_PAGE, | ||||
| 	.stop_page		= ETHERH_STOP_PAGE, | ||||
| }; | ||||
| 
 | ||||
| static const struct ecard_id etherh_ids[] = { | ||||
| 	{ MANU_ANT, PROD_ANT_ETHERM,      ðerm_data       }, | ||||
| 	{ MANU_I3,  PROD_I3_ETHERLAN500,  ðerlan500_data  }, | ||||
| 	{ MANU_I3,  PROD_I3_ETHERLAN600,  ðerlan600_data  }, | ||||
| 	{ MANU_I3,  PROD_I3_ETHERLAN600A, ðerlan600a_data }, | ||||
| 	{ 0xffff,   0xffff } | ||||
| }; | ||||
| 
 | ||||
| static struct ecard_driver etherh_driver = { | ||||
| 	.probe		= etherh_probe, | ||||
| 	.remove		= etherh_remove, | ||||
| 	.id_table	= etherh_ids, | ||||
| 	.drv = { | ||||
| 		.name	= DRV_NAME, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int __init etherh_init(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < 16; i++) { | ||||
| 		etherh_regoffsets[i] = i << 2; | ||||
| 		etherm_regoffsets[i] = i << 5; | ||||
| 	} | ||||
| 
 | ||||
| 	return ecard_register_driver(ðerh_driver); | ||||
| } | ||||
| 
 | ||||
| static void __exit etherh_exit(void) | ||||
| { | ||||
| 	ecard_remove_driver(ðerh_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(etherh_init); | ||||
| module_exit(etherh_exit); | ||||
							
								
								
									
										278
									
								
								drivers/net/ethernet/8390/hydra.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								drivers/net/ethernet/8390/hydra.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,278 @@ | |||
| /* New Hydra driver using generic 8390 core */ | ||||
| /* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */ | ||||
| 
 | ||||
| /* This file is subject to the terms and conditions of the GNU General      */ | ||||
| /* Public License.  See the file COPYING in the main directory of the       */ | ||||
| /* Linux distribution for more details.                                     */ | ||||
| 
 | ||||
| /* Peter De Schrijver (p2@mind.be) */ | ||||
| /* Oldenburg 2000 */ | ||||
| 
 | ||||
| /* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a    */ | ||||
| /* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM  */ | ||||
| /* and 10BASE-2 (thin coax) and AUI connectors.                             */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/ioport.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/skbuff.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/bitops.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <asm/irq.h> | ||||
| #include <asm/amigaints.h> | ||||
| #include <asm/amigahw.h> | ||||
| #include <linux/zorro.h> | ||||
| 
 | ||||
| #define EI_SHIFT(x)	(ei_local->reg_offset[x]) | ||||
| #define ei_inb(port)   in_8(port) | ||||
| #define ei_outb(val,port)  out_8(port,val) | ||||
| #define ei_inb_p(port)   in_8(port) | ||||
| #define ei_outb_p(val,port)  out_8(port,val) | ||||
| 
 | ||||
| static const char version[] = | ||||
|     "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| #define NE_EN0_DCFG     (0x0e*2) | ||||
| 
 | ||||
| #define NESM_START_PG   0x0    /* First page of TX buffer */ | ||||
| #define NESM_STOP_PG    0x40    /* Last page +1 of RX ring */ | ||||
| 
 | ||||
| #define HYDRA_NIC_BASE 0xffe1 | ||||
| #define HYDRA_ADDRPROM 0xffc0 | ||||
| #define HYDRA_VERSION "v3.0alpha" | ||||
| 
 | ||||
| #define WORDSWAP(a)     ((((a)>>8)&0xff) | ((a)<<8)) | ||||
| 
 | ||||
| 
 | ||||
| static int hydra_init_one(struct zorro_dev *z, | ||||
| 				    const struct zorro_device_id *ent); | ||||
| static int hydra_init(struct zorro_dev *z); | ||||
| static int hydra_open(struct net_device *dev); | ||||
| static int hydra_close(struct net_device *dev); | ||||
| static void hydra_reset_8390(struct net_device *dev); | ||||
| static void hydra_get_8390_hdr(struct net_device *dev, | ||||
| 			       struct e8390_pkt_hdr *hdr, int ring_page); | ||||
| static void hydra_block_input(struct net_device *dev, int count, | ||||
| 			      struct sk_buff *skb, int ring_offset); | ||||
| static void hydra_block_output(struct net_device *dev, int count, | ||||
| 			       const unsigned char *buf, int start_page); | ||||
| static void hydra_remove_one(struct zorro_dev *z); | ||||
| static u32 hydra_msg_enable; | ||||
| 
 | ||||
| static struct zorro_device_id hydra_zorro_tbl[] = { | ||||
|     { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET }, | ||||
|     { 0 } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl); | ||||
| 
 | ||||
| static struct zorro_driver hydra_driver = { | ||||
|     .name	= "hydra", | ||||
|     .id_table	= hydra_zorro_tbl, | ||||
|     .probe	= hydra_init_one, | ||||
|     .remove	= hydra_remove_one, | ||||
| }; | ||||
| 
 | ||||
| static int hydra_init_one(struct zorro_dev *z, | ||||
| 			  const struct zorro_device_id *ent) | ||||
| { | ||||
|     int err; | ||||
| 
 | ||||
|     if (!request_mem_region(z->resource.start, 0x10000, "Hydra")) | ||||
| 	return -EBUSY; | ||||
|     if ((err = hydra_init(z))) { | ||||
| 	release_mem_region(z->resource.start, 0x10000); | ||||
| 	return -EBUSY; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct net_device_ops hydra_netdev_ops = { | ||||
| 	.ndo_open		= hydra_open, | ||||
| 	.ndo_stop		= hydra_close, | ||||
| 
 | ||||
| 	.ndo_start_xmit		= __ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= __ei_tx_timeout, | ||||
| 	.ndo_get_stats		= __ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= __ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= __ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int hydra_init(struct zorro_dev *z) | ||||
| { | ||||
|     struct net_device *dev; | ||||
|     unsigned long board = (unsigned long)ZTWO_VADDR(z->resource.start); | ||||
|     unsigned long ioaddr = board+HYDRA_NIC_BASE; | ||||
|     const char name[] = "NE2000"; | ||||
|     int start_page, stop_page; | ||||
|     int j; | ||||
|     int err; | ||||
|     struct ei_device *ei_local; | ||||
| 
 | ||||
|     static u32 hydra_offsets[16] = { | ||||
| 	0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, | ||||
| 	0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, | ||||
|     }; | ||||
| 
 | ||||
|     dev = ____alloc_ei_netdev(0); | ||||
|     if (!dev) | ||||
| 	return -ENOMEM; | ||||
| 
 | ||||
|     for (j = 0; j < ETH_ALEN; j++) | ||||
| 	dev->dev_addr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j)); | ||||
| 
 | ||||
|     /* We must set the 8390 for word mode. */ | ||||
|     z_writeb(0x4b, ioaddr + NE_EN0_DCFG); | ||||
|     start_page = NESM_START_PG; | ||||
|     stop_page = NESM_STOP_PG; | ||||
| 
 | ||||
|     ei_local = netdev_priv(dev); | ||||
|     ei_local->msg_enable = hydra_msg_enable; | ||||
|     dev->base_addr = ioaddr; | ||||
|     dev->irq = IRQ_AMIGA_PORTS; | ||||
| 
 | ||||
|     /* Install the Interrupt handler */ | ||||
|     if (request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, IRQF_SHARED, "Hydra Ethernet", | ||||
| 		    dev)) { | ||||
| 	free_netdev(dev); | ||||
| 	return -EAGAIN; | ||||
|     } | ||||
| 
 | ||||
|     ei_status.name = name; | ||||
|     ei_status.tx_start_page = start_page; | ||||
|     ei_status.stop_page = stop_page; | ||||
|     ei_status.word16 = 1; | ||||
|     ei_status.bigendian = 1; | ||||
| 
 | ||||
|     ei_status.rx_start_page = start_page + TX_PAGES; | ||||
| 
 | ||||
|     ei_status.reset_8390 = hydra_reset_8390; | ||||
|     ei_status.block_input = hydra_block_input; | ||||
|     ei_status.block_output = hydra_block_output; | ||||
|     ei_status.get_8390_hdr = hydra_get_8390_hdr; | ||||
|     ei_status.reg_offset = hydra_offsets; | ||||
| 
 | ||||
|     dev->netdev_ops = &hydra_netdev_ops; | ||||
|     __NS8390_init(dev, 0); | ||||
| 
 | ||||
|     err = register_netdev(dev); | ||||
|     if (err) { | ||||
| 	free_irq(IRQ_AMIGA_PORTS, dev); | ||||
| 	free_netdev(dev); | ||||
| 	return err; | ||||
|     } | ||||
| 
 | ||||
|     zorro_set_drvdata(z, dev); | ||||
| 
 | ||||
|     pr_info("%s: Hydra at %pR, address %pM (hydra.c " HYDRA_VERSION ")\n", | ||||
| 	    dev->name, &z->resource, dev->dev_addr); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int hydra_open(struct net_device *dev) | ||||
| { | ||||
|     __ei_open(dev); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int hydra_close(struct net_device *dev) | ||||
| { | ||||
|     struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
|     netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); | ||||
|     __ei_close(dev); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void hydra_reset_8390(struct net_device *dev) | ||||
| { | ||||
|     netdev_info(dev, "Hydra hw reset not there\n"); | ||||
| } | ||||
| 
 | ||||
| static void hydra_get_8390_hdr(struct net_device *dev, | ||||
| 			       struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
|     int nic_base = dev->base_addr; | ||||
|     short *ptrs; | ||||
|     unsigned long hdr_start= (nic_base-HYDRA_NIC_BASE) + | ||||
| 			     ((ring_page - NESM_START_PG)<<8); | ||||
|     ptrs = (short *)hdr; | ||||
| 
 | ||||
|     *(ptrs++) = z_readw(hdr_start); | ||||
|     *((short *)hdr) = WORDSWAP(*((short *)hdr)); | ||||
|     hdr_start += 2; | ||||
|     *(ptrs++) = z_readw(hdr_start); | ||||
|     *((short *)hdr+1) = WORDSWAP(*((short *)hdr+1)); | ||||
| } | ||||
| 
 | ||||
| static void hydra_block_input(struct net_device *dev, int count, | ||||
| 			      struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
|     unsigned long nic_base = dev->base_addr; | ||||
|     unsigned long mem_base = nic_base - HYDRA_NIC_BASE; | ||||
|     unsigned long xfer_start = mem_base + ring_offset - (NESM_START_PG<<8); | ||||
| 
 | ||||
|     if (count&1) | ||||
| 	count++; | ||||
| 
 | ||||
|     if (xfer_start+count >  mem_base + (NESM_STOP_PG<<8)) { | ||||
| 	int semi_count = (mem_base + (NESM_STOP_PG<<8)) - xfer_start; | ||||
| 
 | ||||
| 	z_memcpy_fromio(skb->data,xfer_start,semi_count); | ||||
| 	count -= semi_count; | ||||
| 	z_memcpy_fromio(skb->data+semi_count, mem_base, count); | ||||
|     } else | ||||
| 	z_memcpy_fromio(skb->data, xfer_start,count); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static void hydra_block_output(struct net_device *dev, int count, | ||||
| 			       const unsigned char *buf, int start_page) | ||||
| { | ||||
|     unsigned long nic_base = dev->base_addr; | ||||
|     unsigned long mem_base = nic_base - HYDRA_NIC_BASE; | ||||
| 
 | ||||
|     if (count&1) | ||||
| 	count++; | ||||
| 
 | ||||
|     z_memcpy_toio(mem_base+((start_page - NESM_START_PG)<<8), buf, count); | ||||
| } | ||||
| 
 | ||||
| static void hydra_remove_one(struct zorro_dev *z) | ||||
| { | ||||
|     struct net_device *dev = zorro_get_drvdata(z); | ||||
| 
 | ||||
|     unregister_netdev(dev); | ||||
|     free_irq(IRQ_AMIGA_PORTS, dev); | ||||
|     release_mem_region(ZTWO_PADDR(dev->base_addr)-HYDRA_NIC_BASE, 0x10000); | ||||
|     free_netdev(dev); | ||||
| } | ||||
| 
 | ||||
| static int __init hydra_init_module(void) | ||||
| { | ||||
|     return zorro_register_driver(&hydra_driver); | ||||
| } | ||||
| 
 | ||||
| static void __exit hydra_cleanup_module(void) | ||||
| { | ||||
|     zorro_unregister_driver(&hydra_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(hydra_init_module); | ||||
| module_exit(hydra_cleanup_module); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										1084
									
								
								drivers/net/ethernet/8390/lib8390.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1084
									
								
								drivers/net/ethernet/8390/lib8390.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										874
									
								
								drivers/net/ethernet/8390/mac8390.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								drivers/net/ethernet/8390/mac8390.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,874 @@ | |||
| /* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
 | ||||
|    Ethernet cards on Linux */ | ||||
| /* Based on the former daynaport.c driver, by Alan Cox.  Some code
 | ||||
|    taken from or inspired by skeleton.c by Donald Becker, acenic.c by | ||||
|    Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker. | ||||
| 
 | ||||
|    This software may be used and distributed according to the terms of | ||||
|    the GNU Public License, incorporated herein by reference.  */ | ||||
| 
 | ||||
| /* 2000-02-28: support added for Dayna and Kinetics cards by
 | ||||
|    A.G.deWijn@phys.uu.nl */ | ||||
| /* 2000-04-04: support added for Dayna2 by bart@etpmod.phys.tue.nl */ | ||||
| /* 2001-04-18: support for DaynaPort E/LC-M by rayk@knightsmanor.org */ | ||||
| /* 2001-05-15: support for Cabletron ported from old daynaport driver
 | ||||
|  * and fixed access to Sonic Sys card which masquerades as a Farallon | ||||
|  * by rayk@knightsmanor.org */ | ||||
| /* 2002-12-30: Try to support more cards, some clues from NetBSD driver */ | ||||
| /* 2003-12-26: Make sure Asante cards always work. */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/types.h> | ||||
| #include <linux/fcntl.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/ptrace.h> | ||||
| #include <linux/ioport.h> | ||||
| #include <linux/nubus.h> | ||||
| #include <linux/in.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/skbuff.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/io.h> | ||||
| 
 | ||||
| #include <asm/dma.h> | ||||
| #include <asm/hwtest.h> | ||||
| #include <asm/macints.h> | ||||
| 
 | ||||
| static char version[] = | ||||
| 	"v0.4 2001-05-15 David Huggins-Daines <dhd@debian.org> and others\n"; | ||||
| 
 | ||||
| #define EI_SHIFT(x)	(ei_local->reg_offset[x]) | ||||
| #define ei_inb(port)	in_8(port) | ||||
| #define ei_outb(val, port)	out_8(port, val) | ||||
| #define ei_inb_p(port)	in_8(port) | ||||
| #define ei_outb_p(val, port)	out_8(port, val) | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| #define WD_START_PG			0x00	/* First page of TX buffer */ | ||||
| #define CABLETRON_RX_START_PG		0x00    /* First page of RX buffer */ | ||||
| #define CABLETRON_RX_STOP_PG		0x30    /* Last page +1 of RX ring */ | ||||
| #define CABLETRON_TX_START_PG		CABLETRON_RX_STOP_PG | ||||
| 						/* First page of TX buffer */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Unfortunately it seems we have to hardcode these for the moment | ||||
|  * Shouldn't the card know about this? | ||||
|  * Does anyone know where to read it off the card? | ||||
|  * Do we trust the data provided by the card? | ||||
|  */ | ||||
| 
 | ||||
| #define DAYNA_8390_BASE		0x80000 | ||||
| #define DAYNA_8390_MEM		0x00000 | ||||
| 
 | ||||
| #define CABLETRON_8390_BASE	0x90000 | ||||
| #define CABLETRON_8390_MEM	0x00000 | ||||
| 
 | ||||
| #define INTERLAN_8390_BASE	0xE0000 | ||||
| #define INTERLAN_8390_MEM	0xD0000 | ||||
| 
 | ||||
| enum mac8390_type { | ||||
| 	MAC8390_NONE = -1, | ||||
| 	MAC8390_APPLE, | ||||
| 	MAC8390_ASANTE, | ||||
| 	MAC8390_FARALLON, | ||||
| 	MAC8390_CABLETRON, | ||||
| 	MAC8390_DAYNA, | ||||
| 	MAC8390_INTERLAN, | ||||
| 	MAC8390_KINETICS, | ||||
| }; | ||||
| 
 | ||||
| static const char *cardname[] = { | ||||
| 	"apple", | ||||
| 	"asante", | ||||
| 	"farallon", | ||||
| 	"cabletron", | ||||
| 	"dayna", | ||||
| 	"interlan", | ||||
| 	"kinetics", | ||||
| }; | ||||
| 
 | ||||
| static const int word16[] = { | ||||
| 	1, /* apple */ | ||||
| 	1, /* asante */ | ||||
| 	1, /* farallon */ | ||||
| 	1, /* cabletron */ | ||||
| 	0, /* dayna */ | ||||
| 	1, /* interlan */ | ||||
| 	0, /* kinetics */ | ||||
| }; | ||||
| 
 | ||||
| /* on which cards do we use NuBus resources? */ | ||||
| static const int useresources[] = { | ||||
| 	1, /* apple */ | ||||
| 	1, /* asante */ | ||||
| 	1, /* farallon */ | ||||
| 	0, /* cabletron */ | ||||
| 	0, /* dayna */ | ||||
| 	0, /* interlan */ | ||||
| 	0, /* kinetics */ | ||||
| }; | ||||
| 
 | ||||
| enum mac8390_access { | ||||
| 	ACCESS_UNKNOWN = 0, | ||||
| 	ACCESS_32, | ||||
| 	ACCESS_16, | ||||
| }; | ||||
| 
 | ||||
| extern int mac8390_memtest(struct net_device *dev); | ||||
| static int mac8390_initdev(struct net_device *dev, struct nubus_dev *ndev, | ||||
| 			   enum mac8390_type type); | ||||
| 
 | ||||
| static int mac8390_open(struct net_device *dev); | ||||
| static int mac8390_close(struct net_device *dev); | ||||
| static void mac8390_no_reset(struct net_device *dev); | ||||
| static void interlan_reset(struct net_device *dev); | ||||
| 
 | ||||
| /* Sane (32-bit chunk memory read/write) - Some Farallon and Apple do this*/ | ||||
| static void sane_get_8390_hdr(struct net_device *dev, | ||||
| 			      struct e8390_pkt_hdr *hdr, int ring_page); | ||||
| static void sane_block_input(struct net_device *dev, int count, | ||||
| 			     struct sk_buff *skb, int ring_offset); | ||||
| static void sane_block_output(struct net_device *dev, int count, | ||||
| 			      const unsigned char *buf, const int start_page); | ||||
| 
 | ||||
| /* dayna_memcpy to and from card */ | ||||
| static void dayna_memcpy_fromcard(struct net_device *dev, void *to, | ||||
| 				int from, int count); | ||||
| static void dayna_memcpy_tocard(struct net_device *dev, int to, | ||||
| 			      const void *from, int count); | ||||
| 
 | ||||
| /* Dayna - Dayna/Kinetics use this */ | ||||
| static void dayna_get_8390_hdr(struct net_device *dev, | ||||
| 			       struct e8390_pkt_hdr *hdr, int ring_page); | ||||
| static void dayna_block_input(struct net_device *dev, int count, | ||||
| 			      struct sk_buff *skb, int ring_offset); | ||||
| static void dayna_block_output(struct net_device *dev, int count, | ||||
| 			       const unsigned char *buf, int start_page); | ||||
| 
 | ||||
| #define memcpy_fromio(a, b, c)	memcpy((a), (void *)(b), (c)) | ||||
| #define memcpy_toio(a, b, c)	memcpy((void *)(a), (b), (c)) | ||||
| 
 | ||||
| #define memcmp_withio(a, b, c)	memcmp((a), (void *)(b), (c)) | ||||
| 
 | ||||
| /* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */ | ||||
| static void slow_sane_get_8390_hdr(struct net_device *dev, | ||||
| 				   struct e8390_pkt_hdr *hdr, int ring_page); | ||||
| static void slow_sane_block_input(struct net_device *dev, int count, | ||||
| 				  struct sk_buff *skb, int ring_offset); | ||||
| static void slow_sane_block_output(struct net_device *dev, int count, | ||||
| 				   const unsigned char *buf, int start_page); | ||||
| static void word_memcpy_tocard(unsigned long tp, const void *fp, int count); | ||||
| static void word_memcpy_fromcard(void *tp, unsigned long fp, int count); | ||||
| static u32 mac8390_msg_enable; | ||||
| 
 | ||||
| static enum mac8390_type __init mac8390_ident(struct nubus_dev *dev) | ||||
| { | ||||
| 	switch (dev->dr_sw) { | ||||
| 	case NUBUS_DRSW_3COM: | ||||
| 		switch (dev->dr_hw) { | ||||
| 		case NUBUS_DRHW_APPLE_SONIC_NB: | ||||
| 		case NUBUS_DRHW_APPLE_SONIC_LC: | ||||
| 		case NUBUS_DRHW_SONNET: | ||||
| 			return MAC8390_NONE; | ||||
| 		default: | ||||
| 			return MAC8390_APPLE; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_APPLE: | ||||
| 		switch (dev->dr_hw) { | ||||
| 		case NUBUS_DRHW_ASANTE_LC: | ||||
| 			return MAC8390_NONE; | ||||
| 		case NUBUS_DRHW_CABLETRON: | ||||
| 			return MAC8390_CABLETRON; | ||||
| 		default: | ||||
| 			return MAC8390_APPLE; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_ASANTE: | ||||
| 		return MAC8390_ASANTE; | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_TECHWORKS: | ||||
| 	case NUBUS_DRSW_DAYNA2: | ||||
| 	case NUBUS_DRSW_DAYNA_LC: | ||||
| 		if (dev->dr_hw == NUBUS_DRHW_CABLETRON) | ||||
| 			return MAC8390_CABLETRON; | ||||
| 		else | ||||
| 			return MAC8390_APPLE; | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_FARALLON: | ||||
| 		return MAC8390_FARALLON; | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_KINETICS: | ||||
| 		switch (dev->dr_hw) { | ||||
| 		case NUBUS_DRHW_INTERLAN: | ||||
| 			return MAC8390_INTERLAN; | ||||
| 		default: | ||||
| 			return MAC8390_KINETICS; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case NUBUS_DRSW_DAYNA: | ||||
| 		/*
 | ||||
| 		 * These correspond to Dayna Sonic cards | ||||
| 		 * which use the macsonic driver | ||||
| 		 */ | ||||
| 		if (dev->dr_hw == NUBUS_DRHW_SMC9194 || | ||||
| 		    dev->dr_hw == NUBUS_DRHW_INTERLAN) | ||||
| 			return MAC8390_NONE; | ||||
| 		else | ||||
| 			return MAC8390_DAYNA; | ||||
| 		break; | ||||
| 	} | ||||
| 	return MAC8390_NONE; | ||||
| } | ||||
| 
 | ||||
| static enum mac8390_access __init mac8390_testio(volatile unsigned long membase) | ||||
| { | ||||
| 	unsigned long outdata = 0xA5A0B5B0; | ||||
| 	unsigned long indata =  0x00000000; | ||||
| 	/* Try writing 32 bits */ | ||||
| 	memcpy_toio(membase, &outdata, 4); | ||||
| 	/* Now compare them */ | ||||
| 	if (memcmp_withio(&outdata, membase, 4) == 0) | ||||
| 		return ACCESS_32; | ||||
| 	/* Write 16 bit output */ | ||||
| 	word_memcpy_tocard(membase, &outdata, 4); | ||||
| 	/* Now read it back */ | ||||
| 	word_memcpy_fromcard(&indata, membase, 4); | ||||
| 	if (outdata == indata) | ||||
| 		return ACCESS_16; | ||||
| 	return ACCESS_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| static int __init mac8390_memsize(unsigned long membase) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	local_irq_save(flags); | ||||
| 	/* Check up to 32K in 4K increments */ | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		volatile unsigned short *m = (unsigned short *)(membase + (i * 0x1000)); | ||||
| 
 | ||||
| 		/* Unwriteable - we have a fully decoded card and the
 | ||||
| 		   RAM end located */ | ||||
| 		if (hwreg_present(m) == 0) | ||||
| 			break; | ||||
| 
 | ||||
| 		/* write a distinctive byte */ | ||||
| 		*m = 0xA5A0 | i; | ||||
| 		/* check that we read back what we wrote */ | ||||
| 		if (*m != (0xA5A0 | i)) | ||||
| 			break; | ||||
| 
 | ||||
| 		/* check for partial decode and wrap */ | ||||
| 		for (j = 0; j < i; j++) { | ||||
| 			volatile unsigned short *p = (unsigned short *)(membase + (j * 0x1000)); | ||||
| 			if (*p != (0xA5A0 | j)) | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	local_irq_restore(flags); | ||||
| 	/*
 | ||||
| 	 * in any case, we stopped once we tried one block too many, | ||||
| 	 * or once we reached 32K | ||||
| 	 */ | ||||
| 	return i * 0x1000; | ||||
| } | ||||
| 
 | ||||
| static bool __init mac8390_init(struct net_device *dev, struct nubus_dev *ndev, | ||||
| 				enum mac8390_type cardtype) | ||||
| { | ||||
| 	struct nubus_dir dir; | ||||
| 	struct nubus_dirent ent; | ||||
| 	int offset; | ||||
| 	volatile unsigned short *i; | ||||
| 
 | ||||
| 	printk_once(KERN_INFO pr_fmt("%s"), version); | ||||
| 
 | ||||
| 	dev->irq = SLOT2IRQ(ndev->board->slot); | ||||
| 	/* This is getting to be a habit */ | ||||
| 	dev->base_addr = (ndev->board->slot_addr | | ||||
| 			  ((ndev->board->slot & 0xf) << 20)); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Get some Nubus info - we will trust the card's idea | ||||
| 	 * of where its memory and registers are. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (nubus_get_func_dir(ndev, &dir) == -1) { | ||||
| 		pr_err("%s: Unable to get Nubus functional directory for slot %X!\n", | ||||
| 		       dev->name, ndev->board->slot); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Get the MAC address */ | ||||
| 	if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) { | ||||
| 		pr_info("%s: Couldn't get MAC address!\n", dev->name); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	nubus_get_rsrc_mem(dev->dev_addr, &ent, 6); | ||||
| 
 | ||||
| 	if (useresources[cardtype] == 1) { | ||||
| 		nubus_rewinddir(&dir); | ||||
| 		if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS, | ||||
| 				    &ent) == -1) { | ||||
| 			pr_err("%s: Memory offset resource for slot %X not found!\n", | ||||
| 			       dev->name, ndev->board->slot); | ||||
| 			return false; | ||||
| 		} | ||||
| 		nubus_get_rsrc_mem(&offset, &ent, 4); | ||||
| 		dev->mem_start = dev->base_addr + offset; | ||||
| 		/* yes, this is how the Apple driver does it */ | ||||
| 		dev->base_addr = dev->mem_start + 0x10000; | ||||
| 		nubus_rewinddir(&dir); | ||||
| 		if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH, | ||||
| 				    &ent) == -1) { | ||||
| 			pr_info("%s: Memory length resource for slot %X not found, probing\n", | ||||
| 				dev->name, ndev->board->slot); | ||||
| 			offset = mac8390_memsize(dev->mem_start); | ||||
| 		} else { | ||||
| 			nubus_get_rsrc_mem(&offset, &ent, 4); | ||||
| 		} | ||||
| 		dev->mem_end = dev->mem_start + offset; | ||||
| 	} else { | ||||
| 		switch (cardtype) { | ||||
| 		case MAC8390_KINETICS: | ||||
| 		case MAC8390_DAYNA: /* it's the same */ | ||||
| 			dev->base_addr = (int)(ndev->board->slot_addr + | ||||
| 					       DAYNA_8390_BASE); | ||||
| 			dev->mem_start = (int)(ndev->board->slot_addr + | ||||
| 					       DAYNA_8390_MEM); | ||||
| 			dev->mem_end = dev->mem_start + | ||||
| 				       mac8390_memsize(dev->mem_start); | ||||
| 			break; | ||||
| 		case MAC8390_INTERLAN: | ||||
| 			dev->base_addr = (int)(ndev->board->slot_addr + | ||||
| 					       INTERLAN_8390_BASE); | ||||
| 			dev->mem_start = (int)(ndev->board->slot_addr + | ||||
| 					       INTERLAN_8390_MEM); | ||||
| 			dev->mem_end = dev->mem_start + | ||||
| 				       mac8390_memsize(dev->mem_start); | ||||
| 			break; | ||||
| 		case MAC8390_CABLETRON: | ||||
| 			dev->base_addr = (int)(ndev->board->slot_addr + | ||||
| 					       CABLETRON_8390_BASE); | ||||
| 			dev->mem_start = (int)(ndev->board->slot_addr + | ||||
| 					       CABLETRON_8390_MEM); | ||||
| 			/* The base address is unreadable if 0x00
 | ||||
| 			 * has been written to the command register | ||||
| 			 * Reset the chip by writing E8390_NODMA + | ||||
| 			 *   E8390_PAGE0 + E8390_STOP just to be | ||||
| 			 *   sure | ||||
| 			 */ | ||||
| 			i = (void *)dev->base_addr; | ||||
| 			*i = 0x21; | ||||
| 			dev->mem_end = dev->mem_start + | ||||
| 				       mac8390_memsize(dev->mem_start); | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			pr_err("Card type %s is unsupported, sorry\n", | ||||
| 			       ndev->board->name); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| struct net_device * __init mac8390_probe(int unit) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	struct nubus_dev *ndev = NULL; | ||||
| 	int err = -ENODEV; | ||||
| 	struct ei_device *ei_local; | ||||
| 
 | ||||
| 	static unsigned int slots; | ||||
| 
 | ||||
| 	enum mac8390_type cardtype; | ||||
| 
 | ||||
| 	/* probably should check for Nubus instead */ | ||||
| 
 | ||||
| 	if (!MACH_IS_MAC) | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 
 | ||||
| 	dev = ____alloc_ei_netdev(0); | ||||
| 	if (!dev) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	if (unit >= 0) | ||||
| 		sprintf(dev->name, "eth%d", unit); | ||||
| 
 | ||||
| 	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET, | ||||
| 				       ndev))) { | ||||
| 		/* Have we seen it already? */ | ||||
| 		if (slots & (1 << ndev->board->slot)) | ||||
| 			continue; | ||||
| 		slots |= 1 << ndev->board->slot; | ||||
| 
 | ||||
| 		cardtype = mac8390_ident(ndev); | ||||
| 		if (cardtype == MAC8390_NONE) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!mac8390_init(dev, ndev, cardtype)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Do the nasty 8390 stuff */ | ||||
| 		if (!mac8390_initdev(dev, ndev, cardtype)) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!ndev) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	 ei_local = netdev_priv(dev); | ||||
| 	 ei_local->msg_enable = mac8390_msg_enable; | ||||
| 
 | ||||
| 	err = register_netdev(dev); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 	return dev; | ||||
| 
 | ||||
| out: | ||||
| 	free_netdev(dev); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| 
 | ||||
| #ifdef MODULE | ||||
| MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others"); | ||||
| MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| /* overkill, of course */ | ||||
| static struct net_device *dev_mac8390[15]; | ||||
| int init_module(void) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < 15; i++) { | ||||
| 		struct net_device *dev = mac8390_probe(-1); | ||||
| 		if (IS_ERR(dev)) | ||||
| 			break; | ||||
| 		dev_mac890[i] = dev; | ||||
| 	} | ||||
| 	if (!i) { | ||||
| 		pr_notice("No useable cards found, driver NOT installed.\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void cleanup_module(void) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < 15; i++) { | ||||
| 		struct net_device *dev = dev_mac890[i]; | ||||
| 		if (dev) { | ||||
| 			unregister_netdev(dev); | ||||
| 			free_netdev(dev); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #endif /* MODULE */ | ||||
| 
 | ||||
| static const struct net_device_ops mac8390_netdev_ops = { | ||||
| 	.ndo_open 		= mac8390_open, | ||||
| 	.ndo_stop		= mac8390_close, | ||||
| 	.ndo_start_xmit		= __ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= __ei_tx_timeout, | ||||
| 	.ndo_get_stats		= __ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= __ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= __ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int __init mac8390_initdev(struct net_device *dev, | ||||
| 				  struct nubus_dev *ndev, | ||||
| 				  enum mac8390_type type) | ||||
| { | ||||
| 	static u32 fwrd4_offsets[16] = { | ||||
| 		0,      4,      8,      12, | ||||
| 		16,     20,     24,     28, | ||||
| 		32,     36,     40,     44, | ||||
| 		48,     52,     56,     60 | ||||
| 	}; | ||||
| 	static u32 back4_offsets[16] = { | ||||
| 		60,     56,     52,     48, | ||||
| 		44,     40,     36,     32, | ||||
| 		28,     24,     20,     16, | ||||
| 		12,     8,      4,      0 | ||||
| 	}; | ||||
| 	static u32 fwrd2_offsets[16] = { | ||||
| 		0,      2,      4,      6, | ||||
| 		8,     10,     12,     14, | ||||
| 		16,    18,     20,     22, | ||||
| 		24,    26,     28,     30 | ||||
| 	}; | ||||
| 
 | ||||
| 	int access_bitmode = 0; | ||||
| 
 | ||||
| 	/* Now fill in our stuff */ | ||||
| 	dev->netdev_ops = &mac8390_netdev_ops; | ||||
| 
 | ||||
| 	/* GAR, ei_status is actually a macro even though it looks global */ | ||||
| 	ei_status.name = cardname[type]; | ||||
| 	ei_status.word16 = word16[type]; | ||||
| 
 | ||||
| 	/* Cabletron's TX/RX buffers are backwards */ | ||||
| 	if (type == MAC8390_CABLETRON) { | ||||
| 		ei_status.tx_start_page = CABLETRON_TX_START_PG; | ||||
| 		ei_status.rx_start_page = CABLETRON_RX_START_PG; | ||||
| 		ei_status.stop_page = CABLETRON_RX_STOP_PG; | ||||
| 		ei_status.rmem_start = dev->mem_start; | ||||
| 		ei_status.rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256; | ||||
| 	} else { | ||||
| 		ei_status.tx_start_page = WD_START_PG; | ||||
| 		ei_status.rx_start_page = WD_START_PG + TX_PAGES; | ||||
| 		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; | ||||
| 		ei_status.rmem_start = dev->mem_start + TX_PAGES*256; | ||||
| 		ei_status.rmem_end = dev->mem_end; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Fill in model-specific information and functions */ | ||||
| 	switch (type) { | ||||
| 	case MAC8390_FARALLON: | ||||
| 	case MAC8390_APPLE: | ||||
| 		switch (mac8390_testio(dev->mem_start)) { | ||||
| 		case ACCESS_UNKNOWN: | ||||
| 			pr_err("Don't know how to access card memory!\n"); | ||||
| 			return -ENODEV; | ||||
| 
 | ||||
| 		case ACCESS_16: | ||||
| 			/* 16 bit card, register map is reversed */ | ||||
| 			ei_status.reset_8390 = mac8390_no_reset; | ||||
| 			ei_status.block_input = slow_sane_block_input; | ||||
| 			ei_status.block_output = slow_sane_block_output; | ||||
| 			ei_status.get_8390_hdr = slow_sane_get_8390_hdr; | ||||
| 			ei_status.reg_offset = back4_offsets; | ||||
| 			break; | ||||
| 
 | ||||
| 		case ACCESS_32: | ||||
| 			/* 32 bit card, register map is reversed */ | ||||
| 			ei_status.reset_8390 = mac8390_no_reset; | ||||
| 			ei_status.block_input = sane_block_input; | ||||
| 			ei_status.block_output = sane_block_output; | ||||
| 			ei_status.get_8390_hdr = sane_get_8390_hdr; | ||||
| 			ei_status.reg_offset = back4_offsets; | ||||
| 			access_bitmode = 1; | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case MAC8390_ASANTE: | ||||
| 		/* Some Asante cards pass the 32 bit test
 | ||||
| 		 * but overwrite system memory when run at 32 bit. | ||||
| 		 * so we run them all at 16 bit. | ||||
| 		 */ | ||||
| 		ei_status.reset_8390 = mac8390_no_reset; | ||||
| 		ei_status.block_input = slow_sane_block_input; | ||||
| 		ei_status.block_output = slow_sane_block_output; | ||||
| 		ei_status.get_8390_hdr = slow_sane_get_8390_hdr; | ||||
| 		ei_status.reg_offset = back4_offsets; | ||||
| 		break; | ||||
| 
 | ||||
| 	case MAC8390_CABLETRON: | ||||
| 		/* 16 bit card, register map is short forward */ | ||||
| 		ei_status.reset_8390 = mac8390_no_reset; | ||||
| 		ei_status.block_input = slow_sane_block_input; | ||||
| 		ei_status.block_output = slow_sane_block_output; | ||||
| 		ei_status.get_8390_hdr = slow_sane_get_8390_hdr; | ||||
| 		ei_status.reg_offset = fwrd2_offsets; | ||||
| 		break; | ||||
| 
 | ||||
| 	case MAC8390_DAYNA: | ||||
| 	case MAC8390_KINETICS: | ||||
| 		/* 16 bit memory, register map is forward */ | ||||
| 		/* dayna and similar */ | ||||
| 		ei_status.reset_8390 = mac8390_no_reset; | ||||
| 		ei_status.block_input = dayna_block_input; | ||||
| 		ei_status.block_output = dayna_block_output; | ||||
| 		ei_status.get_8390_hdr = dayna_get_8390_hdr; | ||||
| 		ei_status.reg_offset = fwrd4_offsets; | ||||
| 		break; | ||||
| 
 | ||||
| 	case MAC8390_INTERLAN: | ||||
| 		/* 16 bit memory, register map is forward */ | ||||
| 		ei_status.reset_8390 = interlan_reset; | ||||
| 		ei_status.block_input = slow_sane_block_input; | ||||
| 		ei_status.block_output = slow_sane_block_output; | ||||
| 		ei_status.get_8390_hdr = slow_sane_get_8390_hdr; | ||||
| 		ei_status.reg_offset = fwrd4_offsets; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		pr_err("Card type %s is unsupported, sorry\n", | ||||
| 		       ndev->board->name); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	__NS8390_init(dev, 0); | ||||
| 
 | ||||
| 	/* Good, done, now spit out some messages */ | ||||
| 	pr_info("%s: %s in slot %X (type %s)\n", | ||||
| 		dev->name, ndev->board->name, ndev->board->slot, | ||||
| 		cardname[type]); | ||||
| 	pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n", | ||||
| 		dev->dev_addr, dev->irq, | ||||
| 		(unsigned int)(dev->mem_end - dev->mem_start) >> 10, | ||||
| 		dev->mem_start, access_bitmode ? 32 : 16); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int mac8390_open(struct net_device *dev) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	__ei_open(dev); | ||||
| 	err = request_irq(dev->irq, __ei_interrupt, 0, "8390 Ethernet", dev); | ||||
| 	if (err) | ||||
| 		pr_err("%s: unable to get IRQ %d\n", dev->name, dev->irq); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int mac8390_close(struct net_device *dev) | ||||
| { | ||||
| 	free_irq(dev->irq, dev); | ||||
| 	__ei_close(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void mac8390_no_reset(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	ei_status.txing = 0; | ||||
| 	netif_info(ei_local, hw, dev, "reset not supported\n"); | ||||
| } | ||||
| 
 | ||||
| static void interlan_reset(struct net_device *dev) | ||||
| { | ||||
| 	unsigned char *target = nubus_slot_addr(IRQ2SLOT(dev->irq)); | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_info(ei_local, hw, dev, "Need to reset the NS8390 t=%lu...", | ||||
| 		   jiffies); | ||||
| 	ei_status.txing = 0; | ||||
| 	target[0xC0000] = 0; | ||||
| 	if (netif_msg_hw(ei_local)) | ||||
| 		pr_cont("reset complete\n"); | ||||
| } | ||||
| 
 | ||||
| /* dayna_memcpy_fromio/dayna_memcpy_toio */ | ||||
| /* directly from daynaport.c by Alan Cox */ | ||||
| static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from, | ||||
| 				  int count) | ||||
| { | ||||
| 	volatile unsigned char *ptr; | ||||
| 	unsigned char *target = to; | ||||
| 	from <<= 1;	/* word, skip overhead */ | ||||
| 	ptr = (unsigned char *)(dev->mem_start+from); | ||||
| 	/* Leading byte? */ | ||||
| 	if (from & 2) { | ||||
| 		*target++ = ptr[-1]; | ||||
| 		ptr += 2; | ||||
| 		count--; | ||||
| 	} | ||||
| 	while (count >= 2) { | ||||
| 		*(unsigned short *)target = *(unsigned short volatile *)ptr; | ||||
| 		ptr += 4;			/* skip cruft */ | ||||
| 		target += 2; | ||||
| 		count -= 2; | ||||
| 	} | ||||
| 	/* Trailing byte? */ | ||||
| 	if (count) | ||||
| 		*target = *ptr; | ||||
| } | ||||
| 
 | ||||
| static void dayna_memcpy_tocard(struct net_device *dev, int to, | ||||
| 				const void *from, int count) | ||||
| { | ||||
| 	volatile unsigned short *ptr; | ||||
| 	const unsigned char *src = from; | ||||
| 	to <<= 1;	/* word, skip overhead */ | ||||
| 	ptr = (unsigned short *)(dev->mem_start+to); | ||||
| 	/* Leading byte? */ | ||||
| 	if (to & 2) {		/* avoid a byte write (stomps on other data) */ | ||||
| 		ptr[-1] = (ptr[-1]&0xFF00)|*src++; | ||||
| 		ptr++; | ||||
| 		count--; | ||||
| 	} | ||||
| 	while (count >= 2) { | ||||
| 		*ptr++ = *(unsigned short *)src;	/* Copy and */ | ||||
| 		ptr++;			/* skip cruft */ | ||||
| 		src += 2; | ||||
| 		count -= 2; | ||||
| 	} | ||||
| 	/* Trailing byte? */ | ||||
| 	if (count) { | ||||
| 		/* card doesn't like byte writes */ | ||||
| 		*ptr = (*ptr & 0x00FF) | (*src << 8); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* sane block input/output */ | ||||
| static void sane_get_8390_hdr(struct net_device *dev, | ||||
| 			      struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	unsigned long hdr_start = (ring_page - WD_START_PG)<<8; | ||||
| 	memcpy_fromio(hdr, dev->mem_start + hdr_start, 4); | ||||
| 	/* Fix endianness */ | ||||
| 	hdr->count = swab16(hdr->count); | ||||
| } | ||||
| 
 | ||||
| static void sane_block_input(struct net_device *dev, int count, | ||||
| 			     struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	unsigned long xfer_base = ring_offset - (WD_START_PG<<8); | ||||
| 	unsigned long xfer_start = xfer_base + dev->mem_start; | ||||
| 
 | ||||
| 	if (xfer_start + count > ei_status.rmem_end) { | ||||
| 		/* We must wrap the input move. */ | ||||
| 		int semi_count = ei_status.rmem_end - xfer_start; | ||||
| 		memcpy_fromio(skb->data, dev->mem_start + xfer_base, | ||||
| 			      semi_count); | ||||
| 		count -= semi_count; | ||||
| 		memcpy_fromio(skb->data + semi_count, ei_status.rmem_start, | ||||
| 			      count); | ||||
| 	} else { | ||||
| 		memcpy_fromio(skb->data, dev->mem_start + xfer_base, count); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void sane_block_output(struct net_device *dev, int count, | ||||
| 			      const unsigned char *buf, int start_page) | ||||
| { | ||||
| 	long shmem = (start_page - WD_START_PG)<<8; | ||||
| 
 | ||||
| 	memcpy_toio(dev->mem_start + shmem, buf, count); | ||||
| } | ||||
| 
 | ||||
| /* dayna block input/output */ | ||||
| static void dayna_get_8390_hdr(struct net_device *dev, | ||||
| 			       struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	unsigned long hdr_start = (ring_page - WD_START_PG)<<8; | ||||
| 
 | ||||
| 	dayna_memcpy_fromcard(dev, hdr, hdr_start, 4); | ||||
| 	/* Fix endianness */ | ||||
| 	hdr->count = (hdr->count & 0xFF) << 8 | (hdr->count >> 8); | ||||
| } | ||||
| 
 | ||||
| static void dayna_block_input(struct net_device *dev, int count, | ||||
| 			      struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	unsigned long xfer_base = ring_offset - (WD_START_PG<<8); | ||||
| 	unsigned long xfer_start = xfer_base+dev->mem_start; | ||||
| 
 | ||||
| 	/* Note the offset math is done in card memory space which is word
 | ||||
| 	   per long onto our space. */ | ||||
| 
 | ||||
| 	if (xfer_start + count > ei_status.rmem_end) { | ||||
| 		/* We must wrap the input move. */ | ||||
| 		int semi_count = ei_status.rmem_end - xfer_start; | ||||
| 		dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count); | ||||
| 		count -= semi_count; | ||||
| 		dayna_memcpy_fromcard(dev, skb->data + semi_count, | ||||
| 				      ei_status.rmem_start - dev->mem_start, | ||||
| 				      count); | ||||
| 	} else { | ||||
| 		dayna_memcpy_fromcard(dev, skb->data, xfer_base, count); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void dayna_block_output(struct net_device *dev, int count, | ||||
| 			       const unsigned char *buf, | ||||
| 			       int start_page) | ||||
| { | ||||
| 	long shmem = (start_page - WD_START_PG)<<8; | ||||
| 
 | ||||
| 	dayna_memcpy_tocard(dev, shmem, buf, count); | ||||
| } | ||||
| 
 | ||||
| /* Cabletron block I/O */ | ||||
| static void slow_sane_get_8390_hdr(struct net_device *dev, | ||||
| 				   struct e8390_pkt_hdr *hdr, | ||||
| 				   int ring_page) | ||||
| { | ||||
| 	unsigned long hdr_start = (ring_page - WD_START_PG)<<8; | ||||
| 	word_memcpy_fromcard(hdr, dev->mem_start + hdr_start, 4); | ||||
| 	/* Register endianism - fix here rather than 8390.c */ | ||||
| 	hdr->count = (hdr->count&0xFF)<<8|(hdr->count>>8); | ||||
| } | ||||
| 
 | ||||
| static void slow_sane_block_input(struct net_device *dev, int count, | ||||
| 				  struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	unsigned long xfer_base = ring_offset - (WD_START_PG<<8); | ||||
| 	unsigned long xfer_start = xfer_base+dev->mem_start; | ||||
| 
 | ||||
| 	if (xfer_start + count > ei_status.rmem_end) { | ||||
| 		/* We must wrap the input move. */ | ||||
| 		int semi_count = ei_status.rmem_end - xfer_start; | ||||
| 		word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base, | ||||
| 				     semi_count); | ||||
| 		count -= semi_count; | ||||
| 		word_memcpy_fromcard(skb->data + semi_count, | ||||
| 				     ei_status.rmem_start, count); | ||||
| 	} else { | ||||
| 		word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base, | ||||
| 				     count); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void slow_sane_block_output(struct net_device *dev, int count, | ||||
| 				   const unsigned char *buf, int start_page) | ||||
| { | ||||
| 	long shmem = (start_page - WD_START_PG)<<8; | ||||
| 
 | ||||
| 	word_memcpy_tocard(dev->mem_start + shmem, buf, count); | ||||
| } | ||||
| 
 | ||||
| static void word_memcpy_tocard(unsigned long tp, const void *fp, int count) | ||||
| { | ||||
| 	volatile unsigned short *to = (void *)tp; | ||||
| 	const unsigned short *from = fp; | ||||
| 
 | ||||
| 	count++; | ||||
| 	count /= 2; | ||||
| 
 | ||||
| 	while (count--) | ||||
| 		*to++ = *from++; | ||||
| } | ||||
| 
 | ||||
| static void word_memcpy_fromcard(void *tp, unsigned long fp, int count) | ||||
| { | ||||
| 	unsigned short *to = tp; | ||||
| 	const volatile unsigned short *from = (const void *)fp; | ||||
| 
 | ||||
| 	count++; | ||||
| 	count /= 2; | ||||
| 
 | ||||
| 	while (count--) | ||||
| 		*to++ = *from++; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										481
									
								
								drivers/net/ethernet/8390/mcf8390.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										481
									
								
								drivers/net/ethernet/8390/mcf8390.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,481 @@ | |||
| /*
 | ||||
|  *  Support for ColdFire CPU based boards using a NS8390 Ethernet device. | ||||
|  * | ||||
|  *  Derived from the many other 8390 drivers. | ||||
|  * | ||||
|  *  (C) Copyright 2012,  Greg Ungerer <gerg@uclinux.org> | ||||
|  * | ||||
|  *  This file is subject to the terms and conditions of the GNU General Public | ||||
|  *  License.  See the file COPYING in the main directory of the Linux | ||||
|  *  distribution for more details. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/io.h> | ||||
| #include <asm/mcf8390.h> | ||||
| 
 | ||||
| static const char version[] = | ||||
| 	"mcf8390.c: (15-06-2012) Greg Ungerer <gerg@uclinux.org>"; | ||||
| 
 | ||||
| #define NE_CMD		0x00 | ||||
| #define NE_DATAPORT	0x10	/* NatSemi-defined port window offset */ | ||||
| #define NE_RESET	0x1f	/* Issue a read to reset ,a write to clear */ | ||||
| #define NE_EN0_ISR	0x07 | ||||
| #define NE_EN0_DCFG	0x0e | ||||
| #define NE_EN0_RSARLO	0x08 | ||||
| #define NE_EN0_RSARHI	0x09 | ||||
| #define NE_EN0_RCNTLO	0x0a | ||||
| #define NE_EN0_RXCR	0x0c | ||||
| #define NE_EN0_TXCR	0x0d | ||||
| #define NE_EN0_RCNTHI	0x0b | ||||
| #define NE_EN0_IMR	0x0f | ||||
| 
 | ||||
| #define NESM_START_PG	0x40	/* First page of TX buffer */ | ||||
| #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ | ||||
| static u32 mcf8390_msg_enable; | ||||
| 
 | ||||
| #ifdef NE2000_ODDOFFSET | ||||
| /*
 | ||||
|  * A lot of the ColdFire boards use a separate address region for odd offset | ||||
|  * register addresses. The following functions convert and map as required. | ||||
|  * Note that the data port accesses are treated a little differently, and | ||||
|  * always accessed via the insX/outsX functions. | ||||
|  */ | ||||
| static inline u32 NE_PTR(u32 addr) | ||||
| { | ||||
| 	if (addr & 1) | ||||
| 		return addr - 1 + NE2000_ODDOFFSET; | ||||
| 	return addr; | ||||
| } | ||||
| 
 | ||||
| static inline u32 NE_DATA_PTR(u32 addr) | ||||
| { | ||||
| 	return addr; | ||||
| } | ||||
| 
 | ||||
| void ei_outb(u32 val, u32 addr) | ||||
| { | ||||
| 	NE2000_BYTE *rp; | ||||
| 
 | ||||
| 	rp = (NE2000_BYTE *) NE_PTR(addr); | ||||
| 	*rp = RSWAP(val); | ||||
| } | ||||
| 
 | ||||
| #define	ei_inb	ei_inb | ||||
| u8 ei_inb(u32 addr) | ||||
| { | ||||
| 	NE2000_BYTE *rp, val; | ||||
| 
 | ||||
| 	rp = (NE2000_BYTE *) NE_PTR(addr); | ||||
| 	val = *rp; | ||||
| 	return (u8) (RSWAP(val) & 0xff); | ||||
| } | ||||
| 
 | ||||
| void ei_insb(u32 addr, void *vbuf, int len) | ||||
| { | ||||
| 	NE2000_BYTE *rp, val; | ||||
| 	u8 *buf; | ||||
| 
 | ||||
| 	buf = (u8 *) vbuf; | ||||
| 	rp = (NE2000_BYTE *) NE_DATA_PTR(addr); | ||||
| 	for (; (len > 0); len--) { | ||||
| 		val = *rp; | ||||
| 		*buf++ = RSWAP(val); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ei_insw(u32 addr, void *vbuf, int len) | ||||
| { | ||||
| 	volatile u16 *rp; | ||||
| 	u16 w, *buf; | ||||
| 
 | ||||
| 	buf = (u16 *) vbuf; | ||||
| 	rp = (volatile u16 *) NE_DATA_PTR(addr); | ||||
| 	for (; (len > 0); len--) { | ||||
| 		w = *rp; | ||||
| 		*buf++ = BSWAP(w); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ei_outsb(u32 addr, const void *vbuf, int len) | ||||
| { | ||||
| 	NE2000_BYTE *rp, val; | ||||
| 	u8 *buf; | ||||
| 
 | ||||
| 	buf = (u8 *) vbuf; | ||||
| 	rp = (NE2000_BYTE *) NE_DATA_PTR(addr); | ||||
| 	for (; (len > 0); len--) { | ||||
| 		val = *buf++; | ||||
| 		*rp = RSWAP(val); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ei_outsw(u32 addr, const void *vbuf, int len) | ||||
| { | ||||
| 	volatile u16 *rp; | ||||
| 	u16 w, *buf; | ||||
| 
 | ||||
| 	buf = (u16 *) vbuf; | ||||
| 	rp = (volatile u16 *) NE_DATA_PTR(addr); | ||||
| 	for (; (len > 0); len--) { | ||||
| 		w = *buf++; | ||||
| 		*rp = BSWAP(w); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #else /* !NE2000_ODDOFFSET */ | ||||
| 
 | ||||
| #define	ei_inb		inb | ||||
| #define	ei_outb		outb | ||||
| #define	ei_insb		insb | ||||
| #define	ei_insw		insw | ||||
| #define	ei_outsb	outsb | ||||
| #define	ei_outsw	outsw | ||||
| 
 | ||||
| #endif /* !NE2000_ODDOFFSET */ | ||||
| 
 | ||||
| #define	ei_inb_p	ei_inb | ||||
| #define	ei_outb_p	ei_outb | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| /*
 | ||||
|  * Hard reset the card. This used to pause for the same period that a | ||||
|  * 8390 reset command required, but that shouldn't be necessary. | ||||
|  */ | ||||
| static void mcf8390_reset_8390(struct net_device *dev) | ||||
| { | ||||
| 	unsigned long reset_start_time = jiffies; | ||||
| 	u32 addr = dev->base_addr; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies); | ||||
| 
 | ||||
| 	ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET); | ||||
| 
 | ||||
| 	ei_status.txing = 0; | ||||
| 	ei_status.dmaing = 0; | ||||
| 
 | ||||
| 	/* This check _should_not_ be necessary, omit eventually. */ | ||||
| 	while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RESET) == 0) { | ||||
| 		if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) { | ||||
| 			netdev_warn(dev, "%s: did not complete\n", __func__); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ei_outb(ENISR_RESET, addr + NE_EN0_ISR); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This *shouldn't* happen. | ||||
|  * If it does, it's the last thing you'll see | ||||
|  */ | ||||
| static void mcf8390_dmaing_err(const char *func, struct net_device *dev, | ||||
| 			       struct ei_device *ei_local) | ||||
| { | ||||
| 	netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n", | ||||
| 		func, ei_local->dmaing, ei_local->irqlock); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Grab the 8390 specific header. Similar to the block_input routine, but | ||||
|  * we don't need to be concerned with ring wrap as the header will be at | ||||
|  * the start of a page, so we optimize accordingly. | ||||
|  */ | ||||
| static void mcf8390_get_8390_hdr(struct net_device *dev, | ||||
| 				 struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	u32 addr = dev->base_addr; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		mcf8390_dmaing_err(__func__, dev, ei_local); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->dmaing |= 0x01; | ||||
| 	ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD); | ||||
| 	ei_outb(ENISR_RDC, addr + NE_EN0_ISR); | ||||
| 	ei_outb(sizeof(struct e8390_pkt_hdr), addr + NE_EN0_RCNTLO); | ||||
| 	ei_outb(0, addr + NE_EN0_RCNTHI); | ||||
| 	ei_outb(0, addr + NE_EN0_RSARLO);		/* On page boundary */ | ||||
| 	ei_outb(ring_page, addr + NE_EN0_RSARHI); | ||||
| 	ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD); | ||||
| 
 | ||||
| 	ei_insw(addr + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr) >> 1); | ||||
| 
 | ||||
| 	outb(ENISR_RDC, addr + NE_EN0_ISR);	/* Ack intr */ | ||||
| 	ei_local->dmaing &= ~0x01; | ||||
| 
 | ||||
| 	hdr->count = cpu_to_le16(hdr->count); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Block input and output, similar to the Crynwr packet driver. | ||||
|  * If you are porting to a new ethercard, look at the packet driver source | ||||
|  * for hints. The NEx000 doesn't share the on-board packet memory -- | ||||
|  * you have to put the packet out through the "remote DMA" dataport | ||||
|  * using z_writeb. | ||||
|  */ | ||||
| static void mcf8390_block_input(struct net_device *dev, int count, | ||||
| 				struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	u32 addr = dev->base_addr; | ||||
| 	char *buf = skb->data; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		mcf8390_dmaing_err(__func__, dev, ei_local); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->dmaing |= 0x01; | ||||
| 	ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD); | ||||
| 	ei_outb(ENISR_RDC, addr + NE_EN0_ISR); | ||||
| 	ei_outb(count & 0xff, addr + NE_EN0_RCNTLO); | ||||
| 	ei_outb(count >> 8, addr + NE_EN0_RCNTHI); | ||||
| 	ei_outb(ring_offset & 0xff, addr + NE_EN0_RSARLO); | ||||
| 	ei_outb(ring_offset >> 8, addr + NE_EN0_RSARHI); | ||||
| 	ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD); | ||||
| 
 | ||||
| 	ei_insw(addr + NE_DATAPORT, buf, count >> 1); | ||||
| 	if (count & 1) | ||||
| 		buf[count - 1] = ei_inb(addr + NE_DATAPORT); | ||||
| 
 | ||||
| 	ei_outb(ENISR_RDC, addr + NE_EN0_ISR);	/* Ack intr */ | ||||
| 	ei_local->dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static void mcf8390_block_output(struct net_device *dev, int count, | ||||
| 				 const unsigned char *buf, | ||||
| 				 const int start_page) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	u32 addr = dev->base_addr; | ||||
| 	unsigned long dma_start; | ||||
| 
 | ||||
| 	/* Make sure we transfer all bytes if 16bit IO writes */ | ||||
| 	if (count & 0x1) | ||||
| 		count++; | ||||
| 
 | ||||
| 	if (ei_local->dmaing) { | ||||
| 		mcf8390_dmaing_err(__func__, dev, ei_local); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_local->dmaing |= 0x01; | ||||
| 	/* We should already be in page 0, but to be safe... */ | ||||
| 	ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, addr + NE_CMD); | ||||
| 
 | ||||
| 	ei_outb(ENISR_RDC, addr + NE_EN0_ISR); | ||||
| 
 | ||||
| 	/* Now the normal output. */ | ||||
| 	ei_outb(count & 0xff, addr + NE_EN0_RCNTLO); | ||||
| 	ei_outb(count >> 8, addr + NE_EN0_RCNTHI); | ||||
| 	ei_outb(0x00, addr + NE_EN0_RSARLO); | ||||
| 	ei_outb(start_page, addr + NE_EN0_RSARHI); | ||||
| 	ei_outb(E8390_RWRITE + E8390_START, addr + NE_CMD); | ||||
| 
 | ||||
| 	ei_outsw(addr + NE_DATAPORT, buf, count >> 1); | ||||
| 
 | ||||
| 	dma_start = jiffies; | ||||
| 	while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RDC) == 0) { | ||||
| 		if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */ | ||||
| 			netdev_warn(dev, "timeout waiting for Tx RDC\n"); | ||||
| 			mcf8390_reset_8390(dev); | ||||
| 			__NS8390_init(dev, 1); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ei_outb(ENISR_RDC, addr + NE_EN0_ISR);	/* Ack intr */ | ||||
| 	ei_local->dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static const struct net_device_ops mcf8390_netdev_ops = { | ||||
| 	.ndo_open		= __ei_open, | ||||
| 	.ndo_stop		= __ei_close, | ||||
| 	.ndo_start_xmit		= __ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= __ei_tx_timeout, | ||||
| 	.ndo_get_stats		= __ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= __ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= __ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int mcf8390_init(struct net_device *dev) | ||||
| { | ||||
| 	static u32 offsets[] = { | ||||
| 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||||
| 		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | ||||
| 	}; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	unsigned char SA_prom[32]; | ||||
| 	u32 addr = dev->base_addr; | ||||
| 	int start_page, stop_page; | ||||
| 	int i, ret; | ||||
| 
 | ||||
| 	mcf8390_reset_8390(dev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Read the 16 bytes of station address PROM. | ||||
| 	 * We must first initialize registers, | ||||
| 	 * similar to NS8390_init(eifdev, 0). | ||||
| 	 * We can't reliably read the SAPROM address without this. | ||||
| 	 * (I learned the hard way!). | ||||
| 	 */ | ||||
| 	{ | ||||
| 		static const struct { | ||||
| 			u32 value; | ||||
| 			u32 offset; | ||||
| 		} program_seq[] = { | ||||
| 			{E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD}, | ||||
| 						/* Select page 0 */ | ||||
| 			{0x48,	NE_EN0_DCFG},	/* 0x48: Set byte-wide access */ | ||||
| 			{0x00,	NE_EN0_RCNTLO},	/* Clear the count regs */ | ||||
| 			{0x00,	NE_EN0_RCNTHI}, | ||||
| 			{0x00,	NE_EN0_IMR},	/* Mask completion irq */ | ||||
| 			{0xFF,	NE_EN0_ISR}, | ||||
| 			{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */ | ||||
| 			{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */ | ||||
| 			{32,	NE_EN0_RCNTLO}, | ||||
| 			{0x00,	NE_EN0_RCNTHI}, | ||||
| 			{0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000 */ | ||||
| 			{0x00,	NE_EN0_RSARHI}, | ||||
| 			{E8390_RREAD + E8390_START, NE_CMD}, | ||||
| 		}; | ||||
| 		for (i = 0; i < ARRAY_SIZE(program_seq); i++) { | ||||
| 			ei_outb(program_seq[i].value, | ||||
| 				 addr + program_seq[i].offset); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < 16; i++) { | ||||
| 		SA_prom[i] = ei_inb(addr + NE_DATAPORT); | ||||
| 		ei_inb(addr + NE_DATAPORT); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We must set the 8390 for word mode. */ | ||||
| 	ei_outb(0x49, addr + NE_EN0_DCFG); | ||||
| 	start_page = NESM_START_PG; | ||||
| 	stop_page = NESM_STOP_PG; | ||||
| 
 | ||||
| 	/* Install the Interrupt handler */ | ||||
| 	ret = request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		dev->dev_addr[i] = SA_prom[i]; | ||||
| 
 | ||||
| 	netdev_dbg(dev, "Found ethernet address: %pM\n", dev->dev_addr); | ||||
| 
 | ||||
| 	ei_local->name = "mcf8390"; | ||||
| 	ei_local->tx_start_page = start_page; | ||||
| 	ei_local->stop_page = stop_page; | ||||
| 	ei_local->word16 = 1; | ||||
| 	ei_local->rx_start_page = start_page + TX_PAGES; | ||||
| 	ei_local->reset_8390 = mcf8390_reset_8390; | ||||
| 	ei_local->block_input = mcf8390_block_input; | ||||
| 	ei_local->block_output = mcf8390_block_output; | ||||
| 	ei_local->get_8390_hdr = mcf8390_get_8390_hdr; | ||||
| 	ei_local->reg_offset = offsets; | ||||
| 
 | ||||
| 	dev->netdev_ops = &mcf8390_netdev_ops; | ||||
| 	__NS8390_init(dev, 0); | ||||
| 	ret = register_netdev(dev); | ||||
| 	if (ret) { | ||||
| 		free_irq(dev->irq, dev); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	netdev_info(dev, "addr=0x%08x irq=%d, Ethernet Address %pM\n", | ||||
| 		addr, dev->irq, dev->dev_addr); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int mcf8390_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	struct ei_device *ei_local; | ||||
| 	struct resource *mem, *irq; | ||||
| 	resource_size_t msize; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||||
| 	if (irq == NULL) { | ||||
| 		dev_err(&pdev->dev, "no IRQ specified?\n"); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (mem == NULL) { | ||||
| 		dev_err(&pdev->dev, "no memory address specified?\n"); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	msize = resource_size(mem); | ||||
| 	if (!request_mem_region(mem->start, msize, pdev->name)) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	dev = ____alloc_ei_netdev(0); | ||||
| 	if (dev == NULL) { | ||||
| 		release_mem_region(mem->start, msize); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	SET_NETDEV_DEV(dev, &pdev->dev); | ||||
| 	platform_set_drvdata(pdev, dev); | ||||
| 	ei_local = netdev_priv(dev); | ||||
| 	ei_local->msg_enable = mcf8390_msg_enable; | ||||
| 
 | ||||
| 	dev->irq = irq->start; | ||||
| 	dev->base_addr = mem->start; | ||||
| 
 | ||||
| 	ret = mcf8390_init(dev); | ||||
| 	if (ret) { | ||||
| 		release_mem_region(mem->start, msize); | ||||
| 		free_netdev(dev); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int mcf8390_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct net_device *dev = platform_get_drvdata(pdev); | ||||
| 	struct resource *mem; | ||||
| 
 | ||||
| 	unregister_netdev(dev); | ||||
| 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (mem) | ||||
| 		release_mem_region(mem->start, resource_size(mem)); | ||||
| 	free_netdev(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver mcf8390_drv = { | ||||
| 	.driver = { | ||||
| 		.name	= "mcf8390", | ||||
| 		.owner	= THIS_MODULE, | ||||
| 	}, | ||||
| 	.probe		= mcf8390_probe, | ||||
| 	.remove		= mcf8390_remove, | ||||
| }; | ||||
| 
 | ||||
| module_platform_driver(mcf8390_drv); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("MCF8390 ColdFire NS8390 driver"); | ||||
| MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_ALIAS("platform:mcf8390"); | ||||
							
								
								
									
										1019
									
								
								drivers/net/ethernet/8390/ne.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1019
									
								
								drivers/net/ethernet/8390/ne.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										741
									
								
								drivers/net/ethernet/8390/ne2k-pci.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										741
									
								
								drivers/net/ethernet/8390/ne2k-pci.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,741 @@ | |||
| /* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */ | ||||
| /*
 | ||||
| 	A Linux device driver for PCI NE2000 clones. | ||||
| 
 | ||||
| 	Authors and other copyright holders: | ||||
| 	1992-2000 by Donald Becker, NE2000 core and various modifications. | ||||
| 	1995-1998 by Paul Gortmaker, core modifications and PCI support. | ||||
| 	Copyright 1993 assigned to the United States Government as represented | ||||
| 	by the Director, National Security Agency. | ||||
| 
 | ||||
| 	This software may be used and distributed according to the terms of | ||||
| 	the GNU General Public License (GPL), incorporated herein by reference. | ||||
| 	Drivers based on or derived from this code fall under the GPL and must | ||||
| 	retain the authorship, copyright and license notice.  This file is not | ||||
| 	a complete program and may only be used when the entire operating | ||||
| 	system is licensed under the GPL. | ||||
| 
 | ||||
| 	The author may be reached as becker@scyld.com, or C/O | ||||
| 	Scyld Computing Corporation | ||||
| 	410 Severn Ave., Suite 210 | ||||
| 	Annapolis MD 21403 | ||||
| 
 | ||||
| 	Issues remaining: | ||||
| 	People are making PCI ne2000 clones! Oh the horror, the horror... | ||||
| 	Limited full-duplex support. | ||||
| */ | ||||
| 
 | ||||
| #define DRV_NAME	"ne2k-pci" | ||||
| #define DRV_VERSION	"1.03" | ||||
| #define DRV_RELDATE	"9/22/2003" | ||||
| 
 | ||||
| 
 | ||||
| /* The user-configurable values.
 | ||||
|    These may be modified when a driver module is loaded.*/ | ||||
| 
 | ||||
| #define MAX_UNITS 8				/* More are supported, limit only on options */ | ||||
| /* Used to pass the full-duplex flag, etc. */ | ||||
| static int full_duplex[MAX_UNITS]; | ||||
| static int options[MAX_UNITS]; | ||||
| 
 | ||||
| /* Force a non std. amount of memory.  Units are 256 byte pages. */ | ||||
| /* #define PACKETBUF_MEMSIZE	0x40 */ | ||||
| 
 | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/pci.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/ethtool.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <asm/irq.h> | ||||
| #include <asm/uaccess.h> | ||||
| 
 | ||||
| #include "8390.h" | ||||
| 
 | ||||
| static u32 ne2k_msg_enable; | ||||
| 
 | ||||
| /* These identify the driver base version and may not be removed. */ | ||||
| static const char version[] = | ||||
| 	KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE | ||||
| 	" D. Becker/P. Gortmaker\n"; | ||||
| 
 | ||||
| #if defined(__powerpc__) | ||||
| #define inl_le(addr)  le32_to_cpu(inl(addr)) | ||||
| #define inw_le(addr)  le16_to_cpu(inw(addr)) | ||||
| #endif | ||||
| 
 | ||||
| #define PFX DRV_NAME ": " | ||||
| 
 | ||||
| MODULE_AUTHOR("Donald Becker / Paul Gortmaker"); | ||||
| MODULE_DESCRIPTION("PCI NE2000 clone driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| module_param_named(msg_enable, ne2k_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH)); | ||||
| module_param_array(options, int, NULL, 0); | ||||
| module_param_array(full_duplex, int, NULL, 0); | ||||
| MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); | ||||
| MODULE_PARM_DESC(options, "Bit 5: full duplex"); | ||||
| MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)"); | ||||
| 
 | ||||
| /* Some defines that people can play with if so inclined. */ | ||||
| 
 | ||||
| /* Use 32 bit data-movement operations instead of 16 bit. */ | ||||
| #define USE_LONGIO | ||||
| 
 | ||||
| /* Do we implement the read before write bugfix ? */ | ||||
| /* #define NE_RW_BUGFIX */ | ||||
| 
 | ||||
| /* Flags.  We rename an existing ei_status field to store flags! */ | ||||
| /* Thus only the low 8 bits are usable for non-init-time flags. */ | ||||
| #define ne2k_flags reg0 | ||||
| enum { | ||||
| 	ONLY_16BIT_IO=8, ONLY_32BIT_IO=4,	/* Chip can do only 16/32-bit xfers. */ | ||||
| 	FORCE_FDX=0x20,						/* User override. */ | ||||
| 	REALTEK_FDX=0x40, HOLTEK_FDX=0x80, | ||||
| 	STOP_PG_0x60=0x100, | ||||
| }; | ||||
| 
 | ||||
| enum ne2k_pci_chipsets { | ||||
| 	CH_RealTek_RTL_8029 = 0, | ||||
| 	CH_Winbond_89C940, | ||||
| 	CH_Compex_RL2000, | ||||
| 	CH_KTI_ET32P2, | ||||
| 	CH_NetVin_NV5000SC, | ||||
| 	CH_Via_86C926, | ||||
| 	CH_SureCom_NE34, | ||||
| 	CH_Winbond_W89C940F, | ||||
| 	CH_Holtek_HT80232, | ||||
| 	CH_Holtek_HT80229, | ||||
| 	CH_Winbond_89C940_8c4a, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static struct { | ||||
| 	char *name; | ||||
| 	int flags; | ||||
| } pci_clone_list[] = { | ||||
| 	{"RealTek RTL-8029", REALTEK_FDX}, | ||||
| 	{"Winbond 89C940", 0}, | ||||
| 	{"Compex RL2000", 0}, | ||||
| 	{"KTI ET32P2", 0}, | ||||
| 	{"NetVin NV5000SC", 0}, | ||||
| 	{"Via 86C926", ONLY_16BIT_IO}, | ||||
| 	{"SureCom NE34", 0}, | ||||
| 	{"Winbond W89C940F", 0}, | ||||
| 	{"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX}, | ||||
| 	{"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 }, | ||||
| 	{"Winbond W89C940(misprogrammed)", 0}, | ||||
| 	{NULL,} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static const struct pci_device_id ne2k_pci_tbl[] = { | ||||
| 	{ 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 }, | ||||
| 	{ 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 }, | ||||
| 	{ 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 }, | ||||
| 	{ 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 }, | ||||
| 	{ 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC }, | ||||
| 	{ 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 }, | ||||
| 	{ 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 }, | ||||
| 	{ 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F }, | ||||
| 	{ 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 }, | ||||
| 	{ 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 }, | ||||
| 	{ 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a }, | ||||
| 	{ 0, } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl); | ||||
| 
 | ||||
| 
 | ||||
| /* ---- No user-serviceable parts below ---- */ | ||||
| 
 | ||||
| #define NE_BASE	 (dev->base_addr) | ||||
| #define NE_CMD	 	0x00 | ||||
| #define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */ | ||||
| #define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */ | ||||
| #define NE_IO_EXTENT	0x20 | ||||
| 
 | ||||
| #define NESM_START_PG	0x40	/* First page of TX buffer */ | ||||
| #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ | ||||
| 
 | ||||
| 
 | ||||
| static int ne2k_pci_open(struct net_device *dev); | ||||
| static int ne2k_pci_close(struct net_device *dev); | ||||
| 
 | ||||
| static void ne2k_pci_reset_8390(struct net_device *dev); | ||||
| static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 			  int ring_page); | ||||
| static void ne2k_pci_block_input(struct net_device *dev, int count, | ||||
| 			  struct sk_buff *skb, int ring_offset); | ||||
| static void ne2k_pci_block_output(struct net_device *dev, const int count, | ||||
| 		const unsigned char *buf, const int start_page); | ||||
| static const struct ethtool_ops ne2k_pci_ethtool_ops; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* There is no room in the standard 8390 structure for extra info we need,
 | ||||
|    so we build a meta/outer-wrapper structure.. */ | ||||
| struct ne2k_pci_card { | ||||
| 	struct net_device *dev; | ||||
| 	struct pci_dev *pci_dev; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet | ||||
|   buffer memory space.  By-the-spec NE2000 clones have 0x57,0x57 in bytes | ||||
|   0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be | ||||
|   detected by their SA prefix. | ||||
| 
 | ||||
|   Reading the SAPROM from a word-wide card with the 8390 set in byte-wide | ||||
|   mode results in doubled values, which can be detected and compensated for. | ||||
| 
 | ||||
|   The probe is also responsible for initializing the card and filling | ||||
|   in the 'dev' and 'ei_status' structures. | ||||
| */ | ||||
| 
 | ||||
| static const struct net_device_ops ne2k_netdev_ops = { | ||||
| 	.ndo_open		= ne2k_pci_open, | ||||
| 	.ndo_stop		= ne2k_pci_close, | ||||
| 	.ndo_start_xmit		= ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= ei_tx_timeout, | ||||
| 	.ndo_get_stats		= ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller = ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int ne2k_pci_init_one(struct pci_dev *pdev, | ||||
| 			     const struct pci_device_id *ent) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	int i; | ||||
| 	unsigned char SA_prom[32]; | ||||
| 	int start_page, stop_page; | ||||
| 	int irq, reg0, chip_idx = ent->driver_data; | ||||
| 	static unsigned int fnd_cnt; | ||||
| 	long ioaddr; | ||||
| 	int flags = pci_clone_list[chip_idx].flags; | ||||
| 	struct ei_device *ei_local; | ||||
| 
 | ||||
| /* when built into the kernel, we only print version if device is found */ | ||||
| #ifndef MODULE | ||||
| 	static int printed_version; | ||||
| 	if (!printed_version++) | ||||
| 		printk(version); | ||||
| #endif | ||||
| 
 | ||||
| 	fnd_cnt++; | ||||
| 
 | ||||
| 	i = pci_enable_device (pdev); | ||||
| 	if (i) | ||||
| 		return i; | ||||
| 
 | ||||
| 	ioaddr = pci_resource_start (pdev, 0); | ||||
| 	irq = pdev->irq; | ||||
| 
 | ||||
| 	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { | ||||
| 		dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { | ||||
| 		dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n", | ||||
| 			NE_IO_EXTENT, ioaddr); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 
 | ||||
| 	reg0 = inb(ioaddr); | ||||
| 	if (reg0 == 0xFF) | ||||
| 		goto err_out_free_res; | ||||
| 
 | ||||
| 	/* Do a preliminary verification that we have a 8390. */ | ||||
| 	{ | ||||
| 		int regd; | ||||
| 		outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); | ||||
| 		regd = inb(ioaddr + 0x0d); | ||||
| 		outb(0xff, ioaddr + 0x0d); | ||||
| 		outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); | ||||
| 		inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ | ||||
| 		if (inb(ioaddr + EN0_COUNTER0) != 0) { | ||||
| 			outb(reg0, ioaddr); | ||||
| 			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */ | ||||
| 			goto err_out_free_res; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */ | ||||
| 	dev = alloc_ei_netdev(); | ||||
| 	if (!dev) { | ||||
| 		dev_err(&pdev->dev, "cannot allocate ethernet device\n"); | ||||
| 		goto err_out_free_res; | ||||
| 	} | ||||
| 	dev->netdev_ops = &ne2k_netdev_ops; | ||||
| 	ei_local = netdev_priv(dev); | ||||
| 	ei_local->msg_enable = ne2k_msg_enable; | ||||
| 
 | ||||
| 	SET_NETDEV_DEV(dev, &pdev->dev); | ||||
| 
 | ||||
| 	/* Reset card. Who knows what dain-bramaged state it was left in. */ | ||||
| 	{ | ||||
| 		unsigned long reset_start_time = jiffies; | ||||
| 
 | ||||
| 		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); | ||||
| 
 | ||||
| 		/* This looks like a horrible timing loop, but it should never take
 | ||||
| 		   more than a few cycles. | ||||
| 		*/ | ||||
| 		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) | ||||
| 			/* Limit wait: '2' avoids jiffy roll-over. */ | ||||
| 			if (jiffies - reset_start_time > 2) { | ||||
| 				dev_err(&pdev->dev, | ||||
| 					"Card failure (no reset ack).\n"); | ||||
| 				goto err_out_free_netdev; | ||||
| 			} | ||||
| 
 | ||||
| 		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */ | ||||
| 	} | ||||
| 
 | ||||
| 	/* Read the 16 bytes of station address PROM.
 | ||||
| 	   We must first initialize registers, similar to NS8390_init(eifdev, 0). | ||||
| 	   We can't reliably read the SAPROM address without this. | ||||
| 	   (I learned the hard way!). */ | ||||
| 	{ | ||||
| 		struct {unsigned char value, offset; } program_seq[] = { | ||||
| 			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ | ||||
| 			{0x49,	EN0_DCFG},	/* Set word-wide access. */ | ||||
| 			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */ | ||||
| 			{0x00,	EN0_RCNTHI}, | ||||
| 			{0x00,	EN0_IMR},	/* Mask completion irq. */ | ||||
| 			{0xFF,	EN0_ISR}, | ||||
| 			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */ | ||||
| 			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */ | ||||
| 			{32,	EN0_RCNTLO}, | ||||
| 			{0x00,	EN0_RCNTHI}, | ||||
| 			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */ | ||||
| 			{0x00,	EN0_RSARHI}, | ||||
| 			{E8390_RREAD+E8390_START, E8390_CMD}, | ||||
| 		}; | ||||
| 		for (i = 0; i < ARRAY_SIZE(program_seq); i++) | ||||
| 			outb(program_seq[i].value, ioaddr + program_seq[i].offset); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	/* Note: all PCI cards have at least 16 bit access, so we don't have
 | ||||
| 	   to check for 8 bit cards.  Most cards permit 32 bit access. */ | ||||
| 	if (flags & ONLY_32BIT_IO) { | ||||
| 		for (i = 0; i < 4 ; i++) | ||||
| 			((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT)); | ||||
| 	} else | ||||
| 		for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++) | ||||
| 			SA_prom[i] = inb(ioaddr + NE_DATAPORT); | ||||
| 
 | ||||
| 	/* We always set the 8390 registers for word mode. */ | ||||
| 	outb(0x49, ioaddr + EN0_DCFG); | ||||
| 	start_page = NESM_START_PG; | ||||
| 
 | ||||
| 	stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG; | ||||
| 
 | ||||
| 	/* Set up the rest of the parameters. */ | ||||
| 	dev->irq = irq; | ||||
| 	dev->base_addr = ioaddr; | ||||
| 	pci_set_drvdata(pdev, dev); | ||||
| 
 | ||||
| 	ei_status.name = pci_clone_list[chip_idx].name; | ||||
| 	ei_status.tx_start_page = start_page; | ||||
| 	ei_status.stop_page = stop_page; | ||||
| 	ei_status.word16 = 1; | ||||
| 	ei_status.ne2k_flags = flags; | ||||
| 	if (fnd_cnt < MAX_UNITS) { | ||||
| 		if (full_duplex[fnd_cnt] > 0  ||  (options[fnd_cnt] & FORCE_FDX)) | ||||
| 			ei_status.ne2k_flags |= FORCE_FDX; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_status.rx_start_page = start_page + TX_PAGES; | ||||
| #ifdef PACKETBUF_MEMSIZE | ||||
| 	/* Allow the packet buffer size to be overridden by know-it-alls. */ | ||||
| 	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; | ||||
| #endif | ||||
| 
 | ||||
| 	ei_status.reset_8390 = &ne2k_pci_reset_8390; | ||||
| 	ei_status.block_input = &ne2k_pci_block_input; | ||||
| 	ei_status.block_output = &ne2k_pci_block_output; | ||||
| 	ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr; | ||||
| 	ei_status.priv = (unsigned long) pdev; | ||||
| 
 | ||||
| 	dev->ethtool_ops = &ne2k_pci_ethtool_ops; | ||||
| 	NS8390_init(dev, 0); | ||||
| 
 | ||||
| 	memcpy(dev->dev_addr, SA_prom, dev->addr_len); | ||||
| 
 | ||||
| 	i = register_netdev(dev); | ||||
| 	if (i) | ||||
| 		goto err_out_free_netdev; | ||||
| 
 | ||||
| 	netdev_info(dev, "%s found at %#lx, IRQ %d, %pM.\n", | ||||
| 		    pci_clone_list[chip_idx].name, ioaddr, dev->irq, | ||||
| 		    dev->dev_addr); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_out_free_netdev: | ||||
| 	free_netdev (dev); | ||||
| err_out_free_res: | ||||
| 	release_region (ioaddr, NE_IO_EXTENT); | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Magic incantation sequence for full duplex on the supported cards. | ||||
|  */ | ||||
| static inline int set_realtek_fdx(struct net_device *dev) | ||||
| { | ||||
| 	long ioaddr = dev->base_addr; | ||||
| 
 | ||||
| 	outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */ | ||||
| 	outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */ | ||||
| 	outb(0x40, ioaddr + 0x06); /* Enable full duplex */ | ||||
| 	outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */ | ||||
| 	outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */ | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int set_holtek_fdx(struct net_device *dev) | ||||
| { | ||||
| 	long ioaddr = dev->base_addr; | ||||
| 
 | ||||
| 	outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ne2k_pci_set_fdx(struct net_device *dev) | ||||
| { | ||||
| 	if (ei_status.ne2k_flags & REALTEK_FDX) | ||||
| 		return set_realtek_fdx(dev); | ||||
| 	else if (ei_status.ne2k_flags & HOLTEK_FDX) | ||||
| 		return set_holtek_fdx(dev); | ||||
| 
 | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
| static int ne2k_pci_open(struct net_device *dev) | ||||
| { | ||||
| 	int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (ei_status.ne2k_flags & FORCE_FDX) | ||||
| 		ne2k_pci_set_fdx(dev); | ||||
| 
 | ||||
| 	ei_open(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ne2k_pci_close(struct net_device *dev) | ||||
| { | ||||
| 	ei_close(dev); | ||||
| 	free_irq(dev->irq, dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Hard reset the card.  This used to pause for the same period that a
 | ||||
|    8390 reset command required, but that shouldn't be necessary. */ | ||||
| static void ne2k_pci_reset_8390(struct net_device *dev) | ||||
| { | ||||
| 	unsigned long reset_start_time = jiffies; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", | ||||
| 		  jiffies); | ||||
| 
 | ||||
| 	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); | ||||
| 
 | ||||
| 	ei_status.txing = 0; | ||||
| 	ei_status.dmaing = 0; | ||||
| 
 | ||||
| 	/* This check _should_not_ be necessary, omit eventually. */ | ||||
| 	while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) | ||||
| 		if (jiffies - reset_start_time > 2) { | ||||
| 			netdev_err(dev, "ne2k_pci_reset_8390() did not complete.\n"); | ||||
| 			break; | ||||
| 		} | ||||
| 	outb(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */ | ||||
| } | ||||
| 
 | ||||
| /* Grab the 8390 specific header. Similar to the block_input routine, but
 | ||||
|    we don't need to be concerned with ring wrap as the header will be at | ||||
|    the start of a page, so we optimize accordingly. */ | ||||
| 
 | ||||
| static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 
 | ||||
| 	long nic_base = dev->base_addr; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in ne2k_pci_get_8390_hdr " | ||||
| 			   "[DMAstat:%d][irqlock:%d].\n", | ||||
| 			   ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); | ||||
| 	outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); | ||||
| 	outb(0, nic_base + EN0_RCNTHI); | ||||
| 	outb(0, nic_base + EN0_RSARLO);		/* On page boundary */ | ||||
| 	outb(ring_page, nic_base + EN0_RSARHI); | ||||
| 	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| 
 | ||||
| 	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { | ||||
| 		insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); | ||||
| 	} else { | ||||
| 		*(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT)); | ||||
| 		le16_to_cpus(&hdr->count); | ||||
| 	} | ||||
| 
 | ||||
| 	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| /* Block input and output, similar to the Crynwr packet driver.  If you
 | ||||
|    are porting to a new ethercard, look at the packet driver source for hints. | ||||
|    The NEx000 doesn't share the on-board packet memory -- you have to put | ||||
|    the packet out through the "remote DMA" dataport using outb. */ | ||||
| 
 | ||||
| static void ne2k_pci_block_input(struct net_device *dev, int count, | ||||
| 				 struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	long nic_base = dev->base_addr; | ||||
| 	char *buf = skb->data; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in ne2k_pci_block_input " | ||||
| 			   "[DMAstat:%d][irqlock:%d].\n", | ||||
| 			   ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	if (ei_status.ne2k_flags & ONLY_32BIT_IO) | ||||
| 		count = (count + 3) & 0xFFFC; | ||||
| 	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); | ||||
| 	outb(count & 0xff, nic_base + EN0_RCNTLO); | ||||
| 	outb(count >> 8, nic_base + EN0_RCNTHI); | ||||
| 	outb(ring_offset & 0xff, nic_base + EN0_RSARLO); | ||||
| 	outb(ring_offset >> 8, nic_base + EN0_RSARHI); | ||||
| 	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| 
 | ||||
| 	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { | ||||
| 		insw(NE_BASE + NE_DATAPORT,buf,count>>1); | ||||
| 		if (count & 0x01) { | ||||
| 			buf[count-1] = inb(NE_BASE + NE_DATAPORT); | ||||
| 		} | ||||
| 	} else { | ||||
| 		insl(NE_BASE + NE_DATAPORT, buf, count>>2); | ||||
| 		if (count & 3) { | ||||
| 			buf += count & ~3; | ||||
| 			if (count & 2) { | ||||
| 				__le16 *b = (__le16 *)buf; | ||||
| 
 | ||||
| 				*b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT)); | ||||
| 				buf = (char *)b; | ||||
| 			} | ||||
| 			if (count & 1) | ||||
| 				*buf = inb(NE_BASE + NE_DATAPORT); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static void ne2k_pci_block_output(struct net_device *dev, int count, | ||||
| 				  const unsigned char *buf, const int start_page) | ||||
| { | ||||
| 	long nic_base = NE_BASE; | ||||
| 	unsigned long dma_start; | ||||
| 
 | ||||
| 	/* On little-endian it's always safe to round the count up for
 | ||||
| 	   word writes. */ | ||||
| 	if (ei_status.ne2k_flags & ONLY_32BIT_IO) | ||||
| 		count = (count + 3) & 0xFFFC; | ||||
| 	else | ||||
| 		if (count & 0x01) | ||||
| 			count++; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "DMAing conflict in ne2k_pci_block_output." | ||||
| 			   "[DMAstat:%d][irqlock:%d]\n", | ||||
| 			   ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	/* We should already be in page 0, but to be safe... */ | ||||
| 	outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); | ||||
| 
 | ||||
| #ifdef NE8390_RW_BUGFIX | ||||
| 	/* Handle the read-before-write bug the same way as the
 | ||||
| 	   Crynwr packet driver -- the NatSemi method doesn't work. | ||||
| 	   Actually this doesn't always work either, but if you have | ||||
| 	   problems with your NEx000 this is better than nothing! */ | ||||
| 	outb(0x42, nic_base + EN0_RCNTLO); | ||||
| 	outb(0x00, nic_base + EN0_RCNTHI); | ||||
| 	outb(0x42, nic_base + EN0_RSARLO); | ||||
| 	outb(0x00, nic_base + EN0_RSARHI); | ||||
| 	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| #endif | ||||
| 	outb(ENISR_RDC, nic_base + EN0_ISR); | ||||
| 
 | ||||
|    /* Now the normal output. */ | ||||
| 	outb(count & 0xff, nic_base + EN0_RCNTLO); | ||||
| 	outb(count >> 8,   nic_base + EN0_RCNTHI); | ||||
| 	outb(0x00, nic_base + EN0_RSARLO); | ||||
| 	outb(start_page, nic_base + EN0_RSARHI); | ||||
| 	outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); | ||||
| 	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { | ||||
| 		outsw(NE_BASE + NE_DATAPORT, buf, count>>1); | ||||
| 	} else { | ||||
| 		outsl(NE_BASE + NE_DATAPORT, buf, count>>2); | ||||
| 		if (count & 3) { | ||||
| 			buf += count & ~3; | ||||
| 			if (count & 2) { | ||||
| 				__le16 *b = (__le16 *)buf; | ||||
| 
 | ||||
| 				outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT); | ||||
| 				buf = (char *)b; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dma_start = jiffies; | ||||
| 
 | ||||
| 	while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) | ||||
| 		if (jiffies - dma_start > 2) {			/* Avoid clock roll-over. */ | ||||
| 			netdev_warn(dev, "timeout waiting for Tx RDC.\n"); | ||||
| 			ne2k_pci_reset_8390(dev); | ||||
| 			NS8390_init(dev,1); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static void ne2k_pci_get_drvinfo(struct net_device *dev, | ||||
| 				 struct ethtool_drvinfo *info) | ||||
| { | ||||
| 	struct ei_device *ei = netdev_priv(dev); | ||||
| 	struct pci_dev *pci_dev = (struct pci_dev *) ei->priv; | ||||
| 
 | ||||
| 	strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); | ||||
| 	strlcpy(info->version, DRV_VERSION, sizeof(info->version)); | ||||
| 	strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); | ||||
| } | ||||
| 
 | ||||
| static u32 ne2k_pci_get_msglevel(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	return ei_local->msg_enable; | ||||
| } | ||||
| 
 | ||||
| static void ne2k_pci_set_msglevel(struct net_device *dev, u32 v) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	ei_local->msg_enable = v; | ||||
| } | ||||
| 
 | ||||
| static const struct ethtool_ops ne2k_pci_ethtool_ops = { | ||||
| 	.get_drvinfo		= ne2k_pci_get_drvinfo, | ||||
| 	.get_msglevel		= ne2k_pci_get_msglevel, | ||||
| 	.set_msglevel		= ne2k_pci_set_msglevel, | ||||
| }; | ||||
| 
 | ||||
| static void ne2k_pci_remove_one(struct pci_dev *pdev) | ||||
| { | ||||
| 	struct net_device *dev = pci_get_drvdata(pdev); | ||||
| 
 | ||||
| 	BUG_ON(!dev); | ||||
| 	unregister_netdev(dev); | ||||
| 	release_region(dev->base_addr, NE_IO_EXTENT); | ||||
| 	free_netdev(dev); | ||||
| 	pci_disable_device(pdev); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state) | ||||
| { | ||||
| 	struct net_device *dev = pci_get_drvdata (pdev); | ||||
| 
 | ||||
| 	netif_device_detach(dev); | ||||
| 	pci_save_state(pdev); | ||||
| 	pci_disable_device(pdev); | ||||
| 	pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ne2k_pci_resume (struct pci_dev *pdev) | ||||
| { | ||||
| 	struct net_device *dev = pci_get_drvdata (pdev); | ||||
| 	int rc; | ||||
| 
 | ||||
| 	pci_set_power_state(pdev, PCI_D0); | ||||
| 	pci_restore_state(pdev); | ||||
| 
 | ||||
| 	rc = pci_enable_device(pdev); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	NS8390_init(dev, 1); | ||||
| 	netif_device_attach(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_PM */ | ||||
| 
 | ||||
| 
 | ||||
| static struct pci_driver ne2k_driver = { | ||||
| 	.name		= DRV_NAME, | ||||
| 	.probe		= ne2k_pci_init_one, | ||||
| 	.remove		= ne2k_pci_remove_one, | ||||
| 	.id_table	= ne2k_pci_tbl, | ||||
| #ifdef CONFIG_PM | ||||
| 	.suspend	= ne2k_pci_suspend, | ||||
| 	.resume		= ne2k_pci_resume, | ||||
| #endif /* CONFIG_PM */ | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static int __init ne2k_pci_init(void) | ||||
| { | ||||
| /* when a module, this is printed whether or not devices are found in probe */ | ||||
| #ifdef MODULE | ||||
| 	printk(version); | ||||
| #endif | ||||
| 	return pci_register_driver(&ne2k_driver); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void __exit ne2k_pci_cleanup(void) | ||||
| { | ||||
| 	pci_unregister_driver (&ne2k_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(ne2k_pci_init); | ||||
| module_exit(ne2k_pci_cleanup); | ||||
							
								
								
									
										1702
									
								
								drivers/net/ethernet/8390/pcnet_cs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1702
									
								
								drivers/net/ethernet/8390/pcnet_cs.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										631
									
								
								drivers/net/ethernet/8390/smc-ultra.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										631
									
								
								drivers/net/ethernet/8390/smc-ultra.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,631 @@ | |||
| /* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ | ||||
| /*
 | ||||
| 	This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. | ||||
| 
 | ||||
| 	Written 1993-1998 by Donald Becker. | ||||
| 
 | ||||
| 	Copyright 1993 United States Government as represented by the | ||||
| 	Director, National Security Agency. | ||||
| 
 | ||||
| 	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 becker@scyld.com, or C/O | ||||
| 	Scyld Computing Corporation | ||||
| 	410 Severn Ave., Suite 210 | ||||
| 	Annapolis MD 21403 | ||||
| 
 | ||||
| 	This driver uses the cards in the 8390-compatible mode. | ||||
| 	Most of the run-time complexity is handled by the generic code in | ||||
| 	8390.c.  The code in this file is responsible for | ||||
| 
 | ||||
| 		ultra_probe()	 	Detecting and initializing the card. | ||||
| 		ultra_probe1() | ||||
| 		ultra_probe_isapnp() | ||||
| 
 | ||||
| 		ultra_open()		The card-specific details of starting, stopping | ||||
| 		ultra_reset_8390()	and resetting the 8390 NIC core. | ||||
| 		ultra_close() | ||||
| 
 | ||||
| 		ultra_block_input()		Routines for reading and writing blocks of | ||||
| 		ultra_block_output()	packet buffer memory. | ||||
| 		ultra_pio_input() | ||||
| 		ultra_pio_output() | ||||
| 
 | ||||
| 	This driver enables the shared memory only when doing the actual data | ||||
| 	transfers to avoid a bug in early version of the card that corrupted | ||||
| 	data transferred by a AHA1542. | ||||
| 
 | ||||
| 	This driver now supports the programmed-I/O (PIO) data transfer mode of | ||||
| 	the EtherEZ. It does not use the non-8390-compatible "Altego" mode. | ||||
| 	That support (if available) is in smc-ez.c. | ||||
| 
 | ||||
| 	Changelog: | ||||
| 
 | ||||
| 	Paul Gortmaker	: multiple card support for module users. | ||||
| 	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided. | ||||
| 	Donald Becker	: 6/6/96 correctly set auto-wrap bit. | ||||
| 	Alexander Sotirov : 1/20/01 Added support for ISAPnP cards | ||||
| 
 | ||||
| 	Note about the ISA PnP support: | ||||
| 
 | ||||
| 	This driver can not autoprobe for more than one SMC EtherEZ PnP card. | ||||
| 	You have to configure the second card manually through the /proc/isapnp | ||||
| 	interface and then load the module with an explicit io=0x___ option. | ||||
| */ | ||||
| 
 | ||||
| static const char version[] = | ||||
| 	"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/isapnp.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <asm/irq.h> | ||||
| 
 | ||||
| #include "8390.h" | ||||
| 
 | ||||
| #define DRV_NAME "smc-ultra" | ||||
| 
 | ||||
| /* A zero-terminated list of I/O addresses to be probed. */ | ||||
| static unsigned int ultra_portlist[] __initdata = | ||||
| {0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; | ||||
| 
 | ||||
| static int ultra_probe1(struct net_device *dev, int ioaddr); | ||||
| 
 | ||||
| #ifdef __ISAPNP__ | ||||
| static int ultra_probe_isapnp(struct net_device *dev); | ||||
| #endif | ||||
| 
 | ||||
| static int ultra_open(struct net_device *dev); | ||||
| static void ultra_reset_8390(struct net_device *dev); | ||||
| static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 						int ring_page); | ||||
| static void ultra_block_input(struct net_device *dev, int count, | ||||
| 						  struct sk_buff *skb, int ring_offset); | ||||
| static void ultra_block_output(struct net_device *dev, int count, | ||||
| 							const unsigned char *buf, const int start_page); | ||||
| static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 						int ring_page); | ||||
| static void ultra_pio_input(struct net_device *dev, int count, | ||||
| 						  struct sk_buff *skb, int ring_offset); | ||||
| static void ultra_pio_output(struct net_device *dev, int count, | ||||
| 							 const unsigned char *buf, const int start_page); | ||||
| static int ultra_close_card(struct net_device *dev); | ||||
| 
 | ||||
| #ifdef __ISAPNP__ | ||||
| static struct isapnp_device_id ultra_device_ids[] __initdata = { | ||||
|         {       ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), | ||||
|                 ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), | ||||
|                 (long) "SMC EtherEZ (8416)" }, | ||||
|         { }	/* terminate list */ | ||||
| }; | ||||
| 
 | ||||
| MODULE_DEVICE_TABLE(isapnp, ultra_device_ids); | ||||
| #endif | ||||
| 
 | ||||
| static u32 ultra_msg_enable; | ||||
| 
 | ||||
| #define START_PG		0x00	/* First page of TX buffer */ | ||||
| 
 | ||||
| #define ULTRA_CMDREG	0		/* Offset to ASIC command register. */ | ||||
| #define	 ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */ | ||||
| #define	 ULTRA_MEMENB	0x40	/* Enable the shared memory. */ | ||||
| #define IOPD	0x02			/* I/O Pipe Data (16 bits), PIO operation. */ | ||||
| #define IOPA	0x07			/* I/O Pipe Address for PIO operation. */ | ||||
| #define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */ | ||||
| #define ULTRA_IO_EXTENT 32 | ||||
| #define EN0_ERWCNT		0x08	/* Early receive warning count. */ | ||||
| 
 | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| static void ultra_poll(struct net_device *dev) | ||||
| { | ||||
| 	disable_irq(dev->irq); | ||||
| 	ei_interrupt(dev->irq, dev); | ||||
| 	enable_irq(dev->irq); | ||||
| } | ||||
| #endif | ||||
| /*	Probe for the Ultra.  This looks like a 8013 with the station
 | ||||
| 	address PROM at I/O ports <base>+8 to <base>+13, with a checksum | ||||
| 	following. | ||||
| */ | ||||
| 
 | ||||
| static int __init do_ultra_probe(struct net_device *dev) | ||||
| { | ||||
| 	int i; | ||||
| 	int base_addr = dev->base_addr; | ||||
| 	int irq = dev->irq; | ||||
| 
 | ||||
| 	if (base_addr > 0x1ff)		/* Check a single specified location. */ | ||||
| 		return ultra_probe1(dev, base_addr); | ||||
| 	else if (base_addr != 0)	/* Don't probe at all. */ | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| #ifdef __ISAPNP__ | ||||
| 	/* Look for any installed ISAPnP cards */ | ||||
| 	if (isapnp_present() && (ultra_probe_isapnp(dev) == 0)) | ||||
| 		return 0; | ||||
| #endif | ||||
| 
 | ||||
| 	for (i = 0; ultra_portlist[i]; i++) { | ||||
| 		dev->irq = irq; | ||||
| 		if (ultra_probe1(dev, ultra_portlist[i]) == 0) | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| #ifndef MODULE | ||||
| struct net_device * __init ultra_probe(int unit) | ||||
| { | ||||
| 	struct net_device *dev = alloc_ei_netdev(); | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	sprintf(dev->name, "eth%d", unit); | ||||
| 	netdev_boot_setup_check(dev); | ||||
| 
 | ||||
| 	err = do_ultra_probe(dev); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 	return dev; | ||||
| out: | ||||
| 	free_netdev(dev); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static const struct net_device_ops ultra_netdev_ops = { | ||||
| 	.ndo_open		= ultra_open, | ||||
| 	.ndo_stop		= ultra_close_card, | ||||
| 
 | ||||
| 	.ndo_start_xmit		= ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= ei_tx_timeout, | ||||
| 	.ndo_get_stats		= ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller 	= ultra_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int __init ultra_probe1(struct net_device *dev, int ioaddr) | ||||
| { | ||||
| 	int i, retval; | ||||
| 	int checksum = 0; | ||||
| 	const char *model_name; | ||||
| 	unsigned char eeprom_irq = 0; | ||||
| 	static unsigned version_printed; | ||||
| 	/* Values from various config regs. */ | ||||
| 	unsigned char num_pages, irqreg, addr, piomode; | ||||
| 	unsigned char idreg = inb(ioaddr + 7); | ||||
| 	unsigned char reg4 = inb(ioaddr + 4) & 0x7f; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	/* Check the ID nibble. */ | ||||
| 	if ((idreg & 0xF0) != 0x20 			/* SMC Ultra */ | ||||
| 		&& (idreg & 0xF0) != 0x40) {		/* SMC EtherEZ */ | ||||
| 		retval = -ENODEV; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Select the station address register set. */ | ||||
| 	outb(reg4, ioaddr + 4); | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) | ||||
| 		checksum += inb(ioaddr + 8 + i); | ||||
| 	if ((checksum & 0xff) != 0xFF) { | ||||
| 		retval = -ENODEV; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) | ||||
| 		netdev_info(dev, version); | ||||
| 
 | ||||
| 	model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; | ||||
| 
 | ||||
| 	for (i = 0; i < 6; i++) | ||||
| 		dev->dev_addr[i] = inb(ioaddr + 8 + i); | ||||
| 
 | ||||
| 	netdev_info(dev, "%s at %#3x, %pM", model_name, | ||||
| 		    ioaddr, dev->dev_addr); | ||||
| 
 | ||||
| 	/* Switch from the station address to the alternate register set and
 | ||||
| 	   read the useful registers there. */ | ||||
| 	outb(0x80 | reg4, ioaddr + 4); | ||||
| 
 | ||||
| 	/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ | ||||
| 	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); | ||||
| 	piomode = inb(ioaddr + 0x8); | ||||
| 	addr = inb(ioaddr + 0xb); | ||||
| 	irqreg = inb(ioaddr + 0xd); | ||||
| 
 | ||||
| 	/* Switch back to the station address register set so that the MS-DOS driver
 | ||||
| 	   can find the card after a warm boot. */ | ||||
| 	outb(reg4, ioaddr + 4); | ||||
| 
 | ||||
| 	if (dev->irq < 2) { | ||||
| 		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; | ||||
| 		int irq; | ||||
| 
 | ||||
| 		/* The IRQ bits are split. */ | ||||
| 		irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; | ||||
| 
 | ||||
| 		if (irq == 0) { | ||||
| 			pr_cont(", failed to detect IRQ line.\n"); | ||||
| 			retval =  -EAGAIN; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		dev->irq = irq; | ||||
| 		eeprom_irq = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The 8390 isn't at the base address, so fake the offset */ | ||||
| 	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; | ||||
| 
 | ||||
| 	{ | ||||
| 		static const int addr_tbl[4] = { | ||||
| 			0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 | ||||
| 		}; | ||||
| 		static const short num_pages_tbl[4] = { | ||||
| 			0x20, 0x40, 0x80, 0xff | ||||
| 		}; | ||||
| 
 | ||||
| 		dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; | ||||
| 		num_pages = num_pages_tbl[(addr >> 4) & 3]; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_status.name = model_name; | ||||
| 	ei_status.word16 = 1; | ||||
| 	ei_status.tx_start_page = START_PG; | ||||
| 	ei_status.rx_start_page = START_PG + TX_PAGES; | ||||
| 	ei_status.stop_page = num_pages; | ||||
| 
 | ||||
| 	ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256); | ||||
| 	if (!ei_status.mem) { | ||||
| 		pr_cont(", failed to ioremap.\n"); | ||||
| 		retval =  -ENOMEM; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256; | ||||
| 
 | ||||
| 	if (piomode) { | ||||
| 		pr_cont(", %s IRQ %d programmed-I/O mode.\n", | ||||
| 			eeprom_irq ? "EEPROM" : "assigned ", dev->irq); | ||||
| 		ei_status.block_input = &ultra_pio_input; | ||||
| 		ei_status.block_output = &ultra_pio_output; | ||||
| 		ei_status.get_8390_hdr = &ultra_pio_get_hdr; | ||||
| 	} else { | ||||
| 		pr_cont(", %s IRQ %d memory %#lx-%#lx.\n", | ||||
| 			eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, | ||||
| 			dev->mem_end-1); | ||||
| 		ei_status.block_input = &ultra_block_input; | ||||
| 		ei_status.block_output = &ultra_block_output; | ||||
| 		ei_status.get_8390_hdr = &ultra_get_8390_hdr; | ||||
| 	} | ||||
| 	ei_status.reset_8390 = &ultra_reset_8390; | ||||
| 
 | ||||
| 	dev->netdev_ops = &ultra_netdev_ops; | ||||
| 	NS8390_init(dev, 0); | ||||
| 	ei_local->msg_enable = ultra_msg_enable; | ||||
| 
 | ||||
| 	retval = register_netdev(dev); | ||||
| 	if (retval) | ||||
| 		goto out; | ||||
| 	return 0; | ||||
| out: | ||||
| 	release_region(ioaddr, ULTRA_IO_EXTENT); | ||||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| #ifdef __ISAPNP__ | ||||
| static int __init ultra_probe_isapnp(struct net_device *dev) | ||||
| { | ||||
|         int i; | ||||
| 
 | ||||
|         for (i = 0; ultra_device_ids[i].vendor != 0; i++) { | ||||
| 		struct pnp_dev *idev = NULL; | ||||
| 
 | ||||
|                 while ((idev = pnp_find_dev(NULL, | ||||
|                                             ultra_device_ids[i].vendor, | ||||
|                                             ultra_device_ids[i].function, | ||||
|                                             idev))) { | ||||
|                         /* Avoid already found cards from previous calls */ | ||||
|                         if (pnp_device_attach(idev) < 0) | ||||
|                         	continue; | ||||
|                         if (pnp_activate_dev(idev) < 0) { | ||||
|                               __again: | ||||
|                         	pnp_device_detach(idev); | ||||
|                         	continue; | ||||
|                         } | ||||
| 			/* if no io and irq, search for next */ | ||||
| 			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) | ||||
| 				goto __again; | ||||
|                         /* found it */ | ||||
| 			dev->base_addr = pnp_port_start(idev, 0); | ||||
| 			dev->irq = pnp_irq(idev, 0); | ||||
| 			netdev_info(dev, | ||||
| 				    "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", | ||||
| 				    (char *) ultra_device_ids[i].driver_data, | ||||
| 				    dev->base_addr, dev->irq); | ||||
|                         if (ultra_probe1(dev, dev->base_addr) != 0) {      /* Shouldn't happen. */ | ||||
| 				netdev_err(dev, | ||||
| 					   "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", | ||||
| 					   dev->base_addr); | ||||
| 				pnp_device_detach(idev); | ||||
| 				return -ENXIO; | ||||
|                         } | ||||
|                         ei_status.priv = (unsigned long)idev; | ||||
|                         break; | ||||
|                 } | ||||
|                 if (!idev) | ||||
|                         continue; | ||||
|                 return 0; | ||||
|         } | ||||
| 
 | ||||
|         return -ENODEV; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int | ||||
| ultra_open(struct net_device *dev) | ||||
| { | ||||
| 	int retval; | ||||
| 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ | ||||
| 	unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, | ||||
| 				   0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; | ||||
| 
 | ||||
| 	retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); | ||||
| 	if (retval) | ||||
| 		return retval; | ||||
| 
 | ||||
| 	outb(0x00, ioaddr);	/* Disable shared memory for safety. */ | ||||
| 	outb(0x80, ioaddr + 5); | ||||
| 	/* Set the IRQ line. */ | ||||
| 	outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); | ||||
| 	outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); | ||||
| 	outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); | ||||
| 
 | ||||
| 	if (ei_status.block_input == &ultra_pio_input) { | ||||
| 		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */ | ||||
| 		outb(0x01, ioaddr + 0x19);  	/* Enable ring read auto-wrap. */ | ||||
| 	} else | ||||
| 		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */ | ||||
| 	/* Set the early receive warning level in window 0 high enough not
 | ||||
| 	   to receive ERW interrupts. */ | ||||
| 	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); | ||||
| 	outb(0xff, dev->base_addr + EN0_ERWCNT); | ||||
| 	ei_open(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| ultra_reset_8390(struct net_device *dev) | ||||
| { | ||||
| 	int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	outb(ULTRA_RESET, cmd_port); | ||||
| 	netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies); | ||||
| 	ei_status.txing = 0; | ||||
| 
 | ||||
| 	outb(0x00, cmd_port);	/* Disable shared memory for safety. */ | ||||
| 	outb(0x80, cmd_port + 5); | ||||
| 	if (ei_status.block_input == &ultra_pio_input) | ||||
| 		outb(0x11, cmd_port + 6);		/* Enable interrupts and PIO. */ | ||||
| 	else | ||||
| 		outb(0x01, cmd_port + 6);		/* Enable interrupts and memory. */ | ||||
| 
 | ||||
| 	netif_dbg(ei_local, hw, dev, "reset done\n"); | ||||
| } | ||||
| 
 | ||||
| /* Grab the 8390 specific header. Similar to the block_input routine, but
 | ||||
|    we don't need to be concerned with ring wrap as the header will be at | ||||
|    the start of a page, so we optimize accordingly. */ | ||||
| 
 | ||||
| static void | ||||
| ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8); | ||||
| 
 | ||||
| 	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);	/* shmem on */ | ||||
| #ifdef __BIG_ENDIAN | ||||
| 	/* Officially this is what we are doing, but the readl() is faster */ | ||||
| 	/* unfortunately it isn't endian aware of the struct               */ | ||||
| 	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); | ||||
| 	hdr->count = le16_to_cpu(hdr->count); | ||||
| #else | ||||
| 	((unsigned int*)hdr)[0] = readl(hdr_start); | ||||
| #endif | ||||
| 	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ | ||||
| } | ||||
| 
 | ||||
| /* Block input and output are easy on shared memory ethercards, the only
 | ||||
|    complication is when the ring buffer wraps. */ | ||||
| 
 | ||||
| static void | ||||
| ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8); | ||||
| 
 | ||||
| 	/* Enable shared memory. */ | ||||
| 	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); | ||||
| 
 | ||||
| 	if (ring_offset + count > ei_status.stop_page*256) { | ||||
| 		/* We must wrap the input move. */ | ||||
| 		int semi_count = ei_status.stop_page*256 - ring_offset; | ||||
| 		memcpy_fromio(skb->data, xfer_start, semi_count); | ||||
| 		count -= semi_count; | ||||
| 		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); | ||||
| 	} else { | ||||
| 		memcpy_fromio(skb->data, xfer_start, count); | ||||
| 	} | ||||
| 
 | ||||
| 	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET);	/* Disable memory. */ | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| ultra_block_output(struct net_device *dev, int count, const unsigned char *buf, | ||||
| 				int start_page) | ||||
| { | ||||
| 	void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8); | ||||
| 
 | ||||
| 	/* Enable shared memory. */ | ||||
| 	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); | ||||
| 
 | ||||
| 	memcpy_toio(shmem, buf, count); | ||||
| 
 | ||||
| 	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ | ||||
| } | ||||
| 
 | ||||
| /* The identical operations for programmed I/O cards.
 | ||||
|    The PIO model is trivial to use: the 16 bit start address is written | ||||
|    byte-sequentially to IOPA, with no intervening I/O operations, and the | ||||
|    data is read or written to the IOPD data port. | ||||
|    The only potential complication is that the address register is shared | ||||
|    and must be always be rewritten between each read/write direction change. | ||||
|    This is no problem for us, as the 8390 code ensures that we are single | ||||
|    threaded. */ | ||||
| static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 						int ring_page) | ||||
| { | ||||
| 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ | ||||
| 	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */ | ||||
| 	outb(ring_page, ioaddr + IOPA); | ||||
| 	insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); | ||||
| } | ||||
| 
 | ||||
| static void ultra_pio_input(struct net_device *dev, int count, | ||||
| 						  struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ | ||||
|     char *buf = skb->data; | ||||
| 
 | ||||
| 	/* For now set the address again, although it should already be correct. */ | ||||
| 	outb(ring_offset, ioaddr + IOPA);	/* Set the address, LSB first. */ | ||||
| 	outb(ring_offset >> 8, ioaddr + IOPA); | ||||
| 	/* We know skbuffs are padded to at least word alignment. */ | ||||
| 	insw(ioaddr + IOPD, buf, (count+1)>>1); | ||||
| } | ||||
| 
 | ||||
| static void ultra_pio_output(struct net_device *dev, int count, | ||||
| 							const unsigned char *buf, const int start_page) | ||||
| { | ||||
| 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ | ||||
| 	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */ | ||||
| 	outb(start_page, ioaddr + IOPA); | ||||
| 	/* An extra odd byte is OK here as well. */ | ||||
| 	outsw(ioaddr + IOPD, buf, (count+1)>>1); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| ultra_close_card(struct net_device *dev) | ||||
| { | ||||
| 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_stop_queue(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); | ||||
| 
 | ||||
| 	outb(0x00, ioaddr + 6);		/* Disable interrupts. */ | ||||
| 	free_irq(dev->irq, dev); | ||||
| 
 | ||||
| 	NS8390_init(dev, 0); | ||||
| 
 | ||||
| 	/* We should someday disable shared memory and change to 8-bit mode
 | ||||
| 	   "just in case"... */ | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef MODULE | ||||
| #define MAX_ULTRA_CARDS	4	/* Max number of Ultra cards per module */ | ||||
| static struct net_device *dev_ultra[MAX_ULTRA_CARDS]; | ||||
| static int io[MAX_ULTRA_CARDS]; | ||||
| static int irq[MAX_ULTRA_CARDS]; | ||||
| 
 | ||||
| module_param_array(io, int, NULL, 0); | ||||
| module_param_array(irq, int, NULL, 0); | ||||
| module_param_named(msg_enable, ultra_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH)); | ||||
| MODULE_PARM_DESC(io, "I/O base address(es)"); | ||||
| MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); | ||||
| MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); | ||||
| MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| /* This is set up so that only a single autoprobe takes place per call.
 | ||||
| ISA device autoprobes on a running machine are not recommended. */ | ||||
| int __init | ||||
| init_module(void) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	int this_dev, found = 0; | ||||
| 
 | ||||
| 	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { | ||||
| 		if (io[this_dev] == 0)  { | ||||
| 			if (this_dev != 0) break; /* only autoprobe 1st one */ | ||||
| 			printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); | ||||
| 		} | ||||
| 		dev = alloc_ei_netdev(); | ||||
| 		if (!dev) | ||||
| 			break; | ||||
| 		dev->irq = irq[this_dev]; | ||||
| 		dev->base_addr = io[this_dev]; | ||||
| 		if (do_ultra_probe(dev) == 0) { | ||||
| 			dev_ultra[found++] = dev; | ||||
| 			continue; | ||||
| 		} | ||||
| 		free_netdev(dev); | ||||
| 		printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); | ||||
| 		break; | ||||
| 	} | ||||
| 	if (found) | ||||
| 		return 0; | ||||
| 	return -ENXIO; | ||||
| } | ||||
| 
 | ||||
| static void cleanup_card(struct net_device *dev) | ||||
| { | ||||
| 	/* NB: ultra_close_card() does free_irq */ | ||||
| #ifdef __ISAPNP__ | ||||
| 	struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; | ||||
| 	if (idev) | ||||
| 		pnp_device_detach(idev); | ||||
| #endif | ||||
| 	release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT); | ||||
| 	iounmap(ei_status.mem); | ||||
| } | ||||
| 
 | ||||
| void __exit | ||||
| cleanup_module(void) | ||||
| { | ||||
| 	int this_dev; | ||||
| 
 | ||||
| 	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { | ||||
| 		struct net_device *dev = dev_ultra[this_dev]; | ||||
| 		if (dev) { | ||||
| 			unregister_netdev(dev); | ||||
| 			cleanup_card(dev); | ||||
| 			free_netdev(dev); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| #endif /* MODULE */ | ||||
							
								
								
									
										303
									
								
								drivers/net/ethernet/8390/stnic.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								drivers/net/ethernet/8390/stnic.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,303 @@ | |||
| /* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
 | ||||
|  * | ||||
|  * This file is subject to the terms and conditions of the GNU General Public | ||||
|  * License.  See the file "COPYING" in the main directory of this archive | ||||
|  * for more details. | ||||
|  * | ||||
|  * Copyright (C) 1999 kaz Kojima | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/ioport.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/delay.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <mach-se/mach/se.h> | ||||
| #include <asm/machvec.h> | ||||
| #ifdef CONFIG_SH_STANDARD_BIOS | ||||
| #include <asm/sh_bios.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "8390.h" | ||||
| 
 | ||||
| #define DRV_NAME "stnic" | ||||
| 
 | ||||
| #define byte	unsigned char | ||||
| #define half	unsigned short | ||||
| #define word	unsigned int | ||||
| #define vbyte	volatile unsigned char | ||||
| #define vhalf	volatile unsigned short | ||||
| #define vword	volatile unsigned int | ||||
| 
 | ||||
| #define STNIC_RUN	0x01	/* 1 == Run, 0 == reset. */ | ||||
| 
 | ||||
| #define START_PG	0	/* First page of TX buffer */ | ||||
| #define STOP_PG		128	/* Last page +1 of RX ring */ | ||||
| 
 | ||||
| /* Alias */ | ||||
| #define STNIC_CR	E8390_CMD | ||||
| #define PG0_RSAR0	EN0_RSARLO | ||||
| #define PG0_RSAR1	EN0_RSARHI | ||||
| #define PG0_RBCR0	EN0_RCNTLO | ||||
| #define PG0_RBCR1	EN0_RCNTHI | ||||
| 
 | ||||
| #define CR_RRD		E8390_RREAD | ||||
| #define CR_RWR		E8390_RWRITE | ||||
| #define CR_PG0		E8390_PAGE0 | ||||
| #define CR_STA		E8390_START | ||||
| #define CR_RDMA		E8390_NODMA | ||||
| 
 | ||||
| /* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS.  */ | ||||
| static byte stnic_eadr[6] = | ||||
| {0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07}; | ||||
| 
 | ||||
| static struct net_device *stnic_dev; | ||||
| 
 | ||||
| static void stnic_reset (struct net_device *dev); | ||||
| static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 			   int ring_page); | ||||
| static void stnic_block_input (struct net_device *dev, int count, | ||||
| 			       struct sk_buff *skb , int ring_offset); | ||||
| static void stnic_block_output (struct net_device *dev, int count, | ||||
| 				const unsigned char *buf, int start_page); | ||||
| 
 | ||||
| static void stnic_init (struct net_device *dev); | ||||
| 
 | ||||
| static u32 stnic_msg_enable; | ||||
| 
 | ||||
| module_param_named(msg_enable, stnic_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH)); | ||||
| MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); | ||||
| 
 | ||||
| /* SH7750 specific read/write io. */ | ||||
| static inline void | ||||
| STNIC_DELAY (void) | ||||
| { | ||||
|   vword trash; | ||||
|   trash = *(vword *) 0xa0000000; | ||||
|   trash = *(vword *) 0xa0000000; | ||||
|   trash = *(vword *) 0xa0000000; | ||||
| } | ||||
| 
 | ||||
| static inline byte | ||||
| STNIC_READ (int reg) | ||||
| { | ||||
|   byte val; | ||||
| 
 | ||||
|   val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff; | ||||
|   STNIC_DELAY (); | ||||
|   return val; | ||||
| } | ||||
| 
 | ||||
| static inline void | ||||
| STNIC_WRITE (int reg, byte val) | ||||
| { | ||||
|   *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8); | ||||
|   STNIC_DELAY (); | ||||
| } | ||||
| 
 | ||||
| static int __init stnic_probe(void) | ||||
| { | ||||
|   struct net_device *dev; | ||||
|   int i, err; | ||||
|   struct ei_device *ei_local; | ||||
| 
 | ||||
|   /* If we are not running on a SolutionEngine, give up now */ | ||||
|   if (! MACH_SE) | ||||
|     return -ENODEV; | ||||
| 
 | ||||
|   /* New style probing API */ | ||||
|   dev = alloc_ei_netdev(); | ||||
|   if (!dev) | ||||
|   	return -ENOMEM; | ||||
| 
 | ||||
| #ifdef CONFIG_SH_STANDARD_BIOS | ||||
|   sh_bios_get_node_addr (stnic_eadr); | ||||
| #endif | ||||
|   for (i = 0; i < ETH_ALEN; i++) | ||||
|     dev->dev_addr[i] = stnic_eadr[i]; | ||||
| 
 | ||||
|   /* Set the base address to point to the NIC, not the "real" base! */ | ||||
|   dev->base_addr = 0x1000; | ||||
|   dev->irq = IRQ_STNIC; | ||||
|   dev->netdev_ops = &ei_netdev_ops; | ||||
| 
 | ||||
|   /* Snarf the interrupt now.  There's no point in waiting since we cannot
 | ||||
|      share and the board will usually be enabled. */ | ||||
|   err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev); | ||||
|   if (err)  { | ||||
| 	netdev_emerg(dev, " unable to get IRQ %d.\n", dev->irq); | ||||
| 	free_netdev(dev); | ||||
| 	return err; | ||||
|   } | ||||
| 
 | ||||
|   ei_status.name = dev->name; | ||||
|   ei_status.word16 = 1; | ||||
| #ifdef __LITTLE_ENDIAN__ | ||||
|   ei_status.bigendian = 0; | ||||
| #else | ||||
|   ei_status.bigendian = 1; | ||||
| #endif | ||||
|   ei_status.tx_start_page = START_PG; | ||||
|   ei_status.rx_start_page = START_PG + TX_PAGES; | ||||
|   ei_status.stop_page = STOP_PG; | ||||
| 
 | ||||
|   ei_status.reset_8390 = &stnic_reset; | ||||
|   ei_status.get_8390_hdr = &stnic_get_hdr; | ||||
|   ei_status.block_input = &stnic_block_input; | ||||
|   ei_status.block_output = &stnic_block_output; | ||||
| 
 | ||||
|   stnic_init (dev); | ||||
|   ei_local = netdev_priv(dev); | ||||
|   ei_local->msg_enable = stnic_msg_enable; | ||||
| 
 | ||||
|   err = register_netdev(dev); | ||||
|   if (err) { | ||||
|     free_irq(dev->irq, dev); | ||||
|     free_netdev(dev); | ||||
|     return err; | ||||
|   } | ||||
|   stnic_dev = dev; | ||||
| 
 | ||||
|   netdev_info(dev, "NS ST-NIC 83902A\n"); | ||||
| 
 | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| stnic_reset (struct net_device *dev) | ||||
| { | ||||
|   struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
|   *(vhalf *) PA_83902_RST = 0; | ||||
|   udelay (5); | ||||
|   netif_warn(ei_local, hw, dev, "8390 reset done (%ld).\n", jiffies); | ||||
|   *(vhalf *) PA_83902_RST = ~0; | ||||
|   udelay (5); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 	       int ring_page) | ||||
| { | ||||
|   struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
|   half buf[2]; | ||||
| 
 | ||||
|   STNIC_WRITE (PG0_RSAR0, 0); | ||||
|   STNIC_WRITE (PG0_RSAR1, ring_page); | ||||
|   STNIC_WRITE (PG0_RBCR0, 4); | ||||
|   STNIC_WRITE (PG0_RBCR1, 0); | ||||
|   STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); | ||||
| 
 | ||||
|   buf[0] = *(vhalf *) PA_83902_IF; | ||||
|   STNIC_DELAY (); | ||||
|   buf[1] = *(vhalf *) PA_83902_IF; | ||||
|   STNIC_DELAY (); | ||||
|   hdr->next = buf[0] >> 8; | ||||
|   hdr->status = buf[0] & 0xff; | ||||
| #ifdef __LITTLE_ENDIAN__ | ||||
|   hdr->count = buf[1]; | ||||
| #else | ||||
|   hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8); | ||||
| #endif | ||||
| 
 | ||||
|   netif_dbg(ei_local, probe, dev, "ring %x status %02x next %02x count %04x.\n", | ||||
| 	    ring_page, hdr->status, hdr->next, hdr->count); | ||||
| 
 | ||||
|   STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); | ||||
| } | ||||
| 
 | ||||
| /* Block input and output, similar to the Crynwr packet driver. If you are
 | ||||
|    porting to a new ethercard look at the packet driver source for hints. | ||||
|    The HP LAN doesn't use shared memory -- we put the packet | ||||
|    out through the "remote DMA" dataport. */ | ||||
| 
 | ||||
| static void | ||||
| stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb, | ||||
| 		   int offset) | ||||
| { | ||||
|   char *buf = skb->data; | ||||
|   half val; | ||||
| 
 | ||||
|   STNIC_WRITE (PG0_RSAR0, offset & 0xff); | ||||
|   STNIC_WRITE (PG0_RSAR1, offset >> 8); | ||||
|   STNIC_WRITE (PG0_RBCR0, length & 0xff); | ||||
|   STNIC_WRITE (PG0_RBCR1, length >> 8); | ||||
|   STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); | ||||
| 
 | ||||
|   if (length & 1) | ||||
|     length++; | ||||
| 
 | ||||
|   while (length > 0) | ||||
|     { | ||||
|       val = *(vhalf *) PA_83902_IF; | ||||
| #ifdef __LITTLE_ENDIAN__ | ||||
|       *buf++ = val & 0xff; | ||||
|       *buf++ = val >> 8; | ||||
| #else | ||||
|       *buf++ = val >> 8; | ||||
|       *buf++ = val & 0xff; | ||||
| #endif | ||||
|       STNIC_DELAY (); | ||||
|       length -= sizeof (half); | ||||
|     } | ||||
| 
 | ||||
|   STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| stnic_block_output (struct net_device *dev, int length, | ||||
| 		    const unsigned char *buf, int output_page) | ||||
| { | ||||
|   STNIC_WRITE (PG0_RBCR0, 1);	/* Write non-zero value */ | ||||
|   STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA); | ||||
|   STNIC_DELAY (); | ||||
| 
 | ||||
|   STNIC_WRITE (PG0_RBCR0, length & 0xff); | ||||
|   STNIC_WRITE (PG0_RBCR1, length >> 8); | ||||
|   STNIC_WRITE (PG0_RSAR0, 0); | ||||
|   STNIC_WRITE (PG0_RSAR1, output_page); | ||||
|   STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA); | ||||
| 
 | ||||
|   if (length & 1) | ||||
|     length++; | ||||
| 
 | ||||
|   while (length > 0) | ||||
|     { | ||||
| #ifdef __LITTLE_ENDIAN__ | ||||
|       *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0]; | ||||
| #else | ||||
|       *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1]; | ||||
| #endif | ||||
|       STNIC_DELAY (); | ||||
|       buf += sizeof (half); | ||||
|       length -= sizeof (half); | ||||
|     } | ||||
| 
 | ||||
|   STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA); | ||||
| } | ||||
| 
 | ||||
| /* This function resets the STNIC if something screws up.  */ | ||||
| static void | ||||
| stnic_init (struct net_device *dev) | ||||
| { | ||||
|   stnic_reset (dev); | ||||
|   NS8390_init (dev, 0); | ||||
| } | ||||
| 
 | ||||
| static void __exit stnic_cleanup(void) | ||||
| { | ||||
| 	unregister_netdev(stnic_dev); | ||||
| 	free_irq(stnic_dev->irq, stnic_dev); | ||||
| 	free_netdev(stnic_dev); | ||||
| } | ||||
| 
 | ||||
| module_init(stnic_probe); | ||||
| module_exit(stnic_cleanup); | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										574
									
								
								drivers/net/ethernet/8390/wd.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										574
									
								
								drivers/net/ethernet/8390/wd.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,574 @@ | |||
| /* wd.c: A WD80x3 ethernet driver for linux. */ | ||||
| /*
 | ||||
| 	Written 1993-94 by Donald Becker. | ||||
| 
 | ||||
| 	Copyright 1993 United States Government as represented by the | ||||
| 	Director, National Security Agency. | ||||
| 
 | ||||
| 	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 becker@scyld.com, or C/O | ||||
| 	Scyld Computing Corporation | ||||
| 	410 Severn Ave., Suite 210 | ||||
| 	Annapolis MD 21403 | ||||
| 
 | ||||
| 	This is a driver for WD8003 and WD8013 "compatible" ethercards. | ||||
| 
 | ||||
| 	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. | ||||
| 
 | ||||
| 	Changelog: | ||||
| 
 | ||||
| 	Paul Gortmaker	: multiple card support for module users, support | ||||
| 			  for non-standard memory sizes. | ||||
| 
 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| static const char version[] = | ||||
| 	"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| #include "8390.h" | ||||
| 
 | ||||
| #define DRV_NAME "wd" | ||||
| 
 | ||||
| /* A zero-terminated list of I/O addresses to be probed. */ | ||||
| static unsigned int wd_portlist[] __initdata = | ||||
| {0x300, 0x280, 0x380, 0x240, 0}; | ||||
| 
 | ||||
| static int wd_probe1(struct net_device *dev, int ioaddr); | ||||
| 
 | ||||
| static int wd_open(struct net_device *dev); | ||||
| static void wd_reset_8390(struct net_device *dev); | ||||
| static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, | ||||
| 						int ring_page); | ||||
| static void wd_block_input(struct net_device *dev, int count, | ||||
| 						  struct sk_buff *skb, int ring_offset); | ||||
| static void wd_block_output(struct net_device *dev, int count, | ||||
| 							const unsigned char *buf, int start_page); | ||||
| static int wd_close(struct net_device *dev); | ||||
| 
 | ||||
| static u32 wd_msg_enable; | ||||
| 
 | ||||
| #define WD_START_PG		0x00	/* First page of TX buffer */ | ||||
| #define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */ | ||||
| #define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */ | ||||
| 
 | ||||
| #define WD_CMDREG		0		/* Offset to ASIC command register. */ | ||||
| #define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */ | ||||
| #define	 WD_MEMENB		0x40	/* Enable the shared memory. */ | ||||
| #define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */ | ||||
| #define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */ | ||||
| #define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */ | ||||
| #define WD_NIC_OFFSET	16		/* Offset to the 8390 from the base_addr. */ | ||||
| #define WD_IO_EXTENT	32 | ||||
| 
 | ||||
| 
 | ||||
| /*	Probe for the WD8003 and WD8013.  These cards have the station
 | ||||
| 	address PROM at I/O ports <base>+8 to <base>+13, with a checksum | ||||
| 	following. A Soundblaster can have the same checksum as an WDethercard, | ||||
| 	so we have an extra exclusionary check for it. | ||||
| 
 | ||||
| 	The wd_probe1() routine initializes the card and fills the | ||||
| 	station address field. */ | ||||
| 
 | ||||
| static int __init do_wd_probe(struct net_device *dev) | ||||
| { | ||||
| 	int i; | ||||
| 	struct resource *r; | ||||
| 	int base_addr = dev->base_addr; | ||||
| 	int irq = dev->irq; | ||||
| 	int mem_start = dev->mem_start; | ||||
| 	int mem_end = dev->mem_end; | ||||
| 
 | ||||
| 	if (base_addr > 0x1ff) {	/* Check a user specified location. */ | ||||
| 		r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); | ||||
| 		if ( r == NULL) | ||||
| 			return -EBUSY; | ||||
| 		i = wd_probe1(dev, base_addr); | ||||
| 		if (i != 0) | ||||
| 			release_region(base_addr, WD_IO_EXTENT); | ||||
| 		else | ||||
| 			r->name = dev->name; | ||||
| 		return i; | ||||
| 	} | ||||
| 	else if (base_addr != 0)	/* Don't probe at all. */ | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	for (i = 0; wd_portlist[i]; i++) { | ||||
| 		int ioaddr = wd_portlist[i]; | ||||
| 		r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); | ||||
| 		if (r == NULL) | ||||
| 			continue; | ||||
| 		if (wd_probe1(dev, ioaddr) == 0) { | ||||
| 			r->name = dev->name; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		release_region(ioaddr, WD_IO_EXTENT); | ||||
| 		dev->irq = irq; | ||||
| 		dev->mem_start = mem_start; | ||||
| 		dev->mem_end = mem_end; | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| #ifndef MODULE | ||||
| struct net_device * __init wd_probe(int unit) | ||||
| { | ||||
| 	struct net_device *dev = alloc_ei_netdev(); | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	sprintf(dev->name, "eth%d", unit); | ||||
| 	netdev_boot_setup_check(dev); | ||||
| 
 | ||||
| 	err = do_wd_probe(dev); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 	return dev; | ||||
| out: | ||||
| 	free_netdev(dev); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static const struct net_device_ops wd_netdev_ops = { | ||||
| 	.ndo_open		= wd_open, | ||||
| 	.ndo_stop		= wd_close, | ||||
| 	.ndo_start_xmit		= ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= ei_tx_timeout, | ||||
| 	.ndo_get_stats		= ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address 	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller 	= ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int __init wd_probe1(struct net_device *dev, int ioaddr) | ||||
| { | ||||
| 	int i; | ||||
| 	int err; | ||||
| 	int checksum = 0; | ||||
| 	int ancient = 0;			/* An old card without config registers. */ | ||||
| 	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */ | ||||
| 	const char *model_name; | ||||
| 	static unsigned version_printed; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) | ||||
| 		checksum += inb(ioaddr + 8 + i); | ||||
| 	if (inb(ioaddr + 8) == 0xff 	/* Extra check to avoid soundcard. */ | ||||
| 		|| inb(ioaddr + 9) == 0xff | ||||
| 		|| (checksum & 0xff) != 0xFF) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	/* Check for semi-valid mem_start/end values if supplied. */ | ||||
| 	if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { | ||||
| 		netdev_warn(dev, | ||||
| 			    "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); | ||||
| 		dev->mem_start = 0; | ||||
| 		dev->mem_end = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) | ||||
| 		netdev_info(dev, version); | ||||
| 
 | ||||
| 	for (i = 0; i < 6; i++) | ||||
| 		dev->dev_addr[i] = inb(ioaddr + 8 + i); | ||||
| 
 | ||||
| 	netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr); | ||||
| 
 | ||||
| 	/* The following PureData probe code was contributed by
 | ||||
| 	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software | ||||
| 	   configuration differently from others so we have to check for them. | ||||
| 	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. | ||||
| 	   */ | ||||
| 	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { | ||||
| 		unsigned char reg5 = inb(ioaddr+5); | ||||
| 
 | ||||
| 		switch (inb(ioaddr+2)) { | ||||
| 		case 0x03: word16 = 0; model_name = "PDI8023-8";	break; | ||||
| 		case 0x05: word16 = 0; model_name = "PDUC8023";	break; | ||||
| 		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; | ||||
| 			/* Either 0x01 (dumb) or they've released a new version. */ | ||||
| 		default:	 word16 = 0; model_name = "PDI8023";	break; | ||||
| 		} | ||||
| 		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; | ||||
| 		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; | ||||
| 	} else {								/* End of PureData probe */ | ||||
| 		/* This method of checking for a 16-bit board is borrowed from the
 | ||||
| 		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03. | ||||
| 		   I'm comparing the two method in alpha test to make certain they | ||||
| 		   return the same result. */ | ||||
| 		/* Check for the old 8 bit board - it has register 0/8 aliasing.
 | ||||
| 		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */ | ||||
| 		for (i = 0; i < 6; i++) | ||||
| 			if (inb(ioaddr+i) != inb(ioaddr+8+i)) | ||||
| 				break; | ||||
| 		if (i >= 6) { | ||||
| 			ancient = 1; | ||||
| 			model_name = "WD8003-old"; | ||||
| 			word16 = 0; | ||||
| 		} else { | ||||
| 			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ | ||||
| 			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ | ||||
| 			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ | ||||
| 				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */ | ||||
| 				int asic_reg5 = inb(ioaddr+WD_CMDREG5); | ||||
| 				/* Magic to set ASIC to word-wide mode. */ | ||||
| 				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); | ||||
| 				outb(tmp, ioaddr+1); | ||||
| 				model_name = "WD8013"; | ||||
| 				word16 = 1;		/* We have a 16bit board here! */ | ||||
| 			} else { | ||||
| 				model_name = "WD8003"; | ||||
| 				word16 = 0; | ||||
| 			} | ||||
| 			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */ | ||||
| 		} | ||||
| #ifndef final_version | ||||
| 		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) | ||||
| 			pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", | ||||
| 				word16 ? 16 : 8, | ||||
| 				(inb(ioaddr+1) & 0x01) ? 16 : 8); | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| #if defined(WD_SHMEM) && WD_SHMEM > 0x80000 | ||||
| 	/* Allow a compile-time override.	 */ | ||||
| 	dev->mem_start = WD_SHMEM; | ||||
| #else | ||||
| 	if (dev->mem_start == 0) { | ||||
| 		/* Sanity and old 8003 check */ | ||||
| 		int reg0 = inb(ioaddr); | ||||
| 		if (reg0 == 0xff || reg0 == 0) { | ||||
| 			/* Future plan: this could check a few likely locations first. */ | ||||
| 			dev->mem_start = 0xd0000; | ||||
| 			pr_cont(" assigning address %#lx", dev->mem_start); | ||||
| 		} else { | ||||
| 			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; | ||||
| 			/* Some boards don't have the register 5 -- it returns 0xff. */ | ||||
| 			if (high_addr_bits == 0x1f || word16 == 0) | ||||
| 				high_addr_bits = 0x01; | ||||
| 			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	/* The 8390 isn't at the base address -- the ASIC regs are there! */ | ||||
| 	dev->base_addr = ioaddr+WD_NIC_OFFSET; | ||||
| 
 | ||||
| 	if (dev->irq < 2) { | ||||
| 		static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4}; | ||||
| 		int reg1 = inb(ioaddr+1); | ||||
| 		int reg4 = inb(ioaddr+4); | ||||
| 		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */ | ||||
| 			short nic_addr = ioaddr+WD_NIC_OFFSET; | ||||
| 			unsigned long irq_mask; | ||||
| 
 | ||||
| 			/* We have an old-style ethercard that doesn't report its IRQ
 | ||||
| 			   line.  Do autoirq to find the IRQ line. Note that this IS NOT | ||||
| 			   a reliable way to trigger an interrupt. */ | ||||
| 			outb_p(E8390_NODMA + E8390_STOP, nic_addr); | ||||
| 			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */ | ||||
| 
 | ||||
| 			irq_mask = probe_irq_on(); | ||||
| 			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */ | ||||
| 			outb_p(0x00, nic_addr + EN0_RCNTLO); | ||||
| 			outb_p(0x00, nic_addr + EN0_RCNTHI); | ||||
| 			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ | ||||
| 			mdelay(20); | ||||
| 			dev->irq = probe_irq_off(irq_mask); | ||||
| 
 | ||||
| 			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */ | ||||
| 
 | ||||
| 			if (netif_msg_drv(ei_local)) | ||||
| 				pr_cont(" autoirq is %d", dev->irq); | ||||
| 			if (dev->irq < 2) | ||||
| 				dev->irq = word16 ? 10 : 5; | ||||
| 		} else | ||||
| 			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; | ||||
| 	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */ | ||||
| 		dev->irq = 9; | ||||
| 
 | ||||
| 	/* Snarf the interrupt now.  There's no point in waiting since we cannot
 | ||||
| 	   share and the board will usually be enabled. */ | ||||
| 	i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); | ||||
| 	if (i) { | ||||
| 		pr_cont(" unable to get IRQ %d.\n", dev->irq); | ||||
| 		return i; | ||||
| 	} | ||||
| 
 | ||||
| 	/* OK, were are certain this is going to work.  Setup the device. */ | ||||
| 	ei_status.name = model_name; | ||||
| 	ei_status.word16 = word16; | ||||
| 	ei_status.tx_start_page = WD_START_PG; | ||||
| 	ei_status.rx_start_page = WD_START_PG + TX_PAGES; | ||||
| 
 | ||||
| 	/* Don't map in the shared memory until the board is actually opened. */ | ||||
| 
 | ||||
| 	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ | ||||
| 	if (dev->mem_end != 0) { | ||||
| 		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; | ||||
| 		ei_status.priv = dev->mem_end - dev->mem_start; | ||||
| 	} else { | ||||
| 		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; | ||||
| 		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; | ||||
| 		ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_status.mem = ioremap(dev->mem_start, ei_status.priv); | ||||
| 	if (!ei_status.mem) { | ||||
| 		free_irq(dev->irq, dev); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", | ||||
| 		model_name, dev->irq, dev->mem_start, dev->mem_end-1); | ||||
| 
 | ||||
| 	ei_status.reset_8390 = wd_reset_8390; | ||||
| 	ei_status.block_input = wd_block_input; | ||||
| 	ei_status.block_output = wd_block_output; | ||||
| 	ei_status.get_8390_hdr = wd_get_8390_hdr; | ||||
| 
 | ||||
| 	dev->netdev_ops = &wd_netdev_ops; | ||||
| 	NS8390_init(dev, 0); | ||||
| 	ei_local->msg_enable = wd_msg_enable; | ||||
| 
 | ||||
| #if 1 | ||||
| 	/* Enable interrupt generation on softconfig cards -- M.U */ | ||||
| 	/* .. but possibly potentially unsafe - Donald */ | ||||
| 	if (inb(ioaddr+14) & 0x20) | ||||
| 		outb(inb(ioaddr+4)|0x80, ioaddr+4); | ||||
| #endif | ||||
| 
 | ||||
| 	err = register_netdev(dev); | ||||
| 	if (err) { | ||||
| 		free_irq(dev->irq, dev); | ||||
| 		iounmap(ei_status.mem); | ||||
| 	} | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| wd_open(struct net_device *dev) | ||||
| { | ||||
|   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 
 | ||||
|   /* Map in the shared memory. Always set register 0 last to remain
 | ||||
| 	 compatible with very old boards. */ | ||||
|   ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; | ||||
|   ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; | ||||
| 
 | ||||
|   if (ei_status.word16) | ||||
| 	  outb(ei_status.reg5, ioaddr+WD_CMDREG5); | ||||
|   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ | ||||
| 
 | ||||
|   return ei_open(dev); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| wd_reset_8390(struct net_device *dev) | ||||
| { | ||||
| 	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	outb(WD_RESET, wd_cmd_port); | ||||
| 	netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n", | ||||
| 		  jiffies); | ||||
| 	ei_status.txing = 0; | ||||
| 
 | ||||
| 	/* Set up the ASIC registers, just in case something changed them. */ | ||||
| 	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); | ||||
| 	if (ei_status.word16) | ||||
| 		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, hw, dev, "reset done\n"); | ||||
| } | ||||
| 
 | ||||
| /* Grab the 8390 specific header. Similar to the block_input routine, but
 | ||||
|    we don't need to be concerned with ring wrap as the header will be at | ||||
|    the start of a page, so we optimize accordingly. */ | ||||
| 
 | ||||
| static void | ||||
| wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 
 | ||||
| 	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 	void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); | ||||
| 
 | ||||
| 	/* We'll always get a 4 byte header read followed by a packet read, so
 | ||||
| 	   we enable 16 bit mode before the header, and disable after the body. */ | ||||
| 	if (ei_status.word16) | ||||
| 		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); | ||||
| 
 | ||||
| #ifdef __BIG_ENDIAN | ||||
| 	/* Officially this is what we are doing, but the readl() is faster */ | ||||
| 	/* unfortunately it isn't endian aware of the struct               */ | ||||
| 	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); | ||||
| 	hdr->count = le16_to_cpu(hdr->count); | ||||
| #else | ||||
| 	((unsigned int*)hdr)[0] = readl(hdr_start); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Block input and output are easy on shared memory ethercards, and trivial
 | ||||
|    on the Western digital card where there is no choice of how to do it. | ||||
|    The only complications are that the ring buffer wraps, and need to map | ||||
|    switch between 8- and 16-bit modes. */ | ||||
| 
 | ||||
| static void | ||||
| wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 	unsigned long offset = ring_offset - (WD_START_PG<<8); | ||||
| 	void __iomem *xfer_start = ei_status.mem + offset; | ||||
| 
 | ||||
| 	if (offset + count > ei_status.priv) { | ||||
| 		/* We must wrap the input move. */ | ||||
| 		int semi_count = ei_status.priv - offset; | ||||
| 		memcpy_fromio(skb->data, xfer_start, semi_count); | ||||
| 		count -= semi_count; | ||||
| 		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); | ||||
| 	} else { | ||||
| 		/* Packet is in one chunk -- we can copy + cksum. */ | ||||
| 		memcpy_fromio(skb->data, xfer_start, count); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */ | ||||
| 	if (ei_status.word16) | ||||
| 		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| wd_block_output(struct net_device *dev, int count, const unsigned char *buf, | ||||
| 				int start_page) | ||||
| { | ||||
| 	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 	void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); | ||||
| 
 | ||||
| 
 | ||||
| 	if (ei_status.word16) { | ||||
| 		/* Turn on and off 16 bit access so that reboot works. */ | ||||
| 		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); | ||||
| 		memcpy_toio(shmem, buf, count); | ||||
| 		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); | ||||
| 	} else | ||||
| 		memcpy_toio(shmem, buf, count); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int | ||||
| wd_close(struct net_device *dev) | ||||
| { | ||||
| 	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); | ||||
| 	ei_close(dev); | ||||
| 
 | ||||
| 	/* Change from 16-bit to 8-bit shared memory so reboot works. */ | ||||
| 	if (ei_status.word16) | ||||
| 		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); | ||||
| 
 | ||||
| 	/* And disable the shared memory. */ | ||||
| 	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef MODULE | ||||
| #define MAX_WD_CARDS	4	/* Max number of wd cards per module */ | ||||
| static struct net_device *dev_wd[MAX_WD_CARDS]; | ||||
| static int io[MAX_WD_CARDS]; | ||||
| static int irq[MAX_WD_CARDS]; | ||||
| static int mem[MAX_WD_CARDS]; | ||||
| static int mem_end[MAX_WD_CARDS];	/* for non std. mem size */ | ||||
| 
 | ||||
| module_param_array(io, int, NULL, 0); | ||||
| module_param_array(irq, int, NULL, 0); | ||||
| module_param_array(mem, int, NULL, 0); | ||||
| module_param_array(mem_end, int, NULL, 0); | ||||
| module_param_named(msg_enable, wd_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH)); | ||||
| MODULE_PARM_DESC(io, "I/O base address(es)"); | ||||
| MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); | ||||
| MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); | ||||
| MODULE_PARM_DESC(mem_end, "memory end address(es)"); | ||||
| MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); | ||||
| MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| /* This is set up so that only a single autoprobe takes place per call.
 | ||||
| ISA device autoprobes on a running machine are not recommended. */ | ||||
| 
 | ||||
| int __init init_module(void) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	int this_dev, found = 0; | ||||
| 
 | ||||
| 	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { | ||||
| 		if (io[this_dev] == 0)  { | ||||
| 			if (this_dev != 0) break; /* only autoprobe 1st one */ | ||||
| 			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); | ||||
| 		} | ||||
| 		dev = alloc_ei_netdev(); | ||||
| 		if (!dev) | ||||
| 			break; | ||||
| 		dev->irq = irq[this_dev]; | ||||
| 		dev->base_addr = io[this_dev]; | ||||
| 		dev->mem_start = mem[this_dev]; | ||||
| 		dev->mem_end = mem_end[this_dev]; | ||||
| 		if (do_wd_probe(dev) == 0) { | ||||
| 			dev_wd[found++] = dev; | ||||
| 			continue; | ||||
| 		} | ||||
| 		free_netdev(dev); | ||||
| 		printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); | ||||
| 		break; | ||||
| 	} | ||||
| 	if (found) | ||||
| 		return 0; | ||||
| 	return -ENXIO; | ||||
| } | ||||
| 
 | ||||
| static void cleanup_card(struct net_device *dev) | ||||
| { | ||||
| 	free_irq(dev->irq, dev); | ||||
| 	release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); | ||||
| 	iounmap(ei_status.mem); | ||||
| } | ||||
| 
 | ||||
| void __exit | ||||
| cleanup_module(void) | ||||
| { | ||||
| 	int this_dev; | ||||
| 
 | ||||
| 	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { | ||||
| 		struct net_device *dev = dev_wd[this_dev]; | ||||
| 		if (dev) { | ||||
| 			unregister_netdev(dev); | ||||
| 			cleanup_card(dev); | ||||
| 			free_netdev(dev); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| #endif /* MODULE */ | ||||
							
								
								
									
										458
									
								
								drivers/net/ethernet/8390/zorro8390.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										458
									
								
								drivers/net/ethernet/8390/zorro8390.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,458 @@ | |||
| /*
 | ||||
|  *  Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver | ||||
|  * | ||||
|  *  (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM) | ||||
|  * | ||||
|  *  --------------------------------------------------------------------------- | ||||
|  * | ||||
|  *  This program is based on all the other NE2000 drivers for Linux | ||||
|  * | ||||
|  *  --------------------------------------------------------------------------- | ||||
|  * | ||||
|  *  This file is subject to the terms and conditions of the GNU General Public | ||||
|  *  License.  See the file COPYING in the main directory of the Linux | ||||
|  *  distribution for more details. | ||||
|  * | ||||
|  *  --------------------------------------------------------------------------- | ||||
|  * | ||||
|  *  The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS | ||||
|  *  Ethernet Controllers. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/zorro.h> | ||||
| #include <linux/jiffies.h> | ||||
| 
 | ||||
| #include <asm/irq.h> | ||||
| #include <asm/amigaints.h> | ||||
| #include <asm/amigahw.h> | ||||
| 
 | ||||
| #define EI_SHIFT(x)		(ei_local->reg_offset[x]) | ||||
| #define ei_inb(port)		in_8(port) | ||||
| #define ei_outb(val, port)	out_8(port, val) | ||||
| #define ei_inb_p(port)		in_8(port) | ||||
| #define ei_outb_p(val, port)	out_8(port, val) | ||||
| 
 | ||||
| static const char version[] = | ||||
| 	"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; | ||||
| 
 | ||||
| static u32 zorro8390_msg_enable; | ||||
| 
 | ||||
| #include "lib8390.c" | ||||
| 
 | ||||
| #define DRV_NAME	"zorro8390" | ||||
| 
 | ||||
| #define NE_BASE		(dev->base_addr) | ||||
| #define NE_CMD		(0x00 * 2) | ||||
| #define NE_DATAPORT	(0x10 * 2)	/* NatSemi-defined port window offset */ | ||||
| #define NE_RESET	(0x1f * 2)	/* Issue a read to reset, | ||||
| 					 * a write to clear. */ | ||||
| #define NE_IO_EXTENT	(0x20 * 2) | ||||
| 
 | ||||
| #define NE_EN0_ISR	(0x07 * 2) | ||||
| #define NE_EN0_DCFG	(0x0e * 2) | ||||
| 
 | ||||
| #define NE_EN0_RSARLO	(0x08 * 2) | ||||
| #define NE_EN0_RSARHI	(0x09 * 2) | ||||
| #define NE_EN0_RCNTLO	(0x0a * 2) | ||||
| #define NE_EN0_RXCR	(0x0c * 2) | ||||
| #define NE_EN0_TXCR	(0x0d * 2) | ||||
| #define NE_EN0_RCNTHI	(0x0b * 2) | ||||
| #define NE_EN0_IMR	(0x0f * 2) | ||||
| 
 | ||||
| #define NESM_START_PG	0x40	/* First page of TX buffer */ | ||||
| #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ | ||||
| 
 | ||||
| #define WORDSWAP(a)	((((a) >> 8) & 0xff) | ((a) << 8)) | ||||
| 
 | ||||
| static struct card_info { | ||||
| 	zorro_id id; | ||||
| 	const char *name; | ||||
| 	unsigned int offset; | ||||
| } cards[] = { | ||||
| 	{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 }, | ||||
| 	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 }, | ||||
| }; | ||||
| 
 | ||||
| /* Hard reset the card.  This used to pause for the same period that a
 | ||||
|  * 8390 reset command required, but that shouldn't be necessary. | ||||
|  */ | ||||
| static void zorro8390_reset_8390(struct net_device *dev) | ||||
| { | ||||
| 	unsigned long reset_start_time = jiffies; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, hw, dev, "resetting - t=%ld...\n", jiffies); | ||||
| 
 | ||||
| 	z_writeb(z_readb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); | ||||
| 
 | ||||
| 	ei_status.txing = 0; | ||||
| 	ei_status.dmaing = 0; | ||||
| 
 | ||||
| 	/* This check _should_not_ be necessary, omit eventually. */ | ||||
| 	while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0) | ||||
| 		if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) { | ||||
| 			netdev_warn(dev, "%s: did not complete\n", __func__); | ||||
| 			break; | ||||
| 		} | ||||
| 	z_writeb(ENISR_RESET, NE_BASE + NE_EN0_ISR);	/* Ack intr */ | ||||
| } | ||||
| 
 | ||||
| /* Grab the 8390 specific header. Similar to the block_input routine, but
 | ||||
|  * we don't need to be concerned with ring wrap as the header will be at | ||||
|  * the start of a page, so we optimize accordingly. | ||||
|  */ | ||||
| static void zorro8390_get_8390_hdr(struct net_device *dev, | ||||
| 				   struct e8390_pkt_hdr *hdr, int ring_page) | ||||
| { | ||||
| 	int nic_base = dev->base_addr; | ||||
| 	int cnt; | ||||
| 	short *ptrs; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen.
 | ||||
| 	 * If it does, it's the last thing you'll see | ||||
| 	 */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_warn(dev, | ||||
| 			    "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n", | ||||
| 			    __func__, ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD); | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
| 	z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO); | ||||
| 	z_writeb(0, nic_base + NE_EN0_RCNTHI); | ||||
| 	z_writeb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */ | ||||
| 	z_writeb(ring_page, nic_base + NE_EN0_RSARHI); | ||||
| 	z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| 
 | ||||
| 	ptrs = (short *)hdr; | ||||
| 	for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++) | ||||
| 		*ptrs++ = z_readw(NE_BASE + NE_DATAPORT); | ||||
| 
 | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */ | ||||
| 
 | ||||
| 	hdr->count = WORDSWAP(hdr->count); | ||||
| 
 | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| /* Block input and output, similar to the Crynwr packet driver.
 | ||||
|  * If you are porting to a new ethercard, look at the packet driver source | ||||
|  * for hints. The NEx000 doesn't share the on-board packet memory -- | ||||
|  * you have to put the packet out through the "remote DMA" dataport | ||||
|  * using z_writeb. | ||||
|  */ | ||||
| static void zorro8390_block_input(struct net_device *dev, int count, | ||||
| 				  struct sk_buff *skb, int ring_offset) | ||||
| { | ||||
| 	int nic_base = dev->base_addr; | ||||
| 	char *buf = skb->data; | ||||
| 	short *ptrs; | ||||
| 	int cnt; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen.
 | ||||
| 	 * If it does, it's the last thing you'll see | ||||
| 	 */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n", | ||||
| 			   __func__, ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD); | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
| 	z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO); | ||||
| 	z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI); | ||||
| 	z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO); | ||||
| 	z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI); | ||||
| 	z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD); | ||||
| 	ptrs = (short *)buf; | ||||
| 	for (cnt = 0; cnt < count >> 1; cnt++) | ||||
| 		*ptrs++ = z_readw(NE_BASE + NE_DATAPORT); | ||||
| 	if (count & 0x01) | ||||
| 		buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT); | ||||
| 
 | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */ | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static void zorro8390_block_output(struct net_device *dev, int count, | ||||
| 				   const unsigned char *buf, | ||||
| 				   const int start_page) | ||||
| { | ||||
| 	int nic_base = NE_BASE; | ||||
| 	unsigned long dma_start; | ||||
| 	short *ptrs; | ||||
| 	int cnt; | ||||
| 
 | ||||
| 	/* Round the count up for word writes.  Do we need to do this?
 | ||||
| 	 * What effect will an odd byte count have on the 8390? | ||||
| 	 * I should check someday. | ||||
| 	 */ | ||||
| 	if (count & 0x01) | ||||
| 		count++; | ||||
| 
 | ||||
| 	/* This *shouldn't* happen.
 | ||||
| 	 * If it does, it's the last thing you'll see | ||||
| 	 */ | ||||
| 	if (ei_status.dmaing) { | ||||
| 		netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n", | ||||
| 			   __func__, ei_status.dmaing, ei_status.irqlock); | ||||
| 		return; | ||||
| 	} | ||||
| 	ei_status.dmaing |= 0x01; | ||||
| 	/* We should already be in page 0, but to be safe... */ | ||||
| 	z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); | ||||
| 
 | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); | ||||
| 
 | ||||
| 	/* Now the normal output. */ | ||||
| 	z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO); | ||||
| 	z_writeb(count >> 8,   nic_base + NE_EN0_RCNTHI); | ||||
| 	z_writeb(0x00, nic_base + NE_EN0_RSARLO); | ||||
| 	z_writeb(start_page, nic_base + NE_EN0_RSARHI); | ||||
| 
 | ||||
| 	z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD); | ||||
| 	ptrs = (short *)buf; | ||||
| 	for (cnt = 0; cnt < count >> 1; cnt++) | ||||
| 		z_writew(*ptrs++, NE_BASE + NE_DATAPORT); | ||||
| 
 | ||||
| 	dma_start = jiffies; | ||||
| 
 | ||||
| 	while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0) | ||||
| 		if (time_after(jiffies, dma_start + 2 * HZ / 100)) { | ||||
| 					/* 20ms */ | ||||
| 			netdev_warn(dev, "timeout waiting for Tx RDC\n"); | ||||
| 			zorro8390_reset_8390(dev); | ||||
| 			__NS8390_init(dev, 1); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */ | ||||
| 	ei_status.dmaing &= ~0x01; | ||||
| } | ||||
| 
 | ||||
| static int zorro8390_open(struct net_device *dev) | ||||
| { | ||||
| 	__ei_open(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int zorro8390_close(struct net_device *dev) | ||||
| { | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 
 | ||||
| 	netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard\n"); | ||||
| 	__ei_close(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void zorro8390_remove_one(struct zorro_dev *z) | ||||
| { | ||||
| 	struct net_device *dev = zorro_get_drvdata(z); | ||||
| 
 | ||||
| 	unregister_netdev(dev); | ||||
| 	free_irq(IRQ_AMIGA_PORTS, dev); | ||||
| 	release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2); | ||||
| 	free_netdev(dev); | ||||
| } | ||||
| 
 | ||||
| static struct zorro_device_id zorro8390_zorro_tbl[] = { | ||||
| 	{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, }, | ||||
| 	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, }, | ||||
| 	{ 0 } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl); | ||||
| 
 | ||||
| static const struct net_device_ops zorro8390_netdev_ops = { | ||||
| 	.ndo_open		= zorro8390_open, | ||||
| 	.ndo_stop		= zorro8390_close, | ||||
| 	.ndo_start_xmit		= __ei_start_xmit, | ||||
| 	.ndo_tx_timeout		= __ei_tx_timeout, | ||||
| 	.ndo_get_stats		= __ei_get_stats, | ||||
| 	.ndo_set_rx_mode	= __ei_set_multicast_list, | ||||
| 	.ndo_validate_addr	= eth_validate_addr, | ||||
| 	.ndo_set_mac_address	= eth_mac_addr, | ||||
| 	.ndo_change_mtu		= eth_change_mtu, | ||||
| #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
| 	.ndo_poll_controller	= __ei_poll, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int zorro8390_init(struct net_device *dev, unsigned long board, | ||||
| 			  const char *name, void __iomem *ioaddr) | ||||
| { | ||||
| 	int i; | ||||
| 	int err; | ||||
| 	unsigned char SA_prom[32]; | ||||
| 	int start_page, stop_page; | ||||
| 	struct ei_device *ei_local = netdev_priv(dev); | ||||
| 	static u32 zorro8390_offsets[16] = { | ||||
| 		0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, | ||||
| 		0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, | ||||
| 	}; | ||||
| 
 | ||||
| 	/* Reset card. Who knows what dain-bramaged state it was left in. */ | ||||
| 	{ | ||||
| 		unsigned long reset_start_time = jiffies; | ||||
| 
 | ||||
| 		z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET); | ||||
| 
 | ||||
| 		while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0) | ||||
| 			if (time_after(jiffies, | ||||
| 				       reset_start_time + 2 * HZ / 100)) { | ||||
| 				netdev_warn(dev, "not found (no reset ack)\n"); | ||||
| 				return -ENODEV; | ||||
| 			} | ||||
| 
 | ||||
| 		z_writeb(0xff, ioaddr + NE_EN0_ISR);	/* Ack all intr. */ | ||||
| 	} | ||||
| 
 | ||||
| 	/* Read the 16 bytes of station address PROM.
 | ||||
| 	 * We must first initialize registers, | ||||
| 	 * similar to NS8390_init(eifdev, 0). | ||||
| 	 * We can't reliably read the SAPROM address without this. | ||||
| 	 * (I learned the hard way!). | ||||
| 	 */ | ||||
| 	{ | ||||
| 		static const struct { | ||||
| 			u32 value; | ||||
| 			u32 offset; | ||||
| 		} program_seq[] = { | ||||
| 			{E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD}, | ||||
| 						/* Select page 0 */ | ||||
| 			{0x48,	NE_EN0_DCFG},	/* 0x48: Set byte-wide access */ | ||||
| 			{0x00,	NE_EN0_RCNTLO},	/* Clear the count regs */ | ||||
| 			{0x00,	NE_EN0_RCNTHI}, | ||||
| 			{0x00,	NE_EN0_IMR},	/* Mask completion irq */ | ||||
| 			{0xFF,	NE_EN0_ISR}, | ||||
| 			{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */ | ||||
| 			{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */ | ||||
| 			{32,	NE_EN0_RCNTLO}, | ||||
| 			{0x00,	NE_EN0_RCNTHI}, | ||||
| 			{0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000 */ | ||||
| 			{0x00,	NE_EN0_RSARHI}, | ||||
| 			{E8390_RREAD + E8390_START, NE_CMD}, | ||||
| 		}; | ||||
| 		for (i = 0; i < ARRAY_SIZE(program_seq); i++) | ||||
| 			z_writeb(program_seq[i].value, | ||||
| 				 ioaddr + program_seq[i].offset); | ||||
| 	} | ||||
| 	for (i = 0; i < 16; i++) { | ||||
| 		SA_prom[i] = z_readb(ioaddr + NE_DATAPORT); | ||||
| 		(void)z_readb(ioaddr + NE_DATAPORT); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We must set the 8390 for word mode. */ | ||||
| 	z_writeb(0x49, ioaddr + NE_EN0_DCFG); | ||||
| 	start_page = NESM_START_PG; | ||||
| 	stop_page = NESM_STOP_PG; | ||||
| 
 | ||||
| 	dev->base_addr = (unsigned long)ioaddr; | ||||
| 	dev->irq = IRQ_AMIGA_PORTS; | ||||
| 
 | ||||
| 	/* Install the Interrupt handler */ | ||||
| 	i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, | ||||
| 			IRQF_SHARED, DRV_NAME, dev); | ||||
| 	if (i) | ||||
| 		return i; | ||||
| 
 | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		dev->dev_addr[i] = SA_prom[i]; | ||||
| 
 | ||||
| 	pr_debug("Found ethernet address: %pM\n", dev->dev_addr); | ||||
| 
 | ||||
| 	ei_status.name = name; | ||||
| 	ei_status.tx_start_page = start_page; | ||||
| 	ei_status.stop_page = stop_page; | ||||
| 	ei_status.word16 = 1; | ||||
| 
 | ||||
| 	ei_status.rx_start_page = start_page + TX_PAGES; | ||||
| 
 | ||||
| 	ei_status.reset_8390 = zorro8390_reset_8390; | ||||
| 	ei_status.block_input = zorro8390_block_input; | ||||
| 	ei_status.block_output = zorro8390_block_output; | ||||
| 	ei_status.get_8390_hdr = zorro8390_get_8390_hdr; | ||||
| 	ei_status.reg_offset = zorro8390_offsets; | ||||
| 
 | ||||
| 	dev->netdev_ops = &zorro8390_netdev_ops; | ||||
| 	__NS8390_init(dev, 0); | ||||
| 
 | ||||
| 	ei_local->msg_enable = zorro8390_msg_enable; | ||||
| 
 | ||||
| 	err = register_netdev(dev); | ||||
| 	if (err) { | ||||
| 		free_irq(IRQ_AMIGA_PORTS, dev); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n", | ||||
| 		    name, board, dev->dev_addr); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int zorro8390_init_one(struct zorro_dev *z, | ||||
| 			      const struct zorro_device_id *ent) | ||||
| { | ||||
| 	struct net_device *dev; | ||||
| 	unsigned long board, ioaddr; | ||||
| 	int err, i; | ||||
| 
 | ||||
| 	for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--) | ||||
| 		if (z->id == cards[i].id) | ||||
| 			break; | ||||
| 	if (i < 0) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	board = z->resource.start; | ||||
| 	ioaddr = board + cards[i].offset; | ||||
| 	dev = ____alloc_ei_netdev(0); | ||||
| 	if (!dev) | ||||
| 		return -ENOMEM; | ||||
| 	if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) { | ||||
| 		free_netdev(dev); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 	err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr)); | ||||
| 	if (err) { | ||||
| 		release_mem_region(ioaddr, NE_IO_EXTENT * 2); | ||||
| 		free_netdev(dev); | ||||
| 		return err; | ||||
| 	} | ||||
| 	zorro_set_drvdata(z, dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct zorro_driver zorro8390_driver = { | ||||
| 	.name		= "zorro8390", | ||||
| 	.id_table	= zorro8390_zorro_tbl, | ||||
| 	.probe		= zorro8390_init_one, | ||||
| 	.remove		= zorro8390_remove_one, | ||||
| }; | ||||
| 
 | ||||
| static int __init zorro8390_init_module(void) | ||||
| { | ||||
| 	return zorro_register_driver(&zorro8390_driver); | ||||
| } | ||||
| 
 | ||||
| static void __exit zorro8390_cleanup_module(void) | ||||
| { | ||||
| 	zorro_unregister_driver(&zorro8390_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(zorro8390_init_module); | ||||
| module_exit(zorro8390_cleanup_module); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228