Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
48
drivers/net/arm/Kconfig
Normal file
48
drivers/net/arm/Kconfig
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Acorn Network device configuration
|
||||
# These are for Acorn's Expansion card network interfaces
|
||||
#
|
||||
config ARM_AM79C961A
|
||||
bool "ARM EBSA110 AM79C961A support"
|
||||
depends on NET_ETHERNET && ARM && ARCH_EBSA110
|
||||
select CRC32
|
||||
help
|
||||
If you wish to compile a kernel for the EBSA-110, then you should
|
||||
always answer Y to this.
|
||||
|
||||
config ARM_ETHER1
|
||||
tristate "Acorn Ether1 support"
|
||||
depends on NET_ETHERNET && ARM && ARCH_ACORN
|
||||
help
|
||||
If you have an Acorn system with one of these (AKA25) network cards,
|
||||
you should say Y to this option if you wish to use it with Linux.
|
||||
|
||||
config ARM_ETHER3
|
||||
tristate "Acorn/ANT Ether3 support"
|
||||
depends on NET_ETHERNET && ARM && ARCH_ACORN
|
||||
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 ARM_ETHERH
|
||||
tristate "I-cubed EtherH/ANT EtherM support"
|
||||
depends on NET_ETHERNET && 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 ARM_AT91_ETHER
|
||||
tristate "AT91RM9200 Ethernet support"
|
||||
depends on NET_ETHERNET && ARM && ARCH_AT91RM9200
|
||||
select MII
|
||||
help
|
||||
If you wish to compile a kernel for the AT91RM9200 and enable
|
||||
ethernet support, then you should always answer Y to this.
|
||||
|
||||
config EP93XX_ETH
|
||||
tristate "EP93xx Ethernet support"
|
||||
depends on NET_ETHERNET && ARM && ARCH_EP93XX
|
||||
help
|
||||
This is a driver for the ethernet hardware included in EP93xx CPUs.
|
||||
Say Y if you are building a kernel for EP93xx based devices.
|
||||
11
drivers/net/arm/Makefile
Normal file
11
drivers/net/arm/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
# File: drivers/net/arm/Makefile
|
||||
#
|
||||
# Makefile for the ARM network device drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
|
||||
obj-$(CONFIG_ARM_ETHERH) += etherh.o
|
||||
obj-$(CONFIG_ARM_ETHER3) += ether3.o
|
||||
obj-$(CONFIG_ARM_ETHER1) += ether1.o
|
||||
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
|
||||
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
|
||||
774
drivers/net/arm/am79c961a.c
Normal file
774
drivers/net/arm/am79c961a.c
Normal file
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* linux/drivers/net/am79c961.c
|
||||
*
|
||||
* by Russell King <rmk@arm.linux.org.uk> 1995-2001.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Derived from various things including skeleton.c
|
||||
*
|
||||
* This is a special driver for the am79c961A Lance chip used in the
|
||||
* Intel (formally Digital Equipment Corp) EBSA110 platform. Please
|
||||
* note that this can not be built as a module (it doesn't make sense).
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#define TX_BUFFERS 15
|
||||
#define RX_BUFFERS 25
|
||||
|
||||
#include "am79c961a.h"
|
||||
|
||||
static irqreturn_t
|
||||
am79c961_interrupt (int irq, void *dev_id);
|
||||
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
|
||||
static const char version[] =
|
||||
"am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n";
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef __arm__
|
||||
static void write_rreg(u_long base, u_int reg, u_int val)
|
||||
{
|
||||
__asm__(
|
||||
"str%?h %1, [%2] @ NET_RAP\n\t"
|
||||
"str%?h %0, [%2, #-4] @ NET_RDP"
|
||||
:
|
||||
: "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
|
||||
}
|
||||
|
||||
static inline unsigned short read_rreg(u_long base_addr, u_int reg)
|
||||
{
|
||||
unsigned short v;
|
||||
__asm__(
|
||||
"str%?h %1, [%2] @ NET_RAP\n\t"
|
||||
"ldr%?h %0, [%2, #-4] @ NET_RDP"
|
||||
: "=r" (v)
|
||||
: "r" (reg), "r" (ISAIO_BASE + 0x0464));
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void write_ireg(u_long base, u_int reg, u_int val)
|
||||
{
|
||||
__asm__(
|
||||
"str%?h %1, [%2] @ NET_RAP\n\t"
|
||||
"str%?h %0, [%2, #8] @ NET_IDP"
|
||||
:
|
||||
: "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
|
||||
}
|
||||
|
||||
static inline unsigned short read_ireg(u_long base_addr, u_int reg)
|
||||
{
|
||||
u_short v;
|
||||
__asm__(
|
||||
"str%?h %1, [%2] @ NAT_RAP\n\t"
|
||||
"ldr%?h %0, [%2, #8] @ NET_IDP\n\t"
|
||||
: "=r" (v)
|
||||
: "r" (reg), "r" (ISAIO_BASE + 0x0464));
|
||||
return v;
|
||||
}
|
||||
|
||||
#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
|
||||
#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1))
|
||||
|
||||
static inline void
|
||||
am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
|
||||
{
|
||||
offset = ISAMEM_BASE + (offset << 1);
|
||||
length = (length + 1) & ~1;
|
||||
if ((int)buf & 2) {
|
||||
__asm__ __volatile__("str%?h %2, [%0], #4"
|
||||
: "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
|
||||
buf += 2;
|
||||
length -= 2;
|
||||
}
|
||||
while (length > 8) {
|
||||
unsigned int tmp, tmp2;
|
||||
__asm__ __volatile__(
|
||||
"ldm%?ia %1!, {%2, %3}\n\t"
|
||||
"str%?h %2, [%0], #4\n\t"
|
||||
"mov%? %2, %2, lsr #16\n\t"
|
||||
"str%?h %2, [%0], #4\n\t"
|
||||
"str%?h %3, [%0], #4\n\t"
|
||||
"mov%? %3, %3, lsr #16\n\t"
|
||||
"str%?h %3, [%0], #4"
|
||||
: "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
|
||||
: "0" (offset), "1" (buf));
|
||||
length -= 8;
|
||||
}
|
||||
while (length > 0) {
|
||||
__asm__ __volatile__("str%?h %2, [%0], #4"
|
||||
: "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
|
||||
buf += 2;
|
||||
length -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
|
||||
{
|
||||
offset = ISAMEM_BASE + (offset << 1);
|
||||
length = (length + 1) & ~1;
|
||||
if ((int)buf & 2) {
|
||||
unsigned int tmp;
|
||||
__asm__ __volatile__(
|
||||
"ldr%?h %2, [%0], #4\n\t"
|
||||
"str%?b %2, [%1], #1\n\t"
|
||||
"mov%? %2, %2, lsr #8\n\t"
|
||||
"str%?b %2, [%1], #1"
|
||||
: "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
|
||||
length -= 2;
|
||||
}
|
||||
while (length > 8) {
|
||||
unsigned int tmp, tmp2, tmp3;
|
||||
__asm__ __volatile__(
|
||||
"ldr%?h %2, [%0], #4\n\t"
|
||||
"ldr%?h %3, [%0], #4\n\t"
|
||||
"orr%? %2, %2, %3, lsl #16\n\t"
|
||||
"ldr%?h %3, [%0], #4\n\t"
|
||||
"ldr%?h %4, [%0], #4\n\t"
|
||||
"orr%? %3, %3, %4, lsl #16\n\t"
|
||||
"stm%?ia %1!, {%2, %3}"
|
||||
: "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
|
||||
: "0" (offset), "1" (buf));
|
||||
length -= 8;
|
||||
}
|
||||
while (length > 0) {
|
||||
unsigned int tmp;
|
||||
__asm__ __volatile__(
|
||||
"ldr%?h %2, [%0], #4\n\t"
|
||||
"str%?b %2, [%1], #1\n\t"
|
||||
"mov%? %2, %2, lsr #8\n\t"
|
||||
"str%?b %2, [%1], #1"
|
||||
: "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
|
||||
length -= 2;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error Not compatible
|
||||
#endif
|
||||
|
||||
static int
|
||||
am79c961_ramtest(struct net_device *dev, unsigned int val)
|
||||
{
|
||||
unsigned char *buffer = kmalloc (65536, GFP_KERNEL);
|
||||
int i, error = 0, errorcount = 0;
|
||||
|
||||
if (!buffer)
|
||||
return 0;
|
||||
memset (buffer, val, 65536);
|
||||
am_writebuffer(dev, 0, buffer, 65536);
|
||||
memset (buffer, val ^ 255, 65536);
|
||||
am_readbuffer(dev, 0, buffer, 65536);
|
||||
for (i = 0; i < 65536; i++) {
|
||||
if (buffer[i] != val && !error) {
|
||||
printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i);
|
||||
error = 1;
|
||||
errorcount ++;
|
||||
} else if (error && buffer[i] == val) {
|
||||
printk ("%05X\n", i);
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
if (error)
|
||||
printk ("10000\n");
|
||||
kfree (buffer);
|
||||
return errorcount;
|
||||
}
|
||||
|
||||
static void
|
||||
am79c961_init_for_open(struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
unsigned char *p;
|
||||
u_int hdr_addr, first_free_addr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Stop the chip.
|
||||
*/
|
||||
spin_lock_irqsave(priv->chip_lock, flags);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
|
||||
spin_unlock_irqrestore(priv->chip_lock, flags);
|
||||
|
||||
write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */
|
||||
write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */
|
||||
write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */
|
||||
write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
|
||||
|
||||
for (i = LADRL; i <= LADRH; i++)
|
||||
write_rreg (dev->base_addr, i, 0);
|
||||
|
||||
for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
|
||||
write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
|
||||
|
||||
i = MODE_PORT_10BT;
|
||||
if (dev->flags & IFF_PROMISC)
|
||||
i |= MODE_PROMISC;
|
||||
|
||||
write_rreg (dev->base_addr, MODE, i);
|
||||
write_rreg (dev->base_addr, POLLINT, 0);
|
||||
write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
|
||||
write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
|
||||
|
||||
first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
|
||||
hdr_addr = 0;
|
||||
|
||||
priv->rxhead = 0;
|
||||
priv->rxtail = 0;
|
||||
priv->rxhdr = hdr_addr;
|
||||
|
||||
for (i = 0; i < RX_BUFFERS; i++) {
|
||||
priv->rxbuffer[i] = first_free_addr;
|
||||
am_writeword (dev, hdr_addr, first_free_addr);
|
||||
am_writeword (dev, hdr_addr + 2, RMD_OWN);
|
||||
am_writeword (dev, hdr_addr + 4, (-1600));
|
||||
am_writeword (dev, hdr_addr + 6, 0);
|
||||
first_free_addr += 1600;
|
||||
hdr_addr += 8;
|
||||
}
|
||||
priv->txhead = 0;
|
||||
priv->txtail = 0;
|
||||
priv->txhdr = hdr_addr;
|
||||
for (i = 0; i < TX_BUFFERS; i++) {
|
||||
priv->txbuffer[i] = first_free_addr;
|
||||
am_writeword (dev, hdr_addr, first_free_addr);
|
||||
am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP);
|
||||
am_writeword (dev, hdr_addr + 4, 0xf000);
|
||||
am_writeword (dev, hdr_addr + 6, 0);
|
||||
first_free_addr += 1600;
|
||||
hdr_addr += 8;
|
||||
}
|
||||
|
||||
write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
|
||||
write_rreg (dev->base_addr, BASERXH, 0);
|
||||
write_rreg (dev->base_addr, BASETXL, priv->txhdr);
|
||||
write_rreg (dev->base_addr, BASERXH, 0);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
|
||||
write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
|
||||
write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
|
||||
}
|
||||
|
||||
static void am79c961_timer(unsigned long data)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)data;
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
unsigned int lnkstat, carrier;
|
||||
|
||||
lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
|
||||
carrier = netif_carrier_ok(dev);
|
||||
|
||||
if (lnkstat && !carrier) {
|
||||
netif_carrier_on(dev);
|
||||
printk("%s: link up\n", dev->name);
|
||||
} else if (!lnkstat && carrier) {
|
||||
netif_carrier_off(dev);
|
||||
printk("%s: link down\n", dev->name);
|
||||
}
|
||||
|
||||
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
/*
|
||||
* Open/initialize the board.
|
||||
*/
|
||||
static int
|
||||
am79c961_open(struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
memset (&priv->stats, 0, sizeof (priv->stats));
|
||||
|
||||
ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
am79c961_init_for_open(dev);
|
||||
|
||||
netif_carrier_off(dev);
|
||||
|
||||
priv->timer.expires = jiffies;
|
||||
add_timer(&priv->timer);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The inverse routine to am79c961_open().
|
||||
*/
|
||||
static int
|
||||
am79c961_close(struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
|
||||
del_timer_sync(&priv->timer);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
netif_carrier_off(dev);
|
||||
|
||||
spin_lock_irqsave(priv->chip_lock, flags);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
|
||||
write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
|
||||
spin_unlock_irqrestore(priv->chip_lock, flags);
|
||||
|
||||
free_irq (dev->irq, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current statistics.
|
||||
*/
|
||||
static struct net_device_stats *am79c961_getstats (struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
return &priv->stats;
|
||||
}
|
||||
|
||||
static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash)
|
||||
{
|
||||
if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) {
|
||||
int idx, bit;
|
||||
u32 crc;
|
||||
|
||||
crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr);
|
||||
|
||||
idx = crc >> 30;
|
||||
bit = (crc >> 26) & 15;
|
||||
|
||||
hash[idx] |= 1 << bit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set or clear promiscuous/multicast mode filter for this adapter.
|
||||
*/
|
||||
static void am79c961_setmulticastlist (struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
unsigned short multi_hash[4], mode;
|
||||
int i, stopped;
|
||||
|
||||
mode = MODE_PORT_10BT;
|
||||
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
mode |= MODE_PROMISC;
|
||||
} else if (dev->flags & IFF_ALLMULTI) {
|
||||
memset(multi_hash, 0xff, sizeof(multi_hash));
|
||||
} else {
|
||||
struct dev_mc_list *dmi;
|
||||
|
||||
memset(multi_hash, 0x00, sizeof(multi_hash));
|
||||
|
||||
for (dmi = dev->mc_list; dmi; dmi = dmi->next)
|
||||
am79c961_mc_hash(dmi, multi_hash);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(priv->chip_lock, flags);
|
||||
|
||||
stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
|
||||
|
||||
if (!stopped) {
|
||||
/*
|
||||
* Put the chip into suspend mode
|
||||
*/
|
||||
write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
|
||||
|
||||
/*
|
||||
* Spin waiting for chip to report suspend mode
|
||||
*/
|
||||
while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
|
||||
spin_unlock_irqrestore(priv->chip_lock, flags);
|
||||
nop();
|
||||
spin_lock_irqsave(priv->chip_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the multicast hash table
|
||||
*/
|
||||
for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++)
|
||||
write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
|
||||
|
||||
/*
|
||||
* Write the mode register
|
||||
*/
|
||||
write_rreg(dev->base_addr, MODE, mode);
|
||||
|
||||
if (!stopped) {
|
||||
/*
|
||||
* Put the chip back into running mode
|
||||
*/
|
||||
write_rreg(dev->base_addr, CTRL1, 0);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(priv->chip_lock, flags);
|
||||
}
|
||||
|
||||
static void am79c961_timeout(struct net_device *dev)
|
||||
{
|
||||
printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n",
|
||||
dev->name);
|
||||
|
||||
/*
|
||||
* ought to do some setup of the tx side here
|
||||
*/
|
||||
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit a packet
|
||||
*/
|
||||
static int
|
||||
am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
unsigned int hdraddr, bufaddr;
|
||||
unsigned int head;
|
||||
unsigned long flags;
|
||||
|
||||
head = priv->txhead;
|
||||
hdraddr = priv->txhdr + (head << 3);
|
||||
bufaddr = priv->txbuffer[head];
|
||||
head += 1;
|
||||
if (head >= TX_BUFFERS)
|
||||
head = 0;
|
||||
|
||||
am_writebuffer (dev, bufaddr, skb->data, skb->len);
|
||||
am_writeword (dev, hdraddr + 4, -skb->len);
|
||||
am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
|
||||
priv->txhead = head;
|
||||
|
||||
spin_lock_irqsave(priv->chip_lock, flags);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
|
||||
dev->trans_start = jiffies;
|
||||
spin_unlock_irqrestore(priv->chip_lock, flags);
|
||||
|
||||
/*
|
||||
* If the next packet is owned by the ethernet device,
|
||||
* then the tx ring is full and we can't add another
|
||||
* packet.
|
||||
*/
|
||||
if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
|
||||
netif_stop_queue(dev);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a good packet(s), get it/them out of the buffers.
|
||||
*/
|
||||
static void
|
||||
am79c961_rx(struct net_device *dev, struct dev_priv *priv)
|
||||
{
|
||||
do {
|
||||
struct sk_buff *skb;
|
||||
u_int hdraddr;
|
||||
u_int pktaddr;
|
||||
u_int status;
|
||||
int len;
|
||||
|
||||
hdraddr = priv->rxhdr + (priv->rxtail << 3);
|
||||
pktaddr = priv->rxbuffer[priv->rxtail];
|
||||
|
||||
status = am_readword (dev, hdraddr + 2);
|
||||
if (status & RMD_OWN) /* do we own it? */
|
||||
break;
|
||||
|
||||
priv->rxtail ++;
|
||||
if (priv->rxtail >= RX_BUFFERS)
|
||||
priv->rxtail = 0;
|
||||
|
||||
if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) {
|
||||
am_writeword (dev, hdraddr + 2, RMD_OWN);
|
||||
priv->stats.rx_errors ++;
|
||||
if (status & RMD_ERR) {
|
||||
if (status & RMD_FRAM)
|
||||
priv->stats.rx_frame_errors ++;
|
||||
if (status & RMD_CRC)
|
||||
priv->stats.rx_crc_errors ++;
|
||||
} else if (status & RMD_STP)
|
||||
priv->stats.rx_length_errors ++;
|
||||
continue;
|
||||
}
|
||||
|
||||
len = am_readword(dev, hdraddr + 6);
|
||||
skb = dev_alloc_skb(len + 2);
|
||||
|
||||
if (skb) {
|
||||
skb->dev = dev;
|
||||
skb_reserve(skb, 2);
|
||||
|
||||
am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
|
||||
am_writeword(dev, hdraddr + 2, RMD_OWN);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
priv->stats.rx_bytes += len;
|
||||
priv->stats.rx_packets ++;
|
||||
} else {
|
||||
am_writeword (dev, hdraddr + 2, RMD_OWN);
|
||||
printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
|
||||
priv->stats.rx_dropped ++;
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update stats for the transmitted packet
|
||||
*/
|
||||
static void
|
||||
am79c961_tx(struct net_device *dev, struct dev_priv *priv)
|
||||
{
|
||||
do {
|
||||
short len;
|
||||
u_int hdraddr;
|
||||
u_int status;
|
||||
|
||||
hdraddr = priv->txhdr + (priv->txtail << 3);
|
||||
status = am_readword (dev, hdraddr + 2);
|
||||
if (status & TMD_OWN)
|
||||
break;
|
||||
|
||||
priv->txtail ++;
|
||||
if (priv->txtail >= TX_BUFFERS)
|
||||
priv->txtail = 0;
|
||||
|
||||
if (status & TMD_ERR) {
|
||||
u_int status2;
|
||||
|
||||
priv->stats.tx_errors ++;
|
||||
|
||||
status2 = am_readword (dev, hdraddr + 6);
|
||||
|
||||
/*
|
||||
* Clear the error byte
|
||||
*/
|
||||
am_writeword (dev, hdraddr + 6, 0);
|
||||
|
||||
if (status2 & TST_RTRY)
|
||||
priv->stats.collisions += 16;
|
||||
if (status2 & TST_LCOL)
|
||||
priv->stats.tx_window_errors ++;
|
||||
if (status2 & TST_LCAR)
|
||||
priv->stats.tx_carrier_errors ++;
|
||||
if (status2 & TST_UFLO)
|
||||
priv->stats.tx_fifo_errors ++;
|
||||
continue;
|
||||
}
|
||||
priv->stats.tx_packets ++;
|
||||
len = am_readword (dev, hdraddr + 4);
|
||||
priv->stats.tx_bytes += -len;
|
||||
} while (priv->txtail != priv->txhead);
|
||||
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
am79c961_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
u_int status, n = 100;
|
||||
int handled = 0;
|
||||
|
||||
do {
|
||||
status = read_rreg(dev->base_addr, CSR0);
|
||||
write_rreg(dev->base_addr, CSR0, status &
|
||||
(CSR0_IENA|CSR0_TINT|CSR0_RINT|
|
||||
CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL));
|
||||
|
||||
if (status & CSR0_RINT) {
|
||||
handled = 1;
|
||||
am79c961_rx(dev, priv);
|
||||
}
|
||||
if (status & CSR0_TINT) {
|
||||
handled = 1;
|
||||
am79c961_tx(dev, priv);
|
||||
}
|
||||
if (status & CSR0_MISS) {
|
||||
handled = 1;
|
||||
priv->stats.rx_dropped ++;
|
||||
}
|
||||
if (status & CSR0_CERR) {
|
||||
handled = 1;
|
||||
mod_timer(&priv->timer, jiffies);
|
||||
}
|
||||
} while (--n && status & (CSR0_RINT | CSR0_TINT));
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void am79c961_poll_controller(struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
am79c961_interrupt(dev->irq, dev, NULL);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialise the chip. Note that we always expect
|
||||
* to be entered with interrupts enabled.
|
||||
*/
|
||||
static int
|
||||
am79c961_hw_init(struct net_device *dev)
|
||||
{
|
||||
struct dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
spin_lock_irq(&priv->chip_lock);
|
||||
write_rreg (dev->base_addr, CSR0, CSR0_STOP);
|
||||
write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
|
||||
spin_unlock_irq(&priv->chip_lock);
|
||||
|
||||
am79c961_ramtest(dev, 0x66);
|
||||
am79c961_ramtest(dev, 0x99);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init am79c961_banner(void)
|
||||
{
|
||||
static unsigned version_printed;
|
||||
|
||||
if (net_debug && version_printed++ == 0)
|
||||
printk(KERN_INFO "%s", version);
|
||||
}
|
||||
|
||||
static int __init am79c961_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct net_device *dev;
|
||||
struct dev_priv *priv;
|
||||
int i, ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct dev_priv));
|
||||
ret = -ENOMEM;
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
/*
|
||||
* Fixed address and IRQ lines here.
|
||||
* The PNP initialisation should have been
|
||||
* done by the ether bootp loader.
|
||||
*/
|
||||
dev->base_addr = res->start;
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
ret = -ENODEV;
|
||||
if (dev->irq < 0)
|
||||
goto nodev;
|
||||
if (!request_region(dev->base_addr, 0x18, dev->name))
|
||||
goto nodev;
|
||||
|
||||
/*
|
||||
* Reset the device.
|
||||
*/
|
||||
inb(dev->base_addr + NET_RESET);
|
||||
udelay(5);
|
||||
|
||||
/*
|
||||
* Check the manufacturer part of the
|
||||
* ether address.
|
||||
*/
|
||||
if (inb(dev->base_addr) != 0x08 ||
|
||||
inb(dev->base_addr + 2) != 0x00 ||
|
||||
inb(dev->base_addr + 4) != 0x2b)
|
||||
goto release;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff;
|
||||
|
||||
am79c961_banner();
|
||||
|
||||
spin_lock_init(&priv->chip_lock);
|
||||
init_timer(&priv->timer);
|
||||
priv->timer.data = (unsigned long)dev;
|
||||
priv->timer.function = am79c961_timer;
|
||||
|
||||
if (am79c961_hw_init(dev))
|
||||
goto release;
|
||||
|
||||
dev->open = am79c961_open;
|
||||
dev->stop = am79c961_close;
|
||||
dev->hard_start_xmit = am79c961_sendpacket;
|
||||
dev->get_stats = am79c961_getstats;
|
||||
dev->set_multicast_list = am79c961_setmulticastlist;
|
||||
dev->tx_timeout = am79c961_timeout;
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
dev->poll_controller = am79c961_poll_controller;
|
||||
#endif
|
||||
|
||||
ret = register_netdev(dev);
|
||||
if (ret == 0) {
|
||||
printk(KERN_INFO "%s: ether address ", dev->name);
|
||||
|
||||
/* Retrive and print the ethernet address. */
|
||||
for (i = 0; i < 6; i++)
|
||||
printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
release:
|
||||
release_region(dev->base_addr, 0x18);
|
||||
nodev:
|
||||
free_netdev(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver am79c961_driver = {
|
||||
.probe = am79c961_probe,
|
||||
.driver = {
|
||||
.name = "am79c961",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init am79c961_init(void)
|
||||
{
|
||||
return platform_driver_register(&am79c961_driver);
|
||||
}
|
||||
|
||||
__initcall(am79c961_init);
|
||||
146
drivers/net/arm/am79c961a.h
Normal file
146
drivers/net/arm/am79c961a.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* linux/drivers/net/arm/am79c961a.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_am79c961a_H
|
||||
#define _LINUX_am79c961a_H
|
||||
|
||||
/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
|
||||
#define DEBUG_TX 2
|
||||
#define DEBUG_RX 4
|
||||
#define DEBUG_INT 8
|
||||
#define DEBUG_IC 16
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define NET_UID 0
|
||||
#define NET_RDP 0x10
|
||||
#define NET_RAP 0x12
|
||||
#define NET_RESET 0x14
|
||||
#define NET_IDP 0x16
|
||||
|
||||
/*
|
||||
* RAP registers
|
||||
*/
|
||||
#define CSR0 0
|
||||
#define CSR0_INIT 0x0001
|
||||
#define CSR0_STRT 0x0002
|
||||
#define CSR0_STOP 0x0004
|
||||
#define CSR0_TDMD 0x0008
|
||||
#define CSR0_TXON 0x0010
|
||||
#define CSR0_RXON 0x0020
|
||||
#define CSR0_IENA 0x0040
|
||||
#define CSR0_INTR 0x0080
|
||||
#define CSR0_IDON 0x0100
|
||||
#define CSR0_TINT 0x0200
|
||||
#define CSR0_RINT 0x0400
|
||||
#define CSR0_MERR 0x0800
|
||||
#define CSR0_MISS 0x1000
|
||||
#define CSR0_CERR 0x2000
|
||||
#define CSR0_BABL 0x4000
|
||||
#define CSR0_ERR 0x8000
|
||||
|
||||
#define CSR3 3
|
||||
#define CSR3_EMBA 0x0008
|
||||
#define CSR3_DXMT2PD 0x0010
|
||||
#define CSR3_LAPPEN 0x0020
|
||||
#define CSR3_DXSUFLO 0x0040
|
||||
#define CSR3_IDONM 0x0100
|
||||
#define CSR3_TINTM 0x0200
|
||||
#define CSR3_RINTM 0x0400
|
||||
#define CSR3_MERRM 0x0800
|
||||
#define CSR3_MISSM 0x1000
|
||||
#define CSR3_BABLM 0x4000
|
||||
#define CSR3_MASKALL 0x5F00
|
||||
|
||||
#define CSR4 4
|
||||
#define CSR4_JABM 0x0001
|
||||
#define CSR4_JAB 0x0002
|
||||
#define CSR4_TXSTRTM 0x0004
|
||||
#define CSR4_TXSTRT 0x0008
|
||||
#define CSR4_RCVCCOM 0x0010
|
||||
#define CSR4_RCVCCO 0x0020
|
||||
#define CSR4_MFCOM 0x0100
|
||||
#define CSR4_MFCO 0x0200
|
||||
#define CSR4_ASTRP_RCV 0x0400
|
||||
#define CSR4_APAD_XMIT 0x0800
|
||||
|
||||
#define CTRL1 5
|
||||
#define CTRL1_SPND 0x0001
|
||||
|
||||
#define LADRL 8
|
||||
#define LADRM1 9
|
||||
#define LADRM2 10
|
||||
#define LADRH 11
|
||||
#define PADRL 12
|
||||
#define PADRM 13
|
||||
#define PADRH 14
|
||||
|
||||
#define MODE 15
|
||||
#define MODE_DISRX 0x0001
|
||||
#define MODE_DISTX 0x0002
|
||||
#define MODE_LOOP 0x0004
|
||||
#define MODE_DTCRC 0x0008
|
||||
#define MODE_COLL 0x0010
|
||||
#define MODE_DRETRY 0x0020
|
||||
#define MODE_INTLOOP 0x0040
|
||||
#define MODE_PORT_AUI 0x0000
|
||||
#define MODE_PORT_10BT 0x0080
|
||||
#define MODE_DRXPA 0x2000
|
||||
#define MODE_DRXBA 0x4000
|
||||
#define MODE_PROMISC 0x8000
|
||||
|
||||
#define BASERXL 24
|
||||
#define BASERXH 25
|
||||
#define BASETXL 30
|
||||
#define BASETXH 31
|
||||
|
||||
#define POLLINT 47
|
||||
|
||||
#define SIZERXR 76
|
||||
#define SIZETXR 78
|
||||
|
||||
#define CSR_MFC 112
|
||||
|
||||
#define RMD_ENP 0x0100
|
||||
#define RMD_STP 0x0200
|
||||
#define RMD_CRC 0x0800
|
||||
#define RMD_FRAM 0x2000
|
||||
#define RMD_ERR 0x4000
|
||||
#define RMD_OWN 0x8000
|
||||
|
||||
#define TMD_ENP 0x0100
|
||||
#define TMD_STP 0x0200
|
||||
#define TMD_MORE 0x1000
|
||||
#define TMD_ERR 0x4000
|
||||
#define TMD_OWN 0x8000
|
||||
|
||||
#define TST_RTRY 0x0400
|
||||
#define TST_LCAR 0x0800
|
||||
#define TST_LCOL 0x1000
|
||||
#define TST_UFLO 0x4000
|
||||
#define TST_BUFF 0x8000
|
||||
|
||||
#define ISALED0 0x0004
|
||||
#define ISALED0_LNKST 0x8000
|
||||
|
||||
struct dev_priv {
|
||||
struct net_device_stats stats;
|
||||
unsigned long rxbuffer[RX_BUFFERS];
|
||||
unsigned long txbuffer[TX_BUFFERS];
|
||||
unsigned char txhead;
|
||||
unsigned char txtail;
|
||||
unsigned char rxhead;
|
||||
unsigned char rxtail;
|
||||
unsigned long rxhdr;
|
||||
unsigned long txhdr;
|
||||
spinlock_t chip_lock;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
#endif
|
||||
1205
drivers/net/arm/at91_ether.c
Normal file
1205
drivers/net/arm/at91_ether.c
Normal file
File diff suppressed because it is too large
Load Diff
103
drivers/net/arm/at91_ether.h
Normal file
103
drivers/net/arm/at91_ether.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Ethernet driver for the Atmel AT91RM9200 (Thunder)
|
||||
*
|
||||
* Copyright (C) SAN People (Pty) Ltd
|
||||
*
|
||||
* Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc.
|
||||
* Initial version by Rick Bronson.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef AT91_ETHERNET
|
||||
#define AT91_ETHERNET
|
||||
|
||||
|
||||
/* Davicom 9161 PHY */
|
||||
#define MII_DM9161_ID 0x0181b880
|
||||
#define MII_DM9161A_ID 0x0181b8a0
|
||||
|
||||
/* Davicom specific registers */
|
||||
#define MII_DSCR_REG 16
|
||||
#define MII_DSCSR_REG 17
|
||||
#define MII_DSINTR_REG 21
|
||||
|
||||
/* Intel LXT971A PHY */
|
||||
#define MII_LXT971A_ID 0x001378E0
|
||||
|
||||
/* Intel specific registers */
|
||||
#define MII_ISINTE_REG 18
|
||||
#define MII_ISINTS_REG 19
|
||||
#define MII_LEDCTRL_REG 20
|
||||
|
||||
/* Realtek RTL8201 PHY */
|
||||
#define MII_RTL8201_ID 0x00008200
|
||||
|
||||
/* Broadcom BCM5221 PHY */
|
||||
#define MII_BCM5221_ID 0x004061e0
|
||||
|
||||
/* Broadcom specific registers */
|
||||
#define MII_BCMINTR_REG 26
|
||||
|
||||
/* National Semiconductor DP83847 */
|
||||
#define MII_DP83847_ID 0x20005c30
|
||||
|
||||
/* Altima AC101L PHY */
|
||||
#define MII_AC101L_ID 0x00225520
|
||||
|
||||
/* Micrel KS8721 PHY */
|
||||
#define MII_KS8721_ID 0x00221610
|
||||
|
||||
/* ........................................................................ */
|
||||
|
||||
#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */
|
||||
#define MAX_RX_DESCR 9 /* max number of receive buffers */
|
||||
|
||||
#define EMAC_DESC_DONE 0x00000001 /* bit for if DMA is done */
|
||||
#define EMAC_DESC_WRAP 0x00000002 /* bit for wrap */
|
||||
|
||||
#define EMAC_BROADCAST 0x80000000 /* broadcast address */
|
||||
#define EMAC_MULTICAST 0x40000000 /* multicast address */
|
||||
#define EMAC_UNICAST 0x20000000 /* unicast address */
|
||||
|
||||
struct rbf_t
|
||||
{
|
||||
unsigned int addr;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
struct recv_desc_bufs
|
||||
{
|
||||
struct rbf_t descriptors[MAX_RX_DESCR]; /* must be on sizeof (rbf_t) boundary */
|
||||
char recv_buf[MAX_RX_DESCR][MAX_RBUFF_SZ]; /* must be on long boundary */
|
||||
};
|
||||
|
||||
struct at91_private
|
||||
{
|
||||
struct net_device_stats stats;
|
||||
struct mii_if_info mii; /* ethtool support */
|
||||
struct at91_eth_data board_data; /* board-specific configuration */
|
||||
struct clk *ether_clk; /* clock */
|
||||
|
||||
/* PHY */
|
||||
unsigned long phy_type; /* type of PHY (PHY_ID) */
|
||||
spinlock_t lock; /* lock for MDI interface */
|
||||
short phy_media; /* media interface type */
|
||||
unsigned short phy_address; /* 5-bit MDI address of PHY (0..31) */
|
||||
struct timer_list check_timer; /* Poll link status */
|
||||
|
||||
/* Transmit */
|
||||
struct sk_buff *skb; /* holds skb until xmit interrupt completes */
|
||||
dma_addr_t skb_physaddr; /* phys addr from pci_map_single */
|
||||
int skb_length; /* saved skb length for pci_unmap_single */
|
||||
|
||||
/* Receive */
|
||||
int rxBuffIndex; /* index into receive descriptor list */
|
||||
struct recv_desc_bufs *dlist; /* descriptor list address */
|
||||
struct recv_desc_bufs *dlist_phys; /* descriptor list physical address */
|
||||
};
|
||||
|
||||
#endif
|
||||
921
drivers/net/arm/ep93xx_eth.c
Normal file
921
drivers/net/arm/ep93xx_eth.c
Normal file
@@ -0,0 +1,921 @@
|
||||
/*
|
||||
* EP93xx ethernet network device driver
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
* Dedicated to Marija Kulikova.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/arch/ep93xx-regs.h>
|
||||
#include <asm/arch/platform.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_MODULE_NAME "ep93xx-eth"
|
||||
#define DRV_MODULE_VERSION "0.1"
|
||||
|
||||
#define RX_QUEUE_ENTRIES 64
|
||||
#define TX_QUEUE_ENTRIES 8
|
||||
|
||||
#define MAX_PKT_SIZE 2044
|
||||
#define PKT_BUF_SIZE 2048
|
||||
|
||||
#define REG_RXCTL 0x0000
|
||||
#define REG_RXCTL_DEFAULT 0x00073800
|
||||
#define REG_TXCTL 0x0004
|
||||
#define REG_TXCTL_ENABLE 0x00000001
|
||||
#define REG_MIICMD 0x0010
|
||||
#define REG_MIICMD_READ 0x00008000
|
||||
#define REG_MIICMD_WRITE 0x00004000
|
||||
#define REG_MIIDATA 0x0014
|
||||
#define REG_MIISTS 0x0018
|
||||
#define REG_MIISTS_BUSY 0x00000001
|
||||
#define REG_SELFCTL 0x0020
|
||||
#define REG_SELFCTL_RESET 0x00000001
|
||||
#define REG_INTEN 0x0024
|
||||
#define REG_INTEN_TX 0x00000008
|
||||
#define REG_INTEN_RX 0x00000007
|
||||
#define REG_INTSTSP 0x0028
|
||||
#define REG_INTSTS_TX 0x00000008
|
||||
#define REG_INTSTS_RX 0x00000004
|
||||
#define REG_INTSTSC 0x002c
|
||||
#define REG_AFP 0x004c
|
||||
#define REG_INDAD0 0x0050
|
||||
#define REG_INDAD1 0x0051
|
||||
#define REG_INDAD2 0x0052
|
||||
#define REG_INDAD3 0x0053
|
||||
#define REG_INDAD4 0x0054
|
||||
#define REG_INDAD5 0x0055
|
||||
#define REG_GIINTMSK 0x0064
|
||||
#define REG_GIINTMSK_ENABLE 0x00008000
|
||||
#define REG_BMCTL 0x0080
|
||||
#define REG_BMCTL_ENABLE_TX 0x00000100
|
||||
#define REG_BMCTL_ENABLE_RX 0x00000001
|
||||
#define REG_BMSTS 0x0084
|
||||
#define REG_BMSTS_RX_ACTIVE 0x00000008
|
||||
#define REG_RXDQBADD 0x0090
|
||||
#define REG_RXDQBLEN 0x0094
|
||||
#define REG_RXDCURADD 0x0098
|
||||
#define REG_RXDENQ 0x009c
|
||||
#define REG_RXSTSQBADD 0x00a0
|
||||
#define REG_RXSTSQBLEN 0x00a4
|
||||
#define REG_RXSTSQCURADD 0x00a8
|
||||
#define REG_RXSTSENQ 0x00ac
|
||||
#define REG_TXDQBADD 0x00b0
|
||||
#define REG_TXDQBLEN 0x00b4
|
||||
#define REG_TXDQCURADD 0x00b8
|
||||
#define REG_TXDENQ 0x00bc
|
||||
#define REG_TXSTSQBADD 0x00c0
|
||||
#define REG_TXSTSQBLEN 0x00c4
|
||||
#define REG_TXSTSQCURADD 0x00c8
|
||||
#define REG_MAXFRMLEN 0x00e8
|
||||
|
||||
struct ep93xx_rdesc
|
||||
{
|
||||
u32 buf_addr;
|
||||
u32 rdesc1;
|
||||
};
|
||||
|
||||
#define RDESC1_NSOF 0x80000000
|
||||
#define RDESC1_BUFFER_INDEX 0x7fff0000
|
||||
#define RDESC1_BUFFER_LENGTH 0x0000ffff
|
||||
|
||||
struct ep93xx_rstat
|
||||
{
|
||||
u32 rstat0;
|
||||
u32 rstat1;
|
||||
};
|
||||
|
||||
#define RSTAT0_RFP 0x80000000
|
||||
#define RSTAT0_RWE 0x40000000
|
||||
#define RSTAT0_EOF 0x20000000
|
||||
#define RSTAT0_EOB 0x10000000
|
||||
#define RSTAT0_AM 0x00c00000
|
||||
#define RSTAT0_RX_ERR 0x00200000
|
||||
#define RSTAT0_OE 0x00100000
|
||||
#define RSTAT0_FE 0x00080000
|
||||
#define RSTAT0_RUNT 0x00040000
|
||||
#define RSTAT0_EDATA 0x00020000
|
||||
#define RSTAT0_CRCE 0x00010000
|
||||
#define RSTAT0_CRCI 0x00008000
|
||||
#define RSTAT0_HTI 0x00003f00
|
||||
#define RSTAT1_RFP 0x80000000
|
||||
#define RSTAT1_BUFFER_INDEX 0x7fff0000
|
||||
#define RSTAT1_FRAME_LENGTH 0x0000ffff
|
||||
|
||||
struct ep93xx_tdesc
|
||||
{
|
||||
u32 buf_addr;
|
||||
u32 tdesc1;
|
||||
};
|
||||
|
||||
#define TDESC1_EOF 0x80000000
|
||||
#define TDESC1_BUFFER_INDEX 0x7fff0000
|
||||
#define TDESC1_BUFFER_ABORT 0x00008000
|
||||
#define TDESC1_BUFFER_LENGTH 0x00000fff
|
||||
|
||||
struct ep93xx_tstat
|
||||
{
|
||||
u32 tstat0;
|
||||
};
|
||||
|
||||
#define TSTAT0_TXFP 0x80000000
|
||||
#define TSTAT0_TXWE 0x40000000
|
||||
#define TSTAT0_FA 0x20000000
|
||||
#define TSTAT0_LCRS 0x10000000
|
||||
#define TSTAT0_OW 0x04000000
|
||||
#define TSTAT0_TXU 0x02000000
|
||||
#define TSTAT0_ECOLL 0x01000000
|
||||
#define TSTAT0_NCOLL 0x001f0000
|
||||
#define TSTAT0_BUFFER_INDEX 0x00007fff
|
||||
|
||||
struct ep93xx_descs
|
||||
{
|
||||
struct ep93xx_rdesc rdesc[RX_QUEUE_ENTRIES];
|
||||
struct ep93xx_tdesc tdesc[TX_QUEUE_ENTRIES];
|
||||
struct ep93xx_rstat rstat[RX_QUEUE_ENTRIES];
|
||||
struct ep93xx_tstat tstat[TX_QUEUE_ENTRIES];
|
||||
};
|
||||
|
||||
struct ep93xx_priv
|
||||
{
|
||||
struct resource *res;
|
||||
void *base_addr;
|
||||
int irq;
|
||||
|
||||
struct ep93xx_descs *descs;
|
||||
dma_addr_t descs_dma_addr;
|
||||
|
||||
void *rx_buf[RX_QUEUE_ENTRIES];
|
||||
void *tx_buf[TX_QUEUE_ENTRIES];
|
||||
|
||||
spinlock_t rx_lock;
|
||||
unsigned int rx_pointer;
|
||||
unsigned int tx_clean_pointer;
|
||||
unsigned int tx_pointer;
|
||||
spinlock_t tx_pending_lock;
|
||||
unsigned int tx_pending;
|
||||
|
||||
struct net_device_stats stats;
|
||||
|
||||
struct mii_if_info mii;
|
||||
u8 mdc_divisor;
|
||||
};
|
||||
|
||||
#define rdb(ep, off) __raw_readb((ep)->base_addr + (off))
|
||||
#define rdw(ep, off) __raw_readw((ep)->base_addr + (off))
|
||||
#define rdl(ep, off) __raw_readl((ep)->base_addr + (off))
|
||||
#define wrb(ep, off, val) __raw_writeb((val), (ep)->base_addr + (off))
|
||||
#define wrw(ep, off, val) __raw_writew((val), (ep)->base_addr + (off))
|
||||
#define wrl(ep, off, val) __raw_writel((val), (ep)->base_addr + (off))
|
||||
|
||||
static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg);
|
||||
|
||||
static struct net_device_stats *ep93xx_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
return &(ep->stats);
|
||||
}
|
||||
|
||||
static int ep93xx_rx(struct net_device *dev, int *budget)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int rx_done;
|
||||
int processed;
|
||||
|
||||
rx_done = 0;
|
||||
processed = 0;
|
||||
while (*budget > 0) {
|
||||
int entry;
|
||||
struct ep93xx_rstat *rstat;
|
||||
u32 rstat0;
|
||||
u32 rstat1;
|
||||
int length;
|
||||
struct sk_buff *skb;
|
||||
|
||||
entry = ep->rx_pointer;
|
||||
rstat = ep->descs->rstat + entry;
|
||||
|
||||
rstat0 = rstat->rstat0;
|
||||
rstat1 = rstat->rstat1;
|
||||
if (!(rstat0 & RSTAT0_RFP) || !(rstat1 & RSTAT1_RFP)) {
|
||||
rx_done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
rstat->rstat0 = 0;
|
||||
rstat->rstat1 = 0;
|
||||
|
||||
if (!(rstat0 & RSTAT0_EOF))
|
||||
printk(KERN_CRIT "ep93xx_rx: not end-of-frame "
|
||||
" %.8x %.8x\n", rstat0, rstat1);
|
||||
if (!(rstat0 & RSTAT0_EOB))
|
||||
printk(KERN_CRIT "ep93xx_rx: not end-of-buffer "
|
||||
" %.8x %.8x\n", rstat0, rstat1);
|
||||
if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry)
|
||||
printk(KERN_CRIT "ep93xx_rx: entry mismatch "
|
||||
" %.8x %.8x\n", rstat0, rstat1);
|
||||
|
||||
if (!(rstat0 & RSTAT0_RWE)) {
|
||||
ep->stats.rx_errors++;
|
||||
if (rstat0 & RSTAT0_OE)
|
||||
ep->stats.rx_fifo_errors++;
|
||||
if (rstat0 & RSTAT0_FE)
|
||||
ep->stats.rx_frame_errors++;
|
||||
if (rstat0 & (RSTAT0_RUNT | RSTAT0_EDATA))
|
||||
ep->stats.rx_length_errors++;
|
||||
if (rstat0 & RSTAT0_CRCE)
|
||||
ep->stats.rx_crc_errors++;
|
||||
goto err;
|
||||
}
|
||||
|
||||
length = rstat1 & RSTAT1_FRAME_LENGTH;
|
||||
if (length > MAX_PKT_SIZE) {
|
||||
printk(KERN_NOTICE "ep93xx_rx: invalid length "
|
||||
" %.8x %.8x\n", rstat0, rstat1);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Strip FCS. */
|
||||
if (rstat0 & RSTAT0_CRCI)
|
||||
length -= 4;
|
||||
|
||||
skb = dev_alloc_skb(length + 2);
|
||||
if (likely(skb != NULL)) {
|
||||
skb->dev = dev;
|
||||
skb_reserve(skb, 2);
|
||||
dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr,
|
||||
length, DMA_FROM_DEVICE);
|
||||
eth_copy_and_sum(skb, ep->rx_buf[entry], length, 0);
|
||||
skb_put(skb, length);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
dev->last_rx = jiffies;
|
||||
|
||||
netif_receive_skb(skb);
|
||||
|
||||
ep->stats.rx_packets++;
|
||||
ep->stats.rx_bytes += length;
|
||||
} else {
|
||||
ep->stats.rx_dropped++;
|
||||
}
|
||||
|
||||
err:
|
||||
ep->rx_pointer = (entry + 1) & (RX_QUEUE_ENTRIES - 1);
|
||||
processed++;
|
||||
dev->quota--;
|
||||
(*budget)--;
|
||||
}
|
||||
|
||||
if (processed) {
|
||||
wrw(ep, REG_RXDENQ, processed);
|
||||
wrw(ep, REG_RXSTSENQ, processed);
|
||||
}
|
||||
|
||||
return !rx_done;
|
||||
}
|
||||
|
||||
static int ep93xx_have_more_rx(struct ep93xx_priv *ep)
|
||||
{
|
||||
struct ep93xx_rstat *rstat = ep->descs->rstat + ep->rx_pointer;
|
||||
return !!((rstat->rstat0 & RSTAT0_RFP) && (rstat->rstat1 & RSTAT1_RFP));
|
||||
}
|
||||
|
||||
static int ep93xx_poll(struct net_device *dev, int *budget)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
|
||||
/*
|
||||
* @@@ Have to stop polling if device is downed while we
|
||||
* are polling.
|
||||
*/
|
||||
|
||||
poll_some_more:
|
||||
if (ep93xx_rx(dev, budget))
|
||||
return 1;
|
||||
|
||||
netif_rx_complete(dev);
|
||||
|
||||
spin_lock_irq(&ep->rx_lock);
|
||||
wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);
|
||||
if (ep93xx_have_more_rx(ep)) {
|
||||
wrl(ep, REG_INTEN, REG_INTEN_TX);
|
||||
wrl(ep, REG_INTSTSP, REG_INTSTS_RX);
|
||||
spin_unlock_irq(&ep->rx_lock);
|
||||
|
||||
if (netif_rx_reschedule(dev, 0))
|
||||
goto poll_some_more;
|
||||
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irq(&ep->rx_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int entry;
|
||||
|
||||
if (unlikely(skb->len > MAX_PKT_SIZE)) {
|
||||
ep->stats.tx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
entry = ep->tx_pointer;
|
||||
ep->tx_pointer = (ep->tx_pointer + 1) & (TX_QUEUE_ENTRIES - 1);
|
||||
|
||||
ep->descs->tdesc[entry].tdesc1 =
|
||||
TDESC1_EOF | (entry << 16) | (skb->len & 0xfff);
|
||||
skb_copy_and_csum_dev(skb, ep->tx_buf[entry]);
|
||||
dma_sync_single(NULL, ep->descs->tdesc[entry].buf_addr,
|
||||
skb->len, DMA_TO_DEVICE);
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
spin_lock_irq(&ep->tx_pending_lock);
|
||||
ep->tx_pending++;
|
||||
if (ep->tx_pending == TX_QUEUE_ENTRIES)
|
||||
netif_stop_queue(dev);
|
||||
spin_unlock_irq(&ep->tx_pending_lock);
|
||||
|
||||
wrl(ep, REG_TXDENQ, 1);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static void ep93xx_tx_complete(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int wake;
|
||||
|
||||
wake = 0;
|
||||
|
||||
spin_lock(&ep->tx_pending_lock);
|
||||
while (1) {
|
||||
int entry;
|
||||
struct ep93xx_tstat *tstat;
|
||||
u32 tstat0;
|
||||
|
||||
entry = ep->tx_clean_pointer;
|
||||
tstat = ep->descs->tstat + entry;
|
||||
|
||||
tstat0 = tstat->tstat0;
|
||||
if (!(tstat0 & TSTAT0_TXFP))
|
||||
break;
|
||||
|
||||
tstat->tstat0 = 0;
|
||||
|
||||
if (tstat0 & TSTAT0_FA)
|
||||
printk(KERN_CRIT "ep93xx_tx_complete: frame aborted "
|
||||
" %.8x\n", tstat0);
|
||||
if ((tstat0 & TSTAT0_BUFFER_INDEX) != entry)
|
||||
printk(KERN_CRIT "ep93xx_tx_complete: entry mismatch "
|
||||
" %.8x\n", tstat0);
|
||||
|
||||
if (tstat0 & TSTAT0_TXWE) {
|
||||
int length = ep->descs->tdesc[entry].tdesc1 & 0xfff;
|
||||
|
||||
ep->stats.tx_packets++;
|
||||
ep->stats.tx_bytes += length;
|
||||
} else {
|
||||
ep->stats.tx_errors++;
|
||||
}
|
||||
|
||||
if (tstat0 & TSTAT0_OW)
|
||||
ep->stats.tx_window_errors++;
|
||||
if (tstat0 & TSTAT0_TXU)
|
||||
ep->stats.tx_fifo_errors++;
|
||||
ep->stats.collisions += (tstat0 >> 16) & 0x1f;
|
||||
|
||||
ep->tx_clean_pointer = (entry + 1) & (TX_QUEUE_ENTRIES - 1);
|
||||
if (ep->tx_pending == TX_QUEUE_ENTRIES)
|
||||
wake = 1;
|
||||
ep->tx_pending--;
|
||||
}
|
||||
spin_unlock(&ep->tx_pending_lock);
|
||||
|
||||
if (wake)
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static irqreturn_t ep93xx_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
u32 status;
|
||||
|
||||
status = rdl(ep, REG_INTSTSC);
|
||||
if (status == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (status & REG_INTSTS_RX) {
|
||||
spin_lock(&ep->rx_lock);
|
||||
if (likely(__netif_rx_schedule_prep(dev))) {
|
||||
wrl(ep, REG_INTEN, REG_INTEN_TX);
|
||||
__netif_rx_schedule(dev);
|
||||
}
|
||||
spin_unlock(&ep->rx_lock);
|
||||
}
|
||||
|
||||
if (status & REG_INTSTS_TX)
|
||||
ep93xx_tx_complete(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ep93xx_free_buffers(struct ep93xx_priv *ep)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
|
||||
dma_addr_t d;
|
||||
|
||||
d = ep->descs->rdesc[i].buf_addr;
|
||||
if (d)
|
||||
dma_unmap_single(NULL, d, PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
if (ep->rx_buf[i] != NULL)
|
||||
free_page((unsigned long)ep->rx_buf[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) {
|
||||
dma_addr_t d;
|
||||
|
||||
d = ep->descs->tdesc[i].buf_addr;
|
||||
if (d)
|
||||
dma_unmap_single(NULL, d, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
|
||||
if (ep->tx_buf[i] != NULL)
|
||||
free_page((unsigned long)ep->tx_buf[i]);
|
||||
}
|
||||
|
||||
dma_free_coherent(NULL, sizeof(struct ep93xx_descs), ep->descs,
|
||||
ep->descs_dma_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware enforces a sub-2K maximum packet size, so we put
|
||||
* two buffers on every hardware page.
|
||||
*/
|
||||
static int ep93xx_alloc_buffers(struct ep93xx_priv *ep)
|
||||
{
|
||||
int i;
|
||||
|
||||
ep->descs = dma_alloc_coherent(NULL, sizeof(struct ep93xx_descs),
|
||||
&ep->descs_dma_addr, GFP_KERNEL | GFP_DMA);
|
||||
if (ep->descs == NULL)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
|
||||
void *page;
|
||||
dma_addr_t d;
|
||||
|
||||
page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||
if (page == NULL)
|
||||
goto err;
|
||||
|
||||
d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(d)) {
|
||||
free_page((unsigned long)page);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ep->rx_buf[i] = page;
|
||||
ep->descs->rdesc[i].buf_addr = d;
|
||||
ep->descs->rdesc[i].rdesc1 = (i << 16) | PKT_BUF_SIZE;
|
||||
|
||||
ep->rx_buf[i + 1] = page + PKT_BUF_SIZE;
|
||||
ep->descs->rdesc[i + 1].buf_addr = d + PKT_BUF_SIZE;
|
||||
ep->descs->rdesc[i + 1].rdesc1 = ((i + 1) << 16) | PKT_BUF_SIZE;
|
||||
}
|
||||
|
||||
for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) {
|
||||
void *page;
|
||||
dma_addr_t d;
|
||||
|
||||
page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||
if (page == NULL)
|
||||
goto err;
|
||||
|
||||
d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(d)) {
|
||||
free_page((unsigned long)page);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ep->tx_buf[i] = page;
|
||||
ep->descs->tdesc[i].buf_addr = d;
|
||||
|
||||
ep->tx_buf[i + 1] = page + PKT_BUF_SIZE;
|
||||
ep->descs->tdesc[i + 1].buf_addr = d + PKT_BUF_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ep93xx_free_buffers(ep);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ep93xx_start_hw(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
unsigned long addr;
|
||||
int i;
|
||||
|
||||
wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (i == 10) {
|
||||
printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to reset\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9));
|
||||
|
||||
/* Does the PHY support preamble suppress? */
|
||||
if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0)
|
||||
wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8));
|
||||
|
||||
/* Receive descriptor ring. */
|
||||
addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc);
|
||||
wrl(ep, REG_RXDQBADD, addr);
|
||||
wrl(ep, REG_RXDCURADD, addr);
|
||||
wrw(ep, REG_RXDQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rdesc));
|
||||
|
||||
/* Receive status ring. */
|
||||
addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rstat);
|
||||
wrl(ep, REG_RXSTSQBADD, addr);
|
||||
wrl(ep, REG_RXSTSQCURADD, addr);
|
||||
wrw(ep, REG_RXSTSQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rstat));
|
||||
|
||||
/* Transmit descriptor ring. */
|
||||
addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tdesc);
|
||||
wrl(ep, REG_TXDQBADD, addr);
|
||||
wrl(ep, REG_TXDQCURADD, addr);
|
||||
wrw(ep, REG_TXDQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tdesc));
|
||||
|
||||
/* Transmit status ring. */
|
||||
addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tstat);
|
||||
wrl(ep, REG_TXSTSQBADD, addr);
|
||||
wrl(ep, REG_TXSTSQCURADD, addr);
|
||||
wrw(ep, REG_TXSTSQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tstat));
|
||||
|
||||
wrl(ep, REG_BMCTL, REG_BMCTL_ENABLE_TX | REG_BMCTL_ENABLE_RX);
|
||||
wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);
|
||||
wrl(ep, REG_GIINTMSK, 0);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((rdl(ep, REG_BMSTS) & REG_BMSTS_RX_ACTIVE) != 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (i == 10) {
|
||||
printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to start\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wrl(ep, REG_RXDENQ, RX_QUEUE_ENTRIES);
|
||||
wrl(ep, REG_RXSTSENQ, RX_QUEUE_ENTRIES);
|
||||
|
||||
wrb(ep, REG_INDAD0, dev->dev_addr[0]);
|
||||
wrb(ep, REG_INDAD1, dev->dev_addr[1]);
|
||||
wrb(ep, REG_INDAD2, dev->dev_addr[2]);
|
||||
wrb(ep, REG_INDAD3, dev->dev_addr[3]);
|
||||
wrb(ep, REG_INDAD4, dev->dev_addr[4]);
|
||||
wrb(ep, REG_INDAD5, dev->dev_addr[5]);
|
||||
wrl(ep, REG_AFP, 0);
|
||||
|
||||
wrl(ep, REG_MAXFRMLEN, (MAX_PKT_SIZE << 16) | MAX_PKT_SIZE);
|
||||
|
||||
wrl(ep, REG_RXCTL, REG_RXCTL_DEFAULT);
|
||||
wrl(ep, REG_TXCTL, REG_TXCTL_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ep93xx_stop_hw(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (i == 10)
|
||||
printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to reset\n");
|
||||
}
|
||||
|
||||
static int ep93xx_open(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
if (ep93xx_alloc_buffers(ep))
|
||||
return -ENOMEM;
|
||||
|
||||
if (is_zero_ether_addr(dev->dev_addr)) {
|
||||
random_ether_addr(dev->dev_addr);
|
||||
printk(KERN_INFO "%s: generated random MAC address "
|
||||
"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,
|
||||
dev->dev_addr[0], dev->dev_addr[1],
|
||||
dev->dev_addr[2], dev->dev_addr[3],
|
||||
dev->dev_addr[4], dev->dev_addr[5]);
|
||||
}
|
||||
|
||||
if (ep93xx_start_hw(dev)) {
|
||||
ep93xx_free_buffers(ep);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_init(&ep->rx_lock);
|
||||
ep->rx_pointer = 0;
|
||||
ep->tx_clean_pointer = 0;
|
||||
ep->tx_pointer = 0;
|
||||
spin_lock_init(&ep->tx_pending_lock);
|
||||
ep->tx_pending = 0;
|
||||
|
||||
err = request_irq(ep->irq, ep93xx_irq, IRQF_SHARED, dev->name, dev);
|
||||
if (err) {
|
||||
ep93xx_stop_hw(dev);
|
||||
ep93xx_free_buffers(ep);
|
||||
return err;
|
||||
}
|
||||
|
||||
wrl(ep, REG_GIINTMSK, REG_GIINTMSK_ENABLE);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_close(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
wrl(ep, REG_GIINTMSK, 0);
|
||||
free_irq(ep->irq, dev);
|
||||
ep93xx_stop_hw(dev);
|
||||
ep93xx_free_buffers(ep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
struct mii_ioctl_data *data = if_mii(ifr);
|
||||
|
||||
return generic_mii_ioctl(&ep->mii, data, cmd, NULL);
|
||||
}
|
||||
|
||||
static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int data;
|
||||
int i;
|
||||
|
||||
wrl(ep, REG_MIICMD, REG_MIICMD_READ | (phy_id << 5) | reg);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (i == 10) {
|
||||
printk(KERN_INFO DRV_MODULE_NAME ": mdio read timed out\n");
|
||||
data = 0xffff;
|
||||
} else {
|
||||
data = rdl(ep, REG_MIIDATA);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
wrl(ep, REG_MIIDATA, data);
|
||||
wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (phy_id << 5) | reg);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (i == 10)
|
||||
printk(KERN_INFO DRV_MODULE_NAME ": mdio write timed out\n");
|
||||
}
|
||||
|
||||
static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
strcpy(info->driver, DRV_MODULE_NAME);
|
||||
strcpy(info->version, DRV_MODULE_VERSION);
|
||||
}
|
||||
|
||||
static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
return mii_ethtool_gset(&ep->mii, cmd);
|
||||
}
|
||||
|
||||
static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
return mii_ethtool_sset(&ep->mii, cmd);
|
||||
}
|
||||
|
||||
static int ep93xx_nway_reset(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
return mii_nway_restart(&ep->mii);
|
||||
}
|
||||
|
||||
static u32 ep93xx_get_link(struct net_device *dev)
|
||||
{
|
||||
struct ep93xx_priv *ep = netdev_priv(dev);
|
||||
return mii_link_ok(&ep->mii);
|
||||
}
|
||||
|
||||
static struct ethtool_ops ep93xx_ethtool_ops = {
|
||||
.get_drvinfo = ep93xx_get_drvinfo,
|
||||
.get_settings = ep93xx_get_settings,
|
||||
.set_settings = ep93xx_set_settings,
|
||||
.nway_reset = ep93xx_nway_reset,
|
||||
.get_link = ep93xx_get_link,
|
||||
};
|
||||
|
||||
struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data)
|
||||
{
|
||||
struct net_device *dev;
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct ep93xx_priv));
|
||||
if (dev == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(dev->dev_addr, data->dev_addr, ETH_ALEN);
|
||||
|
||||
dev->get_stats = ep93xx_get_stats;
|
||||
dev->ethtool_ops = &ep93xx_ethtool_ops;
|
||||
dev->poll = ep93xx_poll;
|
||||
dev->hard_start_xmit = ep93xx_xmit;
|
||||
dev->open = ep93xx_open;
|
||||
dev->stop = ep93xx_close;
|
||||
dev->do_ioctl = ep93xx_ioctl;
|
||||
|
||||
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
|
||||
dev->weight = 64;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
static int ep93xx_eth_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct ep93xx_priv *ep;
|
||||
|
||||
dev = platform_get_drvdata(pdev);
|
||||
if (dev == NULL)
|
||||
return 0;
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
ep = netdev_priv(dev);
|
||||
|
||||
/* @@@ Force down. */
|
||||
unregister_netdev(dev);
|
||||
ep93xx_free_buffers(ep);
|
||||
|
||||
if (ep->base_addr != NULL)
|
||||
iounmap(ep->base_addr);
|
||||
|
||||
if (ep->res != NULL) {
|
||||
release_resource(ep->res);
|
||||
kfree(ep->res);
|
||||
}
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_eth_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_eth_data *data;
|
||||
struct net_device *dev;
|
||||
struct ep93xx_priv *ep;
|
||||
int err;
|
||||
|
||||
if (pdev == NULL)
|
||||
return -ENODEV;
|
||||
data = pdev->dev.platform_data;
|
||||
|
||||
dev = ep93xx_dev_alloc(data);
|
||||
if (dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
ep = netdev_priv(dev);
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
ep->res = request_mem_region(pdev->resource[0].start,
|
||||
pdev->resource[0].end - pdev->resource[0].start + 1,
|
||||
pdev->dev.bus_id);
|
||||
if (ep->res == NULL) {
|
||||
dev_err(&pdev->dev, "Could not reserve memory region\n");
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ep->base_addr = ioremap(pdev->resource[0].start,
|
||||
pdev->resource[0].end - pdev->resource[0].start);
|
||||
if (ep->base_addr == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
ep->irq = pdev->resource[1].start;
|
||||
|
||||
ep->mii.phy_id = data->phy_id;
|
||||
ep->mii.phy_id_mask = 0x1f;
|
||||
ep->mii.reg_num_mask = 0x1f;
|
||||
ep->mii.dev = dev;
|
||||
ep->mii.mdio_read = ep93xx_mdio_read;
|
||||
ep->mii.mdio_write = ep93xx_mdio_write;
|
||||
ep->mdc_divisor = 40; /* Max HCLK 100 MHz, min MDIO clk 2.5 MHz. */
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to register netdev\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: ep93xx on-chip ethernet, IRQ %d, "
|
||||
"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,
|
||||
ep->irq, data->dev_addr[0], data->dev_addr[1],
|
||||
data->dev_addr[2], data->dev_addr[3],
|
||||
data->dev_addr[4], data->dev_addr[5]);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
ep93xx_eth_remove(pdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static struct platform_driver ep93xx_eth_driver = {
|
||||
.probe = ep93xx_eth_probe,
|
||||
.remove = ep93xx_eth_remove,
|
||||
.driver = {
|
||||
.name = "ep93xx-eth",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ep93xx_eth_init_module(void)
|
||||
{
|
||||
printk(KERN_INFO DRV_MODULE_NAME " version " DRV_MODULE_VERSION " loading\n");
|
||||
return platform_driver_register(&ep93xx_eth_driver);
|
||||
}
|
||||
|
||||
static void __exit ep93xx_eth_cleanup_module(void)
|
||||
{
|
||||
platform_driver_unregister(&ep93xx_eth_driver);
|
||||
}
|
||||
|
||||
module_init(ep93xx_eth_init_module);
|
||||
module_exit(ep93xx_eth_cleanup_module);
|
||||
MODULE_LICENSE("GPL");
|
||||
1108
drivers/net/arm/ether1.c
Normal file
1108
drivers/net/arm/ether1.c
Normal file
File diff suppressed because it is too large
Load Diff
281
drivers/net/arm/ether1.h
Normal file
281
drivers/net/arm/ether1.h
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* linux/drivers/acorn/net/ether1.h
|
||||
*
|
||||
* Copyright (C) 1996 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Network driver for Acorn Ether1 cards.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ether1_H
|
||||
#define _LINUX_ether1_H
|
||||
|
||||
#ifdef __ETHER1_C
|
||||
/* use 0 for production, 1 for verification, >2 for debug */
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define priv(dev) ((struct ether1_priv *)netdev_priv(dev))
|
||||
|
||||
/* Page register */
|
||||
#define REG_PAGE (priv(dev)->base + 0x0000)
|
||||
|
||||
/* Control register */
|
||||
#define REG_CONTROL (priv(dev)->base + 0x0004)
|
||||
#define CTRL_RST 0x01
|
||||
#define CTRL_LOOPBACK 0x02
|
||||
#define CTRL_CA 0x04
|
||||
#define CTRL_ACK 0x08
|
||||
|
||||
#define ETHER1_RAM (priv(dev)->base + 0x2000)
|
||||
|
||||
/* HW address */
|
||||
#define IDPROM_ADDRESS (priv(dev)->base + 0x0024)
|
||||
|
||||
struct ether1_priv {
|
||||
void __iomem *base;
|
||||
struct net_device_stats stats;
|
||||
unsigned int tx_link;
|
||||
unsigned int tx_head;
|
||||
volatile unsigned int tx_tail;
|
||||
volatile unsigned int rx_head;
|
||||
volatile unsigned int rx_tail;
|
||||
unsigned char bus_type;
|
||||
unsigned char resetting;
|
||||
unsigned char initialising : 1;
|
||||
unsigned char restart : 1;
|
||||
};
|
||||
|
||||
#define I82586_NULL (-1)
|
||||
|
||||
typedef struct { /* tdr */
|
||||
unsigned short tdr_status;
|
||||
unsigned short tdr_command;
|
||||
unsigned short tdr_link;
|
||||
unsigned short tdr_result;
|
||||
#define TDR_TIME (0x7ff)
|
||||
#define TDR_SHORT (1 << 12)
|
||||
#define TDR_OPEN (1 << 13)
|
||||
#define TDR_XCVRPROB (1 << 14)
|
||||
#define TDR_LNKOK (1 << 15)
|
||||
} tdr_t;
|
||||
|
||||
typedef struct { /* transmit */
|
||||
unsigned short tx_status;
|
||||
unsigned short tx_command;
|
||||
unsigned short tx_link;
|
||||
unsigned short tx_tbdoffset;
|
||||
} tx_t;
|
||||
|
||||
typedef struct { /* tbd */
|
||||
unsigned short tbd_opts;
|
||||
#define TBD_CNT (0x3fff)
|
||||
#define TBD_EOL (1 << 15)
|
||||
unsigned short tbd_link;
|
||||
unsigned short tbd_bufl;
|
||||
unsigned short tbd_bufh;
|
||||
} tbd_t;
|
||||
|
||||
typedef struct { /* rfd */
|
||||
unsigned short rfd_status;
|
||||
#define RFD_NOEOF (1 << 6)
|
||||
#define RFD_FRAMESHORT (1 << 7)
|
||||
#define RFD_DMAOVRN (1 << 8)
|
||||
#define RFD_NORESOURCES (1 << 9)
|
||||
#define RFD_ALIGNERROR (1 << 10)
|
||||
#define RFD_CRCERROR (1 << 11)
|
||||
#define RFD_OK (1 << 13)
|
||||
#define RFD_FDCONSUMED (1 << 14)
|
||||
#define RFD_COMPLETE (1 << 15)
|
||||
unsigned short rfd_command;
|
||||
#define RFD_CMDSUSPEND (1 << 14)
|
||||
#define RFD_CMDEL (1 << 15)
|
||||
unsigned short rfd_link;
|
||||
unsigned short rfd_rbdoffset;
|
||||
unsigned char rfd_dest[6];
|
||||
unsigned char rfd_src[6];
|
||||
unsigned short rfd_len;
|
||||
} rfd_t;
|
||||
|
||||
typedef struct { /* rbd */
|
||||
unsigned short rbd_status;
|
||||
#define RBD_ACNT (0x3fff)
|
||||
#define RBD_ACNTVALID (1 << 14)
|
||||
#define RBD_EOF (1 << 15)
|
||||
unsigned short rbd_link;
|
||||
unsigned short rbd_bufl;
|
||||
unsigned short rbd_bufh;
|
||||
unsigned short rbd_len;
|
||||
} rbd_t;
|
||||
|
||||
typedef struct { /* nop */
|
||||
unsigned short nop_status;
|
||||
unsigned short nop_command;
|
||||
unsigned short nop_link;
|
||||
} nop_t;
|
||||
|
||||
typedef struct { /* set multicast */
|
||||
unsigned short mc_status;
|
||||
unsigned short mc_command;
|
||||
unsigned short mc_link;
|
||||
unsigned short mc_cnt;
|
||||
unsigned char mc_addrs[1][6];
|
||||
} mc_t;
|
||||
|
||||
typedef struct { /* set address */
|
||||
unsigned short sa_status;
|
||||
unsigned short sa_command;
|
||||
unsigned short sa_link;
|
||||
unsigned char sa_addr[6];
|
||||
} sa_t;
|
||||
|
||||
typedef struct { /* config command */
|
||||
unsigned short cfg_status;
|
||||
unsigned short cfg_command;
|
||||
unsigned short cfg_link;
|
||||
unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */
|
||||
unsigned char cfg_fifolim; /* FIFO threshold */
|
||||
unsigned char cfg_byte8;
|
||||
#define CFG8_SRDY (1 << 6)
|
||||
#define CFG8_SAVEBADF (1 << 7)
|
||||
unsigned char cfg_byte9;
|
||||
#define CFG9_ADDRLEN(x) (x)
|
||||
#define CFG9_ADDRLENBUF (1 << 3)
|
||||
#define CFG9_PREAMB2 (0 << 4)
|
||||
#define CFG9_PREAMB4 (1 << 4)
|
||||
#define CFG9_PREAMB8 (2 << 4)
|
||||
#define CFG9_PREAMB16 (3 << 4)
|
||||
#define CFG9_ILOOPBACK (1 << 6)
|
||||
#define CFG9_ELOOPBACK (1 << 7)
|
||||
unsigned char cfg_byte10;
|
||||
#define CFG10_LINPRI(x) (x)
|
||||
#define CFG10_ACR(x) (x << 4)
|
||||
#define CFG10_BOFMET (1 << 7)
|
||||
unsigned char cfg_ifs;
|
||||
unsigned char cfg_slotl;
|
||||
unsigned char cfg_byte13;
|
||||
#define CFG13_SLOTH(x) (x)
|
||||
#define CFG13_RETRY(x) (x << 4)
|
||||
unsigned char cfg_byte14;
|
||||
#define CFG14_PROMISC (1 << 0)
|
||||
#define CFG14_DISBRD (1 << 1)
|
||||
#define CFG14_MANCH (1 << 2)
|
||||
#define CFG14_TNCRS (1 << 3)
|
||||
#define CFG14_NOCRC (1 << 4)
|
||||
#define CFG14_CRC16 (1 << 5)
|
||||
#define CFG14_BTSTF (1 << 6)
|
||||
#define CFG14_FLGPAD (1 << 7)
|
||||
unsigned char cfg_byte15;
|
||||
#define CFG15_CSTF(x) (x)
|
||||
#define CFG15_ICSS (1 << 3)
|
||||
#define CFG15_CDTF(x) (x << 4)
|
||||
#define CFG15_ICDS (1 << 7)
|
||||
unsigned short cfg_minfrmlen;
|
||||
} cfg_t;
|
||||
|
||||
typedef struct { /* scb */
|
||||
unsigned short scb_status; /* status of 82586 */
|
||||
#define SCB_STRXMASK (7 << 4) /* Receive unit status */
|
||||
#define SCB_STRXIDLE (0 << 4) /* Idle */
|
||||
#define SCB_STRXSUSP (1 << 4) /* Suspended */
|
||||
#define SCB_STRXNRES (2 << 4) /* No resources */
|
||||
#define SCB_STRXRDY (4 << 4) /* Ready */
|
||||
#define SCB_STCUMASK (7 << 8) /* Command unit status */
|
||||
#define SCB_STCUIDLE (0 << 8) /* Idle */
|
||||
#define SCB_STCUSUSP (1 << 8) /* Suspended */
|
||||
#define SCB_STCUACTV (2 << 8) /* Active */
|
||||
#define SCB_STRNR (1 << 12) /* Receive unit not ready */
|
||||
#define SCB_STCNA (1 << 13) /* Command unit not ready */
|
||||
#define SCB_STFR (1 << 14) /* Frame received */
|
||||
#define SCB_STCX (1 << 15) /* Command completed */
|
||||
unsigned short scb_command; /* Next command */
|
||||
#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */
|
||||
#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */
|
||||
#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */
|
||||
#define SCB_CMDRXABORT (4 << 4) /* Abort reception */
|
||||
#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */
|
||||
#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */
|
||||
#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */
|
||||
#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */
|
||||
#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */
|
||||
#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */
|
||||
#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */
|
||||
#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */
|
||||
unsigned short scb_cbl_offset; /* Offset of first command unit */
|
||||
unsigned short scb_rfa_offset; /* Offset of first receive frame area */
|
||||
unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/
|
||||
unsigned short scb_aln_errors; /* Misaligned frames */
|
||||
unsigned short scb_rsc_errors; /* Frames lost due to no space */
|
||||
unsigned short scb_ovn_errors; /* Frames lost due to slow bus */
|
||||
} scb_t;
|
||||
|
||||
typedef struct { /* iscp */
|
||||
unsigned short iscp_busy; /* set by CPU before CA */
|
||||
unsigned short iscp_offset; /* offset of SCB */
|
||||
unsigned short iscp_basel; /* base of SCB */
|
||||
unsigned short iscp_baseh;
|
||||
} iscp_t;
|
||||
|
||||
/* this address must be 0xfff6 */
|
||||
typedef struct { /* scp */
|
||||
unsigned short scp_sysbus; /* bus size */
|
||||
#define SCP_SY_16BBUS 0x00
|
||||
#define SCP_SY_8BBUS 0x01
|
||||
unsigned short scp_junk[2]; /* junk */
|
||||
unsigned short scp_iscpl; /* lower 16 bits of iscp */
|
||||
unsigned short scp_iscph; /* upper 16 bits of iscp */
|
||||
} scp_t;
|
||||
|
||||
/* commands */
|
||||
#define CMD_NOP 0
|
||||
#define CMD_SETADDRESS 1
|
||||
#define CMD_CONFIG 2
|
||||
#define CMD_SETMULTICAST 3
|
||||
#define CMD_TX 4
|
||||
#define CMD_TDR 5
|
||||
#define CMD_DUMP 6
|
||||
#define CMD_DIAGNOSE 7
|
||||
|
||||
#define CMD_MASK 7
|
||||
|
||||
#define CMD_INTR (1 << 13)
|
||||
#define CMD_SUSP (1 << 14)
|
||||
#define CMD_EOL (1 << 15)
|
||||
|
||||
#define STAT_COLLISIONS (15)
|
||||
#define STAT_COLLEXCESSIVE (1 << 5)
|
||||
#define STAT_COLLAFTERTX (1 << 6)
|
||||
#define STAT_TXDEFERRED (1 << 7)
|
||||
#define STAT_TXSLOWDMA (1 << 8)
|
||||
#define STAT_TXLOSTCTS (1 << 9)
|
||||
#define STAT_NOCARRIER (1 << 10)
|
||||
#define STAT_FAIL (1 << 11)
|
||||
#define STAT_ABORTED (1 << 12)
|
||||
#define STAT_OK (1 << 13)
|
||||
#define STAT_BUSY (1 << 14)
|
||||
#define STAT_COMPLETE (1 << 15)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ether1 card definitions:
|
||||
*
|
||||
* FAST accesses:
|
||||
* +0 Page register
|
||||
* 16 pages
|
||||
* +4 Control
|
||||
* '1' = reset
|
||||
* '2' = loopback
|
||||
* '4' = CA
|
||||
* '8' = int ack
|
||||
*
|
||||
* RAM at address + 0x2000
|
||||
* Pod. Prod id = 3
|
||||
* Words after ID block [base + 8 words]
|
||||
* +0 pcb issue (0x0c and 0xf3 invalid)
|
||||
* +1 - +6 eth hw address
|
||||
*/
|
||||
933
drivers/net/arm/ether3.c
Normal file
933
drivers/net/arm/ether3.c
Normal file
@@ -0,0 +1,933 @@
|
||||
/*
|
||||
* linux/drivers/acorn/net/ether3.c
|
||||
*
|
||||
* Copyright (C) 1995-2000 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card
|
||||
* for Acorn machines
|
||||
*
|
||||
* By Russell King, with some suggestions from borris@ant.co.uk
|
||||
*
|
||||
* Changelog:
|
||||
* 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet
|
||||
* address up to the higher levels - they're
|
||||
* silently ignored. I/F can now be put into
|
||||
* multicast mode. Receiver routine optimised.
|
||||
* 1.05 RMK 30/02/1996 Now claims interrupt at open when part of
|
||||
* the kernel rather than when a module.
|
||||
* 1.06 RMK 02/03/1996 Various code cleanups
|
||||
* 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit
|
||||
* routines.
|
||||
* 1.08 RMK 14/10/1996 Fixed problem with too many packets,
|
||||
* prevented the kernel message about dropped
|
||||
* packets appearing too many times a second.
|
||||
* Now does not disable all IRQs, only the IRQ
|
||||
* used by this card.
|
||||
* 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low,
|
||||
* but we still service the TX queue if we get a
|
||||
* RX interrupt.
|
||||
* 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004.
|
||||
* 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A.
|
||||
* 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1.
|
||||
* RMK 27/06/1998 Changed asm/delay.h to linux/delay.h.
|
||||
* 1.13 RMK 29/06/1998 Fixed problem with transmission of packets.
|
||||
* Chip seems to have a bug in, whereby if the
|
||||
* packet starts two bytes from the end of the
|
||||
* buffer, it corrupts the receiver chain, and
|
||||
* never updates the transmit status correctly.
|
||||
* 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing.
|
||||
* 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy
|
||||
* hardware.
|
||||
* 1.16 RMK 10/02/2000 Updated for 2.3.43
|
||||
* 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8
|
||||
*/
|
||||
|
||||
#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/in.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/ecard.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n";
|
||||
|
||||
#include "ether3.h"
|
||||
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
|
||||
static void ether3_setmulticastlist(struct net_device *dev);
|
||||
static int ether3_rx(struct net_device *dev, unsigned int maxcnt);
|
||||
static void ether3_tx(struct net_device *dev);
|
||||
static int ether3_open (struct net_device *dev);
|
||||
static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev);
|
||||
static irqreturn_t ether3_interrupt (int irq, void *dev_id);
|
||||
static int ether3_close (struct net_device *dev);
|
||||
static struct net_device_stats *ether3_getstats (struct net_device *dev);
|
||||
static void ether3_setmulticastlist (struct net_device *dev);
|
||||
static void ether3_timeout(struct net_device *dev);
|
||||
|
||||
#define BUS_16 2
|
||||
#define BUS_8 1
|
||||
#define BUS_UNKNOWN 0
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
typedef enum {
|
||||
buffer_write,
|
||||
buffer_read
|
||||
} buffer_rw_t;
|
||||
|
||||
/*
|
||||
* ether3 read/write. Slow things down a bit...
|
||||
* The SEEQ8005 doesn't like us writing to its registers
|
||||
* too quickly.
|
||||
*/
|
||||
static inline void ether3_outb(int v, const void __iomem *r)
|
||||
{
|
||||
writeb(v, r);
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static inline void ether3_outw(int v, const void __iomem *r)
|
||||
{
|
||||
writew(v, r);
|
||||
udelay(1);
|
||||
}
|
||||
#define ether3_inb(r) ({ unsigned int __v = readb((r)); udelay(1); __v; })
|
||||
#define ether3_inw(r) ({ unsigned int __v = readw((r)); udelay(1); __v; })
|
||||
|
||||
static int
|
||||
ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start)
|
||||
{
|
||||
int timeout = 1000;
|
||||
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
|
||||
ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND);
|
||||
|
||||
while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) {
|
||||
if (!timeout--) {
|
||||
printk("%s: setbuffer broken\n", dev->name);
|
||||
priv(dev)->broken = 1;
|
||||
return 1;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
if (read == buffer_read) {
|
||||
ether3_outw(start, REG_DMAADDR);
|
||||
ether3_outw(priv(dev)->regs.command | CMD_FIFOREAD, REG_COMMAND);
|
||||
} else {
|
||||
ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND);
|
||||
ether3_outw(start, REG_DMAADDR);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* write data to the buffer memory
|
||||
*/
|
||||
#define ether3_writebuffer(dev,data,length) \
|
||||
writesw(REG_BUFWIN, (data), (length) >> 1)
|
||||
|
||||
#define ether3_writeword(dev,data) \
|
||||
writew((data), REG_BUFWIN)
|
||||
|
||||
#define ether3_writelong(dev,data) { \
|
||||
void __iomem *reg_bufwin = REG_BUFWIN; \
|
||||
writew((data), reg_bufwin); \
|
||||
writew((data) >> 16, reg_bufwin); \
|
||||
}
|
||||
|
||||
/*
|
||||
* read data from the buffer memory
|
||||
*/
|
||||
#define ether3_readbuffer(dev,data,length) \
|
||||
readsw(REG_BUFWIN, (data), (length) >> 1)
|
||||
|
||||
#define ether3_readword(dev) \
|
||||
readw(REG_BUFWIN)
|
||||
|
||||
#define ether3_readlong(dev) \
|
||||
readw(REG_BUFWIN) | (readw(REG_BUFWIN) << 16)
|
||||
|
||||
/*
|
||||
* Switch LED off...
|
||||
*/
|
||||
static void ether3_ledoff(unsigned long data)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)data;
|
||||
ether3_outw(priv(dev)->regs.config2 |= CFG2_CTRLO, REG_CONFIG2);
|
||||
}
|
||||
|
||||
/*
|
||||
* switch LED on...
|
||||
*/
|
||||
static inline void ether3_ledon(struct net_device *dev)
|
||||
{
|
||||
del_timer(&priv(dev)->timer);
|
||||
priv(dev)->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */
|
||||
priv(dev)->timer.data = (unsigned long)dev;
|
||||
priv(dev)->timer.function = ether3_ledoff;
|
||||
add_timer(&priv(dev)->timer);
|
||||
if (priv(dev)->regs.config2 & CFG2_CTRLO)
|
||||
ether3_outw(priv(dev)->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the ethernet address string from the on board rom.
|
||||
* This is an ascii string!!!
|
||||
*/
|
||||
static int __devinit
|
||||
ether3_addr(char *addr, struct expansion_card *ec)
|
||||
{
|
||||
struct in_chunk_dir cd;
|
||||
char *s;
|
||||
|
||||
if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
|
||||
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;
|
||||
}
|
||||
/* I wonder if we should even let the user continue in this case
|
||||
* - no, it would be better to disable the device
|
||||
*/
|
||||
printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
static int __devinit
|
||||
ether3_ramtest(struct net_device *dev, unsigned char byte)
|
||||
{
|
||||
unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
|
||||
int i,ret = 0;
|
||||
int max_errors = 4;
|
||||
int bad = -1;
|
||||
|
||||
if (!buffer)
|
||||
return 1;
|
||||
|
||||
memset(buffer, byte, RX_END);
|
||||
ether3_setbuffer(dev, buffer_write, 0);
|
||||
ether3_writebuffer(dev, buffer, TX_END);
|
||||
ether3_setbuffer(dev, buffer_write, RX_START);
|
||||
ether3_writebuffer(dev, buffer + RX_START, RX_LEN);
|
||||
memset(buffer, byte ^ 0xff, RX_END);
|
||||
ether3_setbuffer(dev, buffer_read, 0);
|
||||
ether3_readbuffer(dev, buffer, TX_END);
|
||||
ether3_setbuffer(dev, buffer_read, RX_START);
|
||||
ether3_readbuffer(dev, buffer + RX_START, RX_LEN);
|
||||
|
||||
for (i = 0; i < RX_END; i++) {
|
||||
if (buffer[i] != byte) {
|
||||
if (max_errors > 0 && bad != buffer[i]) {
|
||||
printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
|
||||
dev->name, buffer[i], byte, i);
|
||||
ret = 2;
|
||||
max_errors--;
|
||||
bad = i;
|
||||
}
|
||||
} else {
|
||||
if (bad != -1) {
|
||||
if (bad != i - 1)
|
||||
printk(" - 0x%04X\n", i - 1);
|
||||
printk("\n");
|
||||
bad = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bad != -1)
|
||||
printk(" - 0xffff\n");
|
||||
kfree(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
||||
static int __devinit ether3_init_2(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
priv(dev)->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
|
||||
priv(dev)->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
|
||||
priv(dev)->regs.command = 0;
|
||||
|
||||
/*
|
||||
* Set up our hardware address
|
||||
*/
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
|
||||
for (i = 0; i < 6; i++)
|
||||
ether3_outb(dev->dev_addr[i], REG_BUFWIN);
|
||||
|
||||
if (dev->flags & IFF_PROMISC)
|
||||
priv(dev)->regs.config1 |= CFG1_RECVPROMISC;
|
||||
else if (dev->flags & IFF_MULTICAST)
|
||||
priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI;
|
||||
else
|
||||
priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD;
|
||||
|
||||
/*
|
||||
* There is a problem with the NQ8005 in that it occasionally loses the
|
||||
* last two bytes. To get round this problem, we receive the CRC as
|
||||
* well. That way, if we do lose the last two, then it doesn't matter.
|
||||
*/
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
|
||||
ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
|
||||
ether3_outw(priv(dev)->rx_head, REG_RECVPTR);
|
||||
ether3_outw(0, REG_TRANSMITPTR);
|
||||
ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND);
|
||||
ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
|
||||
ether3_outw(priv(dev)->regs.command, REG_COMMAND);
|
||||
|
||||
i = ether3_ramtest(dev, 0x5A);
|
||||
if(i)
|
||||
return i;
|
||||
i = ether3_ramtest(dev, 0x1E);
|
||||
if(i)
|
||||
return i;
|
||||
|
||||
ether3_setbuffer(dev, buffer_write, 0);
|
||||
ether3_writelong(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ether3_init_for_open(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(&priv(dev)->stats, 0, sizeof(struct net_device_stats));
|
||||
|
||||
/* Reset the chip */
|
||||
ether3_outw(CFG2_RESET, REG_CONFIG2);
|
||||
udelay(4);
|
||||
|
||||
priv(dev)->regs.command = 0;
|
||||
ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
|
||||
while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON))
|
||||
barrier();
|
||||
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
|
||||
for (i = 0; i < 6; i++)
|
||||
ether3_outb(dev->dev_addr[i], REG_BUFWIN);
|
||||
|
||||
priv(dev)->tx_head = 0;
|
||||
priv(dev)->tx_tail = 0;
|
||||
priv(dev)->regs.config2 |= CFG2_CTRLO;
|
||||
priv(dev)->rx_head = RX_START;
|
||||
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
|
||||
ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
|
||||
ether3_outw(priv(dev)->rx_head, REG_RECVPTR);
|
||||
ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND);
|
||||
ether3_outw(0, REG_TRANSMITPTR);
|
||||
ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
|
||||
|
||||
ether3_setbuffer(dev, buffer_write, 0);
|
||||
ether3_writelong(dev, 0);
|
||||
|
||||
priv(dev)->regs.command = CMD_ENINTRX | CMD_ENINTTX;
|
||||
ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ether3_probe_bus_8(struct net_device *dev, int val)
|
||||
{
|
||||
int write_low, write_high, read_low, read_high;
|
||||
|
||||
write_low = val & 255;
|
||||
write_high = val >> 8;
|
||||
|
||||
printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low);
|
||||
|
||||
ether3_outb(write_low, REG_RECVPTR);
|
||||
ether3_outb(write_high, REG_RECVPTR + 4);
|
||||
|
||||
read_low = ether3_inb(REG_RECVPTR);
|
||||
read_high = ether3_inb(REG_RECVPTR + 4);
|
||||
|
||||
printk(", read8 [%02X:%02X]\n", read_high, read_low);
|
||||
|
||||
return read_low == write_low && read_high == write_high;
|
||||
}
|
||||
|
||||
static inline int
|
||||
ether3_probe_bus_16(struct net_device *dev, int val)
|
||||
{
|
||||
int read_val;
|
||||
|
||||
ether3_outw(val, REG_RECVPTR);
|
||||
read_val = ether3_inw(REG_RECVPTR);
|
||||
|
||||
printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val);
|
||||
|
||||
return read_val == val;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
ether3_open(struct net_device *dev)
|
||||
{
|
||||
if (!is_valid_ether_addr(dev->dev_addr)) {
|
||||
printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev))
|
||||
return -EAGAIN;
|
||||
|
||||
ether3_init_for_open(dev);
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The inverse routine to ether3_open().
|
||||
*/
|
||||
static int
|
||||
ether3_close(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
|
||||
disable_irq(dev->irq);
|
||||
|
||||
ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
|
||||
priv(dev)->regs.command = 0;
|
||||
while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON))
|
||||
barrier();
|
||||
ether3_outb(0x80, REG_CONFIG2 + 4);
|
||||
ether3_outw(0, REG_COMMAND);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current statistics. This may be called with the card open or
|
||||
* closed.
|
||||
*/
|
||||
static struct net_device_stats *ether3_getstats(struct net_device *dev)
|
||||
{
|
||||
return &priv(dev)->stats;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set or clear promiscuous/multicast mode filter for this adaptor.
|
||||
*
|
||||
* We don't attempt any packet filtering. The card may have a SEEQ 8004
|
||||
* in which does not have the other ethernet address registers present...
|
||||
*/
|
||||
static void ether3_setmulticastlist(struct net_device *dev)
|
||||
{
|
||||
priv(dev)->regs.config1 &= ~CFG1_RECVPROMISC;
|
||||
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
/* promiscuous mode */
|
||||
priv(dev)->regs.config1 |= CFG1_RECVPROMISC;
|
||||
} else if (dev->flags & IFF_ALLMULTI) {
|
||||
priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI;
|
||||
} else
|
||||
priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD;
|
||||
|
||||
ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
|
||||
}
|
||||
|
||||
static void ether3_timeout(struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
del_timer(&priv(dev)->timer);
|
||||
|
||||
local_irq_save(flags);
|
||||
printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name);
|
||||
printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name,
|
||||
ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
|
||||
printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
|
||||
ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
|
||||
printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name,
|
||||
priv(dev)->tx_head, priv(dev)->tx_tail);
|
||||
ether3_setbuffer(dev, buffer_read, priv(dev)->tx_tail);
|
||||
printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev));
|
||||
local_irq_restore(flags);
|
||||
|
||||
priv(dev)->regs.config2 |= CFG2_CTRLO;
|
||||
priv(dev)->stats.tx_errors += 1;
|
||||
ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
|
||||
priv(dev)->tx_head = priv(dev)->tx_tail = 0;
|
||||
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit a packet
|
||||
*/
|
||||
static int
|
||||
ether3_sendpacket(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
|
||||
unsigned int ptr, next_ptr;
|
||||
|
||||
if (priv(dev)->broken) {
|
||||
dev_kfree_skb(skb);
|
||||
priv(dev)->stats.tx_dropped ++;
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
length = (length + 1) & ~1;
|
||||
if (length != skb->len) {
|
||||
if (skb_padto(skb, length))
|
||||
goto out;
|
||||
}
|
||||
|
||||
next_ptr = (priv(dev)->tx_head + 1) & 15;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (priv(dev)->tx_tail == next_ptr) {
|
||||
local_irq_restore(flags);
|
||||
return 1; /* unable to queue */
|
||||
}
|
||||
|
||||
dev->trans_start = jiffies;
|
||||
ptr = 0x600 * priv(dev)->tx_head;
|
||||
priv(dev)->tx_head = next_ptr;
|
||||
next_ptr *= 0x600;
|
||||
|
||||
#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
|
||||
|
||||
ether3_setbuffer(dev, buffer_write, next_ptr);
|
||||
ether3_writelong(dev, 0);
|
||||
ether3_setbuffer(dev, buffer_write, ptr);
|
||||
ether3_writelong(dev, 0);
|
||||
ether3_writebuffer(dev, skb->data, length);
|
||||
ether3_writeword(dev, htons(next_ptr));
|
||||
ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16);
|
||||
ether3_setbuffer(dev, buffer_write, ptr);
|
||||
ether3_writeword(dev, htons((ptr + length + 4)));
|
||||
ether3_writeword(dev, TXHDR_FLAGS >> 16);
|
||||
ether3_ledon(dev);
|
||||
|
||||
if (!(ether3_inw(REG_STATUS) & STAT_TXON)) {
|
||||
ether3_outw(ptr, REG_TRANSMITPTR);
|
||||
ether3_outw(priv(dev)->regs.command | CMD_TXON, REG_COMMAND);
|
||||
}
|
||||
|
||||
next_ptr = (priv(dev)->tx_head + 1) & 15;
|
||||
local_irq_restore(flags);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
if (priv(dev)->tx_tail == next_ptr)
|
||||
netif_stop_queue(dev);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
ether3_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *)dev_id;
|
||||
unsigned int status, handled = IRQ_NONE;
|
||||
|
||||
#if NET_DEBUG > 1
|
||||
if(net_debug & DEBUG_INT)
|
||||
printk("eth3irq: %d ", irq);
|
||||
#endif
|
||||
|
||||
status = ether3_inw(REG_STATUS);
|
||||
|
||||
if (status & STAT_INTRX) {
|
||||
ether3_outw(CMD_ACKINTRX | priv(dev)->regs.command, REG_COMMAND);
|
||||
ether3_rx(dev, 12);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (status & STAT_INTTX) {
|
||||
ether3_outw(CMD_ACKINTTX | priv(dev)->regs.command, REG_COMMAND);
|
||||
ether3_tx(dev);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#if NET_DEBUG > 1
|
||||
if(net_debug & DEBUG_INT)
|
||||
printk("done\n");
|
||||
#endif
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a good packet(s), get it/them out of the buffers.
|
||||
*/
|
||||
static int ether3_rx(struct net_device *dev, unsigned int maxcnt)
|
||||
{
|
||||
unsigned int next_ptr = priv(dev)->rx_head, received = 0;
|
||||
|
||||
ether3_ledon(dev);
|
||||
|
||||
do {
|
||||
unsigned int this_ptr, status;
|
||||
unsigned char addrs[16];
|
||||
|
||||
/*
|
||||
* read the first 16 bytes from the buffer.
|
||||
* This contains the status bytes etc and ethernet addresses,
|
||||
* and we also check the source ethernet address to see if
|
||||
* it originated from us.
|
||||
*/
|
||||
{
|
||||
unsigned int temp_ptr;
|
||||
ether3_setbuffer(dev, buffer_read, next_ptr);
|
||||
temp_ptr = ether3_readword(dev);
|
||||
status = ether3_readword(dev);
|
||||
if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) !=
|
||||
(RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr)
|
||||
break;
|
||||
|
||||
this_ptr = next_ptr + 4;
|
||||
next_ptr = ntohs(temp_ptr);
|
||||
}
|
||||
ether3_setbuffer(dev, buffer_read, this_ptr);
|
||||
ether3_readbuffer(dev, addrs+2, 12);
|
||||
|
||||
if (next_ptr < RX_START || next_ptr >= RX_END) {
|
||||
int i;
|
||||
printk("%s: bad next pointer @%04X: ", dev->name, priv(dev)->rx_head);
|
||||
printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8);
|
||||
for (i = 2; i < 14; i++)
|
||||
printk("%02X ", addrs[i]);
|
||||
printk("\n");
|
||||
next_ptr = priv(dev)->rx_head;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* ignore our own packets...
|
||||
*/
|
||||
if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) &&
|
||||
!(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) {
|
||||
maxcnt ++; /* compensate for loopedback packet */
|
||||
ether3_outw(next_ptr >> 8, REG_RECVEND);
|
||||
} else
|
||||
if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) {
|
||||
unsigned int length = next_ptr - this_ptr;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (next_ptr <= this_ptr)
|
||||
length += RX_END - RX_START;
|
||||
|
||||
skb = dev_alloc_skb(length + 2);
|
||||
if (skb) {
|
||||
unsigned char *buf;
|
||||
|
||||
skb->dev = dev;
|
||||
skb_reserve(skb, 2);
|
||||
buf = skb_put(skb, length);
|
||||
ether3_readbuffer(dev, buf + 12, length - 12);
|
||||
ether3_outw(next_ptr >> 8, REG_RECVEND);
|
||||
*(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2);
|
||||
*(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4);
|
||||
*(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8);
|
||||
*(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
received ++;
|
||||
} else
|
||||
goto dropping;
|
||||
} else {
|
||||
struct net_device_stats *stats = &priv(dev)->stats;
|
||||
ether3_outw(next_ptr >> 8, REG_RECVEND);
|
||||
if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++;
|
||||
if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++;
|
||||
if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++;
|
||||
if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++;
|
||||
stats->rx_errors++;
|
||||
}
|
||||
}
|
||||
while (-- maxcnt);
|
||||
|
||||
done:
|
||||
priv(dev)->stats.rx_packets += received;
|
||||
priv(dev)->rx_head = next_ptr;
|
||||
/*
|
||||
* If rx went off line, then that means that the buffer may be full. We
|
||||
* have dropped at least one packet.
|
||||
*/
|
||||
if (!(ether3_inw(REG_STATUS) & STAT_RXON)) {
|
||||
priv(dev)->stats.rx_dropped ++;
|
||||
ether3_outw(next_ptr, REG_RECVPTR);
|
||||
ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND);
|
||||
}
|
||||
|
||||
return maxcnt;
|
||||
|
||||
dropping:{
|
||||
static unsigned long last_warned;
|
||||
|
||||
ether3_outw(next_ptr >> 8, REG_RECVEND);
|
||||
/*
|
||||
* Don't print this message too many times...
|
||||
*/
|
||||
if (time_after(jiffies, last_warned + 10 * HZ)) {
|
||||
last_warned = jiffies;
|
||||
printk("%s: memory squeeze, dropping packet.\n", dev->name);
|
||||
}
|
||||
priv(dev)->stats.rx_dropped ++;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update stats for the transmitted packet(s)
|
||||
*/
|
||||
static void ether3_tx(struct net_device *dev)
|
||||
{
|
||||
unsigned int tx_tail = priv(dev)->tx_tail;
|
||||
int max_work = 14;
|
||||
|
||||
do {
|
||||
unsigned long status;
|
||||
|
||||
/*
|
||||
* Read the packet header
|
||||
*/
|
||||
ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
|
||||
status = ether3_readlong(dev);
|
||||
|
||||
/*
|
||||
* Check to see if this packet has been transmitted
|
||||
*/
|
||||
if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) !=
|
||||
(TXSTAT_DONE | TXHDR_TRANSMIT))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Update errors
|
||||
*/
|
||||
if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)))
|
||||
priv(dev)->stats.tx_packets++;
|
||||
else {
|
||||
priv(dev)->stats.tx_errors ++;
|
||||
if (status & TXSTAT_16COLLISIONS)
|
||||
priv(dev)->stats.collisions += 16;
|
||||
if (status & TXSTAT_BABBLED)
|
||||
priv(dev)->stats.tx_fifo_errors ++;
|
||||
}
|
||||
|
||||
tx_tail = (tx_tail + 1) & 15;
|
||||
} while (--max_work);
|
||||
|
||||
if (priv(dev)->tx_tail != tx_tail) {
|
||||
priv(dev)->tx_tail = tx_tail;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit ether3_banner(void)
|
||||
{
|
||||
static unsigned version_printed = 0;
|
||||
|
||||
if (net_debug && version_printed++ == 0)
|
||||
printk(KERN_INFO "%s", version);
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
ether3_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
const struct ether3_data *data = id->data;
|
||||
struct net_device *dev;
|
||||
int i, bus_type, ret;
|
||||
|
||||
ether3_banner();
|
||||
|
||||
ret = ecard_request_resources(ec);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct dev_priv));
|
||||
if (!dev) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
SET_MODULE_OWNER(dev);
|
||||
SET_NETDEV_DEV(dev, &ec->dev);
|
||||
|
||||
priv(dev)->base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
|
||||
ecard_resource_len(ec, ECARD_RES_MEMC));
|
||||
if (!priv(dev)->base) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
ec->irqaddr = priv(dev)->base + data->base_offset;
|
||||
ec->irqmask = 0xf0;
|
||||
|
||||
priv(dev)->seeq = priv(dev)->base + data->base_offset;
|
||||
dev->irq = ec->irq;
|
||||
|
||||
ether3_addr(dev->dev_addr, ec);
|
||||
|
||||
init_timer(&priv(dev)->timer);
|
||||
|
||||
/* Reset card...
|
||||
*/
|
||||
ether3_outb(0x80, REG_CONFIG2 + 4);
|
||||
bus_type = BUS_UNKNOWN;
|
||||
udelay(4);
|
||||
|
||||
/* Test using Receive Pointer (16-bit register) to find out
|
||||
* how the ether3 is connected to the bus...
|
||||
*/
|
||||
if (ether3_probe_bus_8(dev, 0x100) &&
|
||||
ether3_probe_bus_8(dev, 0x201))
|
||||
bus_type = BUS_8;
|
||||
|
||||
if (bus_type == BUS_UNKNOWN &&
|
||||
ether3_probe_bus_16(dev, 0x101) &&
|
||||
ether3_probe_bus_16(dev, 0x201))
|
||||
bus_type = BUS_16;
|
||||
|
||||
switch (bus_type) {
|
||||
case BUS_UNKNOWN:
|
||||
printk(KERN_ERR "%s: unable to identify bus width\n", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto free;
|
||||
|
||||
case BUS_8:
|
||||
printk(KERN_ERR "%s: %s found, but is an unsupported "
|
||||
"8-bit card\n", dev->name, data->name);
|
||||
ret = -ENODEV;
|
||||
goto free;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ether3_init_2(dev)) {
|
||||
ret = -ENODEV;
|
||||
goto free;
|
||||
}
|
||||
|
||||
dev->open = ether3_open;
|
||||
dev->stop = ether3_close;
|
||||
dev->hard_start_xmit = ether3_sendpacket;
|
||||
dev->get_stats = ether3_getstats;
|
||||
dev->set_multicast_list = ether3_setmulticastlist;
|
||||
dev->tx_timeout = ether3_timeout;
|
||||
dev->watchdog_timeo = 5 * HZ / 100;
|
||||
|
||||
ret = register_netdev(dev);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
printk("%s: %s in slot %d, ", dev->name, data->name, ec->slot_no);
|
||||
for (i = 0; i < 6; i++)
|
||||
printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
|
||||
|
||||
ecard_set_drvdata(ec, dev);
|
||||
return 0;
|
||||
|
||||
free:
|
||||
if (priv(dev)->base)
|
||||
iounmap(priv(dev)->base);
|
||||
free_netdev(dev);
|
||||
release:
|
||||
ecard_release_resources(ec);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit ether3_remove(struct expansion_card *ec)
|
||||
{
|
||||
struct net_device *dev = ecard_get_drvdata(ec);
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
|
||||
unregister_netdev(dev);
|
||||
iounmap(priv(dev)->base);
|
||||
free_netdev(dev);
|
||||
ecard_release_resources(ec);
|
||||
}
|
||||
|
||||
static struct ether3_data ether3 = {
|
||||
.name = "ether3",
|
||||
.base_offset = 0,
|
||||
};
|
||||
|
||||
static struct ether3_data etherb = {
|
||||
.name = "etherb",
|
||||
.base_offset = 0x800,
|
||||
};
|
||||
|
||||
static const struct ecard_id ether3_ids[] = {
|
||||
{ MANU_ANT2, PROD_ANT_ETHER3, ðer3 },
|
||||
{ MANU_ANT, PROD_ANT_ETHER3, ðer3 },
|
||||
{ MANU_ANT, PROD_ANT_ETHERB, ðerb },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver ether3_driver = {
|
||||
.probe = ether3_probe,
|
||||
.remove = __devexit_p(ether3_remove),
|
||||
.id_table = ether3_ids,
|
||||
.drv = {
|
||||
.name = "ether3",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ether3_init(void)
|
||||
{
|
||||
return ecard_register_driver(ðer3_driver);
|
||||
}
|
||||
|
||||
static void __exit ether3_exit(void)
|
||||
{
|
||||
ecard_remove_driver(ðer3_driver);
|
||||
}
|
||||
|
||||
module_init(ether3_init);
|
||||
module_exit(ether3_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
177
drivers/net/arm/ether3.h
Normal file
177
drivers/net/arm/ether3.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* linux/drivers/acorn/net/ether3.h
|
||||
*
|
||||
* Copyright (C) 1995-2000 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* network driver for Acorn/ANT Ether3 cards
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ether3_H
|
||||
#define _LINUX_ether3_H
|
||||
|
||||
/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
|
||||
#define DEBUG_TX 2
|
||||
#define DEBUG_RX 4
|
||||
#define DEBUG_INT 8
|
||||
#define DEBUG_IC 16
|
||||
#ifndef NET_DEBUG
|
||||
#define NET_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define priv(dev) ((struct dev_priv *)netdev_priv(dev))
|
||||
|
||||
/* Command register definitions & bits */
|
||||
#define REG_COMMAND (priv(dev)->seeq + 0x0000)
|
||||
#define CMD_ENINTDMA 0x0001
|
||||
#define CMD_ENINTRX 0x0002
|
||||
#define CMD_ENINTTX 0x0004
|
||||
#define CMD_ENINTBUFWIN 0x0008
|
||||
#define CMD_ACKINTDMA 0x0010
|
||||
#define CMD_ACKINTRX 0x0020
|
||||
#define CMD_ACKINTTX 0x0040
|
||||
#define CMD_ACKINTBUFWIN 0x0080
|
||||
#define CMD_DMAON 0x0100
|
||||
#define CMD_RXON 0x0200
|
||||
#define CMD_TXON 0x0400
|
||||
#define CMD_DMAOFF 0x0800
|
||||
#define CMD_RXOFF 0x1000
|
||||
#define CMD_TXOFF 0x2000
|
||||
#define CMD_FIFOREAD 0x4000
|
||||
#define CMD_FIFOWRITE 0x8000
|
||||
|
||||
/* status register */
|
||||
#define REG_STATUS (priv(dev)->seeq + 0x0000)
|
||||
#define STAT_ENINTSTAT 0x0001
|
||||
#define STAT_ENINTRX 0x0002
|
||||
#define STAT_ENINTTX 0x0004
|
||||
#define STAT_ENINTBUFWIN 0x0008
|
||||
#define STAT_INTDMA 0x0010
|
||||
#define STAT_INTRX 0x0020
|
||||
#define STAT_INTTX 0x0040
|
||||
#define STAT_INTBUFWIN 0x0080
|
||||
#define STAT_DMAON 0x0100
|
||||
#define STAT_RXON 0x0200
|
||||
#define STAT_TXON 0x0400
|
||||
#define STAT_FIFOFULL 0x2000
|
||||
#define STAT_FIFOEMPTY 0x4000
|
||||
#define STAT_FIFODIR 0x8000
|
||||
|
||||
/* configuration register 1 */
|
||||
#define REG_CONFIG1 (priv(dev)->seeq + 0x0040)
|
||||
#define CFG1_BUFSELSTAT0 0x0000
|
||||
#define CFG1_BUFSELSTAT1 0x0001
|
||||
#define CFG1_BUFSELSTAT2 0x0002
|
||||
#define CFG1_BUFSELSTAT3 0x0003
|
||||
#define CFG1_BUFSELSTAT4 0x0004
|
||||
#define CFG1_BUFSELSTAT5 0x0005
|
||||
#define CFG1_ADDRPROM 0x0006
|
||||
#define CFG1_TRANSEND 0x0007
|
||||
#define CFG1_LOCBUFMEM 0x0008
|
||||
#define CFG1_INTVECTOR 0x0009
|
||||
#define CFG1_RECVSPECONLY 0x0000
|
||||
#define CFG1_RECVSPECBROAD 0x4000
|
||||
#define CFG1_RECVSPECBRMULTI 0x8000
|
||||
#define CFG1_RECVPROMISC 0xC000
|
||||
|
||||
/* The following aren't in 8004 */
|
||||
#define CFG1_DMABURSTCONT 0x0000
|
||||
#define CFG1_DMABURST800NS 0x0010
|
||||
#define CFG1_DMABURST1600NS 0x0020
|
||||
#define CFG1_DMABURST3200NS 0x0030
|
||||
#define CFG1_DMABURST1 0x0000
|
||||
#define CFG1_DMABURST4 0x0040
|
||||
#define CFG1_DMABURST8 0x0080
|
||||
#define CFG1_DMABURST16 0x00C0
|
||||
#define CFG1_RECVCOMPSTAT0 0x0100
|
||||
#define CFG1_RECVCOMPSTAT1 0x0200
|
||||
#define CFG1_RECVCOMPSTAT2 0x0400
|
||||
#define CFG1_RECVCOMPSTAT3 0x0800
|
||||
#define CFG1_RECVCOMPSTAT4 0x1000
|
||||
#define CFG1_RECVCOMPSTAT5 0x2000
|
||||
|
||||
/* configuration register 2 */
|
||||
#define REG_CONFIG2 (priv(dev)->seeq + 0x0080)
|
||||
#define CFG2_BYTESWAP 0x0001
|
||||
#define CFG2_ERRENCRC 0x0008
|
||||
#define CFG2_ERRENDRIBBLE 0x0010
|
||||
#define CFG2_ERRSHORTFRAME 0x0020
|
||||
#define CFG2_SLOTSELECT 0x0040
|
||||
#define CFG2_PREAMSELECT 0x0080
|
||||
#define CFG2_ADDRLENGTH 0x0100
|
||||
#define CFG2_RECVCRC 0x0200
|
||||
#define CFG2_XMITNOCRC 0x0400
|
||||
#define CFG2_LOOPBACK 0x0800
|
||||
#define CFG2_CTRLO 0x1000
|
||||
#define CFG2_RESET 0x8000
|
||||
|
||||
#define REG_RECVEND (priv(dev)->seeq + 0x00c0)
|
||||
|
||||
#define REG_BUFWIN (priv(dev)->seeq + 0x0100)
|
||||
|
||||
#define REG_RECVPTR (priv(dev)->seeq + 0x0140)
|
||||
|
||||
#define REG_TRANSMITPTR (priv(dev)->seeq + 0x0180)
|
||||
|
||||
#define REG_DMAADDR (priv(dev)->seeq + 0x01c0)
|
||||
|
||||
/*
|
||||
* Cards transmit/receive headers
|
||||
*/
|
||||
#define TX_NEXT (0xffff)
|
||||
#define TXHDR_ENBABBLEINT (1 << 16)
|
||||
#define TXHDR_ENCOLLISIONINT (1 << 17)
|
||||
#define TXHDR_EN16COLLISION (1 << 18)
|
||||
#define TXHDR_ENSUCCESS (1 << 19)
|
||||
#define TXHDR_DATAFOLLOWS (1 << 21)
|
||||
#define TXHDR_CHAINCONTINUE (1 << 22)
|
||||
#define TXHDR_TRANSMIT (1 << 23)
|
||||
#define TXSTAT_BABBLED (1 << 24)
|
||||
#define TXSTAT_COLLISION (1 << 25)
|
||||
#define TXSTAT_16COLLISIONS (1 << 26)
|
||||
#define TXSTAT_DONE (1 << 31)
|
||||
|
||||
#define RX_NEXT (0xffff)
|
||||
#define RXHDR_CHAINCONTINUE (1 << 6)
|
||||
#define RXHDR_RECEIVE (1 << 7)
|
||||
#define RXSTAT_OVERSIZE (1 << 8)
|
||||
#define RXSTAT_CRCERROR (1 << 9)
|
||||
#define RXSTAT_DRIBBLEERROR (1 << 10)
|
||||
#define RXSTAT_SHORTPACKET (1 << 11)
|
||||
#define RXSTAT_DONE (1 << 15)
|
||||
|
||||
|
||||
#define TX_START 0x0000
|
||||
#define TX_END 0x6000
|
||||
#define RX_START 0x6000
|
||||
#define RX_LEN 0xA000
|
||||
#define RX_END 0x10000
|
||||
/* must be a power of 2 and greater than MAX_TX_BUFFERED */
|
||||
#define MAX_TXED 16
|
||||
#define MAX_TX_BUFFERED 10
|
||||
|
||||
struct dev_priv {
|
||||
void __iomem *base;
|
||||
void __iomem *seeq;
|
||||
struct {
|
||||
unsigned int command;
|
||||
unsigned int config1;
|
||||
unsigned int config2;
|
||||
} regs;
|
||||
unsigned char tx_head; /* buffer nr to insert next packet */
|
||||
unsigned char tx_tail; /* buffer nr of transmitting packet */
|
||||
unsigned int rx_head; /* address to fetch next packet from */
|
||||
struct net_device_stats stats;
|
||||
struct timer_list timer;
|
||||
int broken; /* 0 = ok, 1 = something went wrong */
|
||||
};
|
||||
|
||||
struct ether3_data {
|
||||
const char name[8];
|
||||
unsigned long base_offset;
|
||||
};
|
||||
|
||||
#endif
|
||||
868
drivers/net/arm/etherh.c
Normal file
868
drivers/net/arm/etherh.c
Normal file
@@ -0,0 +1,868 @@
|
||||
/*
|
||||
* linux/drivers/acorn/net/etherh.c
|
||||
*
|
||||
* Copyright (C) 2000-2002 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* NS8390 I-cubed EtherH and ANT EtherM specific driver
|
||||
* Thanks to I-Cubed for information on their cards.
|
||||
* EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
|
||||
* EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
|
||||
* EtherM integration re-engineered by Russell King.
|
||||
*
|
||||
* Changelog:
|
||||
* 08-12-1996 RMK 1.00 Created
|
||||
* RMK 1.03 Added support for EtherLan500 cards
|
||||
* 23-11-1997 RMK 1.04 Added media autodetection
|
||||
* 16-04-1998 RMK 1.05 Improved media autodetection
|
||||
* 10-02-2000 RMK 1.06 Updated for 2.3.43
|
||||
* 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8
|
||||
* 12-10-1999 CK/TEW EtherM driver first release
|
||||
* 21-12-2000 TTC EtherH/EtherM integration
|
||||
* 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver.
|
||||
* 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/slab.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/system.h>
|
||||
#include <asm/ecard.h>
|
||||
#include <asm/io.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 NET_DEBUG 0
|
||||
#define DEBUG_INIT 2
|
||||
|
||||
#define DRV_NAME "etherh"
|
||||
#define DRV_VERSION "1.11"
|
||||
|
||||
static char version[] __initdata =
|
||||
"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
|
||||
|
||||
#include "../lib8390.c"
|
||||
|
||||
static unsigned int net_debug = NET_DEBUG;
|
||||
|
||||
struct etherh_priv {
|
||||
void __iomem *ioc_fast;
|
||||
void __iomem *memc;
|
||||
void __iomem *dma_base;
|
||||
unsigned int id;
|
||||
void __iomem *ctrl_port;
|
||||
unsigned char ctrl;
|
||||
u32 supported;
|
||||
};
|
||||
|
||||
struct etherh_data {
|
||||
unsigned long ns8390_offset;
|
||||
unsigned long dataport_offset;
|
||||
unsigned long ctrlport_offset;
|
||||
int ctrl_ioc;
|
||||
const char name[16];
|
||||
u32 supported;
|
||||
unsigned char tx_start_page;
|
||||
unsigned char stop_page;
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Russell King");
|
||||
MODULE_DESCRIPTION("EtherH/EtherM driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define ETHERH500_DATAPORT 0x800 /* MEMC */
|
||||
#define ETHERH500_NS8390 0x000 /* MEMC */
|
||||
#define ETHERH500_CTRLPORT 0x800 /* IOC */
|
||||
|
||||
#define ETHERH600_DATAPORT 0x040 /* MEMC */
|
||||
#define ETHERH600_NS8390 0x800 /* MEMC */
|
||||
#define ETHERH600_CTRLPORT 0x200 /* MEMC */
|
||||
|
||||
#define ETHERH_CP_IE 1
|
||||
#define ETHERH_CP_IF 2
|
||||
#define ETHERH_CP_HEARTBEAT 2
|
||||
|
||||
#define ETHERH_TX_START_PAGE 1
|
||||
#define ETHERH_STOP_PAGE 127
|
||||
|
||||
/*
|
||||
* These came from CK/TEW
|
||||
*/
|
||||
#define ETHERM_DATAPORT 0x200 /* MEMC */
|
||||
#define ETHERM_NS8390 0x800 /* MEMC */
|
||||
#define ETHERM_CTRLPORT 0x23c /* MEMC */
|
||||
|
||||
#define ETHERM_TX_START_PAGE 64
|
||||
#define ETHERM_STOP_PAGE 127
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define etherh_priv(dev) \
|
||||
((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
|
||||
|
||||
static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
|
||||
{
|
||||
unsigned char ctrl = eh->ctrl | mask;
|
||||
eh->ctrl = ctrl;
|
||||
writeb(ctrl, eh->ctrl_port);
|
||||
}
|
||||
|
||||
static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
|
||||
{
|
||||
unsigned char ctrl = eh->ctrl & ~mask;
|
||||
eh->ctrl = ctrl;
|
||||
writeb(ctrl, eh->ctrl_port);
|
||||
}
|
||||
|
||||
static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
|
||||
{
|
||||
return readb(eh->ctrl_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void etherh_irq_enable(ecard_t *ec, int irqnr)
|
||||
{
|
||||
struct etherh_priv *eh = ec->irq_data;
|
||||
|
||||
etherh_set_ctrl(eh, ETHERH_CP_IE);
|
||||
}
|
||||
|
||||
static void etherh_irq_disable(ecard_t *ec, int irqnr)
|
||||
{
|
||||
struct etherh_priv *eh = ec->irq_data;
|
||||
|
||||
etherh_clr_ctrl(eh, ETHERH_CP_IE);
|
||||
}
|
||||
|
||||
static expansioncard_ops_t etherh_ops = {
|
||||
.irqenable = etherh_irq_enable,
|
||||
.irqdisable = etherh_irq_disable,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
etherh_setif(struct net_device *dev)
|
||||
{
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
void __iomem *addr;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* set the interface type */
|
||||
switch (etherh_priv(dev)->id) {
|
||||
case PROD_I3_ETHERLAN600:
|
||||
case PROD_I3_ETHERLAN600A:
|
||||
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
|
||||
|
||||
switch (dev->if_port) {
|
||||
case IF_PORT_10BASE2:
|
||||
writeb((readb(addr) & 0xf8) | 1, addr);
|
||||
break;
|
||||
case IF_PORT_10BASET:
|
||||
writeb((readb(addr) & 0xf8), addr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROD_I3_ETHERLAN500:
|
||||
switch (dev->if_port) {
|
||||
case IF_PORT_10BASE2:
|
||||
etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
|
||||
break;
|
||||
|
||||
case IF_PORT_10BASET:
|
||||
etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static int
|
||||
etherh_getifstat(struct net_device *dev)
|
||||
{
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
void __iomem *addr;
|
||||
int stat = 0;
|
||||
|
||||
switch (etherh_priv(dev)->id) {
|
||||
case PROD_I3_ETHERLAN600:
|
||||
case PROD_I3_ETHERLAN600A:
|
||||
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
|
||||
switch (dev->if_port) {
|
||||
case IF_PORT_10BASE2:
|
||||
stat = 1;
|
||||
break;
|
||||
case IF_PORT_10BASET:
|
||||
stat = readb(addr) & 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROD_I3_ETHERLAN500:
|
||||
switch (dev->if_port) {
|
||||
case IF_PORT_10BASE2:
|
||||
stat = 1;
|
||||
break;
|
||||
case IF_PORT_10BASET:
|
||||
stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
stat = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return stat != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the interface. Note that we ignore the other
|
||||
* parts of ifmap, since its mostly meaningless for this driver.
|
||||
*/
|
||||
static int etherh_set_config(struct net_device *dev, struct ifmap *map)
|
||||
{
|
||||
switch (map->port) {
|
||||
case IF_PORT_10BASE2:
|
||||
case IF_PORT_10BASET:
|
||||
/*
|
||||
* If the user explicitly sets the interface
|
||||
* media type, turn off automedia detection.
|
||||
*/
|
||||
dev->flags &= ~IFF_AUTOMEDIA;
|
||||
dev->if_port = map->port;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
etherh_setif(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the 8390 (hard reset). Note that we can't actually do this.
|
||||
*/
|
||||
static void
|
||||
etherh_reset(struct net_device *dev)
|
||||
{
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
void __iomem *addr = (void __iomem *)dev->base_addr;
|
||||
|
||||
writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
|
||||
|
||||
/*
|
||||
* See if we need to change the interface type.
|
||||
* Note that we use 'interface_num' as a flag
|
||||
* to indicate that we need to change the media.
|
||||
*/
|
||||
if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
|
||||
ei_local->interface_num = 0;
|
||||
|
||||
if (dev->if_port == IF_PORT_10BASET)
|
||||
dev->if_port = IF_PORT_10BASE2;
|
||||
else
|
||||
dev->if_port = IF_PORT_10BASET;
|
||||
|
||||
etherh_setif(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a block of data out to the 8390
|
||||
*/
|
||||
static void
|
||||
etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
|
||||
{
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
unsigned long dma_start;
|
||||
void __iomem *dma_base, *addr;
|
||||
|
||||
if (ei_local->dmaing) {
|
||||
printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
|
||||
" DMAstat %d irqlock %d\n", dev->name,
|
||||
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 */
|
||||
printk(KERN_ERR "%s: timeout waiting for TX RDC\n",
|
||||
dev->name);
|
||||
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) {
|
||||
printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
|
||||
" DMAstat %d irqlock %d\n", dev->name,
|
||||
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) {
|
||||
printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: "
|
||||
" DMAstat %d irqlock %d\n", dev->name,
|
||||
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 (!is_valid_ether_addr(dev->dev_addr)) {
|
||||
printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev))
|
||||
return -EAGAIN;
|
||||
|
||||
/*
|
||||
* Make sure that we aren't going to change the
|
||||
* media type on the next reset - we are about to
|
||||
* do automedia manually now.
|
||||
*/
|
||||
ei_local->interface_num = 0;
|
||||
|
||||
/*
|
||||
* If we are doing automedia detection, do it now.
|
||||
* This is more reliable than the 8390's detection.
|
||||
*/
|
||||
if (dev->flags & IFF_AUTOMEDIA) {
|
||||
dev->if_port = IF_PORT_10BASET;
|
||||
etherh_setif(dev);
|
||||
mdelay(1);
|
||||
if (!etherh_getifstat(dev)) {
|
||||
dev->if_port = IF_PORT_10BASE2;
|
||||
etherh_setif(dev);
|
||||
}
|
||||
} else
|
||||
etherh_setif(dev);
|
||||
|
||||
etherh_reset(dev);
|
||||
__ei_open(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The inverse routine to etherh_open().
|
||||
*/
|
||||
static int
|
||||
etherh_close(struct net_device *dev)
|
||||
{
|
||||
__ei_close (dev);
|
||||
free_irq (dev->irq, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialisation
|
||||
*/
|
||||
|
||||
static void __init etherh_banner(void)
|
||||
{
|
||||
static int version_printed;
|
||||
|
||||
if (net_debug && version_printed++ == 0)
|
||||
printk(KERN_INFO "%s", version);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the ethernet address string from the on board rom.
|
||||
* This is an ascii string...
|
||||
*/
|
||||
static int __init etherh_addr(char *addr, struct expansion_card *ec)
|
||||
{
|
||||
struct in_chunk_dir cd;
|
||||
char *s;
|
||||
|
||||
if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
|
||||
printk(KERN_ERR "%s: unable to read podule description string\n",
|
||||
ec->dev.bus_id);
|
||||
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",
|
||||
ec->dev.bus_id, cd.d.string);
|
||||
|
||||
no_addr:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an ethernet address from the system serial number.
|
||||
*/
|
||||
static int __init etherm_addr(char *addr)
|
||||
{
|
||||
unsigned int serial;
|
||||
|
||||
if (system_serial_low == 0 && system_serial_high == 0)
|
||||
return -ENODEV;
|
||||
|
||||
serial = system_serial_low | system_serial_high;
|
||||
|
||||
addr[0] = 0;
|
||||
addr[1] = 0;
|
||||
addr[2] = 0xa4;
|
||||
addr[3] = 0x10 + (serial >> 24);
|
||||
addr[4] = serial >> 16;
|
||||
addr[5] = serial >> 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
|
||||
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
|
||||
strlcpy(info->bus_info, dev->dev.parent->bus_id,
|
||||
sizeof(info->bus_info));
|
||||
}
|
||||
|
||||
static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
cmd->supported = etherh_priv(dev)->supported;
|
||||
cmd->speed = SPEED_10;
|
||||
cmd->duplex = DUPLEX_HALF;
|
||||
cmd->port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
|
||||
cmd->autoneg = dev->flags & IFF_AUTOMEDIA ? AUTONEG_ENABLE : AUTONEG_DISABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
switch (cmd->autoneg) {
|
||||
case AUTONEG_ENABLE:
|
||||
dev->flags |= IFF_AUTOMEDIA;
|
||||
break;
|
||||
|
||||
case AUTONEG_DISABLE:
|
||||
switch (cmd->port) {
|
||||
case PORT_TP:
|
||||
dev->if_port = IF_PORT_10BASET;
|
||||
break;
|
||||
|
||||
case PORT_BNC:
|
||||
dev->if_port = IF_PORT_10BASE2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
dev->flags &= ~IFF_AUTOMEDIA;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
etherh_setif(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops etherh_ethtool_ops = {
|
||||
.get_settings = etherh_get_settings,
|
||||
.set_settings = etherh_set_settings,
|
||||
.get_drvinfo = etherh_get_drvinfo,
|
||||
};
|
||||
|
||||
static u32 etherh_regoffsets[16];
|
||||
static u32 etherm_regoffsets[16];
|
||||
|
||||
static int __init
|
||||
etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
const struct etherh_data *data = id->data;
|
||||
struct ei_device *ei_local;
|
||||
struct net_device *dev;
|
||||
struct etherh_priv *eh;
|
||||
int i, ret;
|
||||
|
||||
etherh_banner();
|
||||
|
||||
ret = ecard_request_resources(ec);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
|
||||
if (!dev) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
SET_MODULE_OWNER(dev);
|
||||
SET_NETDEV_DEV(dev, &ec->dev);
|
||||
|
||||
dev->open = etherh_open;
|
||||
dev->stop = etherh_close;
|
||||
dev->set_config = etherh_set_config;
|
||||
dev->irq = ec->irq;
|
||||
dev->ethtool_ops = ðerh_ethtool_ops;
|
||||
|
||||
if (data->supported & SUPPORTED_Autoneg)
|
||||
dev->flags |= IFF_AUTOMEDIA;
|
||||
if (data->supported & SUPPORTED_TP) {
|
||||
dev->flags |= IFF_PORTSEL;
|
||||
dev->if_port = IF_PORT_10BASET;
|
||||
} else if (data->supported & SUPPORTED_BNC) {
|
||||
dev->flags |= IFF_PORTSEL;
|
||||
dev->if_port = IF_PORT_10BASE2;
|
||||
} else
|
||||
dev->if_port = IF_PORT_UNKNOWN;
|
||||
|
||||
eh = etherh_priv(dev);
|
||||
eh->supported = data->supported;
|
||||
eh->ctrl = 0;
|
||||
eh->id = ec->cid.product;
|
||||
eh->memc = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), PAGE_SIZE);
|
||||
if (!eh->memc) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
eh->ctrl_port = eh->memc;
|
||||
if (data->ctrl_ioc) {
|
||||
eh->ioc_fast = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), 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) {
|
||||
ec->ops = ðerh_ops;
|
||||
ec->irq_data = eh;
|
||||
} else {
|
||||
/*
|
||||
* If we're in the NIC slot, make sure the IRQ is enabled
|
||||
*/
|
||||
etherh_set_ctrl(eh, ETHERH_CP_IE);
|
||||
}
|
||||
|
||||
ei_local = netdev_priv(dev);
|
||||
spin_lock_init(&ei_local->page_lock);
|
||||
|
||||
if (ec->cid.product == PROD_ANT_ETHERM) {
|
||||
etherm_addr(dev->dev_addr);
|
||||
ei_local->reg_offset = etherm_regoffsets;
|
||||
} else {
|
||||
etherh_addr(dev->dev_addr, ec);
|
||||
ei_local->reg_offset = etherh_regoffsets;
|
||||
}
|
||||
|
||||
ei_local->name = dev->name;
|
||||
ei_local->word16 = 1;
|
||||
ei_local->tx_start_page = data->tx_start_page;
|
||||
ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
|
||||
ei_local->stop_page = data->stop_page;
|
||||
ei_local->reset_8390 = etherh_reset;
|
||||
ei_local->block_input = etherh_block_input;
|
||||
ei_local->block_output = etherh_block_output;
|
||||
ei_local->get_8390_hdr = etherh_get_header;
|
||||
ei_local->interface_num = 0;
|
||||
|
||||
etherh_reset(dev);
|
||||
__NS8390_init(dev, 0);
|
||||
|
||||
ret = register_netdev(dev);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
printk(KERN_INFO "%s: %s in slot %d, ",
|
||||
dev->name, data->name, ec->slot_no);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
|
||||
|
||||
ecard_set_drvdata(ec, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
free:
|
||||
if (eh->ioc_fast)
|
||||
iounmap(eh->ioc_fast);
|
||||
if (eh->memc)
|
||||
iounmap(eh->memc);
|
||||
free_netdev(dev);
|
||||
release:
|
||||
ecard_release_resources(ec);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit etherh_remove(struct expansion_card *ec)
|
||||
{
|
||||
struct net_device *dev = ecard_get_drvdata(ec);
|
||||
struct etherh_priv *eh = etherh_priv(dev);
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
|
||||
unregister_netdev(dev);
|
||||
ec->ops = NULL;
|
||||
|
||||
if (eh->ioc_fast)
|
||||
iounmap(eh->ioc_fast);
|
||||
iounmap(eh->memc);
|
||||
|
||||
free_netdev(dev);
|
||||
|
||||
ecard_release_resources(ec);
|
||||
}
|
||||
|
||||
static struct etherh_data etherm_data = {
|
||||
.ns8390_offset = ETHERM_NS8390,
|
||||
.dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT,
|
||||
.ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT,
|
||||
.name = "ANT EtherM",
|
||||
.supported = SUPPORTED_10baseT_Half,
|
||||
.tx_start_page = ETHERM_TX_START_PAGE,
|
||||
.stop_page = ETHERM_STOP_PAGE,
|
||||
};
|
||||
|
||||
static struct etherh_data etherlan500_data = {
|
||||
.ns8390_offset = ETHERH500_NS8390,
|
||||
.dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT,
|
||||
.ctrlport_offset = ETHERH500_CTRLPORT,
|
||||
.ctrl_ioc = 1,
|
||||
.name = "i3 EtherH 500",
|
||||
.supported = SUPPORTED_10baseT_Half,
|
||||
.tx_start_page = ETHERH_TX_START_PAGE,
|
||||
.stop_page = ETHERH_STOP_PAGE,
|
||||
};
|
||||
|
||||
static struct etherh_data etherlan600_data = {
|
||||
.ns8390_offset = ETHERH600_NS8390,
|
||||
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
|
||||
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
|
||||
.name = "i3 EtherH 600",
|
||||
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
|
||||
.tx_start_page = ETHERH_TX_START_PAGE,
|
||||
.stop_page = ETHERH_STOP_PAGE,
|
||||
};
|
||||
|
||||
static struct etherh_data etherlan600a_data = {
|
||||
.ns8390_offset = ETHERH600_NS8390,
|
||||
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
|
||||
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
|
||||
.name = "i3 EtherH 600A",
|
||||
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
|
||||
.tx_start_page = ETHERH_TX_START_PAGE,
|
||||
.stop_page = ETHERH_STOP_PAGE,
|
||||
};
|
||||
|
||||
static const struct ecard_id etherh_ids[] = {
|
||||
{ MANU_ANT, PROD_ANT_ETHERM, ðerm_data },
|
||||
{ MANU_I3, PROD_I3_ETHERLAN500, ðerlan500_data },
|
||||
{ MANU_I3, PROD_I3_ETHERLAN600, ðerlan600_data },
|
||||
{ MANU_I3, PROD_I3_ETHERLAN600A, ðerlan600a_data },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver etherh_driver = {
|
||||
.probe = etherh_probe,
|
||||
.remove = __devexit_p(etherh_remove),
|
||||
.id_table = etherh_ids,
|
||||
.drv = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init etherh_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
etherh_regoffsets[i] = i << 2;
|
||||
etherm_regoffsets[i] = i << 5;
|
||||
}
|
||||
|
||||
return ecard_register_driver(ðerh_driver);
|
||||
}
|
||||
|
||||
static void __exit etherh_exit(void)
|
||||
{
|
||||
ecard_remove_driver(ðerh_driver);
|
||||
}
|
||||
|
||||
module_init(etherh_init);
|
||||
module_exit(etherh_exit);
|
||||
Reference in New Issue
Block a user