Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/* 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, unsigned int txqueue)
{
__ei_tx_timeout(dev, txqueue);
}
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,
#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);
MODULE_DESCRIPTION("National Semiconductor 8390 core driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-1.0+ */
/* 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, unsigned int txqueue);
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, unsigned int txqueue);
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 *dev);
void (*get_8390_hdr)(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
void (*block_output)(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
void (*block_input)(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
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
/* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXCONFIG 0x4
/* EN0_RXCR: Accept no packets */
#define E8390_RXOFF 0x20
#endif
/* EN0_TXCR: Normal transmit mode */
#define E8390_TXCONFIG 0x00
/* EN0_TXCR: Transmitter off */
#define E8390_TXOFF 0x02
/* 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 */
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0-only
/* 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, unsigned int txqueue)
{
__ei_tx_timeout(dev, txqueue);
}
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,
#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);
MODULE_DESCRIPTION("National Semiconductor 8390 core for ISA driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,170 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 8390 device configuration
#
config NET_VENDOR_8390
bool "National Semiconductor 8390 devices"
default y
depends on NET_VENDOR_NATSEMI
help
If you have a network (Ethernet) card belonging to this class, say Y.
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 National Semiconductor 8390 cards. If you say Y,
you will be asked for your specific card in the following questions.
if NET_VENDOR_8390
config AX88796
tristate "ASIX AX88796 NE2000 clone support" if !ZORRO
depends on (ARM || MIPS || SUPERH || ZORRO || COMPILE_TEST)
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 XSURF100
tristate "Amiga XSurf 100 AX88796/NE2000 clone support"
depends on ZORRO
select AX88796
select AX88796B_PHY
help
This driver is for the Individual Computers X-Surf 100 Ethernet
card (based on the Asix AX88796 chip). 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 xsurf100.
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
tristate "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.
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) || MACH_TX49XX || ATARI_ETHERNEC)
select CRC32
help
If you have a network (Ethernet) card of this type, say Y here.
Many Ethernet cards without a specific driver are compatible with
the 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 && HAS_IOPORT
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 here.
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 && HAS_IOPORT
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 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
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0
#
# 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
obj-$(CONFIG_MCF8390) += mcf8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
obj-$(CONFIG_XSURF100) += xsurf100.o
obj-$(CONFIG_ZORRO8390) += zorro8390.o
@@ -0,0 +1,614 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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)
*
* ----------------------------------------------------------------------------
*
*/
#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 */
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, 0444);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
static struct net_device * __init apne_probe(void)
{
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);
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;
eth_hw_addr_set(dev, SA_prom);
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;
}
static struct net_device *apne_dev;
static int __init apne_module_init(void)
{
apne_dev = apne_probe();
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);
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_DESCRIPTION("National Semiconductor 8390 Amiga PCMCIA ethernet driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,858 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/acorn/net/etherh.c
*
* Copyright (C) 2000-2002 Russell King
*
* 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"
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;
WRITE_ONCE(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;
}
/*
* 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 module 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)
{
strscpy(info->driver, DRV_NAME, sizeof(info->driver));
strscpy(info->version, DRV_VERSION, sizeof(info->version));
strscpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
}
static int etherh_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
etherh_priv(dev)->supported);
cmd->base.speed = SPEED_10;
cmd->base.duplex = DUPLEX_HALF;
cmd->base.port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
cmd->base.autoneg = (dev->flags & IFF_AUTOMEDIA ? AUTONEG_ENABLE :
AUTONEG_DISABLE);
return 0;
}
static int etherh_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd)
{
switch (cmd->base.autoneg) {
case AUTONEG_ENABLE:
dev->flags |= IFF_AUTOMEDIA;
break;
case AUTONEG_DISABLE:
switch (cmd->base.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_drvinfo = etherh_get_drvinfo,
.get_ts_info = ethtool_op_get_ts_info,
.get_msglevel = etherh_get_msglevel,
.set_msglevel = etherh_set_msglevel,
.get_link_ksettings = etherh_get_link_ksettings,
.set_link_ksettings = etherh_set_link_ksettings,
};
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,
#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;
u8 addr[ETH_ALEN];
int ret;
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(addr);
ei_local->reg_offset = etherm_regoffsets;
} else {
etherh_addr(addr, ec);
ei_local->reg_offset = etherh_regoffsets;
}
eth_hw_addr_set(dev, addr);
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;
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);
@@ -0,0 +1,274 @@
// SPDX-License-Identifier: GPL-2.0-only
/* New Hydra driver using generic 8390 core */
/* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */
/* 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 struct zorro_device_id hydra_zorro_tbl[] = {
{ .id = ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
{ }
};
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,
#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;
u8 macaddr[ETH_ALEN];
int j;
int err;
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++)
macaddr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j));
eth_hw_addr_set(dev, macaddr);
/* 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;
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_DESCRIPTION("Zorro-II Hydra 8390 ethernet driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,848 @@
// SPDX-License-Identifier: GPL-1.0+
/* 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. */
/* 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_board *board,
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);
/* 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 enum mac8390_type mac8390_ident(struct nubus_rsrc *fres)
{
switch (fres->dr_sw) {
case NUBUS_DRSW_3COM:
switch (fres->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;
}
case NUBUS_DRSW_APPLE:
switch (fres->dr_hw) {
case NUBUS_DRHW_ASANTE_LC:
return MAC8390_NONE;
case NUBUS_DRHW_CABLETRON:
return MAC8390_CABLETRON;
default:
return MAC8390_APPLE;
}
case NUBUS_DRSW_ASANTE:
return MAC8390_ASANTE;
case NUBUS_DRSW_TECHWORKS:
case NUBUS_DRSW_DAYNA2:
case NUBUS_DRSW_DAYNA_LC:
if (fres->dr_hw == NUBUS_DRHW_CABLETRON)
return MAC8390_CABLETRON;
else
return MAC8390_APPLE;
case NUBUS_DRSW_FARALLON:
return MAC8390_FARALLON;
case NUBUS_DRSW_KINETICS:
switch (fres->dr_hw) {
case NUBUS_DRHW_INTERLAN:
return MAC8390_INTERLAN;
default:
return MAC8390_KINETICS;
}
case NUBUS_DRSW_DAYNA:
/*
* These correspond to Dayna Sonic cards
* which use the macsonic driver
*/
if (fres->dr_hw == NUBUS_DRHW_SMC9194 ||
fres->dr_hw == NUBUS_DRHW_INTERLAN)
return MAC8390_NONE;
else
return MAC8390_DAYNA;
}
return MAC8390_NONE;
}
static enum mac8390_access mac8390_testio(unsigned long membase)
{
u32 outdata = 0xA5A0B5B0;
u32 indata = 0;
/* Try writing 32 bits */
nubus_writel(outdata, membase);
/* Now read it back */
indata = nubus_readl(membase);
if (outdata == indata)
return ACCESS_32;
outdata = 0xC5C0D5D0;
indata = 0;
/* 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 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 mac8390_rsrc_init(struct net_device *dev,
struct nubus_rsrc *fres,
enum mac8390_type cardtype)
{
struct nubus_board *board = fres->board;
struct nubus_dir dir;
struct nubus_dirent ent;
int offset;
volatile unsigned short *i;
u8 addr[ETH_ALEN];
dev->irq = SLOT2IRQ(board->slot);
/* This is getting to be a habit */
dev->base_addr = board->slot_addr | ((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(fres, &dir) == -1) {
dev_err(&board->dev,
"Unable to get Nubus functional directory\n");
return false;
}
/* Get the MAC address */
if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
dev_info(&board->dev, "MAC address resource not found\n");
return false;
}
nubus_get_rsrc_mem(addr, &ent, 6);
eth_hw_addr_set(dev, addr);
if (useresources[cardtype] == 1) {
nubus_rewinddir(&dir);
if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
&ent) == -1) {
dev_err(&board->dev,
"Memory offset resource not found\n");
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) {
dev_info(&board->dev,
"Memory length resource not found, probing\n");
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)(board->slot_addr +
DAYNA_8390_BASE);
dev->mem_start = (int)(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)(board->slot_addr +
INTERLAN_8390_BASE);
dev->mem_start = (int)(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)(board->slot_addr +
CABLETRON_8390_BASE);
dev->mem_start = (int)(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:
dev_err(&board->dev,
"No known base address for card type\n");
return false;
}
}
return true;
}
static int mac8390_device_probe(struct nubus_board *board)
{
struct net_device *dev;
int err = -ENODEV;
struct nubus_rsrc *fres;
enum mac8390_type cardtype = MAC8390_NONE;
dev = ____alloc_ei_netdev(0);
if (!dev)
return -ENOMEM;
SET_NETDEV_DEV(dev, &board->dev);
for_each_board_func_rsrc(board, fres) {
if (fres->category != NUBUS_CAT_NETWORK ||
fres->type != NUBUS_TYPE_ETHERNET)
continue;
cardtype = mac8390_ident(fres);
if (cardtype == MAC8390_NONE)
continue;
if (mac8390_rsrc_init(dev, fres, cardtype))
break;
}
if (!fres)
goto out;
err = mac8390_initdev(dev, board, cardtype);
if (err)
goto out;
err = register_netdev(dev);
if (err)
goto out;
nubus_set_drvdata(board, dev);
return 0;
out:
free_netdev(dev);
return err;
}
static void mac8390_device_remove(struct nubus_board *board)
{
struct net_device *dev = nubus_get_drvdata(board);
unregister_netdev(dev);
free_netdev(dev);
}
static struct nubus_driver mac8390_driver = {
.probe = mac8390_device_probe,
.remove = mac8390_device_remove,
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
}
};
MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
MODULE_LICENSE("GPL");
static int __init mac8390_init(void)
{
return nubus_driver_register(&mac8390_driver);
}
module_init(mac8390_init);
static void __exit mac8390_exit(void)
{
nubus_driver_unregister(&mac8390_driver);
}
module_exit(mac8390_exit);
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,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static int mac8390_initdev(struct net_device *dev, struct nubus_board *board,
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:
dev_err(&board->dev,
"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:
dev_err(&board->dev, "Unsupported card type\n");
return -ENODEV;
}
__NS8390_init(dev, 0);
/* Good, done, now spit out some messages */
dev_info(&board->dev, "%s (type %s)\n", board->name, cardname[type]);
dev_info(&board->dev, "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, (void __iomem *)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,
(void __iomem *)dev->mem_start + xfer_base,
semi_count);
count -= semi_count;
memcpy_fromio(skb->data + semi_count,
(void __iomem *)ei_status.rmem_start, count);
} else {
memcpy_fromio(skb->data,
(void __iomem *)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((void __iomem *)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++;
}
@@ -0,0 +1,468 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*
*/
#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 */
#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,
#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;
eth_hw_addr_set(dev, SA_prom);
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 resource *mem;
resource_size_t msize;
int ret, irq;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
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);
dev->irq = irq;
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 void 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);
release_mem_region(mem->start, resource_size(mem));
free_netdev(dev);
}
static struct platform_driver mcf8390_drv = {
.driver = {
.name = "mcf8390",
},
.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
@@ -0,0 +1,723 @@
// SPDX-License-Identifier: GPL-1.0+
/* 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_DESCRIPTION "PCI NE2000 clone driver"
#define DRV_AUTHOR "Donald Becker / Paul Gortmaker"
#define DRV_VERSION "1.03"
#define DRV_RELDATE "9/22/2003"
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* The user-configurable values.
* These may be modified when a driver module is loaded.
*/
/* More are supported, limit only on options */
#define MAX_UNITS 8
/* 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 <linux/io.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
#include "8390.h"
static int ne2k_msg_enable;
static const int default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR);
#if defined(__powerpc__)
#define inl_le(addr) le32_to_cpu(inl(addr))
#define inw_le(addr) le16_to_cpu(inw(addr))
#endif
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
module_param_named(msg_enable, ne2k_msg_enable, int, 0444);
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 {
/* Chip can do only 16/32-bit xfers. */
ONLY_16BIT_IO = 8, ONLY_32BIT_IO = 4,
/* User override. */
FORCE_FDX = 0x20,
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(AS)", 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[] = {
{ PCI_DEVICE(0x10ec, 0x8029), .driver_data = CH_RealTek_RTL_8029 },
{ PCI_DEVICE(0x1050, 0x0940), .driver_data = CH_Winbond_89C940 },
{ PCI_DEVICE(0x11f6, 0x1401), .driver_data = CH_Compex_RL2000 },
{ PCI_DEVICE(0x8e2e, 0x3000), .driver_data = CH_KTI_ET32P2 },
{ PCI_DEVICE(0x4a14, 0x5000), .driver_data = CH_NetVin_NV5000SC },
{ PCI_DEVICE(0x1106, 0x0926), .driver_data = CH_Via_86C926 },
{ PCI_DEVICE(0x10bd, 0x0e34), .driver_data = CH_SureCom_NE34 },
{ PCI_DEVICE(0x1050, 0x5a5a), .driver_data = CH_Winbond_W89C940F },
{ PCI_DEVICE(0x12c3, 0x0058), .driver_data = CH_Holtek_HT80232 },
{ PCI_DEVICE(0x12c3, 0x5598), .driver_data = CH_Holtek_HT80229 },
{ PCI_DEVICE(0x8c4a, 0x1980), .driver_data = CH_Winbond_89C940_8c4a },
{ }
};
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;
/* 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,
#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;
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");
goto err_out;
}
if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME)) {
dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
NE_IO_EXTENT, ioaddr);
goto err_out;
}
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);
/* Clear the counter by reading. */
inb(ioaddr + EN0_COUNTER0);
if (inb(ioaddr + EN0_COUNTER0) != 0) {
outb(reg0, ioaddr);
/* Restore the old values. */
outb(regd, ioaddr + 0x0d);
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 = netif_msg_init(ne2k_msg_enable, default_msg_level);
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;
}
/* Ack all intr. */
outb(0xff, ioaddr + EN0_ISR);
}
/* 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[] = {
/* Select page 0 */
{E8390_NODMA + E8390_PAGE0 + E8390_STOP, E8390_CMD},
/* Set word-wide access */
{0x49, EN0_DCFG},
/* Clear the count regs. */
{0x00, EN0_RCNTLO},
/* Mask completion IRQ */
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR},
{0xFF, EN0_ISR},
/* 0x20 Set to monitor */
{E8390_RXOFF, EN0_RXCR},
/* 0x02 and loopback mode */
{E8390_TXOFF, EN0_TXCR},
{32, EN0_RCNTLO},
{0x00, EN0_RCNTHI},
/* DMA starting at 0x0000 */
{0x00, EN0_RSARLO},
{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);
eth_hw_addr_set(dev, SA_prom);
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);
err_out:
pci_disable_device(pdev);
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, "%s did not complete.\n", __func__);
break;
}
/* Ack intr. */
outb(ENISR_RESET, NE_BASE + EN0_ISR);
}
/* 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 %s [DMAstat:%d][irqlock:%d].\n",
__func__, 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);
}
/* Ack intr. */
outb(ENISR_RDC, nic_base + EN0_ISR);
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 %s [DMAstat:%d][irqlock:%d]\n",
__func__, 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);
}
}
/* Ack intr. */
outb(ENISR_RDC, nic_base + EN0_ISR);
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 %s [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... */
outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
#ifdef NE_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)
/* Avoid clock roll-over. */
if (jiffies - dma_start > 2) {
netdev_warn(dev, "timeout waiting for Tx RDC.\n");
ne2k_pci_reset_8390(dev);
NS8390_init(dev, 1);
break;
}
/* Ack intr. */
outb(ENISR_RDC, nic_base + EN0_ISR);
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;
strscpy(info->driver, DRV_NAME, sizeof(info->driver));
strscpy(info->version, DRV_VERSION, sizeof(info->version));
strscpy(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);
}
static int __maybe_unused ne2k_pci_suspend(struct device *dev_d)
{
struct net_device *dev = dev_get_drvdata(dev_d);
netif_device_detach(dev);
return 0;
}
static int __maybe_unused ne2k_pci_resume(struct device *dev_d)
{
struct net_device *dev = dev_get_drvdata(dev_d);
NS8390_init(dev, 1);
netif_device_attach(dev);
return 0;
}
static SIMPLE_DEV_PM_OPS(ne2k_pci_pm_ops, ne2k_pci_suspend, ne2k_pci_resume);
static struct pci_driver ne2k_driver = {
.name = DRV_NAME,
.probe = ne2k_pci_init_one,
.remove = ne2k_pci_remove_one,
.id_table = ne2k_pci_tbl,
.driver.pm = &ne2k_pci_pm_ops,
};
module_pci_driver(ne2k_driver);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,300 @@
// SPDX-License-Identifier: GPL-2.0-only
/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
*
* 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, 0444);
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;
struct ei_device *ei_local;
int err;
/* 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
eth_hw_addr_set(dev, stnic_eadr);
/* 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_DESCRIPTION("National Semiconductor DP83902AV ethernet driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,377 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/zorro.h>
#include <net/ax88796.h>
#include <asm/amigaints.h>
#define ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF100 \
ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x64, 0)
#define XS100_IRQSTATUS_BASE 0x40
#define XS100_8390_BASE 0x800
/* Longword-access area. Translated to 2 16-bit access cycles by the
* X-Surf 100 FPGA
*/
#define XS100_8390_DATA32_BASE 0x8000
#define XS100_8390_DATA32_SIZE 0x2000
/* Sub-Areas for fast data register access; addresses relative to area begin */
#define XS100_8390_DATA_READ32_BASE 0x0880
#define XS100_8390_DATA_WRITE32_BASE 0x0C80
#define XS100_8390_DATA_AREA_SIZE 0x80
/* force unsigned long back to 'void __iomem *' */
#define ax_convert_addr(_a) ((void __force __iomem *)(_a))
#define ei_inb(_a) z_readb(ax_convert_addr(_a))
#define ei_outb(_v, _a) z_writeb(_v, ax_convert_addr(_a))
#define ei_inw(_a) z_readw(ax_convert_addr(_a))
#define ei_outw(_v, _a) z_writew(_v, ax_convert_addr(_a))
#define ei_inb_p(_a) ei_inb(_a)
#define ei_outb_p(_v, _a) ei_outb(_v, _a)
/* define EI_SHIFT() to take into account our register offsets */
#define EI_SHIFT(x) (ei_local->reg_offset[(x)])
/* Ensure we have our RCR base value */
#define AX88796_PLATFORM
#include "8390.h"
/* from ne.c */
#define NE_CMD EI_SHIFT(0x00)
#define NE_RESET EI_SHIFT(0x1f)
#define NE_DATAPORT EI_SHIFT(0x10)
struct xsurf100_ax_plat_data {
struct ax_plat_data ax;
void __iomem *base_regs;
void __iomem *data_area;
};
static int is_xsurf100_network_irq(struct platform_device *pdev)
{
struct xsurf100_ax_plat_data *xs100 = dev_get_platdata(&pdev->dev);
return (readw(xs100->base_regs + XS100_IRQSTATUS_BASE) & 0xaaaa) != 0;
}
/* These functions guarantee that the iomem is accessed with 32 bit
* cycles only. z_memcpy_fromio / z_memcpy_toio don't
*/
static void z_memcpy_fromio32(void *dst, const void __iomem *src, size_t bytes)
{
while (bytes > 32) {
asm __volatile__
("movem.l (%0)+,%%d0-%%d7\n"
"movem.l %%d0-%%d7,(%1)\n"
"adda.l #32,%1" : "=a"(src), "=a"(dst)
: "0"(src), "1"(dst) : "d0", "d1", "d2", "d3", "d4",
"d5", "d6", "d7", "memory");
bytes -= 32;
}
while (bytes) {
*(uint32_t *)dst = z_readl(src);
src += 4;
dst += 4;
bytes -= 4;
}
}
static void z_memcpy_toio32(void __iomem *dst, const void *src, size_t bytes)
{
while (bytes) {
z_writel(*(const uint32_t *)src, dst);
src += 4;
dst += 4;
bytes -= 4;
}
}
static void xs100_write(struct net_device *dev, const void *src,
unsigned int count)
{
struct ei_device *ei_local = netdev_priv(dev);
struct platform_device *pdev = to_platform_device(dev->dev.parent);
struct xsurf100_ax_plat_data *xs100 = dev_get_platdata(&pdev->dev);
/* copy whole blocks */
while (count > XS100_8390_DATA_AREA_SIZE) {
z_memcpy_toio32(xs100->data_area +
XS100_8390_DATA_WRITE32_BASE, src,
XS100_8390_DATA_AREA_SIZE);
src += XS100_8390_DATA_AREA_SIZE;
count -= XS100_8390_DATA_AREA_SIZE;
}
/* copy whole dwords */
z_memcpy_toio32(xs100->data_area + XS100_8390_DATA_WRITE32_BASE,
src, count & ~3);
src += count & ~3;
if (count & 2) {
ei_outw(*(uint16_t *)src, ei_local->mem + NE_DATAPORT);
src += 2;
}
if (count & 1)
ei_outb(*(uint8_t *)src, ei_local->mem + NE_DATAPORT);
}
static void xs100_read(struct net_device *dev, void *dst, unsigned int count)
{
struct ei_device *ei_local = netdev_priv(dev);
struct platform_device *pdev = to_platform_device(dev->dev.parent);
struct xsurf100_ax_plat_data *xs100 = dev_get_platdata(&pdev->dev);
/* copy whole blocks */
while (count > XS100_8390_DATA_AREA_SIZE) {
z_memcpy_fromio32(dst, xs100->data_area +
XS100_8390_DATA_READ32_BASE,
XS100_8390_DATA_AREA_SIZE);
dst += XS100_8390_DATA_AREA_SIZE;
count -= XS100_8390_DATA_AREA_SIZE;
}
/* copy whole dwords */
z_memcpy_fromio32(dst, xs100->data_area + XS100_8390_DATA_READ32_BASE,
count & ~3);
dst += count & ~3;
if (count & 2) {
*(uint16_t *)dst = ei_inw(ei_local->mem + NE_DATAPORT);
dst += 2;
}
if (count & 1)
*(uint8_t *)dst = ei_inb(ei_local->mem + NE_DATAPORT);
}
/* 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 ei_outb.
*/
static void xs100_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *nic_base = ei_local->mem;
char *buf = skb->data;
if (ei_local->dmaing) {
netdev_err(dev,
"DMAing conflict in %s [DMAstat:%d][irqlock:%d]\n",
__func__,
ei_local->dmaing, ei_local->irqlock);
return;
}
ei_local->dmaing |= 0x01;
ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
ei_outb(count >> 8, nic_base + EN0_RCNTHI);
ei_outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
ei_outb(ring_offset >> 8, nic_base + EN0_RSARHI);
ei_outb(E8390_RREAD + E8390_START, nic_base + NE_CMD);
xs100_read(dev, buf, count);
ei_local->dmaing &= ~1;
}
static void xs100_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *nic_base = ei_local->mem;
unsigned long dma_start;
/* 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_local->word16 && (count & 0x01))
count++;
/* This *shouldn't* happen. If it does, it's the last thing
* you'll see
*/
if (ei_local->dmaing) {
netdev_err(dev,
"DMAing conflict in %s [DMAstat:%d][irqlock:%d]\n",
__func__,
ei_local->dmaing, ei_local->irqlock);
return;
}
ei_local->dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, nic_base + NE_CMD);
ei_outb(ENISR_RDC, nic_base + EN0_ISR);
/* Now the normal output. */
ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
ei_outb(count >> 8, nic_base + EN0_RCNTHI);
ei_outb(0x00, nic_base + EN0_RSARLO);
ei_outb(start_page, nic_base + EN0_RSARHI);
ei_outb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
xs100_write(dev, buf, count);
dma_start = jiffies;
while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
if (jiffies - dma_start > 2 * HZ / 100) { /* 20ms */
netdev_warn(dev, "timeout waiting for Tx RDC.\n");
ei_local->reset_8390(dev);
ax_NS8390_reinit(dev);
break;
}
}
ei_outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_local->dmaing &= ~0x01;
}
static int xsurf100_probe(struct zorro_dev *zdev,
const struct zorro_device_id *ent)
{
struct platform_device *pdev;
struct xsurf100_ax_plat_data ax88796_data;
struct resource res[2] = {
DEFINE_RES_NAMED(IRQ_AMIGA_PORTS, 1, NULL,
IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE),
DEFINE_RES_MEM(zdev->resource.start + XS100_8390_BASE,
4 * 0x20)
};
int reg;
/* This table is referenced in the device structure, so it must
* outlive the scope of xsurf100_probe.
*/
static u32 reg_offsets[32];
int ret = 0;
/* X-Surf 100 control and 32 bit ring buffer data access areas.
* These resources are not used by the ax88796 driver, so must
* be requested here and passed via platform data.
*/
if (!request_mem_region(zdev->resource.start, 0x100, zdev->name)) {
dev_err(&zdev->dev, "cannot reserve X-Surf 100 control registers\n");
return -ENXIO;
}
if (!request_mem_region(zdev->resource.start +
XS100_8390_DATA32_BASE,
XS100_8390_DATA32_SIZE,
"X-Surf 100 32-bit data access")) {
dev_err(&zdev->dev, "cannot reserve 32-bit area\n");
ret = -ENXIO;
goto exit_req;
}
for (reg = 0; reg < 0x20; reg++)
reg_offsets[reg] = 4 * reg;
memset(&ax88796_data, 0, sizeof(ax88796_data));
ax88796_data.ax.flags = AXFLG_HAS_EEPROM;
ax88796_data.ax.wordlength = 2;
ax88796_data.ax.dcr_val = 0x48;
ax88796_data.ax.rcr_val = 0x40;
ax88796_data.ax.reg_offsets = reg_offsets;
ax88796_data.ax.check_irq = is_xsurf100_network_irq;
ax88796_data.base_regs = ioremap(zdev->resource.start, 0x100);
/* error handling for ioremap regs */
if (!ax88796_data.base_regs) {
dev_err(&zdev->dev, "Cannot ioremap area %pR (registers)\n",
&zdev->resource);
ret = -ENXIO;
goto exit_req2;
}
ax88796_data.data_area = ioremap(zdev->resource.start +
XS100_8390_DATA32_BASE, XS100_8390_DATA32_SIZE);
/* error handling for ioremap data */
if (!ax88796_data.data_area) {
dev_err(&zdev->dev,
"Cannot ioremap area %pR offset %x (32-bit access)\n",
&zdev->resource, XS100_8390_DATA32_BASE);
ret = -ENXIO;
goto exit_mem;
}
ax88796_data.ax.block_output = xs100_block_output;
ax88796_data.ax.block_input = xs100_block_input;
pdev = platform_device_register_resndata(&zdev->dev, "ax88796",
zdev->slotaddr, res, 2,
&ax88796_data,
sizeof(ax88796_data));
if (IS_ERR(pdev)) {
dev_err(&zdev->dev, "cannot register platform device\n");
ret = -ENXIO;
goto exit_mem2;
}
zorro_set_drvdata(zdev, pdev);
if (!ret)
return 0;
exit_mem2:
iounmap(ax88796_data.data_area);
exit_mem:
iounmap(ax88796_data.base_regs);
exit_req2:
release_mem_region(zdev->resource.start + XS100_8390_DATA32_BASE,
XS100_8390_DATA32_SIZE);
exit_req:
release_mem_region(zdev->resource.start, 0x100);
return ret;
}
static void xsurf100_remove(struct zorro_dev *zdev)
{
struct platform_device *pdev = zorro_get_drvdata(zdev);
struct xsurf100_ax_plat_data *xs100 = dev_get_platdata(&pdev->dev);
platform_device_unregister(pdev);
iounmap(xs100->base_regs);
release_mem_region(zdev->resource.start, 0x100);
iounmap(xs100->data_area);
release_mem_region(zdev->resource.start + XS100_8390_DATA32_BASE,
XS100_8390_DATA32_SIZE);
}
static const struct zorro_device_id xsurf100_zorro_tbl[] = {
{ .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF100 },
{ }
};
MODULE_DEVICE_TABLE(zorro, xsurf100_zorro_tbl);
static struct zorro_driver xsurf100_driver = {
.name = "xsurf100",
.id_table = xsurf100_zorro_tbl,
.probe = xsurf100_probe,
.remove = xsurf100_remove,
};
module_driver(xsurf100_driver, zorro_register_driver, zorro_unregister_driver);
MODULE_DESCRIPTION("X-Surf 100 driver");
MODULE_AUTHOR("Michael Karcher <kernel@mkarcher.dialup.fu-berlin.de>");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,447 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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
*
* ---------------------------------------------------------------------------
*
* 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";
#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[] = {
{ .id = ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2 },
{ .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF },
{ }
};
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,
#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;
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;
eth_hw_addr_set(dev, SA_prom);
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);
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_DESCRIPTION("Zorro NS8390-based ethernet driver");
MODULE_LICENSE("GPL");