Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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");

View 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 */

View 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");

View 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

View 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

View 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");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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 = &etherh_netdev_ops;
dev->irq = ec->irq;
dev->ethtool_ops = &etherh_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, &etherh_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, &etherm_data },
{ MANU_I3, PROD_I3_ETHERLAN500, &etherlan500_data },
{ MANU_I3, PROD_I3_ETHERLAN600, &etherlan600_data },
{ MANU_I3, PROD_I3_ETHERLAN600A, &etherlan600a_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(&etherh_driver);
}
static void __exit etherh_exit(void)
{
ecard_remove_driver(&etherh_driver);
}
module_init(etherh_init);
module_exit(etherh_exit);

View 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");

File diff suppressed because it is too large Load diff

View 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++;
}

View 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");

File diff suppressed because it is too large Load diff

View 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);

File diff suppressed because it is too large Load diff

View 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 */

View 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");

View 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 */

View 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");