Fixed MTP to work with TWRP

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,971 @@
/* ======================================================================
*
* A PCMCIA ethernet driver for the 3com 3c589 card.
*
* Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
*
* 3c589_cs.c 1.162 2001/10/13 00:08:50
*
* The network driver code is based on Donald Becker's 3c589 code:
*
* Written 1994 by Donald Becker.
* Copyright 1993 United States Government as represented by the
* Director, National Security Agency. This software may be used and
* distributed according to the terms of the GNU General Public License,
* incorporated herein by reference.
* Donald Becker may be reached at becker@scyld.com
*
* Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* ======================================================================
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define DRV_NAME "3c589_cs"
#define DRV_VERSION "1.162-ac"
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
/* To minimize the size of the driver source I only define operating
* constants if they are used several times. You'll need the manual
* if you want to understand driver details.
*/
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_TIMER 0x0a
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define EEPROM_READ 0x0080
#define EEPROM_BUSY 0x8000
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
/* The top five bits written to EL3_CMD are a command, the lower
* 11 bits are the parameter, if applicable.
*/
enum c509cmd {
TotalReset = 0<<11,
SelectWindow = 1<<11,
StartCoax = 2<<11,
RxDisable = 3<<11,
RxEnable = 4<<11,
RxReset = 5<<11,
RxDiscard = 8<<11,
TxEnable = 9<<11,
TxDisable = 10<<11,
TxReset = 11<<11,
FakeIntr = 12<<11,
AckIntr = 13<<11,
SetIntrEnb = 14<<11,
SetStatusEnb = 15<<11,
SetRxFilter = 16<<11,
SetRxThreshold = 17<<11,
SetTxThreshold = 18<<11,
SetTxStart = 19<<11,
StatsEnable = 21<<11,
StatsDisable = 22<<11,
StopCoax = 23<<11
};
enum c509status {
IntLatch = 0x0001,
AdapterFailure = 0x0002,
TxComplete = 0x0004,
TxAvailable = 0x0008,
RxComplete = 0x0010,
RxEarly = 0x0020,
IntReq = 0x0040,
StatsFull = 0x0080,
CmdBusy = 0x1000
};
/* The SetRxFilter command accepts the following classes: */
enum RxFilter {
RxStation = 1,
RxMulticast = 2,
RxBroadcast = 4,
RxProm = 8
};
/* Register window 1 offsets, the window used in normal operation. */
#define TX_FIFO 0x00
#define RX_FIFO 0x00
#define RX_STATUS 0x08
#define TX_STATUS 0x0B
#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */
/* Time in jiffies before concluding Tx hung */
#define TX_TIMEOUT ((400*HZ)/1000)
struct el3_private {
struct pcmcia_device *p_dev;
/* For transceiver monitoring */
struct timer_list media;
u16 media_status;
u16 fast_poll;
unsigned long last_irq;
spinlock_t lock;
};
static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
/*====================================================================*/
/* Module parameters */
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
/* Special hook for setting if_port when module is loaded */
INT_MODULE_PARM(if_port, 0);
/*====================================================================*/
static int tc589_config(struct pcmcia_device *link);
static void tc589_release(struct pcmcia_device *link);
static u16 read_eeprom(unsigned int ioaddr, int index);
static void tc589_reset(struct net_device *dev);
static void media_check(unsigned long arg);
static int el3_config(struct net_device *dev, struct ifmap *map);
static int el3_open(struct net_device *dev);
static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
struct net_device *dev);
static irqreturn_t el3_interrupt(int irq, void *dev_id);
static void update_stats(struct net_device *dev);
static struct net_device_stats *el3_get_stats(struct net_device *dev);
static int el3_rx(struct net_device *dev);
static int el3_close(struct net_device *dev);
static void el3_tx_timeout(struct net_device *dev);
static void set_rx_mode(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static const struct ethtool_ops netdev_ethtool_ops;
static void tc589_detach(struct pcmcia_device *p_dev);
static const struct net_device_ops el3_netdev_ops = {
.ndo_open = el3_open,
.ndo_stop = el3_close,
.ndo_start_xmit = el3_start_xmit,
.ndo_tx_timeout = el3_tx_timeout,
.ndo_set_config = el3_config,
.ndo_get_stats = el3_get_stats,
.ndo_set_rx_mode = set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static int tc589_probe(struct pcmcia_device *link)
{
struct el3_private *lp;
struct net_device *dev;
dev_dbg(&link->dev, "3c589_attach()\n");
/* Create new ethernet device */
dev = alloc_etherdev(sizeof(struct el3_private));
if (!dev)
return -ENOMEM;
lp = netdev_priv(dev);
link->priv = dev;
lp->p_dev = link;
spin_lock_init(&lp->lock);
link->resource[0]->end = 16;
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
link->config_flags |= CONF_ENABLE_IRQ;
link->config_index = 1;
dev->netdev_ops = &el3_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
dev->ethtool_ops = &netdev_ethtool_ops;
return tc589_config(link);
}
static void tc589_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
dev_dbg(&link->dev, "3c589_detach\n");
unregister_netdev(dev);
tc589_release(link);
free_netdev(dev);
} /* tc589_detach */
static int tc589_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
__be16 *phys_addr;
int ret, i, j, multi = 0, fifo;
unsigned int ioaddr;
static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
u8 *buf;
size_t len;
dev_dbg(&link->dev, "3c589_config\n");
phys_addr = (__be16 *)dev->dev_addr;
/* Is this a 3c562? */
if (link->manf_id != MANFID_3COM)
dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
multi = (link->card_id == PRODID_3COM_3C562);
link->io_lines = 16;
/* For the 3c562, the base address must be xx00-xx7f */
for (i = j = 0; j < 0x400; j += 0x10) {
if (multi && (j & 0x80))
continue;
link->resource[0]->start = j ^ 0x300;
i = pcmcia_request_io(link);
if (i == 0)
break;
}
if (i != 0)
goto failed;
ret = pcmcia_request_irq(link, el3_interrupt);
if (ret)
goto failed;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
dev->irq = link->irq;
dev->base_addr = link->resource[0]->start;
ioaddr = dev->base_addr;
EL3WINDOW(0);
/* The 3c589 has an extra EEPROM for configuration info, including
* the hardware address. The 3c562 puts the address in the CIS.
*/
len = pcmcia_get_tuple(link, 0x88, &buf);
if (buf && len >= 6) {
for (i = 0; i < 3; i++)
phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
kfree(buf);
} else {
kfree(buf); /* 0 < len < 6 */
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
if (phys_addr[0] == htons(0x6060)) {
dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
dev->base_addr, dev->base_addr+15);
goto failed;
}
}
/* The address and resource configuration register aren't loaded from
* the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
*/
outw(0x3f00, ioaddr + 8);
fifo = inl(ioaddr);
/* The if_port symbol can be set when the module is loaded */
if ((if_port >= 0) && (if_port <= 3))
dev->if_port = if_port;
else
dev_err(&link->dev, "invalid if_port requested\n");
SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
dev_err(&link->dev, "register_netdev() failed\n");
goto failed;
}
netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
(multi ? "562" : "589"), dev->base_addr, dev->irq,
dev->dev_addr);
netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
(fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
if_names[dev->if_port]);
return 0;
failed:
tc589_release(link);
return -ENODEV;
} /* tc589_config */
static void tc589_release(struct pcmcia_device *link)
{
pcmcia_disable_device(link);
}
static int tc589_suspend(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
if (link->open)
netif_device_detach(dev);
return 0;
}
static int tc589_resume(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
if (link->open) {
tc589_reset(dev);
netif_device_attach(dev);
}
return 0;
}
/*====================================================================*/
/* Use this for commands that may take time to finish */
static void tc589_wait_for_completion(struct net_device *dev, int cmd)
{
int i = 100;
outw(cmd, dev->base_addr + EL3_CMD);
while (--i > 0)
if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
break;
if (i == 0)
netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
}
/* Read a word from the EEPROM using the regular EEPROM access register.
* Assume that we are in register window zero.
*/
static u16 read_eeprom(unsigned int ioaddr, int index)
{
int i;
outw(EEPROM_READ + index, ioaddr + 10);
/* Reading the eeprom takes 162 us */
for (i = 1620; i >= 0; i--)
if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
break;
return inw(ioaddr + 12);
}
/* Set transceiver type, perhaps to something other than what the user
* specified in dev->if_port.
*/
static void tc589_set_xcvr(struct net_device *dev, int if_port)
{
struct el3_private *lp = netdev_priv(dev);
unsigned int ioaddr = dev->base_addr;
EL3WINDOW(0);
switch (if_port) {
case 0:
case 1:
outw(0, ioaddr + 6);
break;
case 2:
outw(3<<14, ioaddr + 6);
break;
case 3:
outw(1<<14, ioaddr + 6);
break;
}
/* On PCMCIA, this just turns on the LED */
outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
/* 10baseT interface, enable link beat and jabber check. */
EL3WINDOW(4);
outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
EL3WINDOW(1);
if (if_port == 2)
lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
else
lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
}
static void dump_status(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
EL3WINDOW(1);
netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
EL3WINDOW(4);
netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
inw(ioaddr+0x0a));
EL3WINDOW(1);
}
/* Reset and restore all of the 3c589 registers. */
static void tc589_reset(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
int i;
EL3WINDOW(0);
outw(0x0001, ioaddr + 4); /* Activate board. */
outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
/* Set the station address in window 2. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
tc589_set_xcvr(dev, dev->if_port);
/* Switch to the stats window, and clear all stats by reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6);
for (i = 0; i < 9; i++)
inb(ioaddr+i);
inw(ioaddr + 10);
inw(ioaddr + 12);
/* Switch to register set 1 for normal use. */
EL3WINDOW(1);
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
/* Allow status bits to be seen. */
outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| AdapterFailure, ioaddr + EL3_CMD);
}
static void netdev_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));
snprintf(info->bus_info, sizeof(info->bus_info),
"PCMCIA 0x%lx", dev->base_addr);
}
static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
};
static int el3_config(struct net_device *dev, struct ifmap *map)
{
if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
if (map->port <= 3) {
dev->if_port = map->port;
netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
tc589_set_xcvr(dev, dev->if_port);
} else {
return -EINVAL;
}
}
return 0;
}
static int el3_open(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
if (!pcmcia_dev_present(link))
return -ENODEV;
link->open++;
netif_start_queue(dev);
tc589_reset(dev);
init_timer(&lp->media);
lp->media.function = media_check;
lp->media.data = (unsigned long) dev;
lp->media.expires = jiffies + HZ;
add_timer(&lp->media);
dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
dev->name, inw(dev->base_addr + EL3_STATUS));
return 0;
}
static void el3_tx_timeout(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
netdev_warn(dev, "Transmit timed out!\n");
dump_status(dev);
dev->stats.tx_errors++;
dev->trans_start = jiffies; /* prevent tx timeout */
/* Issue TX_RESET and TX_START commands. */
tc589_wait_for_completion(dev, TxReset);
outw(TxEnable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
}
static void pop_tx_status(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
int i;
/* Clear the Tx status stack. */
for (i = 32; i > 0; i--) {
u_char tx_status = inb(ioaddr + TX_STATUS);
if (!(tx_status & 0x84))
break;
/* reset transmitter on jabber error or underrun */
if (tx_status & 0x30)
tc589_wait_for_completion(dev, TxReset);
if (tx_status & 0x38) {
netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
outw(TxEnable, ioaddr + EL3_CMD);
dev->stats.tx_aborted_errors++;
}
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
}
static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
struct el3_private *priv = netdev_priv(dev);
unsigned long flags;
netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
(long)skb->len, inw(ioaddr + EL3_STATUS));
spin_lock_irqsave(&priv->lock, flags);
dev->stats.tx_bytes += skb->len;
/* Put out the doubleword header... */
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO);
/* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
if (inw(ioaddr + TX_FREE) <= 1536) {
netif_stop_queue(dev);
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
}
pop_tx_status(dev);
spin_unlock_irqrestore(&priv->lock, flags);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
/* The EL3 interrupt handler. */
static irqreturn_t el3_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
struct el3_private *lp = netdev_priv(dev);
unsigned int ioaddr;
__u16 status;
int i = 0, handled = 1;
if (!netif_device_present(dev))
return IRQ_NONE;
ioaddr = dev->base_addr;
netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
spin_lock(&lp->lock);
while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) {
if ((status & 0xe000) != 0x2000) {
netdev_dbg(dev, "interrupt from dead card\n");
handled = 0;
break;
}
if (status & RxComplete)
el3_rx(dev);
if (status & TxAvailable) {
netdev_dbg(dev, " TX room bit was handled.\n");
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
}
if (status & TxComplete)
pop_tx_status(dev);
if (status & (AdapterFailure | RxEarly | StatsFull)) {
/* Handle all uncommon interrupts. */
if (status & StatsFull) /* Empty statistics. */
update_stats(dev);
if (status & RxEarly) {
/* Rx early is unused. */
el3_rx(dev);
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
}
if (status & AdapterFailure) {
u16 fifo_diag;
EL3WINDOW(4);
fifo_diag = inw(ioaddr + 4);
EL3WINDOW(1);
netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
fifo_diag);
if (fifo_diag & 0x0400) {
/* Tx overrun */
tc589_wait_for_completion(dev, TxReset);
outw(TxEnable, ioaddr + EL3_CMD);
}
if (fifo_diag & 0x2000) {
/* Rx underrun */
tc589_wait_for_completion(dev, RxReset);
set_rx_mode(dev);
outw(RxEnable, ioaddr + EL3_CMD);
}
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
}
}
if (++i > 10) {
netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
status);
/* Clear all interrupts */
outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
break;
}
/* Acknowledge the IRQ. */
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
}
lp->last_irq = jiffies;
spin_unlock(&lp->lock);
netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
inw(ioaddr + EL3_STATUS));
return IRQ_RETVAL(handled);
}
static void media_check(unsigned long arg)
{
struct net_device *dev = (struct net_device *)(arg);
struct el3_private *lp = netdev_priv(dev);
unsigned int ioaddr = dev->base_addr;
u16 media, errs;
unsigned long flags;
if (!netif_device_present(dev))
goto reschedule;
/* Check for pending interrupt with expired latency timer: with
* this, we can limp along even if the interrupt is blocked
*/
if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
(inb(ioaddr + EL3_TIMER) == 0xff)) {
if (!lp->fast_poll)
netdev_warn(dev, "interrupt(s) dropped!\n");
local_irq_save(flags);
el3_interrupt(dev->irq, dev);
local_irq_restore(flags);
lp->fast_poll = HZ;
}
if (lp->fast_poll) {
lp->fast_poll--;
lp->media.expires = jiffies + HZ/100;
add_timer(&lp->media);
return;
}
/* lp->lock guards the EL3 window. Window should always be 1 except
* when the lock is held
*/
spin_lock_irqsave(&lp->lock, flags);
EL3WINDOW(4);
media = inw(ioaddr+WN4_MEDIA) & 0xc810;
/* Ignore collisions unless we've had no irq's recently */
if (time_before(jiffies, lp->last_irq + HZ)) {
media &= ~0x0010;
} else {
/* Try harder to detect carrier errors */
EL3WINDOW(6);
outw(StatsDisable, ioaddr + EL3_CMD);
errs = inb(ioaddr + 0);
outw(StatsEnable, ioaddr + EL3_CMD);
dev->stats.tx_carrier_errors += errs;
if (errs || (lp->media_status & 0x0010))
media |= 0x0010;
}
if (media != lp->media_status) {
if ((media & lp->media_status & 0x8000) &&
((lp->media_status ^ media) & 0x0800))
netdev_info(dev, "%s link beat\n",
(lp->media_status & 0x0800 ? "lost" : "found"));
else if ((media & lp->media_status & 0x4000) &&
((lp->media_status ^ media) & 0x0010))
netdev_info(dev, "coax cable %s\n",
(lp->media_status & 0x0010 ? "ok" : "problem"));
if (dev->if_port == 0) {
if (media & 0x8000) {
if (media & 0x0800)
netdev_info(dev, "flipped to 10baseT\n");
else
tc589_set_xcvr(dev, 2);
} else if (media & 0x4000) {
if (media & 0x0010)
tc589_set_xcvr(dev, 1);
else
netdev_info(dev, "flipped to 10base2\n");
}
}
lp->media_status = media;
}
EL3WINDOW(1);
spin_unlock_irqrestore(&lp->lock, flags);
reschedule:
lp->media.expires = jiffies + HZ;
add_timer(&lp->media);
}
static struct net_device_stats *el3_get_stats(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
unsigned long flags;
struct pcmcia_device *link = lp->p_dev;
if (pcmcia_dev_present(link)) {
spin_lock_irqsave(&lp->lock, flags);
update_stats(dev);
spin_unlock_irqrestore(&lp->lock, flags);
}
return &dev->stats;
}
/* Update statistics. We change to register window 6, so this should be run
* single-threaded if the device is active. This is expected to be a rare
* operation, and it's simpler for the rest of the driver to assume that
* window 1 is always valid rather than use a special window-state variable.
*
* Caller must hold the lock for this
*/
static void update_stats(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
netdev_dbg(dev, "updating the statistics.\n");
/* Turn off statistics updates while reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Switch to the stats window, and read everything. */
EL3WINDOW(6);
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
/* Multiple collisions. */
inb(ioaddr + 2);
dev->stats.collisions += inb(ioaddr + 3);
dev->stats.tx_window_errors += inb(ioaddr + 4);
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
dev->stats.tx_packets += inb(ioaddr + 6);
/* Rx packets */
inb(ioaddr + 7);
/* Tx deferrals */
inb(ioaddr + 8);
/* Rx octets */
inw(ioaddr + 10);
/* Tx octets */
inw(ioaddr + 12);
/* Back to window 1, and turn statistics back on. */
EL3WINDOW(1);
outw(StatsEnable, ioaddr + EL3_CMD);
}
static int el3_rx(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
int worklimit = 32;
short rx_status;
netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
worklimit > 0) {
worklimit--;
if (rx_status & 0x4000) { /* Error, update stats. */
short error = rx_status & 0x3800;
dev->stats.rx_errors++;
switch (error) {
case 0x0000:
dev->stats.rx_over_errors++;
break;
case 0x0800:
dev->stats.rx_length_errors++;
break;
case 0x1000:
dev->stats.rx_frame_errors++;
break;
case 0x1800:
dev->stats.rx_length_errors++;
break;
case 0x2000:
dev->stats.rx_frame_errors++;
break;
case 0x2800:
dev->stats.rx_crc_errors++;
break;
}
} else {
short pkt_len = rx_status & 0x7ff;
struct sk_buff *skb;
skb = netdev_alloc_skb(dev, pkt_len + 5);
netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb_reserve(skb, 2);
insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
(pkt_len+3)>>2);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
pkt_len);
dev->stats.rx_dropped++;
}
}
/* Pop the top of the Rx FIFO */
tc589_wait_for_completion(dev, RxDiscard);
}
if (worklimit == 0)
netdev_warn(dev, "too much work in el3_rx!\n");
return 0;
}
static void set_rx_mode(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
u16 opts = SetRxFilter | RxStation | RxBroadcast;
if (dev->flags & IFF_PROMISC)
opts |= RxMulticast | RxProm;
else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
opts |= RxMulticast;
outw(opts, ioaddr + EL3_CMD);
}
static void set_multicast_list(struct net_device *dev)
{
struct el3_private *priv = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
set_rx_mode(dev);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int el3_close(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
unsigned int ioaddr = dev->base_addr;
dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
if (pcmcia_dev_present(link)) {
/* Turn off statistics ASAP. We update dev->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Disable the receiver and transmitter. */
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
if (dev->if_port == 2)
/* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD);
else if (dev->if_port == 1) {
/* Disable link beat and jabber */
EL3WINDOW(4);
outw(0, ioaddr + WN4_MEDIA);
}
/* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0);
/* But we explicitly zero the IRQ line select anyway. */
outw(0x0f00, ioaddr + WN0_IRQ);
/* Check if the card still exists */
if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
update_stats(dev);
}
link->open--;
netif_stop_queue(dev);
del_timer_sync(&lp->media);
return 0;
}
static const struct pcmcia_device_id tc589_ids[] = {
PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202),
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"),
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, tc589_ids);
static struct pcmcia_driver tc589_driver = {
.owner = THIS_MODULE,
.name = "3c589_cs",
.probe = tc589_probe,
.remove = tc589_detach,
.id_table = tc589_ids,
.suspend = tc589_suspend,
.resume = tc589_resume,
};
module_pcmcia_driver(tc589_driver);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
#
# 3Com Ethernet device configuration
#
config NET_VENDOR_3COM
bool "3Com devices"
default y
depends on ISA || EISA || PCI || PCMCIA
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about 3Com cards. If you say Y, you will be asked for
your specific card in the following questions.
if NET_VENDOR_3COM
config EL3
tristate "3c509/3c579 \"EtherLink III\" support"
depends on (ISA || EISA)
---help---
If you have a network (Ethernet) card belonging to the 3Com
EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
from <http://www.tldp.org/docs.html#howto>.
If your card is not working you may need to use the DOS
setup disk to disable Plug & Play mode, and to select the default
media type.
To compile this driver as a module, choose M here. The module
will be called 3c509.
config 3C515
tristate "3c515 ISA \"Fast EtherLink\""
depends on ISA && ISA_DMA_API
---help---
If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
network card, say Y and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called 3c515.
config PCMCIA_3C574
tristate "3Com 3c574 PCMCIA support"
depends on PCMCIA
---help---
Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
(PC-card) Fast Ethernet card to your computer.
To compile this driver as a module, choose M here: the module will be
called 3c574_cs. If unsure, say N.
config PCMCIA_3C589
tristate "3Com 3c589 PCMCIA support"
depends on PCMCIA
---help---
Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
(PC-card) Ethernet card to your computer.
To compile this driver as a module, choose M here: the module will be
called 3c589_cs. If unsure, say N.
config VORTEX
tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
depends on (PCI || EISA) && HAS_IOPORT_MAP
select MII
---help---
This option enables driver support for a large number of 10Mbps and
10/100Mbps EISA, PCI and Cardbus 3Com network cards:
"Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI
"Boomerang" (EtherLink XL 3c900 or 3c905) PCI
"Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus
"Tornado" (3c905) PCI
"Hurricane" (3c555/3cSOHO) PCI
If you have such a card, say Y and read the Ethernet-HOWTO,
available from <http://www.tldp.org/docs.html#howto>. More
specific information is in
<file:Documentation/networking/vortex.txt> and in the comments at
the beginning of <file:drivers/net/ethernet/3com/3c59x.c>.
To compile this support as a module, choose M here.
config TYPHOON
tristate "3cr990 series \"Typhoon\" support"
depends on PCI
select CRC32
---help---
This option enables driver support for the 3cr990 series of cards:
3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97,
3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server,
3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called typhoon.
endif # NET_VENDOR_3COM

View file

@ -0,0 +1,10 @@
#
# Makefile for the 3Com Ethernet device drivers
#
obj-$(CONFIG_EL3) += 3c509.o
obj-$(CONFIG_3C515) += 3c515.o
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,624 @@
/* typhoon.h: chip info for the 3Com 3CR990 family of controllers */
/*
Written 2002-2003 by David Dillow <dave@thedillows.org>
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
Drivers based on or derived from this code fall under the GPL and must
retain the authorship, copyright and license notice. This file is not
a complete program and may only be used when the entire operating
system is licensed under the GPL.
This software is available on a public web site. It may enable
cryptographic capabilities of the 3Com hardware, and may be
exported from the United States under License Exception "TSU"
pursuant to 15 C.F.R. Section 740.13(e).
This work was funded by the National Library of Medicine under
the Department of Energy project number 0274DD06D1 and NLM project
number Y1-LM-2015-01.
*/
/* All Typhoon ring positions are specificed in bytes, and point to the
* first "clean" entry in the ring -- ie the next entry we use for whatever
* purpose.
*/
/* The Typhoon basic ring
* ringBase: where this ring lives (our virtual address)
* lastWrite: the next entry we'll use
*/
struct basic_ring {
u8 *ringBase;
u32 lastWrite;
};
/* The Typoon transmit ring -- same as a basic ring, plus:
* lastRead: where we're at in regard to cleaning up the ring
* writeRegister: register to use for writing (different for Hi & Lo rings)
*/
struct transmit_ring {
u8 *ringBase;
u32 lastWrite;
u32 lastRead;
int writeRegister;
};
/* The host<->Typhoon ring index structure
* This indicates the current positions in the rings
*
* All values must be in little endian format for the 3XP
*
* rxHiCleared: entry we've cleared to in the Hi receive ring
* rxLoCleared: entry we've cleared to in the Lo receive ring
* rxBuffReady: next entry we'll put a free buffer in
* respCleared: entry we've cleared to in the response ring
*
* txLoCleared: entry the NIC has cleared to in the Lo transmit ring
* txHiCleared: entry the NIC has cleared to in the Hi transmit ring
* rxLoReady: entry the NIC has filled to in the Lo receive ring
* rxBuffCleared: entry the NIC has cleared in the free buffer ring
* cmdCleared: entry the NIC has cleared in the command ring
* respReady: entry the NIC has filled to in the response ring
* rxHiReady: entry the NIC has filled to in the Hi receive ring
*/
struct typhoon_indexes {
/* The first four are written by the host, and read by the NIC */
volatile __le32 rxHiCleared;
volatile __le32 rxLoCleared;
volatile __le32 rxBuffReady;
volatile __le32 respCleared;
/* The remaining are written by the NIC, and read by the host */
volatile __le32 txLoCleared;
volatile __le32 txHiCleared;
volatile __le32 rxLoReady;
volatile __le32 rxBuffCleared;
volatile __le32 cmdCleared;
volatile __le32 respReady;
volatile __le32 rxHiReady;
} __packed;
/* The host<->Typhoon interface
* Our means of communicating where things are
*
* All values must be in little endian format for the 3XP
*
* ringIndex: 64 bit bus address of the index structure
* txLoAddr: 64 bit bus address of the Lo transmit ring
* txLoSize: size (in bytes) of the Lo transmit ring
* txHi*: as above for the Hi priority transmit ring
* rxLo*: as above for the Lo priority receive ring
* rxBuff*: as above for the free buffer ring
* cmd*: as above for the command ring
* resp*: as above for the response ring
* zeroAddr: 64 bit bus address of a zero word (for DMA)
* rxHi*: as above for the Hi Priority receive ring
*
* While there is room for 64 bit addresses, current versions of the 3XP
* only do 32 bit addresses, so the *Hi for each of the above will always
* be zero.
*/
struct typhoon_interface {
__le32 ringIndex;
__le32 ringIndexHi;
__le32 txLoAddr;
__le32 txLoAddrHi;
__le32 txLoSize;
__le32 txHiAddr;
__le32 txHiAddrHi;
__le32 txHiSize;
__le32 rxLoAddr;
__le32 rxLoAddrHi;
__le32 rxLoSize;
__le32 rxBuffAddr;
__le32 rxBuffAddrHi;
__le32 rxBuffSize;
__le32 cmdAddr;
__le32 cmdAddrHi;
__le32 cmdSize;
__le32 respAddr;
__le32 respAddrHi;
__le32 respSize;
__le32 zeroAddr;
__le32 zeroAddrHi;
__le32 rxHiAddr;
__le32 rxHiAddrHi;
__le32 rxHiSize;
} __packed;
/* The Typhoon transmit/fragment descriptor
*
* A packet is described by a packet descriptor, followed by option descriptors,
* if any, then one or more fragment descriptors.
*
* Packet descriptor:
* flags: Descriptor type
* len:i zero, or length of this packet
* addr*: 8 bytes of opaque data to the firmware -- for skb pointer
* processFlags: Determine offload tasks to perform on this packet.
*
* Fragment descriptor:
* flags: Descriptor type
* len:i length of this fragment
* addr: low bytes of DMA address for this part of the packet
* addrHi: hi bytes of DMA address for this part of the packet
* processFlags: must be zero
*
* TYPHOON_DESC_VALID is not mentioned in their docs, but their Linux
* driver uses it.
*/
struct tx_desc {
u8 flags;
#define TYPHOON_TYPE_MASK 0x07
#define TYPHOON_FRAG_DESC 0x00
#define TYPHOON_TX_DESC 0x01
#define TYPHOON_CMD_DESC 0x02
#define TYPHOON_OPT_DESC 0x03
#define TYPHOON_RX_DESC 0x04
#define TYPHOON_RESP_DESC 0x05
#define TYPHOON_OPT_TYPE_MASK 0xf0
#define TYPHOON_OPT_IPSEC 0x00
#define TYPHOON_OPT_TCP_SEG 0x10
#define TYPHOON_CMD_RESPOND 0x40
#define TYPHOON_RESP_ERROR 0x40
#define TYPHOON_RX_ERROR 0x40
#define TYPHOON_DESC_VALID 0x80
u8 numDesc;
__le16 len;
union {
struct {
__le32 addr;
__le32 addrHi;
} frag;
u64 tx_addr; /* opaque for hardware, for TX_DESC */
};
__le32 processFlags;
#define TYPHOON_TX_PF_NO_CRC cpu_to_le32(0x00000001)
#define TYPHOON_TX_PF_IP_CHKSUM cpu_to_le32(0x00000002)
#define TYPHOON_TX_PF_TCP_CHKSUM cpu_to_le32(0x00000004)
#define TYPHOON_TX_PF_TCP_SEGMENT cpu_to_le32(0x00000008)
#define TYPHOON_TX_PF_INSERT_VLAN cpu_to_le32(0x00000010)
#define TYPHOON_TX_PF_IPSEC cpu_to_le32(0x00000020)
#define TYPHOON_TX_PF_VLAN_PRIORITY cpu_to_le32(0x00000040)
#define TYPHOON_TX_PF_UDP_CHKSUM cpu_to_le32(0x00000080)
#define TYPHOON_TX_PF_PAD_FRAME cpu_to_le32(0x00000100)
#define TYPHOON_TX_PF_RESERVED cpu_to_le32(0x00000e00)
#define TYPHOON_TX_PF_VLAN_MASK cpu_to_le32(0x0ffff000)
#define TYPHOON_TX_PF_INTERNAL cpu_to_le32(0xf0000000)
#define TYPHOON_TX_PF_VLAN_TAG_SHIFT 12
} __packed;
/* The TCP Segmentation offload option descriptor
*
* flags: descriptor type
* numDesc: must be 1
* mss_flags: bits 0-11 (little endian) are MSS, 12 is first TSO descriptor
* 13 is list TSO descriptor, set both if only one TSO
* respAddrLo: low bytes of address of the bytesTx field of this descriptor
* bytesTx: total number of bytes in this TSO request
* status: 0 on completion
*/
struct tcpopt_desc {
u8 flags;
u8 numDesc;
__le16 mss_flags;
#define TYPHOON_TSO_FIRST cpu_to_le16(0x1000)
#define TYPHOON_TSO_LAST cpu_to_le16(0x2000)
__le32 respAddrLo;
__le32 bytesTx;
__le32 status;
} __packed;
/* The IPSEC Offload descriptor
*
* flags: descriptor type
* numDesc: must be 1
* ipsecFlags: bit 0: 0 -- generate IV, 1 -- use supplied IV
* sa1, sa2: Security Association IDs for this packet
* reserved: set to 0
*/
struct ipsec_desc {
u8 flags;
u8 numDesc;
__le16 ipsecFlags;
#define TYPHOON_IPSEC_GEN_IV cpu_to_le16(0x0000)
#define TYPHOON_IPSEC_USE_IV cpu_to_le16(0x0001)
__le32 sa1;
__le32 sa2;
__le32 reserved;
} __packed;
/* The Typhoon receive descriptor (Updated by NIC)
*
* flags: Descriptor type, error indication
* numDesc: Always zero
* frameLen: the size of the packet received
* addr: low 32 bytes of the virtual addr passed in for this buffer
* addrHi: high 32 bytes of the virtual addr passed in for this buffer
* rxStatus: Error if set in flags, otherwise result of offload processing
* filterResults: results of filtering on packet, not used
* ipsecResults: Results of IPSEC processing
* vlanTag: the 801.2q TCI from the packet
*/
struct rx_desc {
u8 flags;
u8 numDesc;
__le16 frameLen;
u32 addr; /* opaque, comes from virtAddr */
u32 addrHi; /* opaque, comes from virtAddrHi */
__le32 rxStatus;
#define TYPHOON_RX_ERR_INTERNAL cpu_to_le32(0x00000000)
#define TYPHOON_RX_ERR_FIFO_UNDERRUN cpu_to_le32(0x00000001)
#define TYPHOON_RX_ERR_BAD_SSD cpu_to_le32(0x00000002)
#define TYPHOON_RX_ERR_RUNT cpu_to_le32(0x00000003)
#define TYPHOON_RX_ERR_CRC cpu_to_le32(0x00000004)
#define TYPHOON_RX_ERR_OVERSIZE cpu_to_le32(0x00000005)
#define TYPHOON_RX_ERR_ALIGN cpu_to_le32(0x00000006)
#define TYPHOON_RX_ERR_DRIBBLE cpu_to_le32(0x00000007)
#define TYPHOON_RX_PROTO_MASK cpu_to_le32(0x00000003)
#define TYPHOON_RX_PROTO_UNKNOWN cpu_to_le32(0x00000000)
#define TYPHOON_RX_PROTO_IP cpu_to_le32(0x00000001)
#define TYPHOON_RX_PROTO_IPX cpu_to_le32(0x00000002)
#define TYPHOON_RX_VLAN cpu_to_le32(0x00000004)
#define TYPHOON_RX_IP_FRAG cpu_to_le32(0x00000008)
#define TYPHOON_RX_IPSEC cpu_to_le32(0x00000010)
#define TYPHOON_RX_IP_CHK_FAIL cpu_to_le32(0x00000020)
#define TYPHOON_RX_TCP_CHK_FAIL cpu_to_le32(0x00000040)
#define TYPHOON_RX_UDP_CHK_FAIL cpu_to_le32(0x00000080)
#define TYPHOON_RX_IP_CHK_GOOD cpu_to_le32(0x00000100)
#define TYPHOON_RX_TCP_CHK_GOOD cpu_to_le32(0x00000200)
#define TYPHOON_RX_UDP_CHK_GOOD cpu_to_le32(0x00000400)
__le16 filterResults;
#define TYPHOON_RX_FILTER_MASK cpu_to_le16(0x7fff)
#define TYPHOON_RX_FILTERED cpu_to_le16(0x8000)
__le16 ipsecResults;
#define TYPHOON_RX_OUTER_AH_GOOD cpu_to_le16(0x0001)
#define TYPHOON_RX_OUTER_ESP_GOOD cpu_to_le16(0x0002)
#define TYPHOON_RX_INNER_AH_GOOD cpu_to_le16(0x0004)
#define TYPHOON_RX_INNER_ESP_GOOD cpu_to_le16(0x0008)
#define TYPHOON_RX_OUTER_AH_FAIL cpu_to_le16(0x0010)
#define TYPHOON_RX_OUTER_ESP_FAIL cpu_to_le16(0x0020)
#define TYPHOON_RX_INNER_AH_FAIL cpu_to_le16(0x0040)
#define TYPHOON_RX_INNER_ESP_FAIL cpu_to_le16(0x0080)
#define TYPHOON_RX_UNKNOWN_SA cpu_to_le16(0x0100)
#define TYPHOON_RX_ESP_FORMAT_ERR cpu_to_le16(0x0200)
__be32 vlanTag;
} __packed;
/* The Typhoon free buffer descriptor, used to give a buffer to the NIC
*
* physAddr: low 32 bits of the bus address of the buffer
* physAddrHi: high 32 bits of the bus address of the buffer, always zero
* virtAddr: low 32 bits of the skb address
* virtAddrHi: high 32 bits of the skb address, always zero
*
* the virt* address is basically two 32 bit cookies, just passed back
* from the NIC
*/
struct rx_free {
__le32 physAddr;
__le32 physAddrHi;
u32 virtAddr;
u32 virtAddrHi;
} __packed;
/* The Typhoon command descriptor, used for commands and responses
*
* flags: descriptor type
* numDesc: number of descriptors following in this command/response,
* ie, zero for a one descriptor command
* cmd: the command
* seqNo: sequence number (unused)
* parm1: use varies by command
* parm2: use varies by command
* parm3: use varies by command
*/
struct cmd_desc {
u8 flags;
u8 numDesc;
__le16 cmd;
#define TYPHOON_CMD_TX_ENABLE cpu_to_le16(0x0001)
#define TYPHOON_CMD_TX_DISABLE cpu_to_le16(0x0002)
#define TYPHOON_CMD_RX_ENABLE cpu_to_le16(0x0003)
#define TYPHOON_CMD_RX_DISABLE cpu_to_le16(0x0004)
#define TYPHOON_CMD_SET_RX_FILTER cpu_to_le16(0x0005)
#define TYPHOON_CMD_READ_STATS cpu_to_le16(0x0007)
#define TYPHOON_CMD_XCVR_SELECT cpu_to_le16(0x0013)
#define TYPHOON_CMD_SET_MAX_PKT_SIZE cpu_to_le16(0x001a)
#define TYPHOON_CMD_READ_MEDIA_STATUS cpu_to_le16(0x001b)
#define TYPHOON_CMD_GOTO_SLEEP cpu_to_le16(0x0023)
#define TYPHOON_CMD_SET_MULTICAST_HASH cpu_to_le16(0x0025)
#define TYPHOON_CMD_SET_MAC_ADDRESS cpu_to_le16(0x0026)
#define TYPHOON_CMD_READ_MAC_ADDRESS cpu_to_le16(0x0027)
#define TYPHOON_CMD_VLAN_TYPE_WRITE cpu_to_le16(0x002b)
#define TYPHOON_CMD_CREATE_SA cpu_to_le16(0x0034)
#define TYPHOON_CMD_DELETE_SA cpu_to_le16(0x0035)
#define TYPHOON_CMD_READ_VERSIONS cpu_to_le16(0x0043)
#define TYPHOON_CMD_IRQ_COALESCE_CTRL cpu_to_le16(0x0045)
#define TYPHOON_CMD_ENABLE_WAKE_EVENTS cpu_to_le16(0x0049)
#define TYPHOON_CMD_SET_OFFLOAD_TASKS cpu_to_le16(0x004f)
#define TYPHOON_CMD_HELLO_RESP cpu_to_le16(0x0057)
#define TYPHOON_CMD_HALT cpu_to_le16(0x005d)
#define TYPHOON_CMD_READ_IPSEC_INFO cpu_to_le16(0x005e)
#define TYPHOON_CMD_GET_IPSEC_ENABLE cpu_to_le16(0x0067)
#define TYPHOON_CMD_GET_CMD_LVL cpu_to_le16(0x0069)
u16 seqNo;
__le16 parm1;
__le32 parm2;
__le32 parm3;
} __packed;
/* The Typhoon response descriptor, see command descriptor for details
*/
struct resp_desc {
u8 flags;
u8 numDesc;
__le16 cmd;
__le16 seqNo;
__le16 parm1;
__le32 parm2;
__le32 parm3;
} __packed;
#define INIT_COMMAND_NO_RESPONSE(x, command) \
do { struct cmd_desc *_ptr = (x); \
memset(_ptr, 0, sizeof(struct cmd_desc)); \
_ptr->flags = TYPHOON_CMD_DESC | TYPHOON_DESC_VALID; \
_ptr->cmd = command; \
} while(0)
/* We set seqNo to 1 if we're expecting a response from this command */
#define INIT_COMMAND_WITH_RESPONSE(x, command) \
do { struct cmd_desc *_ptr = (x); \
memset(_ptr, 0, sizeof(struct cmd_desc)); \
_ptr->flags = TYPHOON_CMD_RESPOND | TYPHOON_CMD_DESC; \
_ptr->flags |= TYPHOON_DESC_VALID; \
_ptr->cmd = command; \
_ptr->seqNo = 1; \
} while(0)
/* TYPHOON_CMD_SET_RX_FILTER filter bits (cmd.parm1)
*/
#define TYPHOON_RX_FILTER_DIRECTED cpu_to_le16(0x0001)
#define TYPHOON_RX_FILTER_ALL_MCAST cpu_to_le16(0x0002)
#define TYPHOON_RX_FILTER_BROADCAST cpu_to_le16(0x0004)
#define TYPHOON_RX_FILTER_PROMISCOUS cpu_to_le16(0x0008)
#define TYPHOON_RX_FILTER_MCAST_HASH cpu_to_le16(0x0010)
/* TYPHOON_CMD_READ_STATS response format
*/
struct stats_resp {
u8 flags;
u8 numDesc;
__le16 cmd;
__le16 seqNo;
__le16 unused;
__le32 txPackets;
__le64 txBytes;
__le32 txDeferred;
__le32 txLateCollisions;
__le32 txCollisions;
__le32 txCarrierLost;
__le32 txMultipleCollisions;
__le32 txExcessiveCollisions;
__le32 txFifoUnderruns;
__le32 txMulticastTxOverflows;
__le32 txFiltered;
__le32 rxPacketsGood;
__le64 rxBytesGood;
__le32 rxFifoOverruns;
__le32 BadSSD;
__le32 rxCrcErrors;
__le32 rxOversized;
__le32 rxBroadcast;
__le32 rxMulticast;
__le32 rxOverflow;
__le32 rxFiltered;
__le32 linkStatus;
#define TYPHOON_LINK_STAT_MASK cpu_to_le32(0x00000001)
#define TYPHOON_LINK_GOOD cpu_to_le32(0x00000001)
#define TYPHOON_LINK_BAD cpu_to_le32(0x00000000)
#define TYPHOON_LINK_SPEED_MASK cpu_to_le32(0x00000002)
#define TYPHOON_LINK_100MBPS cpu_to_le32(0x00000002)
#define TYPHOON_LINK_10MBPS cpu_to_le32(0x00000000)
#define TYPHOON_LINK_DUPLEX_MASK cpu_to_le32(0x00000004)
#define TYPHOON_LINK_FULL_DUPLEX cpu_to_le32(0x00000004)
#define TYPHOON_LINK_HALF_DUPLEX cpu_to_le32(0x00000000)
__le32 unused2;
__le32 unused3;
} __packed;
/* TYPHOON_CMD_XCVR_SELECT xcvr values (resp.parm1)
*/
#define TYPHOON_XCVR_10HALF cpu_to_le16(0x0000)
#define TYPHOON_XCVR_10FULL cpu_to_le16(0x0001)
#define TYPHOON_XCVR_100HALF cpu_to_le16(0x0002)
#define TYPHOON_XCVR_100FULL cpu_to_le16(0x0003)
#define TYPHOON_XCVR_AUTONEG cpu_to_le16(0x0004)
/* TYPHOON_CMD_READ_MEDIA_STATUS (resp.parm1)
*/
#define TYPHOON_MEDIA_STAT_CRC_STRIP_DISABLE cpu_to_le16(0x0004)
#define TYPHOON_MEDIA_STAT_COLLISION_DETECT cpu_to_le16(0x0010)
#define TYPHOON_MEDIA_STAT_CARRIER_SENSE cpu_to_le16(0x0020)
#define TYPHOON_MEDIA_STAT_POLARITY_REV cpu_to_le16(0x0400)
#define TYPHOON_MEDIA_STAT_NO_LINK cpu_to_le16(0x0800)
/* TYPHOON_CMD_SET_MULTICAST_HASH enable values (cmd.parm1)
*/
#define TYPHOON_MCAST_HASH_DISABLE cpu_to_le16(0x0000)
#define TYPHOON_MCAST_HASH_ENABLE cpu_to_le16(0x0001)
#define TYPHOON_MCAST_HASH_SET cpu_to_le16(0x0002)
/* TYPHOON_CMD_CREATE_SA descriptor and settings
*/
struct sa_descriptor {
u8 flags;
u8 numDesc;
u16 cmd;
u16 seqNo;
u16 mode;
#define TYPHOON_SA_MODE_NULL cpu_to_le16(0x0000)
#define TYPHOON_SA_MODE_AH cpu_to_le16(0x0001)
#define TYPHOON_SA_MODE_ESP cpu_to_le16(0x0002)
u8 hashFlags;
#define TYPHOON_SA_HASH_ENABLE 0x01
#define TYPHOON_SA_HASH_SHA1 0x02
#define TYPHOON_SA_HASH_MD5 0x04
u8 direction;
#define TYPHOON_SA_DIR_RX 0x00
#define TYPHOON_SA_DIR_TX 0x01
u8 encryptionFlags;
#define TYPHOON_SA_ENCRYPT_ENABLE 0x01
#define TYPHOON_SA_ENCRYPT_DES 0x02
#define TYPHOON_SA_ENCRYPT_3DES 0x00
#define TYPHOON_SA_ENCRYPT_3DES_2KEY 0x00
#define TYPHOON_SA_ENCRYPT_3DES_3KEY 0x04
#define TYPHOON_SA_ENCRYPT_CBC 0x08
#define TYPHOON_SA_ENCRYPT_ECB 0x00
u8 specifyIndex;
#define TYPHOON_SA_SPECIFY_INDEX 0x01
#define TYPHOON_SA_GENERATE_INDEX 0x00
u32 SPI;
u32 destAddr;
u32 destMask;
u8 integKey[20];
u8 confKey[24];
u32 index;
u32 unused;
u32 unused2;
} __packed;
/* TYPHOON_CMD_SET_OFFLOAD_TASKS bits (cmd.parm2 (Tx) & cmd.parm3 (Rx))
* This is all for IPv4.
*/
#define TYPHOON_OFFLOAD_TCP_CHKSUM cpu_to_le32(0x00000002)
#define TYPHOON_OFFLOAD_UDP_CHKSUM cpu_to_le32(0x00000004)
#define TYPHOON_OFFLOAD_IP_CHKSUM cpu_to_le32(0x00000008)
#define TYPHOON_OFFLOAD_IPSEC cpu_to_le32(0x00000010)
#define TYPHOON_OFFLOAD_BCAST_THROTTLE cpu_to_le32(0x00000020)
#define TYPHOON_OFFLOAD_DHCP_PREVENT cpu_to_le32(0x00000040)
#define TYPHOON_OFFLOAD_VLAN cpu_to_le32(0x00000080)
#define TYPHOON_OFFLOAD_FILTERING cpu_to_le32(0x00000100)
#define TYPHOON_OFFLOAD_TCP_SEGMENT cpu_to_le32(0x00000200)
/* TYPHOON_CMD_ENABLE_WAKE_EVENTS bits (cmd.parm1)
*/
#define TYPHOON_WAKE_MAGIC_PKT cpu_to_le16(0x01)
#define TYPHOON_WAKE_LINK_EVENT cpu_to_le16(0x02)
#define TYPHOON_WAKE_ICMP_ECHO cpu_to_le16(0x04)
#define TYPHOON_WAKE_ARP cpu_to_le16(0x08)
/* These are used to load the firmware image on the NIC
*/
struct typhoon_file_header {
u8 tag[8];
__le32 version;
__le32 numSections;
__le32 startAddr;
__le32 hmacDigest[5];
} __packed;
struct typhoon_section_header {
__le32 len;
u16 checksum;
u16 reserved;
__le32 startAddr;
} __packed;
/* The Typhoon Register offsets
*/
#define TYPHOON_REG_SOFT_RESET 0x00
#define TYPHOON_REG_INTR_STATUS 0x04
#define TYPHOON_REG_INTR_ENABLE 0x08
#define TYPHOON_REG_INTR_MASK 0x0c
#define TYPHOON_REG_SELF_INTERRUPT 0x10
#define TYPHOON_REG_HOST2ARM7 0x14
#define TYPHOON_REG_HOST2ARM6 0x18
#define TYPHOON_REG_HOST2ARM5 0x1c
#define TYPHOON_REG_HOST2ARM4 0x20
#define TYPHOON_REG_HOST2ARM3 0x24
#define TYPHOON_REG_HOST2ARM2 0x28
#define TYPHOON_REG_HOST2ARM1 0x2c
#define TYPHOON_REG_HOST2ARM0 0x30
#define TYPHOON_REG_ARM2HOST3 0x34
#define TYPHOON_REG_ARM2HOST2 0x38
#define TYPHOON_REG_ARM2HOST1 0x3c
#define TYPHOON_REG_ARM2HOST0 0x40
#define TYPHOON_REG_BOOT_DATA_LO TYPHOON_REG_HOST2ARM5
#define TYPHOON_REG_BOOT_DATA_HI TYPHOON_REG_HOST2ARM4
#define TYPHOON_REG_BOOT_DEST_ADDR TYPHOON_REG_HOST2ARM3
#define TYPHOON_REG_BOOT_CHECKSUM TYPHOON_REG_HOST2ARM2
#define TYPHOON_REG_BOOT_LENGTH TYPHOON_REG_HOST2ARM1
#define TYPHOON_REG_DOWNLOAD_BOOT_ADDR TYPHOON_REG_HOST2ARM1
#define TYPHOON_REG_DOWNLOAD_HMAC_0 TYPHOON_REG_HOST2ARM2
#define TYPHOON_REG_DOWNLOAD_HMAC_1 TYPHOON_REG_HOST2ARM3
#define TYPHOON_REG_DOWNLOAD_HMAC_2 TYPHOON_REG_HOST2ARM4
#define TYPHOON_REG_DOWNLOAD_HMAC_3 TYPHOON_REG_HOST2ARM5
#define TYPHOON_REG_DOWNLOAD_HMAC_4 TYPHOON_REG_HOST2ARM6
#define TYPHOON_REG_BOOT_RECORD_ADDR_HI TYPHOON_REG_HOST2ARM2
#define TYPHOON_REG_BOOT_RECORD_ADDR_LO TYPHOON_REG_HOST2ARM1
#define TYPHOON_REG_TX_LO_READY TYPHOON_REG_HOST2ARM3
#define TYPHOON_REG_CMD_READY TYPHOON_REG_HOST2ARM2
#define TYPHOON_REG_TX_HI_READY TYPHOON_REG_HOST2ARM1
#define TYPHOON_REG_COMMAND TYPHOON_REG_HOST2ARM0
#define TYPHOON_REG_HEARTBEAT TYPHOON_REG_ARM2HOST3
#define TYPHOON_REG_STATUS TYPHOON_REG_ARM2HOST0
/* 3XP Reset values (TYPHOON_REG_SOFT_RESET)
*/
#define TYPHOON_RESET_ALL 0x7f
#define TYPHOON_RESET_NONE 0x00
/* 3XP irq bits (TYPHOON_REG_INTR{STATUS,ENABLE,MASK})
*
* Some of these came from OpenBSD, as the 3Com docs have it wrong
* (INTR_SELF) or don't list it at all (INTR_*_ABORT)
*
* Enabling irqs on the Heartbeat reg (ArmToHost3) gets you an irq
* about every 8ms, so don't do it.
*/
#define TYPHOON_INTR_HOST_INT 0x00000001
#define TYPHOON_INTR_ARM2HOST0 0x00000002
#define TYPHOON_INTR_ARM2HOST1 0x00000004
#define TYPHOON_INTR_ARM2HOST2 0x00000008
#define TYPHOON_INTR_ARM2HOST3 0x00000010
#define TYPHOON_INTR_DMA0 0x00000020
#define TYPHOON_INTR_DMA1 0x00000040
#define TYPHOON_INTR_DMA2 0x00000080
#define TYPHOON_INTR_DMA3 0x00000100
#define TYPHOON_INTR_MASTER_ABORT 0x00000200
#define TYPHOON_INTR_TARGET_ABORT 0x00000400
#define TYPHOON_INTR_SELF 0x00000800
#define TYPHOON_INTR_RESERVED 0xfffff000
#define TYPHOON_INTR_BOOTCMD TYPHOON_INTR_ARM2HOST0
#define TYPHOON_INTR_ENABLE_ALL 0xffffffef
#define TYPHOON_INTR_ALL 0xffffffff
#define TYPHOON_INTR_NONE 0x00000000
/* The commands for the 3XP chip (TYPHOON_REG_COMMAND)
*/
#define TYPHOON_BOOTCMD_BOOT 0x00
#define TYPHOON_BOOTCMD_WAKEUP 0xfa
#define TYPHOON_BOOTCMD_DNLD_COMPLETE 0xfb
#define TYPHOON_BOOTCMD_SEG_AVAILABLE 0xfc
#define TYPHOON_BOOTCMD_RUNTIME_IMAGE 0xfd
#define TYPHOON_BOOTCMD_REG_BOOT_RECORD 0xff
/* 3XP Status values (TYPHOON_REG_STATUS)
*/
#define TYPHOON_STATUS_WAITING_FOR_BOOT 0x07
#define TYPHOON_STATUS_SECOND_INIT 0x08
#define TYPHOON_STATUS_RUNNING 0x09
#define TYPHOON_STATUS_WAITING_FOR_HOST 0x0d
#define TYPHOON_STATUS_WAITING_FOR_SEGMENT 0x10
#define TYPHOON_STATUS_SLEEPING 0x11
#define TYPHOON_STATUS_HALTED 0x14

View file

@ -0,0 +1,103 @@
/* 8390 core for usual drivers */
static const char version[] =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include "lib8390.c"
int ei_open(struct net_device *dev)
{
return __ei_open(dev);
}
EXPORT_SYMBOL(ei_open);
int ei_close(struct net_device *dev)
{
return __ei_close(dev);
}
EXPORT_SYMBOL(ei_close);
netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
return __ei_start_xmit(skb, dev);
}
EXPORT_SYMBOL(ei_start_xmit);
struct net_device_stats *ei_get_stats(struct net_device *dev)
{
return __ei_get_stats(dev);
}
EXPORT_SYMBOL(ei_get_stats);
void ei_set_multicast_list(struct net_device *dev)
{
__ei_set_multicast_list(dev);
}
EXPORT_SYMBOL(ei_set_multicast_list);
void ei_tx_timeout(struct net_device *dev)
{
__ei_tx_timeout(dev);
}
EXPORT_SYMBOL(ei_tx_timeout);
irqreturn_t ei_interrupt(int irq, void *dev_id)
{
return __ei_interrupt(irq, dev_id);
}
EXPORT_SYMBOL(ei_interrupt);
#ifdef CONFIG_NET_POLL_CONTROLLER
void ei_poll(struct net_device *dev)
{
__ei_poll(dev);
}
EXPORT_SYMBOL(ei_poll);
#endif
const struct net_device_ops ei_netdev_ops = {
.ndo_open = ei_open,
.ndo_stop = ei_close,
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
.ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ei_poll,
#endif
};
EXPORT_SYMBOL(ei_netdev_ops);
struct net_device *__alloc_ei_netdev(int size)
{
struct net_device *dev = ____alloc_ei_netdev(size);
if (dev)
dev->netdev_ops = &ei_netdev_ops;
return dev;
}
EXPORT_SYMBOL(__alloc_ei_netdev);
void NS8390_init(struct net_device *dev, int startp)
{
__NS8390_init(dev, startp);
}
EXPORT_SYMBOL(NS8390_init);
#if defined(MODULE)
static int __init ns8390_module_init(void)
{
return 0;
}
static void __exit ns8390_module_exit(void)
{
}
module_init(ns8390_module_init);
module_exit(ns8390_module_exit);
#endif /* MODULE */
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,225 @@
/* Generic NS8390 register definitions. */
/* This file is part of Donald Becker's 8390 drivers, and is distributed
under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
Some of these names and comments originated from the Crynwr
packet drivers, which are distributed under the GPL. */
#ifndef _8390_h
#define _8390_h
#include <linux/if_ether.h>
#include <linux/ioport.h>
#include <linux/irqreturn.h>
#include <linux/skbuff.h>
#define TX_PAGES 12 /* Two Tx slots */
/* The 8390 specific per-packet-header format. */
struct e8390_pkt_hdr {
unsigned char status; /* status */
unsigned char next; /* pointer to next packet. */
unsigned short count; /* header + packet length in bytes */
};
#ifdef CONFIG_NET_POLL_CONTROLLER
void ei_poll(struct net_device *dev);
void eip_poll(struct net_device *dev);
#endif
/* Without I/O delay - non ISA or later chips */
void NS8390_init(struct net_device *dev, int startp);
int ei_open(struct net_device *dev);
int ei_close(struct net_device *dev);
irqreturn_t ei_interrupt(int irq, void *dev_id);
void ei_tx_timeout(struct net_device *dev);
netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
void ei_set_multicast_list(struct net_device *dev);
struct net_device_stats *ei_get_stats(struct net_device *dev);
extern const struct net_device_ops ei_netdev_ops;
struct net_device *__alloc_ei_netdev(int size);
static inline struct net_device *alloc_ei_netdev(void)
{
return __alloc_ei_netdev(0);
}
/* With I/O delay form */
void NS8390p_init(struct net_device *dev, int startp);
int eip_open(struct net_device *dev);
int eip_close(struct net_device *dev);
irqreturn_t eip_interrupt(int irq, void *dev_id);
void eip_tx_timeout(struct net_device *dev);
netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
void eip_set_multicast_list(struct net_device *dev);
struct net_device_stats *eip_get_stats(struct net_device *dev);
extern const struct net_device_ops eip_netdev_ops;
struct net_device *__alloc_eip_netdev(int size);
static inline struct net_device *alloc_eip_netdev(void)
{
return __alloc_eip_netdev(0);
}
/* You have one of these per-board */
struct ei_device {
const char *name;
void (*reset_8390)(struct net_device *);
void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int);
void (*block_output)(struct net_device *, int, const unsigned char *, int);
void (*block_input)(struct net_device *, int, struct sk_buff *, int);
unsigned long rmem_start;
unsigned long rmem_end;
void __iomem *mem;
unsigned char mcfilter[8];
unsigned open:1;
unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
unsigned bigendian:1; /* 16-bit big endian mode. Do NOT */
/* set this on random 8390 clones! */
unsigned txing:1; /* Transmit Active */
unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
unsigned dmaing:1; /* Remote DMA Active */
unsigned char tx_start_page, rx_start_page, stop_page;
unsigned char current_page; /* Read pointer in buffer */
unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
unsigned char txqueue; /* Tx Packet buffer queue length. */
short tx1, tx2; /* Packet lengths for ping-pong tx. */
short lasttx; /* Alpha version consistency check. */
unsigned char reg0; /* Register '0' in a WD8013 */
unsigned char reg5; /* Register '5' in a WD8013 */
unsigned char saved_irq; /* Original dev->irq value. */
u32 *reg_offset; /* Register mapping table */
spinlock_t page_lock; /* Page register locks */
unsigned long priv; /* Private field to store bus IDs etc. */
u32 msg_enable; /* debug message level */
#ifdef AX88796_PLATFORM
unsigned char rxcr_base; /* default value for RXCR */
#endif
};
/* The maximum number of 8390 interrupt service routines called per IRQ. */
#define MAX_SERVICE 12
/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
#define TX_TIMEOUT (20*HZ/100)
#define ei_status (*(struct ei_device *)netdev_priv(dev))
/* Some generic ethernet register configurations. */
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
#define E8390_RX_IRQ_MASK 0x5
#ifdef AX88796_PLATFORM
#define E8390_RXCONFIG (ei_status.rxcr_base | 0x04)
#define E8390_RXOFF (ei_status.rxcr_base | 0x20)
#else
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
#endif
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
/* Register accessed at EN_CMD, the 8390 base addr. */
#define E8390_STOP 0x01 /* Stop and reset the chip */
#define E8390_START 0x02 /* Start the chip, clear reset */
#define E8390_TRANS 0x04 /* Transmit a frame */
#define E8390_RREAD 0x08 /* Remote read */
#define E8390_RWRITE 0x10 /* Remote write */
#define E8390_NODMA 0x20 /* Remote DMA */
#define E8390_PAGE0 0x00 /* Select page chip registers */
#define E8390_PAGE1 0x40 /* using the two high-order bits */
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
/*
* Only generate indirect loads given a machine that needs them.
* - removed AMIGA_PCMCIA from this list, handled as ISA io now
* - the _p for generates no delay by default 8390p.c overrides this.
*/
#ifndef ei_inb
#define ei_inb(_p) inb(_p)
#define ei_outb(_v,_p) outb(_v,_p)
#define ei_inb_p(_p) inb(_p)
#define ei_outb_p(_v,_p) outb(_v,_p)
#endif
#ifndef EI_SHIFT
#define EI_SHIFT(x) (x)
#endif
#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */
#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */
#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */
#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */
#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */
#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */
#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */
#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */
#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */
#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */
#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */
#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */
#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */
#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */
#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */
#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */
#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */
#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */
#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */
#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */
#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */
#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */
#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */
#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */
/* Bits in EN0_ISR - Interrupt status register */
#define ENISR_RX 0x01 /* Receiver, no error */
#define ENISR_TX 0x02 /* Transmitter, no error */
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
#define ENISR_RDC 0x40 /* remote dma complete */
#define ENISR_RESET 0x80 /* Reset completed */
#define ENISR_ALL 0x3f /* Interrupts we will enable */
/* Bits in EN0_DCFG - Data config register */
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
#define ENDCFG_BOS 0x02 /* byte order selection */
/* Page 1 register offsets. */
#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */
#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */
#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */
#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */
#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */
/* Bits in received packet status byte and EN0_RSR*/
#define ENRSR_RXOK 0x01 /* Received a good packet */
#define ENRSR_CRC 0x02 /* CRC error */
#define ENRSR_FAE 0x04 /* frame alignment error */
#define ENRSR_FO 0x08 /* FIFO overrun */
#define ENRSR_MPA 0x10 /* missed pkt */
#define ENRSR_PHY 0x20 /* physical/multicast address */
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
#define ENRSR_DEF 0x80 /* deferring */
/* Transmitted packet status, EN0_TSR. */
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
#define ENTSR_COL 0x04 /* The transmit collided at least once. */
#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
#endif /* _8390_h */

View file

@ -0,0 +1,105 @@
/* 8390 core for ISA devices needing bus delays */
static const char version[] =
"8390p.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#define ei_inb(_p) inb(_p)
#define ei_outb(_v, _p) outb(_v, _p)
#define ei_inb_p(_p) inb_p(_p)
#define ei_outb_p(_v, _p) outb_p(_v, _p)
#include "lib8390.c"
int eip_open(struct net_device *dev)
{
return __ei_open(dev);
}
EXPORT_SYMBOL(eip_open);
int eip_close(struct net_device *dev)
{
return __ei_close(dev);
}
EXPORT_SYMBOL(eip_close);
netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
return __ei_start_xmit(skb, dev);
}
EXPORT_SYMBOL(eip_start_xmit);
struct net_device_stats *eip_get_stats(struct net_device *dev)
{
return __ei_get_stats(dev);
}
EXPORT_SYMBOL(eip_get_stats);
void eip_set_multicast_list(struct net_device *dev)
{
__ei_set_multicast_list(dev);
}
EXPORT_SYMBOL(eip_set_multicast_list);
void eip_tx_timeout(struct net_device *dev)
{
__ei_tx_timeout(dev);
}
EXPORT_SYMBOL(eip_tx_timeout);
irqreturn_t eip_interrupt(int irq, void *dev_id)
{
return __ei_interrupt(irq, dev_id);
}
EXPORT_SYMBOL(eip_interrupt);
#ifdef CONFIG_NET_POLL_CONTROLLER
void eip_poll(struct net_device *dev)
{
__ei_poll(dev);
}
EXPORT_SYMBOL(eip_poll);
#endif
const struct net_device_ops eip_netdev_ops = {
.ndo_open = eip_open,
.ndo_stop = eip_close,
.ndo_start_xmit = eip_start_xmit,
.ndo_tx_timeout = eip_tx_timeout,
.ndo_get_stats = eip_get_stats,
.ndo_set_rx_mode = eip_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = eip_poll,
#endif
};
EXPORT_SYMBOL(eip_netdev_ops);
struct net_device *__alloc_eip_netdev(int size)
{
struct net_device *dev = ____alloc_ei_netdev(size);
if (dev)
dev->netdev_ops = &eip_netdev_ops;
return dev;
}
EXPORT_SYMBOL(__alloc_eip_netdev);
void NS8390p_init(struct net_device *dev, int startp)
{
__NS8390_init(dev, startp);
}
EXPORT_SYMBOL(NS8390p_init);
static int __init NS8390p_init_module(void)
{
return 0;
}
static void __exit NS8390p_cleanup_module(void)
{
}
module_init(NS8390p_init_module);
module_exit(NS8390p_cleanup_module);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,206 @@
#
# 8390 device configuration
#
config NET_VENDOR_8390
bool "National Semi-conductor 8390 devices"
default y
depends on NET_VENDOR_NATSEMI
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Western Digital cards. If you say Y, you will be
asked for your specific card in the following questions.
if NET_VENDOR_8390
config PCMCIA_AXNET
tristate "Asix AX88190 PCMCIA support"
depends on PCMCIA
---help---
Say Y here if you intend to attach an Asix AX88190-based PCMCIA
(PC-card) Fast Ethernet card to your computer. These cards are
nearly NE2000 compatible but need a separate driver due to a few
misfeatures.
To compile this driver as a module, choose M here: the module will be
called axnet_cs. If unsure, say N.
config AX88796
tristate "ASIX AX88796 NE2000 clone support"
depends on (ARM || MIPS || SUPERH)
select CRC32
select PHYLIB
select MDIO_BITBANG
---help---
AX88796 driver, using platform bus to provide
chip detection and resources
config AX88796_93CX6
bool "ASIX AX88796 external 93CX6 eeprom support"
depends on AX88796
select EEPROM_93CX6
---help---
Select this if your platform comes with an external 93CX6 eeprom.
config HYDRA
tristate "Hydra support"
depends on ZORRO
select CRC32
---help---
If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
To compile this driver as a module, choose M here: the module
will be called hydra.
config ARM_ETHERH
tristate "I-cubed EtherH/ANT EtherM support"
depends on ARM && ARCH_ACORN
select CRC32
---help---
If you have an Acorn system with one of these network cards, you
should say Y to this option if you wish to use it with Linux.
config MAC8390
bool "Macintosh NS 8390 based ethernet cards"
depends on MAC
select CRC32
---help---
If you want to include a driver to support Nubus or LC-PDS
Ethernet cards using an NS8390 chipset or its equivalent, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
config MCF8390
tristate "ColdFire NS8390 based Ethernet support"
depends on COLDFIRE
select CRC32
---help---
This driver is for Ethernet devices using an NS8390-compatible
chipset on many common ColdFire CPU based boards. Many of the older
Freescale dev boards use this, and some other common boards like
some SnapGear routers do as well.
If you have one of these boards and want to use the network interface
on them then choose Y. To compile this driver as a module, choose M
here, the module will be called mcf8390.
config NE2000
tristate "NE2000/NE1000 support"
depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX || \
ATARI_ETHERNEC)
select CRC32
---help---
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. Many Ethernet cards
without a specific driver are compatible with NE2000.
If you have a PCI NE2000 card however, say N here and Y to "PCI
NE2000 and clone support" below.
To compile this driver as a module, choose M here. The module
will be called ne.
config NE2K_PCI
tristate "PCI NE2000 and clones support (see help)"
depends on PCI
select CRC32
---help---
This driver is for NE2000 compatible PCI cards. It will not work
with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
support" below). If you have a PCI NE2000 network (Ethernet) card,
say Y and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
This driver also works for the following NE2000 clone cards:
RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2
NetVin NV5000SC Via 86C926 SureCom NE34 Winbond
Holtek HT80232 Holtek HT80229
To compile this driver as a module, choose M here. The module
will be called ne2k-pci.
config APNE
tristate "PCMCIA NE2000 support"
depends on AMIGA_PCMCIA
select CRC32
---help---
If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise,
say N.
To compile this driver as a module, choose M here: the module
will be called apne.
config PCMCIA_PCNET
tristate "NE2000 compatible PCMCIA support"
depends on PCMCIA
select CRC32
---help---
Say Y here if you intend to attach an NE2000 compatible PCMCIA
(PC-card) Ethernet or Fast Ethernet card to your computer.
To compile this driver as a module, choose M here: the module will be
called pcnet_cs. If unsure, say N.
config STNIC
tristate "National DP83902AV support"
depends on SUPERH
select CRC32
---help---
Support for cards based on the National Semiconductor DP83902AV
ST-NIC Serial Network Interface Controller for Twisted Pair. This
is a 10Mbit/sec Ethernet controller. Product overview and specs at
<http://www.national.com/pf/DP/DP83902A.html>.
If unsure, say N.
config ULTRA
tristate "SMC Ultra support"
depends on ISA
select CRC32
---help---
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Important: There have been many reports that, with some motherboards
mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
such as some BusLogic models) causes corruption problems with many
operating systems. The Linux smc-ultra driver has a work-around for
this but keep it in mind if you have such a SCSI card and have
problems.
To compile this driver as a module, choose M here. The module
will be called smc-ultra.
config WD80x3
tristate "WD80*3 support"
depends on ISA
select CRC32
---help---
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called wd.
config ZORRO8390
tristate "Zorro NS8390-based Ethernet support"
depends on ZORRO
select CRC32
---help---
This driver is for Zorro Ethernet cards using an NS8390-compatible
chipset, like the Village Tronic Ariadne II and the Individual
Computers X-Surf Ethernet cards. If you have such a card, say Y.
Otherwise, say N.
To compile this driver as a module, choose M here: the module
will be called zorro8390.
endif # NET_VENDOR_8390

View file

@ -0,0 +1,18 @@
#
# Makefile for the 8390 network device drivers.
#
obj-$(CONFIG_MAC8390) += mac8390.o
obj-$(CONFIG_APNE) += apne.o 8390.o
obj-$(CONFIG_ARM_ETHERH) += etherh.o
obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_HYDRA) += hydra.o 8390.o
obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o

View file

@ -0,0 +1,626 @@
/*
* Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
*
* (C) Copyright 1997 Alain Malek
* (Alain.Malek@cryogen.com)
*
* ----------------------------------------------------------------------------
*
* This program is based on
*
* ne.c: A general non-shared-memory NS8390 ethernet driver for linux
* Written 1992-94 by Donald Becker.
*
* 8390.c: A general NS8390 ethernet driver core for linux.
* Written 1992-94 by Donald Becker.
*
* cnetdevice: A Sana-II ethernet driver for AmigaOS
* Written by Bruce Abbott (bhabbott@inhb.co.nz)
*
* ----------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ----------------------------------------------------------------------------
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <asm/amigayle.h>
#include <asm/amipcmcia.h>
#include "8390.h"
/* ---- No user-serviceable parts below ---- */
#define DRV_NAME "apne"
#define NE_BASE (dev->base_addr)
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x20
#define NE_EN0_ISR 0x07
#define NE_EN0_DCFG 0x0e
#define NE_EN0_RSARLO 0x08
#define NE_EN0_RSARHI 0x09
#define NE_EN0_RCNTLO 0x0a
#define NE_EN0_RXCR 0x0c
#define NE_EN0_TXCR 0x0d
#define NE_EN0_RCNTHI 0x0b
#define NE_EN0_IMR 0x0f
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
struct net_device * __init apne_probe(int unit);
static int apne_probe1(struct net_device *dev, int ioaddr);
static void apne_reset_8390(struct net_device *dev);
static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void apne_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void apne_block_output(struct net_device *dev, const int count,
const unsigned char *buf, const int start_page);
static irqreturn_t apne_interrupt(int irq, void *dev_id);
static int init_pcmcia(void);
/* IO base address used for nic */
#define IOBASE 0x300
/*
use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
you can find the values to use by looking at the cnet.device
config file example (the default values are for the CNET40BC card)
*/
/*
#define MANUAL_CONFIG 0x20
#define MANUAL_OFFSET 0x3f8
#define MANUAL_HWADDR0 0x00
#define MANUAL_HWADDR1 0x12
#define MANUAL_HWADDR2 0x34
#define MANUAL_HWADDR3 0x56
#define MANUAL_HWADDR4 0x78
#define MANUAL_HWADDR5 0x9a
*/
static const char version[] =
"apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
static int apne_owned; /* signal if card already owned */
static u32 apne_msg_enable;
module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
struct net_device * __init apne_probe(int unit)
{
struct net_device *dev;
struct ei_device *ei_local;
#ifndef MANUAL_CONFIG
char tuple[8];
#endif
int err;
if (!MACH_IS_AMIGA)
return ERR_PTR(-ENODEV);
if (apne_owned)
return ERR_PTR(-ENODEV);
if ( !(AMIGAHW_PRESENT(PCMCIA)) )
return ERR_PTR(-ENODEV);
pr_info("Looking for PCMCIA ethernet card : ");
/* check if a card is inserted */
if (!(PCMCIA_INSERTED)) {
pr_cont("NO PCMCIA card inserted\n");
return ERR_PTR(-ENODEV);
}
dev = alloc_ei_netdev();
if (!dev)
return ERR_PTR(-ENOMEM);
if (unit >= 0) {
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
}
ei_local = netdev_priv(dev);
ei_local->msg_enable = apne_msg_enable;
/* disable pcmcia irq for readtuple */
pcmcia_disable_irq();
#ifndef MANUAL_CONFIG
if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
(tuple[2] != CISTPL_FUNCID_NETWORK)) {
pr_cont("not an ethernet card\n");
/* XXX: shouldn't we re-enable irq here? */
free_netdev(dev);
return ERR_PTR(-ENODEV);
}
#endif
pr_cont("ethernet PCMCIA card inserted\n");
if (!init_pcmcia()) {
/* XXX: shouldn't we re-enable irq here? */
free_netdev(dev);
return ERR_PTR(-ENODEV);
}
if (!request_region(IOBASE, 0x20, DRV_NAME)) {
free_netdev(dev);
return ERR_PTR(-EBUSY);
}
err = apne_probe1(dev, IOBASE);
if (err) {
release_region(IOBASE, 0x20);
free_netdev(dev);
return ERR_PTR(err);
}
err = register_netdev(dev);
if (!err)
return dev;
pcmcia_disable_irq();
free_irq(IRQ_AMIGA_PORTS, dev);
pcmcia_reset();
release_region(IOBASE, 0x20);
free_netdev(dev);
return ERR_PTR(err);
}
static int __init apne_probe1(struct net_device *dev, int ioaddr)
{
int i;
unsigned char SA_prom[32];
int wordlength = 2;
const char *name = NULL;
int start_page, stop_page;
#ifndef MANUAL_HWADDR0
int neX000, ctron;
#endif
static unsigned version_printed;
if ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
netdev_info(dev, version);
netdev_info(dev, "PCMCIA NE*000 ethercard probe");
/* Reset card. Who knows what dain-bramaged state it was left in. */
{ unsigned long reset_start_time = jiffies;
outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
pr_cont(" not found (no reset ack).\n");
return -ENODEV;
}
outb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
}
#ifndef MANUAL_HWADDR0
/* Read the 16 bytes of station address PROM.
We must first initialize registers, similar to NS8390_init(eifdev, 0).
We can't reliably read the SAPROM address without this.
(I learned the hard way!). */
{
struct {unsigned long value, offset; } program_seq[] = {
{E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/
{0x48, NE_EN0_DCFG}, /* Set byte-wide (0x48) access. */
{0x00, NE_EN0_RCNTLO}, /* Clear the count regs. */
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_IMR}, /* Mask completion irq. */
{0xFF, NE_EN0_ISR},
{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode. */
{32, NE_EN0_RCNTLO},
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000. */
{0x00, NE_EN0_RSARHI},
{E8390_RREAD+E8390_START, NE_CMD},
};
for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
outb(program_seq[i].value, ioaddr + program_seq[i].offset);
}
}
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
SA_prom[i] = inb(ioaddr + NE_DATAPORT);
SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
if (SA_prom[i] != SA_prom[i+1])
wordlength = 1;
}
/* At this point, wordlength *only* tells us if the SA_prom is doubled
up or not because some broken PCI cards don't respect the byte-wide
request in program_seq above, and hence don't have doubled up values.
These broken cards would otherwise be detected as an ne1000. */
if (wordlength == 2)
for (i = 0; i < 16; i++)
SA_prom[i] = SA_prom[i+i];
if (wordlength == 2) {
/* We must set the 8390 for word mode. */
outb(0x49, ioaddr + NE_EN0_DCFG);
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
} else {
start_page = NE1SM_START_PG;
stop_page = NE1SM_STOP_PG;
}
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
/* Set up the rest of the parameters. */
if (neX000) {
name = (wordlength == 2) ? "NE2000" : "NE1000";
} else if (ctron) {
name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
start_page = 0x01;
stop_page = (wordlength == 2) ? 0x40 : 0x20;
} else {
pr_cont(" not found.\n");
return -ENXIO;
}
#else
wordlength = 2;
/* We must set the 8390 for word mode. */
outb(0x49, ioaddr + NE_EN0_DCFG);
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
SA_prom[0] = MANUAL_HWADDR0;
SA_prom[1] = MANUAL_HWADDR1;
SA_prom[2] = MANUAL_HWADDR2;
SA_prom[3] = MANUAL_HWADDR3;
SA_prom[4] = MANUAL_HWADDR4;
SA_prom[5] = MANUAL_HWADDR5;
name = "NE2000";
#endif
dev->base_addr = ioaddr;
dev->irq = IRQ_AMIGA_PORTS;
dev->netdev_ops = &ei_netdev_ops;
/* Install the Interrupt handler */
i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
if (i) return i;
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = SA_prom[i];
pr_cont(" %pM\n", dev->dev_addr);
netdev_info(dev, "%s found.\n", name);
ei_status.name = name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
ei_status.word16 = (wordlength == 2);
ei_status.rx_start_page = start_page + TX_PAGES;
ei_status.reset_8390 = &apne_reset_8390;
ei_status.block_input = &apne_block_input;
ei_status.block_output = &apne_block_output;
ei_status.get_8390_hdr = &apne_get_8390_hdr;
NS8390_init(dev, 0);
pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */
pcmcia_enable_irq();
apne_owned = 1;
return 0;
}
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
static void
apne_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
struct ei_device *ei_local = netdev_priv(dev);
init_pcmcia();
netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
ei_status.txing = 0;
ei_status.dmaing = 0;
/* This check _should_not_ be necessary, omit eventually. */
while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
netdev_err(dev, "ne_reset_8390() did not complete.\n");
break;
}
outb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr. */
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */
static void
apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
int nic_base = dev->base_addr;
int cnt;
char *ptrc;
short *ptrs;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne_get_8390_hdr "
"[DMAstat:%d][irqlock:%d][intr:%d].\n",
ei_status.dmaing, ei_status.irqlock, dev->irq);
return;
}
ei_status.dmaing |= 0x01;
outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb(ENISR_RDC, nic_base + NE_EN0_ISR);
outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
outb(0, nic_base + NE_EN0_RCNTHI);
outb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
outb(ring_page, nic_base + NE_EN0_RSARHI);
outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
ptrs = (short*)hdr;
for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
*ptrs++ = inw(NE_BASE + NE_DATAPORT);
} else {
ptrc = (char*)hdr;
for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
*ptrc++ = inb(NE_BASE + NE_DATAPORT);
}
outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
le16_to_cpus(&hdr->count);
}
/* Block input and output, similar to the Crynwr packet driver. If you
are porting to a new ethercard, look at the packet driver source for hints.
The NEx000 doesn't share the on-board packet memory -- you have to put
the packet out through the "remote DMA" dataport using outb. */
static void
apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
{
int nic_base = dev->base_addr;
char *buf = skb->data;
char *ptrc;
short *ptrs;
int cnt;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne_block_input "
"[DMAstat:%d][irqlock:%d][intr:%d].\n",
ei_status.dmaing, ei_status.irqlock, dev->irq);
return;
}
ei_status.dmaing |= 0x01;
outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb(ENISR_RDC, nic_base + NE_EN0_ISR);
outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
outb(count >> 8, nic_base + NE_EN0_RCNTHI);
outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
ptrs = (short*)buf;
for (cnt = 0; cnt < (count>>1); cnt++)
*ptrs++ = inw(NE_BASE + NE_DATAPORT);
if (count & 0x01) {
buf[count-1] = inb(NE_BASE + NE_DATAPORT);
}
} else {
ptrc = buf;
for (cnt = 0; cnt < count; cnt++)
*ptrc++ = inb(NE_BASE + NE_DATAPORT);
}
outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
static void
apne_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page)
{
int nic_base = NE_BASE;
unsigned long dma_start;
char *ptrc;
short *ptrs;
int cnt;
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
I should check someday. */
if (ei_status.word16 && (count & 0x01))
count++;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne_block_output."
"[DMAstat:%d][irqlock:%d][intr:%d]\n",
ei_status.dmaing, ei_status.irqlock, dev->irq);
return;
}
ei_status.dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
outb(ENISR_RDC, nic_base + NE_EN0_ISR);
/* Now the normal output. */
outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
outb(count >> 8, nic_base + NE_EN0_RCNTHI);
outb(0x00, nic_base + NE_EN0_RSARLO);
outb(start_page, nic_base + NE_EN0_RSARHI);
outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
ptrs = (short*)buf;
for (cnt = 0; cnt < count>>1; cnt++)
outw(*ptrs++, NE_BASE+NE_DATAPORT);
} else {
ptrc = (char*)buf;
for (cnt = 0; cnt < count; cnt++)
outb(*ptrc++, NE_BASE + NE_DATAPORT);
}
dma_start = jiffies;
while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
netdev_warn(dev, "timeout waiting for Tx RDC.\n");
apne_reset_8390(dev);
NS8390_init(dev,1);
break;
}
outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
static irqreturn_t apne_interrupt(int irq, void *dev_id)
{
unsigned char pcmcia_intreq;
if (!(gayle.inten & GAYLE_IRQ_IRQ))
return IRQ_NONE;
pcmcia_intreq = pcmcia_get_intreq();
if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
pcmcia_ack_int(pcmcia_intreq);
return IRQ_NONE;
}
if (apne_msg_enable & NETIF_MSG_INTR)
pr_debug("pcmcia intreq = %x\n", pcmcia_intreq);
pcmcia_disable_irq(); /* to get rid of the sti() within ei_interrupt */
ei_interrupt(irq, dev_id);
pcmcia_ack_int(pcmcia_get_intreq());
pcmcia_enable_irq();
return IRQ_HANDLED;
}
#ifdef MODULE
static struct net_device *apne_dev;
static int __init apne_module_init(void)
{
apne_dev = apne_probe(-1);
return PTR_ERR_OR_ZERO(apne_dev);
}
static void __exit apne_module_exit(void)
{
unregister_netdev(apne_dev);
pcmcia_disable_irq();
free_irq(IRQ_AMIGA_PORTS, apne_dev);
pcmcia_reset();
release_region(IOBASE, 0x20);
free_netdev(apne_dev);
}
module_init(apne_module_init);
module_exit(apne_module_exit);
#endif
static int init_pcmcia(void)
{
u_char config;
#ifndef MANUAL_CONFIG
u_char tuple[32];
int offset_len;
#endif
u_long offset;
pcmcia_reset();
pcmcia_program_voltage(PCMCIA_0V);
pcmcia_access_speed(PCMCIA_SPEED_250NS);
pcmcia_write_enable();
#ifdef MANUAL_CONFIG
config = MANUAL_CONFIG;
#else
/* get and write config byte to enable IO port */
if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
return 0;
config = tuple[2] & 0x3f;
#endif
#ifdef MANUAL_OFFSET
offset = MANUAL_OFFSET;
#else
if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
return 0;
offset_len = (tuple[2] & 0x3) + 1;
offset = 0;
while(offset_len--) {
offset = (offset << 8) | tuple[4+offset_len];
}
#endif
out_8(GAYLE_ATTRIBUTE+offset, config);
return 1;
}
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,874 @@
/*
* linux/drivers/acorn/net/etherh.c
*
* Copyright (C) 2000-2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* NS8390 I-cubed EtherH and ANT EtherM specific driver
* Thanks to I-Cubed for information on their cards.
* EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
* EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
* EtherM integration re-engineered by Russell King.
*
* Changelog:
* 08-12-1996 RMK 1.00 Created
* RMK 1.03 Added support for EtherLan500 cards
* 23-11-1997 RMK 1.04 Added media autodetection
* 16-04-1998 RMK 1.05 Improved media autodetection
* 10-02-2000 RMK 1.06 Updated for 2.3.43
* 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8
* 12-10-1999 CK/TEW EtherM driver first release
* 21-12-2000 TTC EtherH/EtherM integration
* 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver.
* 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <asm/system_info.h>
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#define ei_inb(_p) readb((void __iomem *)_p)
#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p)
#define ei_inb_p(_p) readb((void __iomem *)_p)
#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p)
#define DRV_NAME "etherh"
#define DRV_VERSION "1.11"
static char version[] =
"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
#include "lib8390.c"
static u32 etherh_msg_enable;
struct etherh_priv {
void __iomem *ioc_fast;
void __iomem *memc;
void __iomem *dma_base;
unsigned int id;
void __iomem *ctrl_port;
unsigned char ctrl;
u32 supported;
};
struct etherh_data {
unsigned long ns8390_offset;
unsigned long dataport_offset;
unsigned long ctrlport_offset;
int ctrl_ioc;
const char name[16];
u32 supported;
unsigned char tx_start_page;
unsigned char stop_page;
};
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("EtherH/EtherM driver");
MODULE_LICENSE("GPL");
#define ETHERH500_DATAPORT 0x800 /* MEMC */
#define ETHERH500_NS8390 0x000 /* MEMC */
#define ETHERH500_CTRLPORT 0x800 /* IOC */
#define ETHERH600_DATAPORT 0x040 /* MEMC */
#define ETHERH600_NS8390 0x800 /* MEMC */
#define ETHERH600_CTRLPORT 0x200 /* MEMC */
#define ETHERH_CP_IE 1
#define ETHERH_CP_IF 2
#define ETHERH_CP_HEARTBEAT 2
#define ETHERH_TX_START_PAGE 1
#define ETHERH_STOP_PAGE 127
/*
* These came from CK/TEW
*/
#define ETHERM_DATAPORT 0x200 /* MEMC */
#define ETHERM_NS8390 0x800 /* MEMC */
#define ETHERM_CTRLPORT 0x23c /* MEMC */
#define ETHERM_TX_START_PAGE 64
#define ETHERM_STOP_PAGE 127
/* ------------------------------------------------------------------------ */
#define etherh_priv(dev) \
((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
{
unsigned char ctrl = eh->ctrl | mask;
eh->ctrl = ctrl;
writeb(ctrl, eh->ctrl_port);
}
static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
{
unsigned char ctrl = eh->ctrl & ~mask;
eh->ctrl = ctrl;
writeb(ctrl, eh->ctrl_port);
}
static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
{
return readb(eh->ctrl_port);
}
static void etherh_irq_enable(ecard_t *ec, int irqnr)
{
struct etherh_priv *eh = ec->irq_data;
etherh_set_ctrl(eh, ETHERH_CP_IE);
}
static void etherh_irq_disable(ecard_t *ec, int irqnr)
{
struct etherh_priv *eh = ec->irq_data;
etherh_clr_ctrl(eh, ETHERH_CP_IE);
}
static expansioncard_ops_t etherh_ops = {
.irqenable = etherh_irq_enable,
.irqdisable = etherh_irq_disable,
};
static void
etherh_setif(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
unsigned long flags;
void __iomem *addr;
local_irq_save(flags);
/* set the interface type */
switch (etherh_priv(dev)->id) {
case PROD_I3_ETHERLAN600:
case PROD_I3_ETHERLAN600A:
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
switch (dev->if_port) {
case IF_PORT_10BASE2:
writeb((readb(addr) & 0xf8) | 1, addr);
break;
case IF_PORT_10BASET:
writeb((readb(addr) & 0xf8), addr);
break;
}
break;
case PROD_I3_ETHERLAN500:
switch (dev->if_port) {
case IF_PORT_10BASE2:
etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
break;
case IF_PORT_10BASET:
etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
break;
}
break;
default:
break;
}
local_irq_restore(flags);
}
static int
etherh_getifstat(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *addr;
int stat = 0;
switch (etherh_priv(dev)->id) {
case PROD_I3_ETHERLAN600:
case PROD_I3_ETHERLAN600A:
addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
switch (dev->if_port) {
case IF_PORT_10BASE2:
stat = 1;
break;
case IF_PORT_10BASET:
stat = readb(addr) & 4;
break;
}
break;
case PROD_I3_ETHERLAN500:
switch (dev->if_port) {
case IF_PORT_10BASE2:
stat = 1;
break;
case IF_PORT_10BASET:
stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
break;
}
break;
default:
stat = 0;
break;
}
return stat != 0;
}
/*
* Configure the interface. Note that we ignore the other
* parts of ifmap, since its mostly meaningless for this driver.
*/
static int etherh_set_config(struct net_device *dev, struct ifmap *map)
{
switch (map->port) {
case IF_PORT_10BASE2:
case IF_PORT_10BASET:
/*
* If the user explicitly sets the interface
* media type, turn off automedia detection.
*/
dev->flags &= ~IFF_AUTOMEDIA;
dev->if_port = map->port;
break;
default:
return -EINVAL;
}
etherh_setif(dev);
return 0;
}
/*
* Reset the 8390 (hard reset). Note that we can't actually do this.
*/
static void
etherh_reset(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *addr = (void __iomem *)dev->base_addr;
writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
/*
* See if we need to change the interface type.
* Note that we use 'interface_num' as a flag
* to indicate that we need to change the media.
*/
if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
ei_local->interface_num = 0;
if (dev->if_port == IF_PORT_10BASET)
dev->if_port = IF_PORT_10BASE2;
else
dev->if_port = IF_PORT_10BASET;
etherh_setif(dev);
}
}
/*
* Write a block of data out to the 8390
*/
static void
etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
{
struct ei_device *ei_local = netdev_priv(dev);
unsigned long dma_start;
void __iomem *dma_base, *addr;
if (ei_local->dmaing) {
netdev_err(dev, "DMAing conflict in etherh_block_input: "
" DMAstat %d irqlock %d\n",
ei_local->dmaing, ei_local->irqlock);
return;
}
/*
* Make sure we have a round number of bytes if we're in word mode.
*/
if (count & 1 && ei_local->word16)
count++;
ei_local->dmaing = 1;
addr = (void __iomem *)dev->base_addr;
dma_base = etherh_priv(dev)->dma_base;
count = (count + 1) & ~1;
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
writeb (0x42, addr + EN0_RCNTLO);
writeb (0x00, addr + EN0_RCNTHI);
writeb (0x42, addr + EN0_RSARLO);
writeb (0x00, addr + EN0_RSARHI);
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
udelay (1);
writeb (ENISR_RDC, addr + EN0_ISR);
writeb (count, addr + EN0_RCNTLO);
writeb (count >> 8, addr + EN0_RCNTHI);
writeb (0, addr + EN0_RSARLO);
writeb (start_page, addr + EN0_RSARHI);
writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
if (ei_local->word16)
writesw (dma_base, buf, count >> 1);
else
writesb (dma_base, buf, count);
dma_start = jiffies;
while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0)
if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
netdev_warn(dev, "timeout waiting for TX RDC\n");
etherh_reset (dev);
__NS8390_init (dev, 1);
break;
}
writeb (ENISR_RDC, addr + EN0_ISR);
ei_local->dmaing = 0;
}
/*
* Read a block of data from the 8390
*/
static void
etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
{
struct ei_device *ei_local = netdev_priv(dev);
unsigned char *buf;
void __iomem *dma_base, *addr;
if (ei_local->dmaing) {
netdev_err(dev, "DMAing conflict in etherh_block_input: "
" DMAstat %d irqlock %d\n",
ei_local->dmaing, ei_local->irqlock);
return;
}
ei_local->dmaing = 1;
addr = (void __iomem *)dev->base_addr;
dma_base = etherh_priv(dev)->dma_base;
buf = skb->data;
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
writeb (count, addr + EN0_RCNTLO);
writeb (count >> 8, addr + EN0_RCNTHI);
writeb (ring_offset, addr + EN0_RSARLO);
writeb (ring_offset >> 8, addr + EN0_RSARHI);
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
if (ei_local->word16) {
readsw (dma_base, buf, count >> 1);
if (count & 1)
buf[count - 1] = readb (dma_base);
} else
readsb (dma_base, buf, count);
writeb (ENISR_RDC, addr + EN0_ISR);
ei_local->dmaing = 0;
}
/*
* Read a header from the 8390
*/
static void
etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *dma_base, *addr;
if (ei_local->dmaing) {
netdev_err(dev, "DMAing conflict in etherh_get_header: "
" DMAstat %d irqlock %d\n",
ei_local->dmaing, ei_local->irqlock);
return;
}
ei_local->dmaing = 1;
addr = (void __iomem *)dev->base_addr;
dma_base = etherh_priv(dev)->dma_base;
writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
writeb (sizeof (*hdr), addr + EN0_RCNTLO);
writeb (0, addr + EN0_RCNTHI);
writeb (0, addr + EN0_RSARLO);
writeb (ring_page, addr + EN0_RSARHI);
writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
if (ei_local->word16)
readsw (dma_base, hdr, sizeof (*hdr) >> 1);
else
readsb (dma_base, hdr, sizeof (*hdr));
writeb (ENISR_RDC, addr + EN0_ISR);
ei_local->dmaing = 0;
}
/*
* Open/initialize the board. This is called (in the current kernel)
* sometime after booting when the 'ifconfig' program is run.
*
* This routine should set everything up anew at each open, even
* registers that "should" only need to be set once at boot, so that
* there is non-reboot way to recover if something goes wrong.
*/
static int
etherh_open(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev))
return -EAGAIN;
/*
* Make sure that we aren't going to change the
* media type on the next reset - we are about to
* do automedia manually now.
*/
ei_local->interface_num = 0;
/*
* If we are doing automedia detection, do it now.
* This is more reliable than the 8390's detection.
*/
if (dev->flags & IFF_AUTOMEDIA) {
dev->if_port = IF_PORT_10BASET;
etherh_setif(dev);
mdelay(1);
if (!etherh_getifstat(dev)) {
dev->if_port = IF_PORT_10BASE2;
etherh_setif(dev);
}
} else
etherh_setif(dev);
etherh_reset(dev);
__ei_open(dev);
return 0;
}
/*
* The inverse routine to etherh_open().
*/
static int
etherh_close(struct net_device *dev)
{
__ei_close (dev);
free_irq (dev->irq, dev);
return 0;
}
/*
* Initialisation
*/
static void __init etherh_banner(void)
{
static int version_printed;
if ((etherh_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
pr_info("%s", version);
}
/*
* Read the ethernet address string from the on board rom.
* This is an ascii string...
*/
static int etherh_addr(char *addr, struct expansion_card *ec)
{
struct in_chunk_dir cd;
char *s;
if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
printk(KERN_ERR "%s: unable to read podule description string\n",
dev_name(&ec->dev));
goto no_addr;
}
s = strchr(cd.d.string, '(');
if (s) {
int i;
for (i = 0; i < 6; i++) {
addr[i] = simple_strtoul(s + 1, &s, 0x10);
if (*s != (i == 5? ')' : ':'))
break;
}
if (i == 6)
return 0;
}
printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
dev_name(&ec->dev), cd.d.string);
no_addr:
return -ENODEV;
}
/*
* Create an ethernet address from the system serial number.
*/
static int __init etherm_addr(char *addr)
{
unsigned int serial;
if (system_serial_low == 0 && system_serial_high == 0)
return -ENODEV;
serial = system_serial_low | system_serial_high;
addr[0] = 0;
addr[1] = 0;
addr[2] = 0xa4;
addr[3] = 0x10 + (serial >> 24);
addr[4] = serial >> 16;
addr[5] = serial >> 8;
return 0;
}
static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
}
static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
cmd->supported = etherh_priv(dev)->supported;
ethtool_cmd_speed_set(cmd, SPEED_10);
cmd->duplex = DUPLEX_HALF;
cmd->port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
cmd->autoneg = (dev->flags & IFF_AUTOMEDIA ?
AUTONEG_ENABLE : AUTONEG_DISABLE);
return 0;
}
static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
switch (cmd->autoneg) {
case AUTONEG_ENABLE:
dev->flags |= IFF_AUTOMEDIA;
break;
case AUTONEG_DISABLE:
switch (cmd->port) {
case PORT_TP:
dev->if_port = IF_PORT_10BASET;
break;
case PORT_BNC:
dev->if_port = IF_PORT_10BASE2;
break;
default:
return -EINVAL;
}
dev->flags &= ~IFF_AUTOMEDIA;
break;
default:
return -EINVAL;
}
etherh_setif(dev);
return 0;
}
static u32 etherh_get_msglevel(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
return ei_local->msg_enable;
}
static void etherh_set_msglevel(struct net_device *dev, u32 v)
{
struct ei_device *ei_local = netdev_priv(dev);
ei_local->msg_enable = v;
}
static const struct ethtool_ops etherh_ethtool_ops = {
.get_settings = etherh_get_settings,
.set_settings = etherh_set_settings,
.get_drvinfo = etherh_get_drvinfo,
.get_ts_info = ethtool_op_get_ts_info,
.get_msglevel = etherh_get_msglevel,
.set_msglevel = etherh_set_msglevel,
};
static const struct net_device_ops etherh_netdev_ops = {
.ndo_open = etherh_open,
.ndo_stop = etherh_close,
.ndo_set_config = etherh_set_config,
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
.ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static u32 etherh_regoffsets[16];
static u32 etherm_regoffsets[16];
static int
etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
{
const struct etherh_data *data = id->data;
struct ei_device *ei_local;
struct net_device *dev;
struct etherh_priv *eh;
int ret;
etherh_banner();
ret = ecard_request_resources(ec);
if (ret)
goto out;
dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
if (!dev) {
ret = -ENOMEM;
goto release;
}
SET_NETDEV_DEV(dev, &ec->dev);
dev->netdev_ops = &etherh_netdev_ops;
dev->irq = ec->irq;
dev->ethtool_ops = &etherh_ethtool_ops;
if (data->supported & SUPPORTED_Autoneg)
dev->flags |= IFF_AUTOMEDIA;
if (data->supported & SUPPORTED_TP) {
dev->flags |= IFF_PORTSEL;
dev->if_port = IF_PORT_10BASET;
} else if (data->supported & SUPPORTED_BNC) {
dev->flags |= IFF_PORTSEL;
dev->if_port = IF_PORT_10BASE2;
} else
dev->if_port = IF_PORT_UNKNOWN;
eh = etherh_priv(dev);
eh->supported = data->supported;
eh->ctrl = 0;
eh->id = ec->cid.product;
eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE);
if (!eh->memc) {
ret = -ENOMEM;
goto free;
}
eh->ctrl_port = eh->memc;
if (data->ctrl_ioc) {
eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE);
if (!eh->ioc_fast) {
ret = -ENOMEM;
goto free;
}
eh->ctrl_port = eh->ioc_fast;
}
dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset;
eh->dma_base = eh->memc + data->dataport_offset;
eh->ctrl_port += data->ctrlport_offset;
/*
* IRQ and control port handling - only for non-NIC slot cards.
*/
if (ec->slot_no != 8) {
ecard_setirq(ec, &etherh_ops, eh);
} else {
/*
* If we're in the NIC slot, make sure the IRQ is enabled
*/
etherh_set_ctrl(eh, ETHERH_CP_IE);
}
ei_local = netdev_priv(dev);
spin_lock_init(&ei_local->page_lock);
if (ec->cid.product == PROD_ANT_ETHERM) {
etherm_addr(dev->dev_addr);
ei_local->reg_offset = etherm_regoffsets;
} else {
etherh_addr(dev->dev_addr, ec);
ei_local->reg_offset = etherh_regoffsets;
}
ei_local->name = dev->name;
ei_local->word16 = 1;
ei_local->tx_start_page = data->tx_start_page;
ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
ei_local->stop_page = data->stop_page;
ei_local->reset_8390 = etherh_reset;
ei_local->block_input = etherh_block_input;
ei_local->block_output = etherh_block_output;
ei_local->get_8390_hdr = etherh_get_header;
ei_local->interface_num = 0;
ei_local->msg_enable = etherh_msg_enable;
etherh_reset(dev);
__NS8390_init(dev, 0);
ret = register_netdev(dev);
if (ret)
goto free;
netdev_info(dev, "%s in slot %d, %pM\n",
data->name, ec->slot_no, dev->dev_addr);
ecard_set_drvdata(ec, dev);
return 0;
free:
free_netdev(dev);
release:
ecard_release_resources(ec);
out:
return ret;
}
static void etherh_remove(struct expansion_card *ec)
{
struct net_device *dev = ecard_get_drvdata(ec);
ecard_set_drvdata(ec, NULL);
unregister_netdev(dev);
free_netdev(dev);
ecard_release_resources(ec);
}
static struct etherh_data etherm_data = {
.ns8390_offset = ETHERM_NS8390,
.dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT,
.ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT,
.name = "ANT EtherM",
.supported = SUPPORTED_10baseT_Half,
.tx_start_page = ETHERM_TX_START_PAGE,
.stop_page = ETHERM_STOP_PAGE,
};
static struct etherh_data etherlan500_data = {
.ns8390_offset = ETHERH500_NS8390,
.dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT,
.ctrlport_offset = ETHERH500_CTRLPORT,
.ctrl_ioc = 1,
.name = "i3 EtherH 500",
.supported = SUPPORTED_10baseT_Half,
.tx_start_page = ETHERH_TX_START_PAGE,
.stop_page = ETHERH_STOP_PAGE,
};
static struct etherh_data etherlan600_data = {
.ns8390_offset = ETHERH600_NS8390,
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
.name = "i3 EtherH 600",
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
.tx_start_page = ETHERH_TX_START_PAGE,
.stop_page = ETHERH_STOP_PAGE,
};
static struct etherh_data etherlan600a_data = {
.ns8390_offset = ETHERH600_NS8390,
.dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT,
.ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT,
.name = "i3 EtherH 600A",
.supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
.tx_start_page = ETHERH_TX_START_PAGE,
.stop_page = ETHERH_STOP_PAGE,
};
static const struct ecard_id etherh_ids[] = {
{ MANU_ANT, PROD_ANT_ETHERM, &etherm_data },
{ MANU_I3, PROD_I3_ETHERLAN500, &etherlan500_data },
{ MANU_I3, PROD_I3_ETHERLAN600, &etherlan600_data },
{ MANU_I3, PROD_I3_ETHERLAN600A, &etherlan600a_data },
{ 0xffff, 0xffff }
};
static struct ecard_driver etherh_driver = {
.probe = etherh_probe,
.remove = etherh_remove,
.id_table = etherh_ids,
.drv = {
.name = DRV_NAME,
},
};
static int __init etherh_init(void)
{
int i;
for (i = 0; i < 16; i++) {
etherh_regoffsets[i] = i << 2;
etherm_regoffsets[i] = i << 5;
}
return ecard_register_driver(&etherh_driver);
}
static void __exit etherh_exit(void)
{
ecard_remove_driver(&etherh_driver);
}
module_init(etherh_init);
module_exit(etherh_exit);

View file

@ -0,0 +1,278 @@
/* New Hydra driver using generic 8390 core */
/* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */
/* This file is subject to the terms and conditions of the GNU General */
/* Public License. See the file COPYING in the main directory of the */
/* Linux distribution for more details. */
/* Peter De Schrijver (p2@mind.be) */
/* Oldenburg 2000 */
/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a */
/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM */
/* and 10BASE-2 (thin coax) and AUI connectors. */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <linux/zorro.h>
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#define ei_inb(port) in_8(port)
#define ei_outb(val,port) out_8(port,val)
#define ei_inb_p(port) in_8(port)
#define ei_outb_p(val,port) out_8(port,val)
static const char version[] =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include "lib8390.c"
#define NE_EN0_DCFG (0x0e*2)
#define NESM_START_PG 0x0 /* First page of TX buffer */
#define NESM_STOP_PG 0x40 /* Last page +1 of RX ring */
#define HYDRA_NIC_BASE 0xffe1
#define HYDRA_ADDRPROM 0xffc0
#define HYDRA_VERSION "v3.0alpha"
#define WORDSWAP(a) ((((a)>>8)&0xff) | ((a)<<8))
static int hydra_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent);
static int hydra_init(struct zorro_dev *z);
static int hydra_open(struct net_device *dev);
static int hydra_close(struct net_device *dev);
static void hydra_reset_8390(struct net_device *dev);
static void hydra_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
static void hydra_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void hydra_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void hydra_remove_one(struct zorro_dev *z);
static u32 hydra_msg_enable;
static struct zorro_device_id hydra_zorro_tbl[] = {
{ ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
{ 0 }
};
MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl);
static struct zorro_driver hydra_driver = {
.name = "hydra",
.id_table = hydra_zorro_tbl,
.probe = hydra_init_one,
.remove = hydra_remove_one,
};
static int hydra_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent)
{
int err;
if (!request_mem_region(z->resource.start, 0x10000, "Hydra"))
return -EBUSY;
if ((err = hydra_init(z))) {
release_mem_region(z->resource.start, 0x10000);
return -EBUSY;
}
return 0;
}
static const struct net_device_ops hydra_netdev_ops = {
.ndo_open = hydra_open,
.ndo_stop = hydra_close,
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
.ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static int hydra_init(struct zorro_dev *z)
{
struct net_device *dev;
unsigned long board = (unsigned long)ZTWO_VADDR(z->resource.start);
unsigned long ioaddr = board+HYDRA_NIC_BASE;
const char name[] = "NE2000";
int start_page, stop_page;
int j;
int err;
struct ei_device *ei_local;
static u32 hydra_offsets[16] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
};
dev = ____alloc_ei_netdev(0);
if (!dev)
return -ENOMEM;
for (j = 0; j < ETH_ALEN; j++)
dev->dev_addr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j));
/* We must set the 8390 for word mode. */
z_writeb(0x4b, ioaddr + NE_EN0_DCFG);
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
ei_local = netdev_priv(dev);
ei_local->msg_enable = hydra_msg_enable;
dev->base_addr = ioaddr;
dev->irq = IRQ_AMIGA_PORTS;
/* Install the Interrupt handler */
if (request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, IRQF_SHARED, "Hydra Ethernet",
dev)) {
free_netdev(dev);
return -EAGAIN;
}
ei_status.name = name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
ei_status.word16 = 1;
ei_status.bigendian = 1;
ei_status.rx_start_page = start_page + TX_PAGES;
ei_status.reset_8390 = hydra_reset_8390;
ei_status.block_input = hydra_block_input;
ei_status.block_output = hydra_block_output;
ei_status.get_8390_hdr = hydra_get_8390_hdr;
ei_status.reg_offset = hydra_offsets;
dev->netdev_ops = &hydra_netdev_ops;
__NS8390_init(dev, 0);
err = register_netdev(dev);
if (err) {
free_irq(IRQ_AMIGA_PORTS, dev);
free_netdev(dev);
return err;
}
zorro_set_drvdata(z, dev);
pr_info("%s: Hydra at %pR, address %pM (hydra.c " HYDRA_VERSION ")\n",
dev->name, &z->resource, dev->dev_addr);
return 0;
}
static int hydra_open(struct net_device *dev)
{
__ei_open(dev);
return 0;
}
static int hydra_close(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
__ei_close(dev);
return 0;
}
static void hydra_reset_8390(struct net_device *dev)
{
netdev_info(dev, "Hydra hw reset not there\n");
}
static void hydra_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page)
{
int nic_base = dev->base_addr;
short *ptrs;
unsigned long hdr_start= (nic_base-HYDRA_NIC_BASE) +
((ring_page - NESM_START_PG)<<8);
ptrs = (short *)hdr;
*(ptrs++) = z_readw(hdr_start);
*((short *)hdr) = WORDSWAP(*((short *)hdr));
hdr_start += 2;
*(ptrs++) = z_readw(hdr_start);
*((short *)hdr+1) = WORDSWAP(*((short *)hdr+1));
}
static void hydra_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
unsigned long nic_base = dev->base_addr;
unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
unsigned long xfer_start = mem_base + ring_offset - (NESM_START_PG<<8);
if (count&1)
count++;
if (xfer_start+count > mem_base + (NESM_STOP_PG<<8)) {
int semi_count = (mem_base + (NESM_STOP_PG<<8)) - xfer_start;
z_memcpy_fromio(skb->data,xfer_start,semi_count);
count -= semi_count;
z_memcpy_fromio(skb->data+semi_count, mem_base, count);
} else
z_memcpy_fromio(skb->data, xfer_start,count);
}
static void hydra_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page)
{
unsigned long nic_base = dev->base_addr;
unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
if (count&1)
count++;
z_memcpy_toio(mem_base+((start_page - NESM_START_PG)<<8), buf, count);
}
static void hydra_remove_one(struct zorro_dev *z)
{
struct net_device *dev = zorro_get_drvdata(z);
unregister_netdev(dev);
free_irq(IRQ_AMIGA_PORTS, dev);
release_mem_region(ZTWO_PADDR(dev->base_addr)-HYDRA_NIC_BASE, 0x10000);
free_netdev(dev);
}
static int __init hydra_init_module(void)
{
return zorro_register_driver(&hydra_driver);
}
static void __exit hydra_cleanup_module(void)
{
zorro_unregister_driver(&hydra_driver);
}
module_init(hydra_init_module);
module_exit(hydra_cleanup_module);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,874 @@
/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
Ethernet cards on Linux */
/* Based on the former daynaport.c driver, by Alan Cox. Some code
taken from or inspired by skeleton.c by Donald Becker, acenic.c by
Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker.
This software may be used and distributed according to the terms of
the GNU Public License, incorporated herein by reference. */
/* 2000-02-28: support added for Dayna and Kinetics cards by
A.G.deWijn@phys.uu.nl */
/* 2000-04-04: support added for Dayna2 by bart@etpmod.phys.tue.nl */
/* 2001-04-18: support for DaynaPort E/LC-M by rayk@knightsmanor.org */
/* 2001-05-15: support for Cabletron ported from old daynaport driver
* and fixed access to Sonic Sys card which masquerades as a Farallon
* by rayk@knightsmanor.org */
/* 2002-12-30: Try to support more cards, some clues from NetBSD driver */
/* 2003-12-26: Make sure Asante cards always work. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/nubus.h>
#include <linux/in.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <asm/dma.h>
#include <asm/hwtest.h>
#include <asm/macints.h>
static char version[] =
"v0.4 2001-05-15 David Huggins-Daines <dhd@debian.org> and others\n";
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#define ei_inb(port) in_8(port)
#define ei_outb(val, port) out_8(port, val)
#define ei_inb_p(port) in_8(port)
#define ei_outb_p(val, port) out_8(port, val)
#include "lib8390.c"
#define WD_START_PG 0x00 /* First page of TX buffer */
#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */
#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG
/* First page of TX buffer */
/*
* Unfortunately it seems we have to hardcode these for the moment
* Shouldn't the card know about this?
* Does anyone know where to read it off the card?
* Do we trust the data provided by the card?
*/
#define DAYNA_8390_BASE 0x80000
#define DAYNA_8390_MEM 0x00000
#define CABLETRON_8390_BASE 0x90000
#define CABLETRON_8390_MEM 0x00000
#define INTERLAN_8390_BASE 0xE0000
#define INTERLAN_8390_MEM 0xD0000
enum mac8390_type {
MAC8390_NONE = -1,
MAC8390_APPLE,
MAC8390_ASANTE,
MAC8390_FARALLON,
MAC8390_CABLETRON,
MAC8390_DAYNA,
MAC8390_INTERLAN,
MAC8390_KINETICS,
};
static const char *cardname[] = {
"apple",
"asante",
"farallon",
"cabletron",
"dayna",
"interlan",
"kinetics",
};
static const int word16[] = {
1, /* apple */
1, /* asante */
1, /* farallon */
1, /* cabletron */
0, /* dayna */
1, /* interlan */
0, /* kinetics */
};
/* on which cards do we use NuBus resources? */
static const int useresources[] = {
1, /* apple */
1, /* asante */
1, /* farallon */
0, /* cabletron */
0, /* dayna */
0, /* interlan */
0, /* kinetics */
};
enum mac8390_access {
ACCESS_UNKNOWN = 0,
ACCESS_32,
ACCESS_16,
};
extern int mac8390_memtest(struct net_device *dev);
static int mac8390_initdev(struct net_device *dev, struct nubus_dev *ndev,
enum mac8390_type type);
static int mac8390_open(struct net_device *dev);
static int mac8390_close(struct net_device *dev);
static void mac8390_no_reset(struct net_device *dev);
static void interlan_reset(struct net_device *dev);
/* Sane (32-bit chunk memory read/write) - Some Farallon and Apple do this*/
static void sane_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
static void sane_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void sane_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page);
/* dayna_memcpy to and from card */
static void dayna_memcpy_fromcard(struct net_device *dev, void *to,
int from, int count);
static void dayna_memcpy_tocard(struct net_device *dev, int to,
const void *from, int count);
/* Dayna - Dayna/Kinetics use this */
static void dayna_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
static void dayna_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void dayna_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c))
#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c))
#define memcmp_withio(a, b, c) memcmp((a), (void *)(b), (c))
/* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */
static void slow_sane_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page);
static void slow_sane_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void slow_sane_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void word_memcpy_tocard(unsigned long tp, const void *fp, int count);
static void word_memcpy_fromcard(void *tp, unsigned long fp, int count);
static u32 mac8390_msg_enable;
static enum mac8390_type __init mac8390_ident(struct nubus_dev *dev)
{
switch (dev->dr_sw) {
case NUBUS_DRSW_3COM:
switch (dev->dr_hw) {
case NUBUS_DRHW_APPLE_SONIC_NB:
case NUBUS_DRHW_APPLE_SONIC_LC:
case NUBUS_DRHW_SONNET:
return MAC8390_NONE;
default:
return MAC8390_APPLE;
}
break;
case NUBUS_DRSW_APPLE:
switch (dev->dr_hw) {
case NUBUS_DRHW_ASANTE_LC:
return MAC8390_NONE;
case NUBUS_DRHW_CABLETRON:
return MAC8390_CABLETRON;
default:
return MAC8390_APPLE;
}
break;
case NUBUS_DRSW_ASANTE:
return MAC8390_ASANTE;
break;
case NUBUS_DRSW_TECHWORKS:
case NUBUS_DRSW_DAYNA2:
case NUBUS_DRSW_DAYNA_LC:
if (dev->dr_hw == NUBUS_DRHW_CABLETRON)
return MAC8390_CABLETRON;
else
return MAC8390_APPLE;
break;
case NUBUS_DRSW_FARALLON:
return MAC8390_FARALLON;
break;
case NUBUS_DRSW_KINETICS:
switch (dev->dr_hw) {
case NUBUS_DRHW_INTERLAN:
return MAC8390_INTERLAN;
default:
return MAC8390_KINETICS;
}
break;
case NUBUS_DRSW_DAYNA:
/*
* These correspond to Dayna Sonic cards
* which use the macsonic driver
*/
if (dev->dr_hw == NUBUS_DRHW_SMC9194 ||
dev->dr_hw == NUBUS_DRHW_INTERLAN)
return MAC8390_NONE;
else
return MAC8390_DAYNA;
break;
}
return MAC8390_NONE;
}
static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
{
unsigned long outdata = 0xA5A0B5B0;
unsigned long indata = 0x00000000;
/* Try writing 32 bits */
memcpy_toio(membase, &outdata, 4);
/* Now compare them */
if (memcmp_withio(&outdata, membase, 4) == 0)
return ACCESS_32;
/* Write 16 bit output */
word_memcpy_tocard(membase, &outdata, 4);
/* Now read it back */
word_memcpy_fromcard(&indata, membase, 4);
if (outdata == indata)
return ACCESS_16;
return ACCESS_UNKNOWN;
}
static int __init mac8390_memsize(unsigned long membase)
{
unsigned long flags;
int i, j;
local_irq_save(flags);
/* Check up to 32K in 4K increments */
for (i = 0; i < 8; i++) {
volatile unsigned short *m = (unsigned short *)(membase + (i * 0x1000));
/* Unwriteable - we have a fully decoded card and the
RAM end located */
if (hwreg_present(m) == 0)
break;
/* write a distinctive byte */
*m = 0xA5A0 | i;
/* check that we read back what we wrote */
if (*m != (0xA5A0 | i))
break;
/* check for partial decode and wrap */
for (j = 0; j < i; j++) {
volatile unsigned short *p = (unsigned short *)(membase + (j * 0x1000));
if (*p != (0xA5A0 | j))
break;
}
}
local_irq_restore(flags);
/*
* in any case, we stopped once we tried one block too many,
* or once we reached 32K
*/
return i * 0x1000;
}
static bool __init mac8390_init(struct net_device *dev, struct nubus_dev *ndev,
enum mac8390_type cardtype)
{
struct nubus_dir dir;
struct nubus_dirent ent;
int offset;
volatile unsigned short *i;
printk_once(KERN_INFO pr_fmt("%s"), version);
dev->irq = SLOT2IRQ(ndev->board->slot);
/* This is getting to be a habit */
dev->base_addr = (ndev->board->slot_addr |
((ndev->board->slot & 0xf) << 20));
/*
* Get some Nubus info - we will trust the card's idea
* of where its memory and registers are.
*/
if (nubus_get_func_dir(ndev, &dir) == -1) {
pr_err("%s: Unable to get Nubus functional directory for slot %X!\n",
dev->name, ndev->board->slot);
return false;
}
/* Get the MAC address */
if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
pr_info("%s: Couldn't get MAC address!\n", dev->name);
return false;
}
nubus_get_rsrc_mem(dev->dev_addr, &ent, 6);
if (useresources[cardtype] == 1) {
nubus_rewinddir(&dir);
if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
&ent) == -1) {
pr_err("%s: Memory offset resource for slot %X not found!\n",
dev->name, ndev->board->slot);
return false;
}
nubus_get_rsrc_mem(&offset, &ent, 4);
dev->mem_start = dev->base_addr + offset;
/* yes, this is how the Apple driver does it */
dev->base_addr = dev->mem_start + 0x10000;
nubus_rewinddir(&dir);
if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH,
&ent) == -1) {
pr_info("%s: Memory length resource for slot %X not found, probing\n",
dev->name, ndev->board->slot);
offset = mac8390_memsize(dev->mem_start);
} else {
nubus_get_rsrc_mem(&offset, &ent, 4);
}
dev->mem_end = dev->mem_start + offset;
} else {
switch (cardtype) {
case MAC8390_KINETICS:
case MAC8390_DAYNA: /* it's the same */
dev->base_addr = (int)(ndev->board->slot_addr +
DAYNA_8390_BASE);
dev->mem_start = (int)(ndev->board->slot_addr +
DAYNA_8390_MEM);
dev->mem_end = dev->mem_start +
mac8390_memsize(dev->mem_start);
break;
case MAC8390_INTERLAN:
dev->base_addr = (int)(ndev->board->slot_addr +
INTERLAN_8390_BASE);
dev->mem_start = (int)(ndev->board->slot_addr +
INTERLAN_8390_MEM);
dev->mem_end = dev->mem_start +
mac8390_memsize(dev->mem_start);
break;
case MAC8390_CABLETRON:
dev->base_addr = (int)(ndev->board->slot_addr +
CABLETRON_8390_BASE);
dev->mem_start = (int)(ndev->board->slot_addr +
CABLETRON_8390_MEM);
/* The base address is unreadable if 0x00
* has been written to the command register
* Reset the chip by writing E8390_NODMA +
* E8390_PAGE0 + E8390_STOP just to be
* sure
*/
i = (void *)dev->base_addr;
*i = 0x21;
dev->mem_end = dev->mem_start +
mac8390_memsize(dev->mem_start);
break;
default:
pr_err("Card type %s is unsupported, sorry\n",
ndev->board->name);
return false;
}
}
return true;
}
struct net_device * __init mac8390_probe(int unit)
{
struct net_device *dev;
struct nubus_dev *ndev = NULL;
int err = -ENODEV;
struct ei_device *ei_local;
static unsigned int slots;
enum mac8390_type cardtype;
/* probably should check for Nubus instead */
if (!MACH_IS_MAC)
return ERR_PTR(-ENODEV);
dev = ____alloc_ei_netdev(0);
if (!dev)
return ERR_PTR(-ENOMEM);
if (unit >= 0)
sprintf(dev->name, "eth%d", unit);
while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET,
ndev))) {
/* Have we seen it already? */
if (slots & (1 << ndev->board->slot))
continue;
slots |= 1 << ndev->board->slot;
cardtype = mac8390_ident(ndev);
if (cardtype == MAC8390_NONE)
continue;
if (!mac8390_init(dev, ndev, cardtype))
continue;
/* Do the nasty 8390 stuff */
if (!mac8390_initdev(dev, ndev, cardtype))
break;
}
if (!ndev)
goto out;
ei_local = netdev_priv(dev);
ei_local->msg_enable = mac8390_msg_enable;
err = register_netdev(dev);
if (err)
goto out;
return dev;
out:
free_netdev(dev);
return ERR_PTR(err);
}
#ifdef MODULE
MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
MODULE_LICENSE("GPL");
/* overkill, of course */
static struct net_device *dev_mac8390[15];
int init_module(void)
{
int i;
for (i = 0; i < 15; i++) {
struct net_device *dev = mac8390_probe(-1);
if (IS_ERR(dev))
break;
dev_mac890[i] = dev;
}
if (!i) {
pr_notice("No useable cards found, driver NOT installed.\n");
return -ENODEV;
}
return 0;
}
void cleanup_module(void)
{
int i;
for (i = 0; i < 15; i++) {
struct net_device *dev = dev_mac890[i];
if (dev) {
unregister_netdev(dev);
free_netdev(dev);
}
}
}
#endif /* MODULE */
static const struct net_device_ops mac8390_netdev_ops = {
.ndo_open = mac8390_open,
.ndo_stop = mac8390_close,
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
.ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static int __init mac8390_initdev(struct net_device *dev,
struct nubus_dev *ndev,
enum mac8390_type type)
{
static u32 fwrd4_offsets[16] = {
0, 4, 8, 12,
16, 20, 24, 28,
32, 36, 40, 44,
48, 52, 56, 60
};
static u32 back4_offsets[16] = {
60, 56, 52, 48,
44, 40, 36, 32,
28, 24, 20, 16,
12, 8, 4, 0
};
static u32 fwrd2_offsets[16] = {
0, 2, 4, 6,
8, 10, 12, 14,
16, 18, 20, 22,
24, 26, 28, 30
};
int access_bitmode = 0;
/* Now fill in our stuff */
dev->netdev_ops = &mac8390_netdev_ops;
/* GAR, ei_status is actually a macro even though it looks global */
ei_status.name = cardname[type];
ei_status.word16 = word16[type];
/* Cabletron's TX/RX buffers are backwards */
if (type == MAC8390_CABLETRON) {
ei_status.tx_start_page = CABLETRON_TX_START_PG;
ei_status.rx_start_page = CABLETRON_RX_START_PG;
ei_status.stop_page = CABLETRON_RX_STOP_PG;
ei_status.rmem_start = dev->mem_start;
ei_status.rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
} else {
ei_status.tx_start_page = WD_START_PG;
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
ei_status.rmem_end = dev->mem_end;
}
/* Fill in model-specific information and functions */
switch (type) {
case MAC8390_FARALLON:
case MAC8390_APPLE:
switch (mac8390_testio(dev->mem_start)) {
case ACCESS_UNKNOWN:
pr_err("Don't know how to access card memory!\n");
return -ENODEV;
case ACCESS_16:
/* 16 bit card, register map is reversed */
ei_status.reset_8390 = mac8390_no_reset;
ei_status.block_input = slow_sane_block_input;
ei_status.block_output = slow_sane_block_output;
ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
ei_status.reg_offset = back4_offsets;
break;
case ACCESS_32:
/* 32 bit card, register map is reversed */
ei_status.reset_8390 = mac8390_no_reset;
ei_status.block_input = sane_block_input;
ei_status.block_output = sane_block_output;
ei_status.get_8390_hdr = sane_get_8390_hdr;
ei_status.reg_offset = back4_offsets;
access_bitmode = 1;
break;
}
break;
case MAC8390_ASANTE:
/* Some Asante cards pass the 32 bit test
* but overwrite system memory when run at 32 bit.
* so we run them all at 16 bit.
*/
ei_status.reset_8390 = mac8390_no_reset;
ei_status.block_input = slow_sane_block_input;
ei_status.block_output = slow_sane_block_output;
ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
ei_status.reg_offset = back4_offsets;
break;
case MAC8390_CABLETRON:
/* 16 bit card, register map is short forward */
ei_status.reset_8390 = mac8390_no_reset;
ei_status.block_input = slow_sane_block_input;
ei_status.block_output = slow_sane_block_output;
ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
ei_status.reg_offset = fwrd2_offsets;
break;
case MAC8390_DAYNA:
case MAC8390_KINETICS:
/* 16 bit memory, register map is forward */
/* dayna and similar */
ei_status.reset_8390 = mac8390_no_reset;
ei_status.block_input = dayna_block_input;
ei_status.block_output = dayna_block_output;
ei_status.get_8390_hdr = dayna_get_8390_hdr;
ei_status.reg_offset = fwrd4_offsets;
break;
case MAC8390_INTERLAN:
/* 16 bit memory, register map is forward */
ei_status.reset_8390 = interlan_reset;
ei_status.block_input = slow_sane_block_input;
ei_status.block_output = slow_sane_block_output;
ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
ei_status.reg_offset = fwrd4_offsets;
break;
default:
pr_err("Card type %s is unsupported, sorry\n",
ndev->board->name);
return -ENODEV;
}
__NS8390_init(dev, 0);
/* Good, done, now spit out some messages */
pr_info("%s: %s in slot %X (type %s)\n",
dev->name, ndev->board->name, ndev->board->slot,
cardname[type]);
pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
dev->dev_addr, dev->irq,
(unsigned int)(dev->mem_end - dev->mem_start) >> 10,
dev->mem_start, access_bitmode ? 32 : 16);
return 0;
}
static int mac8390_open(struct net_device *dev)
{
int err;
__ei_open(dev);
err = request_irq(dev->irq, __ei_interrupt, 0, "8390 Ethernet", dev);
if (err)
pr_err("%s: unable to get IRQ %d\n", dev->name, dev->irq);
return err;
}
static int mac8390_close(struct net_device *dev)
{
free_irq(dev->irq, dev);
__ei_close(dev);
return 0;
}
static void mac8390_no_reset(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
ei_status.txing = 0;
netif_info(ei_local, hw, dev, "reset not supported\n");
}
static void interlan_reset(struct net_device *dev)
{
unsigned char *target = nubus_slot_addr(IRQ2SLOT(dev->irq));
struct ei_device *ei_local = netdev_priv(dev);
netif_info(ei_local, hw, dev, "Need to reset the NS8390 t=%lu...",
jiffies);
ei_status.txing = 0;
target[0xC0000] = 0;
if (netif_msg_hw(ei_local))
pr_cont("reset complete\n");
}
/* dayna_memcpy_fromio/dayna_memcpy_toio */
/* directly from daynaport.c by Alan Cox */
static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from,
int count)
{
volatile unsigned char *ptr;
unsigned char *target = to;
from <<= 1; /* word, skip overhead */
ptr = (unsigned char *)(dev->mem_start+from);
/* Leading byte? */
if (from & 2) {
*target++ = ptr[-1];
ptr += 2;
count--;
}
while (count >= 2) {
*(unsigned short *)target = *(unsigned short volatile *)ptr;
ptr += 4; /* skip cruft */
target += 2;
count -= 2;
}
/* Trailing byte? */
if (count)
*target = *ptr;
}
static void dayna_memcpy_tocard(struct net_device *dev, int to,
const void *from, int count)
{
volatile unsigned short *ptr;
const unsigned char *src = from;
to <<= 1; /* word, skip overhead */
ptr = (unsigned short *)(dev->mem_start+to);
/* Leading byte? */
if (to & 2) { /* avoid a byte write (stomps on other data) */
ptr[-1] = (ptr[-1]&0xFF00)|*src++;
ptr++;
count--;
}
while (count >= 2) {
*ptr++ = *(unsigned short *)src; /* Copy and */
ptr++; /* skip cruft */
src += 2;
count -= 2;
}
/* Trailing byte? */
if (count) {
/* card doesn't like byte writes */
*ptr = (*ptr & 0x00FF) | (*src << 8);
}
}
/* sane block input/output */
static void sane_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page)
{
unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
memcpy_fromio(hdr, dev->mem_start + hdr_start, 4);
/* Fix endianness */
hdr->count = swab16(hdr->count);
}
static void sane_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
unsigned long xfer_start = xfer_base + dev->mem_start;
if (xfer_start + count > ei_status.rmem_end) {
/* We must wrap the input move. */
int semi_count = ei_status.rmem_end - xfer_start;
memcpy_fromio(skb->data, dev->mem_start + xfer_base,
semi_count);
count -= semi_count;
memcpy_fromio(skb->data + semi_count, ei_status.rmem_start,
count);
} else {
memcpy_fromio(skb->data, dev->mem_start + xfer_base, count);
}
}
static void sane_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page)
{
long shmem = (start_page - WD_START_PG)<<8;
memcpy_toio(dev->mem_start + shmem, buf, count);
}
/* dayna block input/output */
static void dayna_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page)
{
unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
dayna_memcpy_fromcard(dev, hdr, hdr_start, 4);
/* Fix endianness */
hdr->count = (hdr->count & 0xFF) << 8 | (hdr->count >> 8);
}
static void dayna_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
unsigned long xfer_start = xfer_base+dev->mem_start;
/* Note the offset math is done in card memory space which is word
per long onto our space. */
if (xfer_start + count > ei_status.rmem_end) {
/* We must wrap the input move. */
int semi_count = ei_status.rmem_end - xfer_start;
dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
count -= semi_count;
dayna_memcpy_fromcard(dev, skb->data + semi_count,
ei_status.rmem_start - dev->mem_start,
count);
} else {
dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
}
}
static void dayna_block_output(struct net_device *dev, int count,
const unsigned char *buf,
int start_page)
{
long shmem = (start_page - WD_START_PG)<<8;
dayna_memcpy_tocard(dev, shmem, buf, count);
}
/* Cabletron block I/O */
static void slow_sane_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr,
int ring_page)
{
unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
word_memcpy_fromcard(hdr, dev->mem_start + hdr_start, 4);
/* Register endianism - fix here rather than 8390.c */
hdr->count = (hdr->count&0xFF)<<8|(hdr->count>>8);
}
static void slow_sane_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
unsigned long xfer_start = xfer_base+dev->mem_start;
if (xfer_start + count > ei_status.rmem_end) {
/* We must wrap the input move. */
int semi_count = ei_status.rmem_end - xfer_start;
word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
semi_count);
count -= semi_count;
word_memcpy_fromcard(skb->data + semi_count,
ei_status.rmem_start, count);
} else {
word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
count);
}
}
static void slow_sane_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page)
{
long shmem = (start_page - WD_START_PG)<<8;
word_memcpy_tocard(dev->mem_start + shmem, buf, count);
}
static void word_memcpy_tocard(unsigned long tp, const void *fp, int count)
{
volatile unsigned short *to = (void *)tp;
const unsigned short *from = fp;
count++;
count /= 2;
while (count--)
*to++ = *from++;
}
static void word_memcpy_fromcard(void *tp, unsigned long fp, int count)
{
unsigned short *to = tp;
const volatile unsigned short *from = (const void *)fp;
count++;
count /= 2;
while (count--)
*to++ = *from++;
}

View file

@ -0,0 +1,481 @@
/*
* Support for ColdFire CPU based boards using a NS8390 Ethernet device.
*
* Derived from the many other 8390 drivers.
*
* (C) Copyright 2012, Greg Ungerer <gerg@uclinux.org>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/jiffies.h>
#include <linux/io.h>
#include <asm/mcf8390.h>
static const char version[] =
"mcf8390.c: (15-06-2012) Greg Ungerer <gerg@uclinux.org>";
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset */
#define NE_RESET 0x1f /* Issue a read to reset ,a write to clear */
#define NE_EN0_ISR 0x07
#define NE_EN0_DCFG 0x0e
#define NE_EN0_RSARLO 0x08
#define NE_EN0_RSARHI 0x09
#define NE_EN0_RCNTLO 0x0a
#define NE_EN0_RXCR 0x0c
#define NE_EN0_TXCR 0x0d
#define NE_EN0_RCNTHI 0x0b
#define NE_EN0_IMR 0x0f
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
static u32 mcf8390_msg_enable;
#ifdef NE2000_ODDOFFSET
/*
* A lot of the ColdFire boards use a separate address region for odd offset
* register addresses. The following functions convert and map as required.
* Note that the data port accesses are treated a little differently, and
* always accessed via the insX/outsX functions.
*/
static inline u32 NE_PTR(u32 addr)
{
if (addr & 1)
return addr - 1 + NE2000_ODDOFFSET;
return addr;
}
static inline u32 NE_DATA_PTR(u32 addr)
{
return addr;
}
void ei_outb(u32 val, u32 addr)
{
NE2000_BYTE *rp;
rp = (NE2000_BYTE *) NE_PTR(addr);
*rp = RSWAP(val);
}
#define ei_inb ei_inb
u8 ei_inb(u32 addr)
{
NE2000_BYTE *rp, val;
rp = (NE2000_BYTE *) NE_PTR(addr);
val = *rp;
return (u8) (RSWAP(val) & 0xff);
}
void ei_insb(u32 addr, void *vbuf, int len)
{
NE2000_BYTE *rp, val;
u8 *buf;
buf = (u8 *) vbuf;
rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
for (; (len > 0); len--) {
val = *rp;
*buf++ = RSWAP(val);
}
}
void ei_insw(u32 addr, void *vbuf, int len)
{
volatile u16 *rp;
u16 w, *buf;
buf = (u16 *) vbuf;
rp = (volatile u16 *) NE_DATA_PTR(addr);
for (; (len > 0); len--) {
w = *rp;
*buf++ = BSWAP(w);
}
}
void ei_outsb(u32 addr, const void *vbuf, int len)
{
NE2000_BYTE *rp, val;
u8 *buf;
buf = (u8 *) vbuf;
rp = (NE2000_BYTE *) NE_DATA_PTR(addr);
for (; (len > 0); len--) {
val = *buf++;
*rp = RSWAP(val);
}
}
void ei_outsw(u32 addr, const void *vbuf, int len)
{
volatile u16 *rp;
u16 w, *buf;
buf = (u16 *) vbuf;
rp = (volatile u16 *) NE_DATA_PTR(addr);
for (; (len > 0); len--) {
w = *buf++;
*rp = BSWAP(w);
}
}
#else /* !NE2000_ODDOFFSET */
#define ei_inb inb
#define ei_outb outb
#define ei_insb insb
#define ei_insw insw
#define ei_outsb outsb
#define ei_outsw outsw
#endif /* !NE2000_ODDOFFSET */
#define ei_inb_p ei_inb
#define ei_outb_p ei_outb
#include "lib8390.c"
/*
* Hard reset the card. This used to pause for the same period that a
* 8390 reset command required, but that shouldn't be necessary.
*/
static void mcf8390_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
u32 addr = dev->base_addr;
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies);
ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
ei_status.txing = 0;
ei_status.dmaing = 0;
/* This check _should_not_ be necessary, omit eventually. */
while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RESET) == 0) {
if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
netdev_warn(dev, "%s: did not complete\n", __func__);
break;
}
}
ei_outb(ENISR_RESET, addr + NE_EN0_ISR);
}
/*
* This *shouldn't* happen.
* If it does, it's the last thing you'll see
*/
static void mcf8390_dmaing_err(const char *func, struct net_device *dev,
struct ei_device *ei_local)
{
netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
func, ei_local->dmaing, ei_local->irqlock);
}
/*
* Grab the 8390 specific header. Similar to the block_input routine, but
* we don't need to be concerned with ring wrap as the header will be at
* the start of a page, so we optimize accordingly.
*/
static void mcf8390_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page)
{
struct ei_device *ei_local = netdev_priv(dev);
u32 addr = dev->base_addr;
if (ei_local->dmaing) {
mcf8390_dmaing_err(__func__, dev, ei_local);
return;
}
ei_local->dmaing |= 0x01;
ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
ei_outb(sizeof(struct e8390_pkt_hdr), addr + NE_EN0_RCNTLO);
ei_outb(0, addr + NE_EN0_RCNTHI);
ei_outb(0, addr + NE_EN0_RSARLO); /* On page boundary */
ei_outb(ring_page, addr + NE_EN0_RSARHI);
ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
ei_insw(addr + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr) >> 1);
outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
ei_local->dmaing &= ~0x01;
hdr->count = cpu_to_le16(hdr->count);
}
/*
* Block input and output, similar to the Crynwr packet driver.
* If you are porting to a new ethercard, look at the packet driver source
* for hints. The NEx000 doesn't share the on-board packet memory --
* you have to put the packet out through the "remote DMA" dataport
* using z_writeb.
*/
static void mcf8390_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
struct ei_device *ei_local = netdev_priv(dev);
u32 addr = dev->base_addr;
char *buf = skb->data;
if (ei_local->dmaing) {
mcf8390_dmaing_err(__func__, dev, ei_local);
return;
}
ei_local->dmaing |= 0x01;
ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, addr + NE_CMD);
ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
ei_outb(ring_offset & 0xff, addr + NE_EN0_RSARLO);
ei_outb(ring_offset >> 8, addr + NE_EN0_RSARHI);
ei_outb(E8390_RREAD + E8390_START, addr + NE_CMD);
ei_insw(addr + NE_DATAPORT, buf, count >> 1);
if (count & 1)
buf[count - 1] = ei_inb(addr + NE_DATAPORT);
ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
ei_local->dmaing &= ~0x01;
}
static void mcf8390_block_output(struct net_device *dev, int count,
const unsigned char *buf,
const int start_page)
{
struct ei_device *ei_local = netdev_priv(dev);
u32 addr = dev->base_addr;
unsigned long dma_start;
/* Make sure we transfer all bytes if 16bit IO writes */
if (count & 0x1)
count++;
if (ei_local->dmaing) {
mcf8390_dmaing_err(__func__, dev, ei_local);
return;
}
ei_local->dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
ei_outb(E8390_PAGE0 + E8390_START + E8390_NODMA, addr + NE_CMD);
ei_outb(ENISR_RDC, addr + NE_EN0_ISR);
/* Now the normal output. */
ei_outb(count & 0xff, addr + NE_EN0_RCNTLO);
ei_outb(count >> 8, addr + NE_EN0_RCNTHI);
ei_outb(0x00, addr + NE_EN0_RSARLO);
ei_outb(start_page, addr + NE_EN0_RSARHI);
ei_outb(E8390_RWRITE + E8390_START, addr + NE_CMD);
ei_outsw(addr + NE_DATAPORT, buf, count >> 1);
dma_start = jiffies;
while ((ei_inb(addr + NE_EN0_ISR) & ENISR_RDC) == 0) {
if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
netdev_warn(dev, "timeout waiting for Tx RDC\n");
mcf8390_reset_8390(dev);
__NS8390_init(dev, 1);
break;
}
}
ei_outb(ENISR_RDC, addr + NE_EN0_ISR); /* Ack intr */
ei_local->dmaing &= ~0x01;
}
static const struct net_device_ops mcf8390_netdev_ops = {
.ndo_open = __ei_open,
.ndo_stop = __ei_close,
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
.ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static int mcf8390_init(struct net_device *dev)
{
static u32 offsets[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
struct ei_device *ei_local = netdev_priv(dev);
unsigned char SA_prom[32];
u32 addr = dev->base_addr;
int start_page, stop_page;
int i, ret;
mcf8390_reset_8390(dev);
/*
* Read the 16 bytes of station address PROM.
* We must first initialize registers,
* similar to NS8390_init(eifdev, 0).
* We can't reliably read the SAPROM address without this.
* (I learned the hard way!).
*/
{
static const struct {
u32 value;
u32 offset;
} program_seq[] = {
{E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
/* Select page 0 */
{0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
{0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_IMR}, /* Mask completion irq */
{0xFF, NE_EN0_ISR},
{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
{32, NE_EN0_RCNTLO},
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
{0x00, NE_EN0_RSARHI},
{E8390_RREAD + E8390_START, NE_CMD},
};
for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
ei_outb(program_seq[i].value,
addr + program_seq[i].offset);
}
}
for (i = 0; i < 16; i++) {
SA_prom[i] = ei_inb(addr + NE_DATAPORT);
ei_inb(addr + NE_DATAPORT);
}
/* We must set the 8390 for word mode. */
ei_outb(0x49, addr + NE_EN0_DCFG);
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
/* Install the Interrupt handler */
ret = request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev);
if (ret)
return ret;
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = SA_prom[i];
netdev_dbg(dev, "Found ethernet address: %pM\n", dev->dev_addr);
ei_local->name = "mcf8390";
ei_local->tx_start_page = start_page;
ei_local->stop_page = stop_page;
ei_local->word16 = 1;
ei_local->rx_start_page = start_page + TX_PAGES;
ei_local->reset_8390 = mcf8390_reset_8390;
ei_local->block_input = mcf8390_block_input;
ei_local->block_output = mcf8390_block_output;
ei_local->get_8390_hdr = mcf8390_get_8390_hdr;
ei_local->reg_offset = offsets;
dev->netdev_ops = &mcf8390_netdev_ops;
__NS8390_init(dev, 0);
ret = register_netdev(dev);
if (ret) {
free_irq(dev->irq, dev);
return ret;
}
netdev_info(dev, "addr=0x%08x irq=%d, Ethernet Address %pM\n",
addr, dev->irq, dev->dev_addr);
return 0;
}
static int mcf8390_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct ei_device *ei_local;
struct resource *mem, *irq;
resource_size_t msize;
int ret;
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq == NULL) {
dev_err(&pdev->dev, "no IRQ specified?\n");
return -ENXIO;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem == NULL) {
dev_err(&pdev->dev, "no memory address specified?\n");
return -ENXIO;
}
msize = resource_size(mem);
if (!request_mem_region(mem->start, msize, pdev->name))
return -EBUSY;
dev = ____alloc_ei_netdev(0);
if (dev == NULL) {
release_mem_region(mem->start, msize);
return -ENOMEM;
}
SET_NETDEV_DEV(dev, &pdev->dev);
platform_set_drvdata(pdev, dev);
ei_local = netdev_priv(dev);
ei_local->msg_enable = mcf8390_msg_enable;
dev->irq = irq->start;
dev->base_addr = mem->start;
ret = mcf8390_init(dev);
if (ret) {
release_mem_region(mem->start, msize);
free_netdev(dev);
return ret;
}
return 0;
}
static int mcf8390_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct resource *mem;
unregister_netdev(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem)
release_mem_region(mem->start, resource_size(mem));
free_netdev(dev);
return 0;
}
static struct platform_driver mcf8390_drv = {
.driver = {
.name = "mcf8390",
.owner = THIS_MODULE,
},
.probe = mcf8390_probe,
.remove = mcf8390_remove,
};
module_platform_driver(mcf8390_drv);
MODULE_DESCRIPTION("MCF8390 ColdFire NS8390 driver");
MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mcf8390");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,741 @@
/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
/*
A Linux device driver for PCI NE2000 clones.
Authors and other copyright holders:
1992-2000 by Donald Becker, NE2000 core and various modifications.
1995-1998 by Paul Gortmaker, core modifications and PCI support.
Copyright 1993 assigned to the United States Government as represented
by the Director, National Security Agency.
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
Drivers based on or derived from this code fall under the GPL and must
retain the authorship, copyright and license notice. This file is not
a complete program and may only be used when the entire operating
system is licensed under the GPL.
The author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
Issues remaining:
People are making PCI ne2000 clones! Oh the horror, the horror...
Limited full-duplex support.
*/
#define DRV_NAME "ne2k-pci"
#define DRV_VERSION "1.03"
#define DRV_RELDATE "9/22/2003"
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#define MAX_UNITS 8 /* More are supported, limit only on options */
/* Used to pass the full-duplex flag, etc. */
static int full_duplex[MAX_UNITS];
static int options[MAX_UNITS];
/* Force a non std. amount of memory. Units are 256 byte pages. */
/* #define PACKETBUF_MEMSIZE 0x40 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include "8390.h"
static u32 ne2k_msg_enable;
/* These identify the driver base version and may not be removed. */
static const char version[] =
KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE
" D. Becker/P. Gortmaker\n";
#if defined(__powerpc__)
#define inl_le(addr) le32_to_cpu(inl(addr))
#define inw_le(addr) le16_to_cpu(inw(addr))
#endif
#define PFX DRV_NAME ": "
MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
MODULE_DESCRIPTION("PCI NE2000 clone driver");
MODULE_LICENSE("GPL");
module_param_named(msg_enable, ne2k_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
module_param_array(options, int, NULL, 0);
module_param_array(full_duplex, int, NULL, 0);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
MODULE_PARM_DESC(options, "Bit 5: full duplex");
MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
/* Some defines that people can play with if so inclined. */
/* Use 32 bit data-movement operations instead of 16 bit. */
#define USE_LONGIO
/* Do we implement the read before write bugfix ? */
/* #define NE_RW_BUGFIX */
/* Flags. We rename an existing ei_status field to store flags! */
/* Thus only the low 8 bits are usable for non-init-time flags. */
#define ne2k_flags reg0
enum {
ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */
FORCE_FDX=0x20, /* User override. */
REALTEK_FDX=0x40, HOLTEK_FDX=0x80,
STOP_PG_0x60=0x100,
};
enum ne2k_pci_chipsets {
CH_RealTek_RTL_8029 = 0,
CH_Winbond_89C940,
CH_Compex_RL2000,
CH_KTI_ET32P2,
CH_NetVin_NV5000SC,
CH_Via_86C926,
CH_SureCom_NE34,
CH_Winbond_W89C940F,
CH_Holtek_HT80232,
CH_Holtek_HT80229,
CH_Winbond_89C940_8c4a,
};
static struct {
char *name;
int flags;
} pci_clone_list[] = {
{"RealTek RTL-8029", REALTEK_FDX},
{"Winbond 89C940", 0},
{"Compex RL2000", 0},
{"KTI ET32P2", 0},
{"NetVin NV5000SC", 0},
{"Via 86C926", ONLY_16BIT_IO},
{"SureCom NE34", 0},
{"Winbond W89C940F", 0},
{"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
{"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
{"Winbond W89C940(misprogrammed)", 0},
{NULL,}
};
static const struct pci_device_id ne2k_pci_tbl[] = {
{ 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
{ 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
{ 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
{ 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
{ 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
{ 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
{ 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
{ 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
{ 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
{ 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
{ 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl);
/* ---- No user-serviceable parts below ---- */
#define NE_BASE (dev->base_addr)
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x20
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
static int ne2k_pci_open(struct net_device *dev);
static int ne2k_pci_close(struct net_device *dev);
static void ne2k_pci_reset_8390(struct net_device *dev);
static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void ne2k_pci_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void ne2k_pci_block_output(struct net_device *dev, const int count,
const unsigned char *buf, const int start_page);
static const struct ethtool_ops ne2k_pci_ethtool_ops;
/* There is no room in the standard 8390 structure for extra info we need,
so we build a meta/outer-wrapper structure.. */
struct ne2k_pci_card {
struct net_device *dev;
struct pci_dev *pci_dev;
};
/*
NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes
0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
detected by their SA prefix.
Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
mode results in doubled values, which can be detected and compensated for.
The probe is also responsible for initializing the card and filling
in the 'dev' and 'ei_status' structures.
*/
static const struct net_device_ops ne2k_netdev_ops = {
.ndo_open = ne2k_pci_open,
.ndo_stop = ne2k_pci_close,
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
.ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ei_poll,
#endif
};
static int ne2k_pci_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *dev;
int i;
unsigned char SA_prom[32];
int start_page, stop_page;
int irq, reg0, chip_idx = ent->driver_data;
static unsigned int fnd_cnt;
long ioaddr;
int flags = pci_clone_list[chip_idx].flags;
struct ei_device *ei_local;
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
static int printed_version;
if (!printed_version++)
printk(version);
#endif
fnd_cnt++;
i = pci_enable_device (pdev);
if (i)
return i;
ioaddr = pci_resource_start (pdev, 0);
irq = pdev->irq;
if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n");
return -ENODEV;
}
if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
NE_IO_EXTENT, ioaddr);
return -EBUSY;
}
reg0 = inb(ioaddr);
if (reg0 == 0xFF)
goto err_out_free_res;
/* Do a preliminary verification that we have a 8390. */
{
int regd;
outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
regd = inb(ioaddr + 0x0d);
outb(0xff, ioaddr + 0x0d);
outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
if (inb(ioaddr + EN0_COUNTER0) != 0) {
outb(reg0, ioaddr);
outb(regd, ioaddr + 0x0d); /* Restore the old values. */
goto err_out_free_res;
}
}
/* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */
dev = alloc_ei_netdev();
if (!dev) {
dev_err(&pdev->dev, "cannot allocate ethernet device\n");
goto err_out_free_res;
}
dev->netdev_ops = &ne2k_netdev_ops;
ei_local = netdev_priv(dev);
ei_local->msg_enable = ne2k_msg_enable;
SET_NETDEV_DEV(dev, &pdev->dev);
/* Reset card. Who knows what dain-bramaged state it was left in. */
{
unsigned long reset_start_time = jiffies;
outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
/* This looks like a horrible timing loop, but it should never take
more than a few cycles.
*/
while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
/* Limit wait: '2' avoids jiffy roll-over. */
if (jiffies - reset_start_time > 2) {
dev_err(&pdev->dev,
"Card failure (no reset ack).\n");
goto err_out_free_netdev;
}
outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
}
/* Read the 16 bytes of station address PROM.
We must first initialize registers, similar to NS8390_init(eifdev, 0).
We can't reliably read the SAPROM address without this.
(I learned the hard way!). */
{
struct {unsigned char value, offset; } program_seq[] = {
{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
{0x49, EN0_DCFG}, /* Set word-wide access. */
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR}, /* Mask completion irq. */
{0xFF, EN0_ISR},
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
{32, EN0_RCNTLO},
{0x00, EN0_RCNTHI},
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
{0x00, EN0_RSARHI},
{E8390_RREAD+E8390_START, E8390_CMD},
};
for (i = 0; i < ARRAY_SIZE(program_seq); i++)
outb(program_seq[i].value, ioaddr + program_seq[i].offset);
}
/* Note: all PCI cards have at least 16 bit access, so we don't have
to check for 8 bit cards. Most cards permit 32 bit access. */
if (flags & ONLY_32BIT_IO) {
for (i = 0; i < 4 ; i++)
((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT));
} else
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
SA_prom[i] = inb(ioaddr + NE_DATAPORT);
/* We always set the 8390 registers for word mode. */
outb(0x49, ioaddr + EN0_DCFG);
start_page = NESM_START_PG;
stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG;
/* Set up the rest of the parameters. */
dev->irq = irq;
dev->base_addr = ioaddr;
pci_set_drvdata(pdev, dev);
ei_status.name = pci_clone_list[chip_idx].name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
ei_status.word16 = 1;
ei_status.ne2k_flags = flags;
if (fnd_cnt < MAX_UNITS) {
if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX))
ei_status.ne2k_flags |= FORCE_FDX;
}
ei_status.rx_start_page = start_page + TX_PAGES;
#ifdef PACKETBUF_MEMSIZE
/* Allow the packet buffer size to be overridden by know-it-alls. */
ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
#endif
ei_status.reset_8390 = &ne2k_pci_reset_8390;
ei_status.block_input = &ne2k_pci_block_input;
ei_status.block_output = &ne2k_pci_block_output;
ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
ei_status.priv = (unsigned long) pdev;
dev->ethtool_ops = &ne2k_pci_ethtool_ops;
NS8390_init(dev, 0);
memcpy(dev->dev_addr, SA_prom, dev->addr_len);
i = register_netdev(dev);
if (i)
goto err_out_free_netdev;
netdev_info(dev, "%s found at %#lx, IRQ %d, %pM.\n",
pci_clone_list[chip_idx].name, ioaddr, dev->irq,
dev->dev_addr);
return 0;
err_out_free_netdev:
free_netdev (dev);
err_out_free_res:
release_region (ioaddr, NE_IO_EXTENT);
return -ENODEV;
}
/*
* Magic incantation sequence for full duplex on the supported cards.
*/
static inline int set_realtek_fdx(struct net_device *dev)
{
long ioaddr = dev->base_addr;
outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */
outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */
outb(0x40, ioaddr + 0x06); /* Enable full duplex */
outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */
outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */
return 0;
}
static inline int set_holtek_fdx(struct net_device *dev)
{
long ioaddr = dev->base_addr;
outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
return 0;
}
static int ne2k_pci_set_fdx(struct net_device *dev)
{
if (ei_status.ne2k_flags & REALTEK_FDX)
return set_realtek_fdx(dev);
else if (ei_status.ne2k_flags & HOLTEK_FDX)
return set_holtek_fdx(dev);
return -EOPNOTSUPP;
}
static int ne2k_pci_open(struct net_device *dev)
{
int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev);
if (ret)
return ret;
if (ei_status.ne2k_flags & FORCE_FDX)
ne2k_pci_set_fdx(dev);
ei_open(dev);
return 0;
}
static int ne2k_pci_close(struct net_device *dev)
{
ei_close(dev);
free_irq(dev->irq, dev);
return 0;
}
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
static void ne2k_pci_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n",
jiffies);
outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
ei_status.txing = 0;
ei_status.dmaing = 0;
/* This check _should_not_ be necessary, omit eventually. */
while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2) {
netdev_err(dev, "ne2k_pci_reset_8390() did not complete.\n");
break;
}
outb(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */
static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
long nic_base = dev->base_addr;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne2k_pci_get_8390_hdr "
"[DMAstat:%d][irqlock:%d].\n",
ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
outb(0, nic_base + EN0_RCNTHI);
outb(0, nic_base + EN0_RSARLO); /* On page boundary */
outb(ring_page, nic_base + EN0_RSARHI);
outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
} else {
*(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT));
le16_to_cpus(&hdr->count);
}
outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
/* Block input and output, similar to the Crynwr packet driver. If you
are porting to a new ethercard, look at the packet driver source for hints.
The NEx000 doesn't share the on-board packet memory -- you have to put
the packet out through the "remote DMA" dataport using outb. */
static void ne2k_pci_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
long nic_base = dev->base_addr;
char *buf = skb->data;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne2k_pci_block_input "
"[DMAstat:%d][irqlock:%d].\n",
ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
if (ei_status.ne2k_flags & ONLY_32BIT_IO)
count = (count + 3) & 0xFFFC;
outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb(count & 0xff, nic_base + EN0_RCNTLO);
outb(count >> 8, nic_base + EN0_RCNTHI);
outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
outb(ring_offset >> 8, nic_base + EN0_RSARHI);
outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
insw(NE_BASE + NE_DATAPORT,buf,count>>1);
if (count & 0x01) {
buf[count-1] = inb(NE_BASE + NE_DATAPORT);
}
} else {
insl(NE_BASE + NE_DATAPORT, buf, count>>2);
if (count & 3) {
buf += count & ~3;
if (count & 2) {
__le16 *b = (__le16 *)buf;
*b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT));
buf = (char *)b;
}
if (count & 1)
*buf = inb(NE_BASE + NE_DATAPORT);
}
}
outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
static void ne2k_pci_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page)
{
long nic_base = NE_BASE;
unsigned long dma_start;
/* On little-endian it's always safe to round the count up for
word writes. */
if (ei_status.ne2k_flags & ONLY_32BIT_IO)
count = (count + 3) & 0xFFFC;
else
if (count & 0x01)
count++;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing) {
netdev_err(dev, "DMAing conflict in ne2k_pci_block_output."
"[DMAstat:%d][irqlock:%d]\n",
ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
#ifdef NE8390_RW_BUGFIX
/* Handle the read-before-write bug the same way as the
Crynwr packet driver -- the NatSemi method doesn't work.
Actually this doesn't always work either, but if you have
problems with your NEx000 this is better than nothing! */
outb(0x42, nic_base + EN0_RCNTLO);
outb(0x00, nic_base + EN0_RCNTHI);
outb(0x42, nic_base + EN0_RSARLO);
outb(0x00, nic_base + EN0_RSARHI);
outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
#endif
outb(ENISR_RDC, nic_base + EN0_ISR);
/* Now the normal output. */
outb(count & 0xff, nic_base + EN0_RCNTLO);
outb(count >> 8, nic_base + EN0_RCNTHI);
outb(0x00, nic_base + EN0_RSARLO);
outb(start_page, nic_base + EN0_RSARHI);
outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
} else {
outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
if (count & 3) {
buf += count & ~3;
if (count & 2) {
__le16 *b = (__le16 *)buf;
outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT);
buf = (char *)b;
}
}
}
dma_start = jiffies;
while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */
netdev_warn(dev, "timeout waiting for Tx RDC.\n");
ne2k_pci_reset_8390(dev);
NS8390_init(dev,1);
break;
}
outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
static void ne2k_pci_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct ei_device *ei = netdev_priv(dev);
struct pci_dev *pci_dev = (struct pci_dev *) ei->priv;
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info));
}
static u32 ne2k_pci_get_msglevel(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
return ei_local->msg_enable;
}
static void ne2k_pci_set_msglevel(struct net_device *dev, u32 v)
{
struct ei_device *ei_local = netdev_priv(dev);
ei_local->msg_enable = v;
}
static const struct ethtool_ops ne2k_pci_ethtool_ops = {
.get_drvinfo = ne2k_pci_get_drvinfo,
.get_msglevel = ne2k_pci_get_msglevel,
.set_msglevel = ne2k_pci_set_msglevel,
};
static void ne2k_pci_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
BUG_ON(!dev);
unregister_netdev(dev);
release_region(dev->base_addr, NE_IO_EXTENT);
free_netdev(dev);
pci_disable_device(pdev);
}
#ifdef CONFIG_PM
static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata (pdev);
netif_device_detach(dev);
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
static int ne2k_pci_resume (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata (pdev);
int rc;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
rc = pci_enable_device(pdev);
if (rc)
return rc;
NS8390_init(dev, 1);
netif_device_attach(dev);
return 0;
}
#endif /* CONFIG_PM */
static struct pci_driver ne2k_driver = {
.name = DRV_NAME,
.probe = ne2k_pci_init_one,
.remove = ne2k_pci_remove_one,
.id_table = ne2k_pci_tbl,
#ifdef CONFIG_PM
.suspend = ne2k_pci_suspend,
.resume = ne2k_pci_resume,
#endif /* CONFIG_PM */
};
static int __init ne2k_pci_init(void)
{
/* when a module, this is printed whether or not devices are found in probe */
#ifdef MODULE
printk(version);
#endif
return pci_register_driver(&ne2k_driver);
}
static void __exit ne2k_pci_cleanup(void)
{
pci_unregister_driver (&ne2k_driver);
}
module_init(ne2k_pci_init);
module_exit(ne2k_pci_cleanup);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,631 @@
/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
/*
This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
Written 1993-1998 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
The author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
This driver uses the cards in the 8390-compatible mode.
Most of the run-time complexity is handled by the generic code in
8390.c. The code in this file is responsible for
ultra_probe() Detecting and initializing the card.
ultra_probe1()
ultra_probe_isapnp()
ultra_open() The card-specific details of starting, stopping
ultra_reset_8390() and resetting the 8390 NIC core.
ultra_close()
ultra_block_input() Routines for reading and writing blocks of
ultra_block_output() packet buffer memory.
ultra_pio_input()
ultra_pio_output()
This driver enables the shared memory only when doing the actual data
transfers to avoid a bug in early version of the card that corrupted
data transferred by a AHA1542.
This driver now supports the programmed-I/O (PIO) data transfer mode of
the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
That support (if available) is in smc-ez.c.
Changelog:
Paul Gortmaker : multiple card support for module users.
Donald Becker : 4/17/96 PIO support, minor potential problems avoided.
Donald Becker : 6/6/96 correctly set auto-wrap bit.
Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
Note about the ISA PnP support:
This driver can not autoprobe for more than one SMC EtherEZ PnP card.
You have to configure the second card manually through the /proc/isapnp
interface and then load the module with an explicit io=0x___ option.
*/
static const char version[] =
"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/isapnp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
#include "8390.h"
#define DRV_NAME "smc-ultra"
/* A zero-terminated list of I/O addresses to be probed. */
static unsigned int ultra_portlist[] __initdata =
{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
static int ultra_probe1(struct net_device *dev, int ioaddr);
#ifdef __ISAPNP__
static int ultra_probe_isapnp(struct net_device *dev);
#endif
static int ultra_open(struct net_device *dev);
static void ultra_reset_8390(struct net_device *dev);
static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void ultra_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void ultra_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page);
static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void ultra_pio_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void ultra_pio_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page);
static int ultra_close_card(struct net_device *dev);
#ifdef __ISAPNP__
static struct isapnp_device_id ultra_device_ids[] __initdata = {
{ ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
(long) "SMC EtherEZ (8416)" },
{ } /* terminate list */
};
MODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
#endif
static u32 ultra_msg_enable;
#define START_PG 0x00 /* First page of TX buffer */
#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */
#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */
#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
#define ULTRA_IO_EXTENT 32
#define EN0_ERWCNT 0x08 /* Early receive warning count. */
#ifdef CONFIG_NET_POLL_CONTROLLER
static void ultra_poll(struct net_device *dev)
{
disable_irq(dev->irq);
ei_interrupt(dev->irq, dev);
enable_irq(dev->irq);
}
#endif
/* Probe for the Ultra. This looks like a 8013 with the station
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
following.
*/
static int __init do_ultra_probe(struct net_device *dev)
{
int i;
int base_addr = dev->base_addr;
int irq = dev->irq;
if (base_addr > 0x1ff) /* Check a single specified location. */
return ultra_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
return -ENXIO;
#ifdef __ISAPNP__
/* Look for any installed ISAPnP cards */
if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
return 0;
#endif
for (i = 0; ultra_portlist[i]; i++) {
dev->irq = irq;
if (ultra_probe1(dev, ultra_portlist[i]) == 0)
return 0;
}
return -ENODEV;
}
#ifndef MODULE
struct net_device * __init ultra_probe(int unit)
{
struct net_device *dev = alloc_ei_netdev();
int err;
if (!dev)
return ERR_PTR(-ENOMEM);
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
err = do_ultra_probe(dev);
if (err)
goto out;
return dev;
out:
free_netdev(dev);
return ERR_PTR(err);
}
#endif
static const struct net_device_ops ultra_netdev_ops = {
.ndo_open = ultra_open,
.ndo_stop = ultra_close_card,
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
.ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ultra_poll,
#endif
};
static int __init ultra_probe1(struct net_device *dev, int ioaddr)
{
int i, retval;
int checksum = 0;
const char *model_name;
unsigned char eeprom_irq = 0;
static unsigned version_printed;
/* Values from various config regs. */
unsigned char num_pages, irqreg, addr, piomode;
unsigned char idreg = inb(ioaddr + 7);
unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
struct ei_device *ei_local = netdev_priv(dev);
if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
return -EBUSY;
/* Check the ID nibble. */
if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
&& (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */
retval = -ENODEV;
goto out;
}
/* Select the station address register set. */
outb(reg4, ioaddr + 4);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
if ((checksum & 0xff) != 0xFF) {
retval = -ENODEV;
goto out;
}
if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
netdev_info(dev, version);
model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
netdev_info(dev, "%s at %#3x, %pM", model_name,
ioaddr, dev->dev_addr);
/* Switch from the station address to the alternate register set and
read the useful registers there. */
outb(0x80 | reg4, ioaddr + 4);
/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
piomode = inb(ioaddr + 0x8);
addr = inb(ioaddr + 0xb);
irqreg = inb(ioaddr + 0xd);
/* Switch back to the station address register set so that the MS-DOS driver
can find the card after a warm boot. */
outb(reg4, ioaddr + 4);
if (dev->irq < 2) {
unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
int irq;
/* The IRQ bits are split. */
irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
if (irq == 0) {
pr_cont(", failed to detect IRQ line.\n");
retval = -EAGAIN;
goto out;
}
dev->irq = irq;
eeprom_irq = 1;
}
/* The 8390 isn't at the base address, so fake the offset */
dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
{
static const int addr_tbl[4] = {
0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
};
static const short num_pages_tbl[4] = {
0x20, 0x40, 0x80, 0xff
};
dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
num_pages = num_pages_tbl[(addr >> 4) & 3];
}
ei_status.name = model_name;
ei_status.word16 = 1;
ei_status.tx_start_page = START_PG;
ei_status.rx_start_page = START_PG + TX_PAGES;
ei_status.stop_page = num_pages;
ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
if (!ei_status.mem) {
pr_cont(", failed to ioremap.\n");
retval = -ENOMEM;
goto out;
}
dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
if (piomode) {
pr_cont(", %s IRQ %d programmed-I/O mode.\n",
eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
ei_status.block_input = &ultra_pio_input;
ei_status.block_output = &ultra_pio_output;
ei_status.get_8390_hdr = &ultra_pio_get_hdr;
} else {
pr_cont(", %s IRQ %d memory %#lx-%#lx.\n",
eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start,
dev->mem_end-1);
ei_status.block_input = &ultra_block_input;
ei_status.block_output = &ultra_block_output;
ei_status.get_8390_hdr = &ultra_get_8390_hdr;
}
ei_status.reset_8390 = &ultra_reset_8390;
dev->netdev_ops = &ultra_netdev_ops;
NS8390_init(dev, 0);
ei_local->msg_enable = ultra_msg_enable;
retval = register_netdev(dev);
if (retval)
goto out;
return 0;
out:
release_region(ioaddr, ULTRA_IO_EXTENT);
return retval;
}
#ifdef __ISAPNP__
static int __init ultra_probe_isapnp(struct net_device *dev)
{
int i;
for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
struct pnp_dev *idev = NULL;
while ((idev = pnp_find_dev(NULL,
ultra_device_ids[i].vendor,
ultra_device_ids[i].function,
idev))) {
/* Avoid already found cards from previous calls */
if (pnp_device_attach(idev) < 0)
continue;
if (pnp_activate_dev(idev) < 0) {
__again:
pnp_device_detach(idev);
continue;
}
/* if no io and irq, search for next */
if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
goto __again;
/* found it */
dev->base_addr = pnp_port_start(idev, 0);
dev->irq = pnp_irq(idev, 0);
netdev_info(dev,
"smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
(char *) ultra_device_ids[i].driver_data,
dev->base_addr, dev->irq);
if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
netdev_err(dev,
"smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n",
dev->base_addr);
pnp_device_detach(idev);
return -ENXIO;
}
ei_status.priv = (unsigned long)idev;
break;
}
if (!idev)
continue;
return 0;
}
return -ENODEV;
}
#endif
static int
ultra_open(struct net_device *dev)
{
int retval;
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
if (retval)
return retval;
outb(0x00, ioaddr); /* Disable shared memory for safety. */
outb(0x80, ioaddr + 5);
/* Set the IRQ line. */
outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
if (ei_status.block_input == &ultra_pio_input) {
outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */
outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */
} else
outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
/* Set the early receive warning level in window 0 high enough not
to receive ERW interrupts. */
outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
outb(0xff, dev->base_addr + EN0_ERWCNT);
ei_open(dev);
return 0;
}
static void
ultra_reset_8390(struct net_device *dev)
{
int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
struct ei_device *ei_local = netdev_priv(dev);
outb(ULTRA_RESET, cmd_port);
netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies);
ei_status.txing = 0;
outb(0x00, cmd_port); /* Disable shared memory for safety. */
outb(0x80, cmd_port + 5);
if (ei_status.block_input == &ultra_pio_input)
outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */
else
outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */
netif_dbg(ei_local, hw, dev, "reset done\n");
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */
static void
ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
#ifdef __BIG_ENDIAN
/* Officially this is what we are doing, but the readl() is faster */
/* unfortunately it isn't endian aware of the struct */
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
hdr->count = le16_to_cpu(hdr->count);
#else
((unsigned int*)hdr)[0] = readl(hdr_start);
#endif
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
}
/* Block input and output are easy on shared memory ethercards, the only
complication is when the ring buffer wraps. */
static void
ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
{
void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
/* Enable shared memory. */
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
if (ring_offset + count > ei_status.stop_page*256) {
/* We must wrap the input move. */
int semi_count = ei_status.stop_page*256 - ring_offset;
memcpy_fromio(skb->data, xfer_start, semi_count);
count -= semi_count;
memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
} else {
memcpy_fromio(skb->data, xfer_start, count);
}
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
}
static void
ultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
int start_page)
{
void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
/* Enable shared memory. */
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
memcpy_toio(shmem, buf, count);
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
}
/* The identical operations for programmed I/O cards.
The PIO model is trivial to use: the 16 bit start address is written
byte-sequentially to IOPA, with no intervening I/O operations, and the
data is read or written to the IOPD data port.
The only potential complication is that the address register is shared
and must be always be rewritten between each read/write direction change.
This is no problem for us, as the 8390 code ensures that we are single
threaded. */
static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
outb(ring_page, ioaddr + IOPA);
insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
}
static void ultra_pio_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
char *buf = skb->data;
/* For now set the address again, although it should already be correct. */
outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */
outb(ring_offset >> 8, ioaddr + IOPA);
/* We know skbuffs are padded to at least word alignment. */
insw(ioaddr + IOPD, buf, (count+1)>>1);
}
static void ultra_pio_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
outb(start_page, ioaddr + IOPA);
/* An extra odd byte is OK here as well. */
outsw(ioaddr + IOPD, buf, (count+1)>>1);
}
static int
ultra_close_card(struct net_device *dev)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
struct ei_device *ei_local = netdev_priv(dev);
netif_stop_queue(dev);
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
outb(0x00, ioaddr + 6); /* Disable interrupts. */
free_irq(dev->irq, dev);
NS8390_init(dev, 0);
/* We should someday disable shared memory and change to 8-bit mode
"just in case"... */
return 0;
}
#ifdef MODULE
#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
static struct net_device *dev_ultra[MAX_ULTRA_CARDS];
static int io[MAX_ULTRA_CARDS];
static int irq[MAX_ULTRA_CARDS];
module_param_array(io, int, NULL, 0);
module_param_array(irq, int, NULL, 0);
module_param_named(msg_enable, ultra_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
MODULE_PARM_DESC(io, "I/O base address(es)");
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
MODULE_LICENSE("GPL");
/* This is set up so that only a single autoprobe takes place per call.
ISA device autoprobes on a running machine are not recommended. */
int __init
init_module(void)
{
struct net_device *dev;
int this_dev, found = 0;
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
if (io[this_dev] == 0) {
if (this_dev != 0) break; /* only autoprobe 1st one */
printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
}
dev = alloc_ei_netdev();
if (!dev)
break;
dev->irq = irq[this_dev];
dev->base_addr = io[this_dev];
if (do_ultra_probe(dev) == 0) {
dev_ultra[found++] = dev;
continue;
}
free_netdev(dev);
printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
break;
}
if (found)
return 0;
return -ENXIO;
}
static void cleanup_card(struct net_device *dev)
{
/* NB: ultra_close_card() does free_irq */
#ifdef __ISAPNP__
struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
if (idev)
pnp_device_detach(idev);
#endif
release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
iounmap(ei_status.mem);
}
void __exit
cleanup_module(void)
{
int this_dev;
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
struct net_device *dev = dev_ultra[this_dev];
if (dev) {
unregister_netdev(dev);
cleanup_card(dev);
free_netdev(dev);
}
}
}
#endif /* MODULE */

View file

@ -0,0 +1,303 @@
/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1999 kaz Kojima
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <mach-se/mach/se.h>
#include <asm/machvec.h>
#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
#endif
#include "8390.h"
#define DRV_NAME "stnic"
#define byte unsigned char
#define half unsigned short
#define word unsigned int
#define vbyte volatile unsigned char
#define vhalf volatile unsigned short
#define vword volatile unsigned int
#define STNIC_RUN 0x01 /* 1 == Run, 0 == reset. */
#define START_PG 0 /* First page of TX buffer */
#define STOP_PG 128 /* Last page +1 of RX ring */
/* Alias */
#define STNIC_CR E8390_CMD
#define PG0_RSAR0 EN0_RSARLO
#define PG0_RSAR1 EN0_RSARHI
#define PG0_RBCR0 EN0_RCNTLO
#define PG0_RBCR1 EN0_RCNTHI
#define CR_RRD E8390_RREAD
#define CR_RWR E8390_RWRITE
#define CR_PG0 E8390_PAGE0
#define CR_STA E8390_START
#define CR_RDMA E8390_NODMA
/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS. */
static byte stnic_eadr[6] =
{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07};
static struct net_device *stnic_dev;
static void stnic_reset (struct net_device *dev);
static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void stnic_block_input (struct net_device *dev, int count,
struct sk_buff *skb , int ring_offset);
static void stnic_block_output (struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void stnic_init (struct net_device *dev);
static u32 stnic_msg_enable;
module_param_named(msg_enable, stnic_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
/* SH7750 specific read/write io. */
static inline void
STNIC_DELAY (void)
{
vword trash;
trash = *(vword *) 0xa0000000;
trash = *(vword *) 0xa0000000;
trash = *(vword *) 0xa0000000;
}
static inline byte
STNIC_READ (int reg)
{
byte val;
val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff;
STNIC_DELAY ();
return val;
}
static inline void
STNIC_WRITE (int reg, byte val)
{
*(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8);
STNIC_DELAY ();
}
static int __init stnic_probe(void)
{
struct net_device *dev;
int i, err;
struct ei_device *ei_local;
/* If we are not running on a SolutionEngine, give up now */
if (! MACH_SE)
return -ENODEV;
/* New style probing API */
dev = alloc_ei_netdev();
if (!dev)
return -ENOMEM;
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_get_node_addr (stnic_eadr);
#endif
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = stnic_eadr[i];
/* Set the base address to point to the NIC, not the "real" base! */
dev->base_addr = 0x1000;
dev->irq = IRQ_STNIC;
dev->netdev_ops = &ei_netdev_ops;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev);
if (err) {
netdev_emerg(dev, " unable to get IRQ %d.\n", dev->irq);
free_netdev(dev);
return err;
}
ei_status.name = dev->name;
ei_status.word16 = 1;
#ifdef __LITTLE_ENDIAN__
ei_status.bigendian = 0;
#else
ei_status.bigendian = 1;
#endif
ei_status.tx_start_page = START_PG;
ei_status.rx_start_page = START_PG + TX_PAGES;
ei_status.stop_page = STOP_PG;
ei_status.reset_8390 = &stnic_reset;
ei_status.get_8390_hdr = &stnic_get_hdr;
ei_status.block_input = &stnic_block_input;
ei_status.block_output = &stnic_block_output;
stnic_init (dev);
ei_local = netdev_priv(dev);
ei_local->msg_enable = stnic_msg_enable;
err = register_netdev(dev);
if (err) {
free_irq(dev->irq, dev);
free_netdev(dev);
return err;
}
stnic_dev = dev;
netdev_info(dev, "NS ST-NIC 83902A\n");
return 0;
}
static void
stnic_reset (struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
*(vhalf *) PA_83902_RST = 0;
udelay (5);
netif_warn(ei_local, hw, dev, "8390 reset done (%ld).\n", jiffies);
*(vhalf *) PA_83902_RST = ~0;
udelay (5);
}
static void
stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page)
{
struct ei_device *ei_local = netdev_priv(dev);
half buf[2];
STNIC_WRITE (PG0_RSAR0, 0);
STNIC_WRITE (PG0_RSAR1, ring_page);
STNIC_WRITE (PG0_RBCR0, 4);
STNIC_WRITE (PG0_RBCR1, 0);
STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
buf[0] = *(vhalf *) PA_83902_IF;
STNIC_DELAY ();
buf[1] = *(vhalf *) PA_83902_IF;
STNIC_DELAY ();
hdr->next = buf[0] >> 8;
hdr->status = buf[0] & 0xff;
#ifdef __LITTLE_ENDIAN__
hdr->count = buf[1];
#else
hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8);
#endif
netif_dbg(ei_local, probe, dev, "ring %x status %02x next %02x count %04x.\n",
ring_page, hdr->status, hdr->next, hdr->count);
STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}
/* Block input and output, similar to the Crynwr packet driver. If you are
porting to a new ethercard look at the packet driver source for hints.
The HP LAN doesn't use shared memory -- we put the packet
out through the "remote DMA" dataport. */
static void
stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb,
int offset)
{
char *buf = skb->data;
half val;
STNIC_WRITE (PG0_RSAR0, offset & 0xff);
STNIC_WRITE (PG0_RSAR1, offset >> 8);
STNIC_WRITE (PG0_RBCR0, length & 0xff);
STNIC_WRITE (PG0_RBCR1, length >> 8);
STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
if (length & 1)
length++;
while (length > 0)
{
val = *(vhalf *) PA_83902_IF;
#ifdef __LITTLE_ENDIAN__
*buf++ = val & 0xff;
*buf++ = val >> 8;
#else
*buf++ = val >> 8;
*buf++ = val & 0xff;
#endif
STNIC_DELAY ();
length -= sizeof (half);
}
STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}
static void
stnic_block_output (struct net_device *dev, int length,
const unsigned char *buf, int output_page)
{
STNIC_WRITE (PG0_RBCR0, 1); /* Write non-zero value */
STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
STNIC_DELAY ();
STNIC_WRITE (PG0_RBCR0, length & 0xff);
STNIC_WRITE (PG0_RBCR1, length >> 8);
STNIC_WRITE (PG0_RSAR0, 0);
STNIC_WRITE (PG0_RSAR1, output_page);
STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);
if (length & 1)
length++;
while (length > 0)
{
#ifdef __LITTLE_ENDIAN__
*(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0];
#else
*(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1];
#endif
STNIC_DELAY ();
buf += sizeof (half);
length -= sizeof (half);
}
STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
}
/* This function resets the STNIC if something screws up. */
static void
stnic_init (struct net_device *dev)
{
stnic_reset (dev);
NS8390_init (dev, 0);
}
static void __exit stnic_cleanup(void)
{
unregister_netdev(stnic_dev);
free_irq(stnic_dev->irq, stnic_dev);
free_netdev(stnic_dev);
}
module_init(stnic_probe);
module_exit(stnic_cleanup);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,574 @@
/* wd.c: A WD80x3 ethernet driver for linux. */
/*
Written 1993-94 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
The author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
This is a driver for WD8003 and WD8013 "compatible" ethercards.
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
Changelog:
Paul Gortmaker : multiple card support for module users, support
for non-standard memory sizes.
*/
static const char version[] =
"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <asm/io.h>
#include "8390.h"
#define DRV_NAME "wd"
/* A zero-terminated list of I/O addresses to be probed. */
static unsigned int wd_portlist[] __initdata =
{0x300, 0x280, 0x380, 0x240, 0};
static int wd_probe1(struct net_device *dev, int ioaddr);
static int wd_open(struct net_device *dev);
static void wd_reset_8390(struct net_device *dev);
static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void wd_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void wd_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static int wd_close(struct net_device *dev);
static u32 wd_msg_enable;
#define WD_START_PG 0x00 /* First page of TX buffer */
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
#define WD_CMDREG 0 /* Offset to ASIC command register. */
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
#define WD_MEMENB 0x40 /* Enable the shared memory. */
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
#define WD_IO_EXTENT 32
/* Probe for the WD8003 and WD8013. These cards have the station
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
following. A Soundblaster can have the same checksum as an WDethercard,
so we have an extra exclusionary check for it.
The wd_probe1() routine initializes the card and fills the
station address field. */
static int __init do_wd_probe(struct net_device *dev)
{
int i;
struct resource *r;
int base_addr = dev->base_addr;
int irq = dev->irq;
int mem_start = dev->mem_start;
int mem_end = dev->mem_end;
if (base_addr > 0x1ff) { /* Check a user specified location. */
r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
if ( r == NULL)
return -EBUSY;
i = wd_probe1(dev, base_addr);
if (i != 0)
release_region(base_addr, WD_IO_EXTENT);
else
r->name = dev->name;
return i;
}
else if (base_addr != 0) /* Don't probe at all. */
return -ENXIO;
for (i = 0; wd_portlist[i]; i++) {
int ioaddr = wd_portlist[i];
r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
if (r == NULL)
continue;
if (wd_probe1(dev, ioaddr) == 0) {
r->name = dev->name;
return 0;
}
release_region(ioaddr, WD_IO_EXTENT);
dev->irq = irq;
dev->mem_start = mem_start;
dev->mem_end = mem_end;
}
return -ENODEV;
}
#ifndef MODULE
struct net_device * __init wd_probe(int unit)
{
struct net_device *dev = alloc_ei_netdev();
int err;
if (!dev)
return ERR_PTR(-ENOMEM);
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
err = do_wd_probe(dev);
if (err)
goto out;
return dev;
out:
free_netdev(dev);
return ERR_PTR(err);
}
#endif
static const struct net_device_ops wd_netdev_ops = {
.ndo_open = wd_open,
.ndo_stop = wd_close,
.ndo_start_xmit = ei_start_xmit,
.ndo_tx_timeout = ei_tx_timeout,
.ndo_get_stats = ei_get_stats,
.ndo_set_rx_mode = ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ei_poll,
#endif
};
static int __init wd_probe1(struct net_device *dev, int ioaddr)
{
int i;
int err;
int checksum = 0;
int ancient = 0; /* An old card without config registers. */
int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
const char *model_name;
static unsigned version_printed;
struct ei_device *ei_local = netdev_priv(dev);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
|| inb(ioaddr + 9) == 0xff
|| (checksum & 0xff) != 0xFF)
return -ENODEV;
/* Check for semi-valid mem_start/end values if supplied. */
if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
netdev_warn(dev,
"wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
dev->mem_start = 0;
dev->mem_end = 0;
}
if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
netdev_info(dev, version);
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr);
/* The following PureData probe code was contributed by
Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
configuration differently from others so we have to check for them.
This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
*/
if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
unsigned char reg5 = inb(ioaddr+5);
switch (inb(ioaddr+2)) {
case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
case 0x05: word16 = 0; model_name = "PDUC8023"; break;
case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
/* Either 0x01 (dumb) or they've released a new version. */
default: word16 = 0; model_name = "PDI8023"; break;
}
dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
} else { /* End of PureData probe */
/* This method of checking for a 16-bit board is borrowed from the
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
I'm comparing the two method in alpha test to make certain they
return the same result. */
/* Check for the old 8 bit board - it has register 0/8 aliasing.
Do NOT check i>=6 here -- it hangs the old 8003 boards! */
for (i = 0; i < 6; i++)
if (inb(ioaddr+i) != inb(ioaddr+8+i))
break;
if (i >= 6) {
ancient = 1;
model_name = "WD8003-old";
word16 = 0;
} else {
int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
/* Magic to set ASIC to word-wide mode. */
outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
outb(tmp, ioaddr+1);
model_name = "WD8013";
word16 = 1; /* We have a 16bit board here! */
} else {
model_name = "WD8003";
word16 = 0;
}
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
}
#ifndef final_version
if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
word16 ? 16 : 8,
(inb(ioaddr+1) & 0x01) ? 16 : 8);
#endif
}
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
/* Allow a compile-time override. */
dev->mem_start = WD_SHMEM;
#else
if (dev->mem_start == 0) {
/* Sanity and old 8003 check */
int reg0 = inb(ioaddr);
if (reg0 == 0xff || reg0 == 0) {
/* Future plan: this could check a few likely locations first. */
dev->mem_start = 0xd0000;
pr_cont(" assigning address %#lx", dev->mem_start);
} else {
int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
/* Some boards don't have the register 5 -- it returns 0xff. */
if (high_addr_bits == 0x1f || word16 == 0)
high_addr_bits = 0x01;
dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
}
}
#endif
/* The 8390 isn't at the base address -- the ASIC regs are there! */
dev->base_addr = ioaddr+WD_NIC_OFFSET;
if (dev->irq < 2) {
static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
int reg1 = inb(ioaddr+1);
int reg4 = inb(ioaddr+4);
if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
short nic_addr = ioaddr+WD_NIC_OFFSET;
unsigned long irq_mask;
/* We have an old-style ethercard that doesn't report its IRQ
line. Do autoirq to find the IRQ line. Note that this IS NOT
a reliable way to trigger an interrupt. */
outb_p(E8390_NODMA + E8390_STOP, nic_addr);
outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
irq_mask = probe_irq_on();
outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
outb_p(0x00, nic_addr + EN0_RCNTLO);
outb_p(0x00, nic_addr + EN0_RCNTHI);
outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
mdelay(20);
dev->irq = probe_irq_off(irq_mask);
outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
if (netif_msg_drv(ei_local))
pr_cont(" autoirq is %d", dev->irq);
if (dev->irq < 2)
dev->irq = word16 ? 10 : 5;
} else
dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
} else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
dev->irq = 9;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
if (i) {
pr_cont(" unable to get IRQ %d.\n", dev->irq);
return i;
}
/* OK, were are certain this is going to work. Setup the device. */
ei_status.name = model_name;
ei_status.word16 = word16;
ei_status.tx_start_page = WD_START_PG;
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
/* Don't map in the shared memory until the board is actually opened. */
/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
if (dev->mem_end != 0) {
ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
ei_status.priv = dev->mem_end - dev->mem_start;
} else {
ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
}
ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
if (!ei_status.mem) {
free_irq(dev->irq, dev);
return -ENOMEM;
}
pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
model_name, dev->irq, dev->mem_start, dev->mem_end-1);
ei_status.reset_8390 = wd_reset_8390;
ei_status.block_input = wd_block_input;
ei_status.block_output = wd_block_output;
ei_status.get_8390_hdr = wd_get_8390_hdr;
dev->netdev_ops = &wd_netdev_ops;
NS8390_init(dev, 0);
ei_local->msg_enable = wd_msg_enable;
#if 1
/* Enable interrupt generation on softconfig cards -- M.U */
/* .. but possibly potentially unsafe - Donald */
if (inb(ioaddr+14) & 0x20)
outb(inb(ioaddr+4)|0x80, ioaddr+4);
#endif
err = register_netdev(dev);
if (err) {
free_irq(dev->irq, dev);
iounmap(ei_status.mem);
}
return err;
}
static int
wd_open(struct net_device *dev)
{
int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
/* Map in the shared memory. Always set register 0 last to remain
compatible with very old boards. */
ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
if (ei_status.word16)
outb(ei_status.reg5, ioaddr+WD_CMDREG5);
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
return ei_open(dev);
}
static void
wd_reset_8390(struct net_device *dev)
{
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
struct ei_device *ei_local = netdev_priv(dev);
outb(WD_RESET, wd_cmd_port);
netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n",
jiffies);
ei_status.txing = 0;
/* Set up the ASIC registers, just in case something changed them. */
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
if (ei_status.word16)
outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
netif_dbg(ei_local, hw, dev, "reset done\n");
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */
static void
wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
/* We'll always get a 4 byte header read followed by a packet read, so
we enable 16 bit mode before the header, and disable after the body. */
if (ei_status.word16)
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
#ifdef __BIG_ENDIAN
/* Officially this is what we are doing, but the readl() is faster */
/* unfortunately it isn't endian aware of the struct */
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
hdr->count = le16_to_cpu(hdr->count);
#else
((unsigned int*)hdr)[0] = readl(hdr_start);
#endif
}
/* Block input and output are easy on shared memory ethercards, and trivial
on the Western digital card where there is no choice of how to do it.
The only complications are that the ring buffer wraps, and need to map
switch between 8- and 16-bit modes. */
static void
wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
{
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
unsigned long offset = ring_offset - (WD_START_PG<<8);
void __iomem *xfer_start = ei_status.mem + offset;
if (offset + count > ei_status.priv) {
/* We must wrap the input move. */
int semi_count = ei_status.priv - offset;
memcpy_fromio(skb->data, xfer_start, semi_count);
count -= semi_count;
memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
} else {
/* Packet is in one chunk -- we can copy + cksum. */
memcpy_fromio(skb->data, xfer_start, count);
}
/* Turn off 16 bit access so that reboot works. ISA brain-damage */
if (ei_status.word16)
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
}
static void
wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
int start_page)
{
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
if (ei_status.word16) {
/* Turn on and off 16 bit access so that reboot works. */
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
memcpy_toio(shmem, buf, count);
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
} else
memcpy_toio(shmem, buf, count);
}
static int
wd_close(struct net_device *dev)
{
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
ei_close(dev);
/* Change from 16-bit to 8-bit shared memory so reboot works. */
if (ei_status.word16)
outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
/* And disable the shared memory. */
outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
return 0;
}
#ifdef MODULE
#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
static struct net_device *dev_wd[MAX_WD_CARDS];
static int io[MAX_WD_CARDS];
static int irq[MAX_WD_CARDS];
static int mem[MAX_WD_CARDS];
static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */
module_param_array(io, int, NULL, 0);
module_param_array(irq, int, NULL, 0);
module_param_array(mem, int, NULL, 0);
module_param_array(mem_end, int, NULL, 0);
module_param_named(msg_enable, wd_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
MODULE_PARM_DESC(io, "I/O base address(es)");
MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
MODULE_PARM_DESC(mem_end, "memory end address(es)");
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
MODULE_LICENSE("GPL");
/* This is set up so that only a single autoprobe takes place per call.
ISA device autoprobes on a running machine are not recommended. */
int __init init_module(void)
{
struct net_device *dev;
int this_dev, found = 0;
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
if (io[this_dev] == 0) {
if (this_dev != 0) break; /* only autoprobe 1st one */
printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
}
dev = alloc_ei_netdev();
if (!dev)
break;
dev->irq = irq[this_dev];
dev->base_addr = io[this_dev];
dev->mem_start = mem[this_dev];
dev->mem_end = mem_end[this_dev];
if (do_wd_probe(dev) == 0) {
dev_wd[found++] = dev;
continue;
}
free_netdev(dev);
printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
break;
}
if (found)
return 0;
return -ENXIO;
}
static void cleanup_card(struct net_device *dev)
{
free_irq(dev->irq, dev);
release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
iounmap(ei_status.mem);
}
void __exit
cleanup_module(void)
{
int this_dev;
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
struct net_device *dev = dev_wd[this_dev];
if (dev) {
unregister_netdev(dev);
cleanup_card(dev);
free_netdev(dev);
}
}
}
#endif /* MODULE */

View file

@ -0,0 +1,458 @@
/*
* Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver
*
* (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM)
*
* ---------------------------------------------------------------------------
*
* This program is based on all the other NE2000 drivers for Linux
*
* ---------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ---------------------------------------------------------------------------
*
* The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS
* Ethernet Controllers.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/zorro.h>
#include <linux/jiffies.h>
#include <asm/irq.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#define ei_inb(port) in_8(port)
#define ei_outb(val, port) out_8(port, val)
#define ei_inb_p(port) in_8(port)
#define ei_outb_p(val, port) out_8(port, val)
static const char version[] =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
static u32 zorro8390_msg_enable;
#include "lib8390.c"
#define DRV_NAME "zorro8390"
#define NE_BASE (dev->base_addr)
#define NE_CMD (0x00 * 2)
#define NE_DATAPORT (0x10 * 2) /* NatSemi-defined port window offset */
#define NE_RESET (0x1f * 2) /* Issue a read to reset,
* a write to clear. */
#define NE_IO_EXTENT (0x20 * 2)
#define NE_EN0_ISR (0x07 * 2)
#define NE_EN0_DCFG (0x0e * 2)
#define NE_EN0_RSARLO (0x08 * 2)
#define NE_EN0_RSARHI (0x09 * 2)
#define NE_EN0_RCNTLO (0x0a * 2)
#define NE_EN0_RXCR (0x0c * 2)
#define NE_EN0_TXCR (0x0d * 2)
#define NE_EN0_RCNTHI (0x0b * 2)
#define NE_EN0_IMR (0x0f * 2)
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
#define WORDSWAP(a) ((((a) >> 8) & 0xff) | ((a) << 8))
static struct card_info {
zorro_id id;
const char *name;
unsigned int offset;
} cards[] = {
{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 },
{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 },
};
/* Hard reset the card. This used to pause for the same period that a
* 8390 reset command required, but that shouldn't be necessary.
*/
static void zorro8390_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, hw, dev, "resetting - t=%ld...\n", jiffies);
z_writeb(z_readb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
ei_status.txing = 0;
ei_status.dmaing = 0;
/* This check _should_not_ be necessary, omit eventually. */
while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0)
if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
netdev_warn(dev, "%s: did not complete\n", __func__);
break;
}
z_writeb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr */
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
* we don't need to be concerned with ring wrap as the header will be at
* the start of a page, so we optimize accordingly.
*/
static void zorro8390_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr, int ring_page)
{
int nic_base = dev->base_addr;
int cnt;
short *ptrs;
/* This *shouldn't* happen.
* If it does, it's the last thing you'll see
*/
if (ei_status.dmaing) {
netdev_warn(dev,
"%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
__func__, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
z_writeb(0, nic_base + NE_EN0_RCNTHI);
z_writeb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */
z_writeb(ring_page, nic_base + NE_EN0_RSARHI);
z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
ptrs = (short *)hdr;
for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++)
*ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
hdr->count = WORDSWAP(hdr->count);
ei_status.dmaing &= ~0x01;
}
/* Block input and output, similar to the Crynwr packet driver.
* If you are porting to a new ethercard, look at the packet driver source
* for hints. The NEx000 doesn't share the on-board packet memory --
* you have to put the packet out through the "remote DMA" dataport
* using z_writeb.
*/
static void zorro8390_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
{
int nic_base = dev->base_addr;
char *buf = skb->data;
short *ptrs;
int cnt;
/* This *shouldn't* happen.
* If it does, it's the last thing you'll see
*/
if (ei_status.dmaing) {
netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
__func__, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
ptrs = (short *)buf;
for (cnt = 0; cnt < count >> 1; cnt++)
*ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
if (count & 0x01)
buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT);
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
ei_status.dmaing &= ~0x01;
}
static void zorro8390_block_output(struct net_device *dev, int count,
const unsigned char *buf,
const int start_page)
{
int nic_base = NE_BASE;
unsigned long dma_start;
short *ptrs;
int cnt;
/* Round the count up for word writes. Do we need to do this?
* What effect will an odd byte count have on the 8390?
* I should check someday.
*/
if (count & 0x01)
count++;
/* This *shouldn't* happen.
* If it does, it's the last thing you'll see
*/
if (ei_status.dmaing) {
netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
__func__, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
/* Now the normal output. */
z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
z_writeb(0x00, nic_base + NE_EN0_RSARLO);
z_writeb(start_page, nic_base + NE_EN0_RSARHI);
z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
ptrs = (short *)buf;
for (cnt = 0; cnt < count >> 1; cnt++)
z_writew(*ptrs++, NE_BASE + NE_DATAPORT);
dma_start = jiffies;
while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
if (time_after(jiffies, dma_start + 2 * HZ / 100)) {
/* 20ms */
netdev_warn(dev, "timeout waiting for Tx RDC\n");
zorro8390_reset_8390(dev);
__NS8390_init(dev, 1);
break;
}
z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr */
ei_status.dmaing &= ~0x01;
}
static int zorro8390_open(struct net_device *dev)
{
__ei_open(dev);
return 0;
}
static int zorro8390_close(struct net_device *dev)
{
struct ei_device *ei_local = netdev_priv(dev);
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard\n");
__ei_close(dev);
return 0;
}
static void zorro8390_remove_one(struct zorro_dev *z)
{
struct net_device *dev = zorro_get_drvdata(z);
unregister_netdev(dev);
free_irq(IRQ_AMIGA_PORTS, dev);
release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2);
free_netdev(dev);
}
static struct zorro_device_id zorro8390_zorro_tbl[] = {
{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, },
{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, },
{ 0 }
};
MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl);
static const struct net_device_ops zorro8390_netdev_ops = {
.ndo_open = zorro8390_open,
.ndo_stop = zorro8390_close,
.ndo_start_xmit = __ei_start_xmit,
.ndo_tx_timeout = __ei_tx_timeout,
.ndo_get_stats = __ei_get_stats,
.ndo_set_rx_mode = __ei_set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = __ei_poll,
#endif
};
static int zorro8390_init(struct net_device *dev, unsigned long board,
const char *name, void __iomem *ioaddr)
{
int i;
int err;
unsigned char SA_prom[32];
int start_page, stop_page;
struct ei_device *ei_local = netdev_priv(dev);
static u32 zorro8390_offsets[16] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
};
/* Reset card. Who knows what dain-bramaged state it was left in. */
{
unsigned long reset_start_time = jiffies;
z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
if (time_after(jiffies,
reset_start_time + 2 * HZ / 100)) {
netdev_warn(dev, "not found (no reset ack)\n");
return -ENODEV;
}
z_writeb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */
}
/* Read the 16 bytes of station address PROM.
* We must first initialize registers,
* similar to NS8390_init(eifdev, 0).
* We can't reliably read the SAPROM address without this.
* (I learned the hard way!).
*/
{
static const struct {
u32 value;
u32 offset;
} program_seq[] = {
{E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
/* Select page 0 */
{0x48, NE_EN0_DCFG}, /* 0x48: Set byte-wide access */
{0x00, NE_EN0_RCNTLO}, /* Clear the count regs */
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_IMR}, /* Mask completion irq */
{0xFF, NE_EN0_ISR},
{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
{32, NE_EN0_RCNTLO},
{0x00, NE_EN0_RCNTHI},
{0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000 */
{0x00, NE_EN0_RSARHI},
{E8390_RREAD + E8390_START, NE_CMD},
};
for (i = 0; i < ARRAY_SIZE(program_seq); i++)
z_writeb(program_seq[i].value,
ioaddr + program_seq[i].offset);
}
for (i = 0; i < 16; i++) {
SA_prom[i] = z_readb(ioaddr + NE_DATAPORT);
(void)z_readb(ioaddr + NE_DATAPORT);
}
/* We must set the 8390 for word mode. */
z_writeb(0x49, ioaddr + NE_EN0_DCFG);
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
dev->base_addr = (unsigned long)ioaddr;
dev->irq = IRQ_AMIGA_PORTS;
/* Install the Interrupt handler */
i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt,
IRQF_SHARED, DRV_NAME, dev);
if (i)
return i;
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = SA_prom[i];
pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
ei_status.name = name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
ei_status.word16 = 1;
ei_status.rx_start_page = start_page + TX_PAGES;
ei_status.reset_8390 = zorro8390_reset_8390;
ei_status.block_input = zorro8390_block_input;
ei_status.block_output = zorro8390_block_output;
ei_status.get_8390_hdr = zorro8390_get_8390_hdr;
ei_status.reg_offset = zorro8390_offsets;
dev->netdev_ops = &zorro8390_netdev_ops;
__NS8390_init(dev, 0);
ei_local->msg_enable = zorro8390_msg_enable;
err = register_netdev(dev);
if (err) {
free_irq(IRQ_AMIGA_PORTS, dev);
return err;
}
netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n",
name, board, dev->dev_addr);
return 0;
}
static int zorro8390_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent)
{
struct net_device *dev;
unsigned long board, ioaddr;
int err, i;
for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--)
if (z->id == cards[i].id)
break;
if (i < 0)
return -ENODEV;
board = z->resource.start;
ioaddr = board + cards[i].offset;
dev = ____alloc_ei_netdev(0);
if (!dev)
return -ENOMEM;
if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) {
free_netdev(dev);
return -EBUSY;
}
err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr));
if (err) {
release_mem_region(ioaddr, NE_IO_EXTENT * 2);
free_netdev(dev);
return err;
}
zorro_set_drvdata(z, dev);
return 0;
}
static struct zorro_driver zorro8390_driver = {
.name = "zorro8390",
.id_table = zorro8390_zorro_tbl,
.probe = zorro8390_init_one,
.remove = zorro8390_remove_one,
};
static int __init zorro8390_init_module(void)
{
return zorro_register_driver(&zorro8390_driver);
}
static void __exit zorro8390_cleanup_module(void)
{
zorro_unregister_driver(&zorro8390_driver);
}
module_init(zorro8390_init_module);
module_exit(zorro8390_cleanup_module);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,189 @@
#
# Ethernet LAN device configuration
#
menuconfig ETHERNET
bool "Ethernet driver support"
depends on NET
default y
---help---
This section contains all the Ethernet device drivers.
if ETHERNET
config MDIO
tristate
config SUNGEM_PHY
tristate
source "drivers/net/ethernet/3com/Kconfig"
source "drivers/net/ethernet/adaptec/Kconfig"
source "drivers/net/ethernet/aeroflex/Kconfig"
source "drivers/net/ethernet/agere/Kconfig"
source "drivers/net/ethernet/allwinner/Kconfig"
source "drivers/net/ethernet/alteon/Kconfig"
source "drivers/net/ethernet/altera/Kconfig"
source "drivers/net/ethernet/amd/Kconfig"
source "drivers/net/ethernet/apm/Kconfig"
source "drivers/net/ethernet/apple/Kconfig"
source "drivers/net/ethernet/arc/Kconfig"
source "drivers/net/ethernet/atheros/Kconfig"
source "drivers/net/ethernet/cadence/Kconfig"
source "drivers/net/ethernet/adi/Kconfig"
source "drivers/net/ethernet/broadcom/Kconfig"
source "drivers/net/ethernet/brocade/Kconfig"
source "drivers/net/ethernet/calxeda/Kconfig"
source "drivers/net/ethernet/chelsio/Kconfig"
source "drivers/net/ethernet/cirrus/Kconfig"
source "drivers/net/ethernet/cisco/Kconfig"
config CX_ECAT
tristate "Beckhoff CX5020 EtherCAT master support"
depends on PCI
depends on X86 || COMPILE_TEST
---help---
Driver for EtherCAT master module located on CCAT FPGA
that can be found on Beckhoff CX5020, and possibly other of CX
Beckhoff CX series industrial PCs.
To compile this driver as a module, choose M here. The module
will be called ec_bhf.
source "drivers/net/ethernet/davicom/Kconfig"
config DNET
tristate "Dave ethernet support (DNET)"
depends on HAS_IOMEM
select PHYLIB
---help---
The Dave ethernet interface (DNET) is found on Qong Board FPGA.
Say Y to include support for the DNET chip.
To compile this driver as a module, choose M here: the module
will be called dnet.
source "drivers/net/ethernet/dec/Kconfig"
source "drivers/net/ethernet/dlink/Kconfig"
source "drivers/net/ethernet/emulex/Kconfig"
source "drivers/net/ethernet/neterion/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
source "drivers/net/ethernet/fujitsu/Kconfig"
source "drivers/net/ethernet/hisilicon/Kconfig"
source "drivers/net/ethernet/hp/Kconfig"
source "drivers/net/ethernet/ibm/Kconfig"
source "drivers/net/ethernet/intel/Kconfig"
source "drivers/net/ethernet/i825xx/Kconfig"
source "drivers/net/ethernet/xscale/Kconfig"
source "drivers/net/ethernet/icplus/Kconfig"
config JME
tristate "JMicron(R) PCI-Express Gigabit Ethernet support"
depends on PCI
select CRC32
select MII
---help---
This driver supports the PCI-Express gigabit ethernet adapters
based on JMicron JMC250 chipset.
To compile this driver as a module, choose M here. The module
will be called jme.
config KORINA
tristate "Korina (IDT RC32434) Ethernet support"
depends on MIKROTIK_RB532
---help---
If you have a Mikrotik RouterBoard 500 or IDT RC32434
based system say Y. Otherwise say N.
config LANTIQ_ETOP
tristate "Lantiq SoC ETOP driver"
depends on SOC_TYPE_XWAY
---help---
Support for the MII0 inside the Lantiq SoC
source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"
config FEALNX
tristate "Myson MTD-8xx PCI Ethernet support"
depends on PCI
select CRC32
select MII
---help---
Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
cards. <http://www.myson.com.tw/>
source "drivers/net/ethernet/natsemi/Kconfig"
source "drivers/net/ethernet/8390/Kconfig"
config NET_NETX
tristate "NetX Ethernet support"
select MII
depends on ARCH_NETX
---help---
This is support for the Hilscher netX builtin Ethernet ports
To compile this driver as a module, choose M here. The module
will be called netx-eth.
source "drivers/net/ethernet/nuvoton/Kconfig"
source "drivers/net/ethernet/nvidia/Kconfig"
source "drivers/net/ethernet/nxp/Kconfig"
source "drivers/net/ethernet/octeon/Kconfig"
source "drivers/net/ethernet/oki-semi/Kconfig"
config ETHOC
tristate "OpenCores 10/100 Mbps Ethernet MAC support"
depends on HAS_IOMEM && HAS_DMA
select MII
select PHYLIB
select CRC32
select BITREVERSE
---help---
Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
source "drivers/net/ethernet/packetengines/Kconfig"
source "drivers/net/ethernet/pasemi/Kconfig"
source "drivers/net/ethernet/qlogic/Kconfig"
source "drivers/net/ethernet/qualcomm/Kconfig"
source "drivers/net/ethernet/realtek/Kconfig"
source "drivers/net/ethernet/renesas/Kconfig"
source "drivers/net/ethernet/rdc/Kconfig"
config S6GMAC
tristate "S6105 GMAC ethernet support"
depends on XTENSA_VARIANT_S6000
select PHYLIB
---help---
This driver supports the on chip ethernet device on the
S6105 xtensa processor.
To compile this driver as a module, choose M here. The module
will be called s6gmac.
source "drivers/net/ethernet/samsung/Kconfig"
source "drivers/net/ethernet/seeq/Kconfig"
source "drivers/net/ethernet/silan/Kconfig"
source "drivers/net/ethernet/sis/Kconfig"
source "drivers/net/ethernet/sfc/Kconfig"
source "drivers/net/ethernet/sgi/Kconfig"
source "drivers/net/ethernet/smsc/Kconfig"
source "drivers/net/ethernet/stmicro/Kconfig"
source "drivers/net/ethernet/sun/Kconfig"
source "drivers/net/ethernet/tehuti/Kconfig"
source "drivers/net/ethernet/ti/Kconfig"
source "drivers/net/ethernet/tile/Kconfig"
source "drivers/net/ethernet/toshiba/Kconfig"
source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
source "drivers/net/ethernet/wiznet/Kconfig"
source "drivers/net/ethernet/xilinx/Kconfig"
source "drivers/net/ethernet/xircom/Kconfig"
endif # ETHERNET

View file

@ -0,0 +1,86 @@
#
# Makefile for the Linux network Ethernet device drivers.
#
obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
obj-$(CONFIG_NET_VENDOR_8390) += 8390/
obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
obj-$(CONFIG_GRETH) += aeroflex/
obj-$(CONFIG_NET_VENDOR_AGERE) += agere/
obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
obj-$(CONFIG_ALTERA_TSE) += altera/
obj-$(CONFIG_NET_VENDOR_AMD) += amd/
obj-$(CONFIG_NET_XGENE) += apm/
obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
obj-$(CONFIG_NET_VENDOR_ARC) += arc/
obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
obj-$(CONFIG_NET_CADENCE) += cadence/
obj-$(CONFIG_NET_BFIN) += adi/
obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/
obj-$(CONFIG_NET_CALXEDA_XGMAC) += calxeda/
obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
obj-$(CONFIG_NET_VENDOR_CIRRUS) += cirrus/
obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/
obj-$(CONFIG_CX_ECAT) += ec_bhf.o
obj-$(CONFIG_DM9000) += davicom/
obj-$(CONFIG_DNET) += dnet.o
obj-$(CONFIG_NET_VENDOR_DEC) += dec/
obj-$(CONFIG_NET_VENDOR_DLINK) += dlink/
obj-$(CONFIG_NET_VENDOR_EMULEX) += emulex/
obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
obj-$(CONFIG_NET_VENDOR_HP) += hp/
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
obj-$(CONFIG_NET_VENDOR_XSCALE) += xscale/
obj-$(CONFIG_IP1000) += icplus/
obj-$(CONFIG_JME) += jme.o
obj-$(CONFIG_KORINA) += korina.o
obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
obj-$(CONFIG_NET_NETX) += netx-eth.o
obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
obj-$(CONFIG_LPC_ENET) += nxp/
obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/
obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/
obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_S6GMAC) += s6gmac.o
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
obj-$(CONFIG_NET_VENDOR_SIS) += sis/
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
obj-$(CONFIG_NET_VENDOR_SUN) += sun/
obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
obj-$(CONFIG_NET_VENDOR_TI) += ti/
obj-$(CONFIG_TILE_NET) += tile/
obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/

View file

@ -0,0 +1,35 @@
#
# Adaptec network device configuration
#
config NET_VENDOR_ADAPTEC
bool "Adaptec devices"
default y
depends on PCI
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Adaptec cards. If you say Y, you will be asked for
your specific card in the following questions.
if NET_VENDOR_ADAPTEC
config ADAPTEC_STARFIRE
tristate "Adaptec Starfire/DuraLAN support"
depends on PCI
select CRC32
select MII
---help---
Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network
adapter. The DuraLAN chip is used on the 64 bit PCI boards from
Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip
driver.
To compile this driver as a module, choose M here: the module
will be called starfire. This is recommended.
endif # NET_VENDOR_ADAPTEC

View file

@ -0,0 +1,5 @@
#
# Makefile for the Adaptec network device drivers.
#
obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
#
# Blackfin device configuration
#
config NET_BFIN
bool "Blackfin devices"
depends on BF516 || BF518 || BF526 || BF527 || BF536 || BF537
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
Make sure you know the name of your card. Read the Ethernet-HOWTO,
available from <http://www.tldp.org/docs.html#howto>.
If unsure, say Y.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the remaining Blackfin card questions. If you say Y, you will be
asked for your specific card in the following questions.
if NET_BFIN
config BFIN_MAC
tristate "Blackfin on-chip MAC support"
depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
select CRC32
select MII
select PHYLIB
select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
---help---
This is the driver for Blackfin on-chip mac device. Say Y if you want
it compiled into the kernel. This driver is also available as a
module ( = code which can be inserted in and removed from the running
kernel whenever you want). The module will be called bfin_mac.
config BFIN_MAC_USE_L1
bool "Use L1 memory for rx/tx packets"
depends on BFIN_MAC && (BF527 || BF537)
default y
---help---
To get maximum network performance, you should use L1 memory as rx/tx
buffers. Say N here if you want to reserve L1 memory for other uses.
config BFIN_TX_DESC_NUM
int "Number of transmit buffer packets"
depends on BFIN_MAC
range 6 10 if BFIN_MAC_USE_L1
range 10 100
default "10"
---help---
Set the number of buffer packets used in driver.
config BFIN_RX_DESC_NUM
int "Number of receive buffer packets"
depends on BFIN_MAC
range 20 64
default "20"
---help---
Set the number of buffer packets used in driver.
config BFIN_MAC_USE_HWSTAMP
bool "Use IEEE 1588 hwstamp"
depends on BFIN_MAC && BF518
select PTP_1588_CLOCK
default y
---help---
To support the IEEE 1588 Precision Time Protocol (PTP), select y here
endif # NET_BFIN

View file

@ -0,0 +1,5 @@
#
# Makefile for the Blackfin device drivers.
#
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
/*
* Blackfin On-Chip MAC Driver
*
* Copyright 2004-2007 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#ifndef _BFIN_MAC_H_
#define _BFIN_MAC_H_
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/timer.h>
#include <linux/etherdevice.h>
#include <linux/bfin_mac.h>
/*
* Disable hardware checksum for bug #5600 if writeback cache is
* enabled. Otherwize, corrupted RX packet will be sent up stack
* without error mark.
*/
#ifndef CONFIG_BFIN_EXTMEM_WRITEBACK
#define BFIN_MAC_CSUM_OFFLOAD
#endif
#define TX_RECLAIM_JIFFIES (HZ / 5)
#define BFIN_MAC_RX_IRQ_DISABLED 1
struct dma_descriptor {
struct dma_descriptor *next_dma_desc;
unsigned long start_addr;
unsigned short config;
unsigned short x_count;
};
struct status_area_rx {
#if defined(BFIN_MAC_CSUM_OFFLOAD)
unsigned short ip_hdr_csum; /* ip header checksum */
/* ip payload(udp or tcp or others) checksum */
unsigned short ip_payload_csum;
#endif
unsigned long status_word; /* the frame status word */
};
struct status_area_tx {
unsigned long status_word; /* the frame status word */
};
/* use two descriptors for a packet */
struct net_dma_desc_rx {
struct net_dma_desc_rx *next;
struct sk_buff *skb;
struct dma_descriptor desc_a;
struct dma_descriptor desc_b;
struct status_area_rx status;
};
/* use two descriptors for a packet */
struct net_dma_desc_tx {
struct net_dma_desc_tx *next;
struct sk_buff *skb;
struct dma_descriptor desc_a;
struct dma_descriptor desc_b;
unsigned char packet[1560];
struct status_area_tx status;
};
struct bfin_mac_local {
/*
* these are things that the kernel wants me to keep, so users
* can find out semi-useless statistics of how well the card is
* performing
*/
struct net_device_stats stats;
spinlock_t lock;
int wol; /* Wake On Lan */
int irq_wake_requested;
struct timer_list tx_reclaim_timer;
struct net_device *ndev;
struct napi_struct napi;
unsigned long flags;
/* Data for EMAC_VLAN1 regs */
u16 vlan1_mask, vlan2_mask;
/* MII and PHY stuffs */
int old_link; /* used by bf537_adjust_link */
int old_speed;
int old_duplex;
struct phy_device *phydev;
struct mii_bus *mii_bus;
#if defined(CONFIG_BFIN_MAC_USE_HWSTAMP)
u32 addend;
unsigned int shift;
s32 max_ppb;
struct hwtstamp_config stamp_cfg;
struct ptp_clock_info caps;
struct ptp_clock *clock;
int phc_index;
spinlock_t phc_lock; /* protects time lo/hi registers */
#endif
};
int bfin_get_ether_addr(char *addr);
#endif

View file

@ -0,0 +1,11 @@
#
# Aeroflex Gaisler network device configuration
#
config GRETH
tristate "Aeroflex Gaisler GRETH Ethernet MAC support"
depends on SPARC
select PHYLIB
select CRC32
---help---
Say Y here if you want to use the Aeroflex Gaisler GRETH Ethernet MAC.

View file

@ -0,0 +1,5 @@
#
# Makefile for the Aeroflex Gaisler network device drivers.
#
obj-$(CONFIG_GRETH) += greth.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
#ifndef GRETH_H
#define GRETH_H
#include <linux/phy.h>
/* Register bits and masks */
#define GRETH_RESET 0x40
#define GRETH_MII_BUSY 0x8
#define GRETH_MII_NVALID 0x10
#define GRETH_CTRL_FD 0x10
#define GRETH_CTRL_PR 0x20
#define GRETH_CTRL_SP 0x80
#define GRETH_CTRL_GB 0x100
#define GRETH_CTRL_PSTATIEN 0x400
#define GRETH_CTRL_MCEN 0x800
#define GRETH_CTRL_DISDUPLEX 0x1000
#define GRETH_STATUS_PHYSTAT 0x100
#define GRETH_BD_EN 0x800
#define GRETH_BD_WR 0x1000
#define GRETH_BD_IE 0x2000
#define GRETH_BD_LEN 0x7FF
#define GRETH_TXEN 0x1
#define GRETH_INT_TE 0x2
#define GRETH_INT_TX 0x8
#define GRETH_TXI 0x4
#define GRETH_TXBD_STATUS 0x0001C000
#define GRETH_TXBD_MORE 0x20000
#define GRETH_TXBD_IPCS 0x40000
#define GRETH_TXBD_TCPCS 0x80000
#define GRETH_TXBD_UDPCS 0x100000
#define GRETH_TXBD_CSALL (GRETH_TXBD_IPCS | GRETH_TXBD_TCPCS | GRETH_TXBD_UDPCS)
#define GRETH_TXBD_ERR_LC 0x10000
#define GRETH_TXBD_ERR_UE 0x4000
#define GRETH_TXBD_ERR_AL 0x8000
#define GRETH_INT_RE 0x1
#define GRETH_INT_RX 0x4
#define GRETH_RXEN 0x2
#define GRETH_RXI 0x8
#define GRETH_RXBD_STATUS 0xFFFFC000
#define GRETH_RXBD_ERR_AE 0x4000
#define GRETH_RXBD_ERR_FT 0x8000
#define GRETH_RXBD_ERR_CRC 0x10000
#define GRETH_RXBD_ERR_OE 0x20000
#define GRETH_RXBD_ERR_LE 0x40000
#define GRETH_RXBD_IP 0x80000
#define GRETH_RXBD_IP_CSERR 0x100000
#define GRETH_RXBD_UDP 0x200000
#define GRETH_RXBD_UDP_CSERR 0x400000
#define GRETH_RXBD_TCP 0x800000
#define GRETH_RXBD_TCP_CSERR 0x1000000
#define GRETH_RXBD_IP_FRAG 0x2000000
#define GRETH_RXBD_MCAST 0x4000000
/* Descriptor parameters */
#define GRETH_TXBD_NUM 128
#define GRETH_TXBD_NUM_MASK (GRETH_TXBD_NUM-1)
#define GRETH_TX_BUF_SIZE 2048
#define GRETH_RXBD_NUM 128
#define GRETH_RXBD_NUM_MASK (GRETH_RXBD_NUM-1)
#define GRETH_RX_BUF_SIZE 2048
/* Buffers per page */
#define GRETH_RX_BUF_PPGAE (PAGE_SIZE/GRETH_RX_BUF_SIZE)
#define GRETH_TX_BUF_PPGAE (PAGE_SIZE/GRETH_TX_BUF_SIZE)
/* How many pages are needed for buffers */
#define GRETH_RX_BUF_PAGE_NUM (GRETH_RXBD_NUM/GRETH_RX_BUF_PPGAE)
#define GRETH_TX_BUF_PAGE_NUM (GRETH_TXBD_NUM/GRETH_TX_BUF_PPGAE)
/* Buffer size.
* Gbit MAC uses tagged maximum frame size which is 1518 excluding CRC.
* Set to 1520 to make all buffers word aligned for non-gbit MAC.
*/
#define MAX_FRAME_SIZE 1520
/* GRETH APB registers */
struct greth_regs {
u32 control;
u32 status;
u32 esa_msb;
u32 esa_lsb;
u32 mdio;
u32 tx_desc_p;
u32 rx_desc_p;
u32 edclip;
u32 hash_msb;
u32 hash_lsb;
};
/* GRETH buffer descriptor */
struct greth_bd {
u32 stat;
u32 addr;
};
struct greth_private {
struct sk_buff *rx_skbuff[GRETH_RXBD_NUM];
struct sk_buff *tx_skbuff[GRETH_TXBD_NUM];
unsigned char *tx_bufs[GRETH_TXBD_NUM];
unsigned char *rx_bufs[GRETH_RXBD_NUM];
u16 tx_bufs_length[GRETH_TXBD_NUM];
u16 tx_next;
u16 tx_last;
u16 tx_free; /* only used on 10/100Mbit */
u16 rx_cur;
struct greth_regs *regs; /* Address of controller registers. */
struct greth_bd *rx_bd_base; /* Address of Rx BDs. */
struct greth_bd *tx_bd_base; /* Address of Tx BDs. */
dma_addr_t rx_bd_base_phys;
dma_addr_t tx_bd_base_phys;
int irq;
struct device *dev; /* Pointer to platform_device->dev */
struct net_device *netdev;
struct napi_struct napi;
spinlock_t devlock;
struct phy_device *phy;
struct mii_bus *mdio;
int mdio_irqs[PHY_MAX_ADDR];
unsigned int link;
unsigned int speed;
unsigned int duplex;
u32 msg_enable;
u8 phyaddr;
u8 multicast;
u8 gbit_mac;
u8 mdio_int_en;
u8 edcl;
};
#endif

View file

@ -0,0 +1,31 @@
#
# Agere device configuration
#
config NET_VENDOR_AGERE
bool "Agere devices"
default y
depends on PCI
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Agere devices. If you say Y, you will be asked
for your specific card in the following questions.
if NET_VENDOR_AGERE
config ET131X
tristate "Agere ET-1310 Gigabit Ethernet support"
depends on PCI
select PHYLIB
---help---
This driver supports Agere ET-1310 ethernet adapters.
To compile this driver as a module, choose M here. The module
will be called et131x.
endif # NET_VENDOR_AGERE

View file

@ -0,0 +1,5 @@
#
# Makefile for the Agere ET-131x ethernet driver
#
obj-$(CONFIG_ET131X) += et131x.o

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#
# Allwinner device configuration
#
config NET_VENDOR_ALLWINNER
bool "Allwinner devices"
default y
depends on ARCH_SUNXI
---help---
If you have a network (Ethernet) card belonging to this
class, say Y and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly
affect the kernel: saying N will just cause the configurator
to skip all the questions about Allwinner cards. If you say Y,
you will be asked for your specific card in the following
questions.
if NET_VENDOR_ALLWINNER
config SUN4I_EMAC
tristate "Allwinner A10 EMAC support"
depends on ARCH_SUNXI
depends on OF
select CRC32
select MII
select PHYLIB
select MDIO_SUN4I
---help---
Support for Allwinner A10 EMAC ethernet driver.
To compile this driver as a module, choose M here. The module
will be called sun4i-emac.
endif # NET_VENDOR_ALLWINNER

View file

@ -0,0 +1,5 @@
#
# Makefile for the Allwinner device drivers.
#
obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o

View file

@ -0,0 +1,970 @@
/*
* Allwinner EMAC Fast Ethernet driver for Linux.
*
* Copyright 2012-2013 Stefan Roese <sr@denx.de>
* Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
*
* Based on the Linux driver provided by Allwinner:
* Copyright (C) 1997 Sten Wang
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include "sun4i-emac.h"
#define DRV_NAME "sun4i-emac"
#define DRV_VERSION "1.02"
#define EMAC_MAX_FRAME_LEN 0x0600
/* Transmit timeout, default 5 seconds. */
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
/* EMAC register address locking.
*
* The EMAC uses an address register to control where data written
* to the data register goes. This means that the address register
* must be preserved over interrupts or similar calls.
*
* During interrupt and other critical calls, a spinlock is used to
* protect the system, but the calls themselves save the address
* in the address register in case they are interrupting another
* access to the device.
*
* For general accesses a lock is provided so that calls which are
* allowed to sleep are serialised so that the address register does
* not need to be saved. This lock also serves to serialise access
* to the EEPROM and PHY access registers which are shared between
* these two devices.
*/
/* The driver supports the original EMACE, and now the two newer
* devices, EMACA and EMACB.
*/
struct emac_board_info {
struct clk *clk;
struct device *dev;
struct platform_device *pdev;
spinlock_t lock;
void __iomem *membase;
u32 msg_enable;
struct net_device *ndev;
struct sk_buff *skb_last;
u16 tx_fifo_stat;
int emacrx_completed_flag;
struct phy_device *phy_dev;
struct device_node *phy_node;
unsigned int link;
unsigned int speed;
unsigned int duplex;
phy_interface_t phy_interface;
};
static void emac_update_speed(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
unsigned int reg_val;
/* set EMAC SPEED, depend on PHY */
reg_val = readl(db->membase + EMAC_MAC_SUPP_REG);
reg_val &= ~(0x1 << 8);
if (db->speed == SPEED_100)
reg_val |= 1 << 8;
writel(reg_val, db->membase + EMAC_MAC_SUPP_REG);
}
static void emac_update_duplex(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
unsigned int reg_val;
/* set duplex depend on phy */
reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN;
if (db->duplex)
reg_val |= EMAC_MAC_CTL1_DUPLEX_EN;
writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
}
static void emac_handle_link_change(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
struct phy_device *phydev = db->phy_dev;
unsigned long flags;
int status_change = 0;
if (phydev->link) {
if (db->speed != phydev->speed) {
spin_lock_irqsave(&db->lock, flags);
db->speed = phydev->speed;
emac_update_speed(dev);
spin_unlock_irqrestore(&db->lock, flags);
status_change = 1;
}
if (db->duplex != phydev->duplex) {
spin_lock_irqsave(&db->lock, flags);
db->duplex = phydev->duplex;
emac_update_duplex(dev);
spin_unlock_irqrestore(&db->lock, flags);
status_change = 1;
}
}
if (phydev->link != db->link) {
if (!phydev->link) {
db->speed = 0;
db->duplex = -1;
}
db->link = phydev->link;
status_change = 1;
}
if (status_change)
phy_print_status(phydev);
}
static int emac_mdio_probe(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
/* to-do: PHY interrupts are currently not supported */
/* attach the mac to the phy */
db->phy_dev = of_phy_connect(db->ndev, db->phy_node,
&emac_handle_link_change, 0,
db->phy_interface);
if (!db->phy_dev) {
netdev_err(db->ndev, "could not find the PHY\n");
return -ENODEV;
}
/* mask with MAC supported features */
db->phy_dev->supported &= PHY_BASIC_FEATURES;
db->phy_dev->advertising = db->phy_dev->supported;
db->link = 0;
db->speed = 0;
db->duplex = -1;
return 0;
}
static void emac_mdio_remove(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
phy_disconnect(db->phy_dev);
db->phy_dev = NULL;
}
static void emac_reset(struct emac_board_info *db)
{
dev_dbg(db->dev, "resetting device\n");
/* RESET device */
writel(0, db->membase + EMAC_CTL_REG);
udelay(200);
writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG);
udelay(200);
}
static void emac_outblk_32bit(void __iomem *reg, void *data, int count)
{
writesl(reg, data, round_up(count, 4) / 4);
}
static void emac_inblk_32bit(void __iomem *reg, void *data, int count)
{
readsl(reg, data, round_up(count, 4) / 4);
}
static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct emac_board_info *dm = netdev_priv(dev);
struct phy_device *phydev = dm->phy_dev;
if (!netif_running(dev))
return -EINVAL;
if (!phydev)
return -ENODEV;
return phy_mii_ioctl(phydev, rq, cmd);
}
/* ethtool ops */
static void emac_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME));
strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION));
strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info));
}
static int emac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct emac_board_info *dm = netdev_priv(dev);
struct phy_device *phydev = dm->phy_dev;
if (!phydev)
return -ENODEV;
return phy_ethtool_gset(phydev, cmd);
}
static int emac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct emac_board_info *dm = netdev_priv(dev);
struct phy_device *phydev = dm->phy_dev;
if (!phydev)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
static const struct ethtool_ops emac_ethtool_ops = {
.get_drvinfo = emac_get_drvinfo,
.get_settings = emac_get_settings,
.set_settings = emac_set_settings,
.get_link = ethtool_op_get_link,
};
static unsigned int emac_setup(struct net_device *ndev)
{
struct emac_board_info *db = netdev_priv(ndev);
unsigned int reg_val;
/* set up TX */
reg_val = readl(db->membase + EMAC_TX_MODE_REG);
writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN,
db->membase + EMAC_TX_MODE_REG);
/* set MAC */
/* set MAC CTL0 */
reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN |
EMAC_MAC_CTL0_TX_FLOW_CTL_EN,
db->membase + EMAC_MAC_CTL0_REG);
/* set MAC CTL1 */
reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN;
reg_val |= EMAC_MAC_CTL1_CRC_EN;
reg_val |= EMAC_MAC_CTL1_PAD_EN;
writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
/* set up IPGT */
writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG);
/* set up IPGR */
writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2,
db->membase + EMAC_MAC_IPGR_REG);
/* set up Collison window */
writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM,
db->membase + EMAC_MAC_CLRT_REG);
/* set up Max Frame Length */
writel(EMAC_MAX_FRAME_LEN,
db->membase + EMAC_MAC_MAXF_REG);
return 0;
}
static void emac_set_rx_mode(struct net_device *ndev)
{
struct emac_board_info *db = netdev_priv(ndev);
unsigned int reg_val;
/* set up RX */
reg_val = readl(db->membase + EMAC_RX_CTL_REG);
if (ndev->flags & IFF_PROMISC)
reg_val |= EMAC_RX_CTL_PASS_ALL_EN;
else
reg_val &= ~EMAC_RX_CTL_PASS_ALL_EN;
writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN |
EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN |
EMAC_RX_CTL_ACCEPT_MULTICAST_EN |
EMAC_RX_CTL_ACCEPT_BROADCAST_EN,
db->membase + EMAC_RX_CTL_REG);
}
static unsigned int emac_powerup(struct net_device *ndev)
{
struct emac_board_info *db = netdev_priv(ndev);
unsigned int reg_val;
/* initial EMAC */
/* flush RX FIFO */
reg_val = readl(db->membase + EMAC_RX_CTL_REG);
reg_val |= 0x8;
writel(reg_val, db->membase + EMAC_RX_CTL_REG);
udelay(1);
/* initial MAC */
/* soft reset MAC */
reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET;
writel(reg_val, db->membase + EMAC_MAC_CTL0_REG);
/* set MII clock */
reg_val = readl(db->membase + EMAC_MAC_MCFG_REG);
reg_val &= (~(0xf << 2));
reg_val |= (0xD << 2);
writel(reg_val, db->membase + EMAC_MAC_MCFG_REG);
/* clear RX counter */
writel(0x0, db->membase + EMAC_RX_FBC_REG);
/* disable all interrupt and clear interrupt status */
writel(0, db->membase + EMAC_INT_CTL_REG);
reg_val = readl(db->membase + EMAC_INT_STA_REG);
writel(reg_val, db->membase + EMAC_INT_STA_REG);
udelay(1);
/* set up EMAC */
emac_setup(ndev);
/* set mac_address to chip */
writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev->
dev_addr[2], db->membase + EMAC_MAC_A1_REG);
writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev->
dev_addr[5], db->membase + EMAC_MAC_A0_REG);
mdelay(1);
return 0;
}
static int emac_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
struct emac_board_info *db = netdev_priv(dev);
if (netif_running(dev))
return -EBUSY;
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev->
dev_addr[2], db->membase + EMAC_MAC_A1_REG);
writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev->
dev_addr[5], db->membase + EMAC_MAC_A0_REG);
return 0;
}
/* Initialize emac board */
static void emac_init_device(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
unsigned long flags;
unsigned int reg_val;
spin_lock_irqsave(&db->lock, flags);
emac_update_speed(dev);
emac_update_duplex(dev);
/* enable RX/TX */
reg_val = readl(db->membase + EMAC_CTL_REG);
writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN,
db->membase + EMAC_CTL_REG);
/* enable RX/TX0/RX Hlevel interrup */
reg_val = readl(db->membase + EMAC_INT_CTL_REG);
reg_val |= (0xf << 0) | (0x01 << 8);
writel(reg_val, db->membase + EMAC_INT_CTL_REG);
spin_unlock_irqrestore(&db->lock, flags);
}
/* Our watchdog timed out. Called by the networking layer */
static void emac_timeout(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
unsigned long flags;
if (netif_msg_timer(db))
dev_err(db->dev, "tx time out.\n");
/* Save previous register address */
spin_lock_irqsave(&db->lock, flags);
netif_stop_queue(dev);
emac_reset(db);
emac_init_device(dev);
/* We can accept TX packets again */
dev->trans_start = jiffies;
netif_wake_queue(dev);
/* Restore previous register address */
spin_unlock_irqrestore(&db->lock, flags);
}
/* Hardware start transmission.
* Send a packet to media from the upper layer.
*/
static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
unsigned long channel;
unsigned long flags;
channel = db->tx_fifo_stat & 3;
if (channel == 3)
return 1;
channel = (channel == 1 ? 1 : 0);
spin_lock_irqsave(&db->lock, flags);
writel(channel, db->membase + EMAC_TX_INS_REG);
emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG,
skb->data, skb->len);
dev->stats.tx_bytes += skb->len;
db->tx_fifo_stat |= 1 << channel;
/* TX control: First packet immediately send, second packet queue */
if (channel == 0) {
/* set TX len */
writel(skb->len, db->membase + EMAC_TX_PL0_REG);
/* start translate from fifo to phy */
writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1,
db->membase + EMAC_TX_CTL0_REG);
/* save the time stamp */
dev->trans_start = jiffies;
} else if (channel == 1) {
/* set TX len */
writel(skb->len, db->membase + EMAC_TX_PL1_REG);
/* start translate from fifo to phy */
writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1,
db->membase + EMAC_TX_CTL1_REG);
/* save the time stamp */
dev->trans_start = jiffies;
}
if ((db->tx_fifo_stat & 3) == 3) {
/* Second packet */
netif_stop_queue(dev);
}
spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
dev_consume_skb_any(skb);
return NETDEV_TX_OK;
}
/* EMAC interrupt handler
* receive the packet to upper layer, free the transmitted packet
*/
static void emac_tx_done(struct net_device *dev, struct emac_board_info *db,
unsigned int tx_status)
{
/* One packet sent complete */
db->tx_fifo_stat &= ~(tx_status & 3);
if (3 == (tx_status & 3))
dev->stats.tx_packets += 2;
else
dev->stats.tx_packets++;
if (netif_msg_tx_done(db))
dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
netif_wake_queue(dev);
}
/* Received a packet and pass to upper layer
*/
static void emac_rx(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
struct sk_buff *skb;
u8 *rdptr;
bool good_packet;
static int rxlen_last;
unsigned int reg_val;
u32 rxhdr, rxstatus, rxcount, rxlen;
/* Check packet ready or not */
while (1) {
/* race warning: the first packet might arrive with
* the interrupts disabled, but the second will fix
* it
*/
rxcount = readl(db->membase + EMAC_RX_FBC_REG);
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "RXCount: %x\n", rxcount);
if ((db->skb_last != NULL) && (rxlen_last > 0)) {
dev->stats.rx_bytes += rxlen_last;
/* Pass to upper layer */
db->skb_last->protocol = eth_type_trans(db->skb_last,
dev);
netif_rx(db->skb_last);
dev->stats.rx_packets++;
db->skb_last = NULL;
rxlen_last = 0;
reg_val = readl(db->membase + EMAC_RX_CTL_REG);
reg_val &= ~EMAC_RX_CTL_DMA_EN;
writel(reg_val, db->membase + EMAC_RX_CTL_REG);
}
if (!rxcount) {
db->emacrx_completed_flag = 1;
reg_val = readl(db->membase + EMAC_INT_CTL_REG);
reg_val |= (0xf << 0) | (0x01 << 8);
writel(reg_val, db->membase + EMAC_INT_CTL_REG);
/* had one stuck? */
rxcount = readl(db->membase + EMAC_RX_FBC_REG);
if (!rxcount)
return;
}
reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG);
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "receive header: %x\n", reg_val);
if (reg_val != EMAC_UNDOCUMENTED_MAGIC) {
/* disable RX */
reg_val = readl(db->membase + EMAC_CTL_REG);
writel(reg_val & ~EMAC_CTL_RX_EN,
db->membase + EMAC_CTL_REG);
/* Flush RX FIFO */
reg_val = readl(db->membase + EMAC_RX_CTL_REG);
writel(reg_val | (1 << 3),
db->membase + EMAC_RX_CTL_REG);
do {
reg_val = readl(db->membase + EMAC_RX_CTL_REG);
} while (reg_val & (1 << 3));
/* enable RX */
reg_val = readl(db->membase + EMAC_CTL_REG);
writel(reg_val | EMAC_CTL_RX_EN,
db->membase + EMAC_CTL_REG);
reg_val = readl(db->membase + EMAC_INT_CTL_REG);
reg_val |= (0xf << 0) | (0x01 << 8);
writel(reg_val, db->membase + EMAC_INT_CTL_REG);
db->emacrx_completed_flag = 1;
return;
}
/* A packet ready now & Get status/length */
good_packet = true;
emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
&rxhdr, sizeof(rxhdr));
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr)));
rxlen = EMAC_RX_IO_DATA_LEN(rxhdr);
rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr);
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "RX: status %02x, length %04x\n",
rxstatus, rxlen);
/* Packet Status check */
if (rxlen < 0x40) {
good_packet = false;
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
}
if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) {
good_packet = false;
if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) {
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "crc error\n");
dev->stats.rx_crc_errors++;
}
if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) {
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "length error\n");
dev->stats.rx_length_errors++;
}
}
/* Move data from EMAC */
if (good_packet) {
skb = netdev_alloc_skb(dev, rxlen + 4);
if (!skb)
continue;
skb_reserve(skb, 2);
rdptr = (u8 *) skb_put(skb, rxlen - 4);
/* Read received packet from RX SRAM */
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "RxLen %x\n", rxlen);
emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
rdptr, rxlen);
dev->stats.rx_bytes += rxlen;
/* Pass to upper layer */
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
}
}
}
static irqreturn_t emac_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct emac_board_info *db = netdev_priv(dev);
int int_status;
unsigned long flags;
unsigned int reg_val;
/* A real interrupt coming */
/* holders of db->lock must always block IRQs */
spin_lock_irqsave(&db->lock, flags);
/* Disable all interrupts */
writel(0, db->membase + EMAC_INT_CTL_REG);
/* Got EMAC interrupt status */
/* Got ISR */
int_status = readl(db->membase + EMAC_INT_STA_REG);
/* Clear ISR status */
writel(int_status, db->membase + EMAC_INT_STA_REG);
if (netif_msg_intr(db))
dev_dbg(db->dev, "emac interrupt %02x\n", int_status);
/* Received the coming packet */
if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) {
/* carrier lost */
db->emacrx_completed_flag = 0;
emac_rx(dev);
}
/* Transmit Interrupt check */
if (int_status & (0x01 | 0x02))
emac_tx_done(dev, db, int_status);
if (int_status & (0x04 | 0x08))
netdev_info(dev, " ab : %x\n", int_status);
/* Re-enable interrupt mask */
if (db->emacrx_completed_flag == 1) {
reg_val = readl(db->membase + EMAC_INT_CTL_REG);
reg_val |= (0xf << 0) | (0x01 << 8);
writel(reg_val, db->membase + EMAC_INT_CTL_REG);
}
spin_unlock_irqrestore(&db->lock, flags);
return IRQ_HANDLED;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
* Used by netconsole
*/
static void emac_poll_controller(struct net_device *dev)
{
disable_irq(dev->irq);
emac_interrupt(dev->irq, dev);
enable_irq(dev->irq);
}
#endif
/* Open the interface.
* The interface is opened whenever "ifconfig" actives it.
*/
static int emac_open(struct net_device *dev)
{
struct emac_board_info *db = netdev_priv(dev);
int ret;
if (netif_msg_ifup(db))
dev_dbg(db->dev, "enabling %s\n", dev->name);
if (request_irq(dev->irq, &emac_interrupt, 0, dev->name, dev))
return -EAGAIN;
/* Initialize EMAC board */
emac_reset(db);
emac_init_device(dev);
ret = emac_mdio_probe(dev);
if (ret < 0) {
free_irq(dev->irq, dev);
netdev_err(dev, "cannot probe MDIO bus\n");
return ret;
}
phy_start(db->phy_dev);
netif_start_queue(dev);
return 0;
}
static void emac_shutdown(struct net_device *dev)
{
unsigned int reg_val;
struct emac_board_info *db = netdev_priv(dev);
/* Disable all interrupt */
writel(0, db->membase + EMAC_INT_CTL_REG);
/* clear interupt status */
reg_val = readl(db->membase + EMAC_INT_STA_REG);
writel(reg_val, db->membase + EMAC_INT_STA_REG);
/* Disable RX/TX */
reg_val = readl(db->membase + EMAC_CTL_REG);
reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET);
writel(reg_val, db->membase + EMAC_CTL_REG);
}
/* Stop the interface.
* The interface is stopped when it is brought.
*/
static int emac_stop(struct net_device *ndev)
{
struct emac_board_info *db = netdev_priv(ndev);
if (netif_msg_ifdown(db))
dev_dbg(db->dev, "shutting down %s\n", ndev->name);
netif_stop_queue(ndev);
netif_carrier_off(ndev);
phy_stop(db->phy_dev);
emac_mdio_remove(ndev);
emac_shutdown(ndev);
free_irq(ndev->irq, ndev);
return 0;
}
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_stop,
.ndo_start_xmit = emac_start_xmit,
.ndo_tx_timeout = emac_timeout,
.ndo_set_rx_mode = emac_set_rx_mode,
.ndo_do_ioctl = emac_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = emac_set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = emac_poll_controller,
#endif
};
/* Search EMAC board, allocate space and register it
*/
static int emac_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct emac_board_info *db;
struct net_device *ndev;
int ret = 0;
const char *mac_addr;
ndev = alloc_etherdev(sizeof(struct emac_board_info));
if (!ndev) {
dev_err(&pdev->dev, "could not allocate device.\n");
return -ENOMEM;
}
SET_NETDEV_DEV(ndev, &pdev->dev);
db = netdev_priv(ndev);
memset(db, 0, sizeof(*db));
db->dev = &pdev->dev;
db->ndev = ndev;
db->pdev = pdev;
spin_lock_init(&db->lock);
db->membase = of_iomap(np, 0);
if (!db->membase) {
dev_err(&pdev->dev, "failed to remap registers\n");
ret = -ENOMEM;
goto out;
}
/* fill in parameters for net-dev structure */
ndev->base_addr = (unsigned long)db->membase;
ndev->irq = irq_of_parse_and_map(np, 0);
if (ndev->irq == -ENXIO) {
netdev_err(ndev, "No irq resource\n");
ret = ndev->irq;
goto out;
}
db->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(db->clk))
goto out;
clk_prepare_enable(db->clk);
db->phy_node = of_parse_phandle(np, "phy", 0);
if (!db->phy_node) {
dev_err(&pdev->dev, "no associated PHY\n");
ret = -ENODEV;
goto out;
}
/* Read MAC-address from DT */
mac_addr = of_get_mac_address(np);
if (mac_addr)
memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
/* Check if the MAC address is valid, if not get a random one */
if (!is_valid_ether_addr(ndev->dev_addr)) {
eth_hw_addr_random(ndev);
dev_warn(&pdev->dev, "using random MAC address %pM\n",
ndev->dev_addr);
}
db->emacrx_completed_flag = 1;
emac_powerup(ndev);
emac_reset(db);
ndev->netdev_ops = &emac_netdev_ops;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
ndev->ethtool_ops = &emac_ethtool_ops;
platform_set_drvdata(pdev, ndev);
/* Carrier starts down, phylib will bring it up */
netif_carrier_off(ndev);
ret = register_netdev(ndev);
if (ret) {
dev_err(&pdev->dev, "Registering netdev failed!\n");
ret = -ENODEV;
goto out;
}
dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n",
ndev->name, db->membase, ndev->irq, ndev->dev_addr);
return 0;
out:
dev_err(db->dev, "not found (%d).\n", ret);
free_netdev(ndev);
return ret;
}
static int emac_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
unregister_netdev(ndev);
free_netdev(ndev);
dev_dbg(&pdev->dev, "released and freed device\n");
return 0;
}
static int emac_suspend(struct platform_device *dev, pm_message_t state)
{
struct net_device *ndev = platform_get_drvdata(dev);
netif_carrier_off(ndev);
netif_device_detach(ndev);
emac_shutdown(ndev);
return 0;
}
static int emac_resume(struct platform_device *dev)
{
struct net_device *ndev = platform_get_drvdata(dev);
struct emac_board_info *db = netdev_priv(ndev);
emac_reset(db);
emac_init_device(ndev);
netif_device_attach(ndev);
return 0;
}
static const struct of_device_id emac_of_match[] = {
{.compatible = "allwinner,sun4i-a10-emac",},
/* Deprecated */
{.compatible = "allwinner,sun4i-emac",},
{},
};
MODULE_DEVICE_TABLE(of, emac_of_match);
static struct platform_driver emac_driver = {
.driver = {
.name = "sun4i-emac",
.of_match_table = emac_of_match,
},
.probe = emac_probe,
.remove = emac_remove,
.suspend = emac_suspend,
.resume = emac_resume,
};
module_platform_driver(emac_driver);
MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Allwinner A10 emac network driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,108 @@
/*
* Allwinner EMAC Fast Ethernet driver for Linux.
*
* Copyright 2012 Stefan Roese <sr@denx.de>
* Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
*
* Based on the Linux driver provided by Allwinner:
* Copyright (C) 1997 Sten Wang
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef _SUN4I_EMAC_H_
#define _SUN4I_EMAC_H_
#define EMAC_CTL_REG (0x00)
#define EMAC_CTL_RESET (1 << 0)
#define EMAC_CTL_TX_EN (1 << 1)
#define EMAC_CTL_RX_EN (1 << 2)
#define EMAC_TX_MODE_REG (0x04)
#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0)
#define EMAC_TX_MODE_DMA_EN (1 << 1)
#define EMAC_TX_FLOW_REG (0x08)
#define EMAC_TX_CTL0_REG (0x0c)
#define EMAC_TX_CTL1_REG (0x10)
#define EMAC_TX_INS_REG (0x14)
#define EMAC_TX_PL0_REG (0x18)
#define EMAC_TX_PL1_REG (0x1c)
#define EMAC_TX_STA_REG (0x20)
#define EMAC_TX_IO_DATA_REG (0x24)
#define EMAC_TX_IO_DATA1_REG (0x28)
#define EMAC_TX_TSVL0_REG (0x2c)
#define EMAC_TX_TSVH0_REG (0x30)
#define EMAC_TX_TSVL1_REG (0x34)
#define EMAC_TX_TSVH1_REG (0x38)
#define EMAC_RX_CTL_REG (0x3c)
#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1)
#define EMAC_RX_CTL_DMA_EN (1 << 2)
#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4)
#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5)
#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6)
#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7)
#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8)
#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16)
#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17)
#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20)
#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21)
#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22)
#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24)
#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25)
#define EMAC_RX_HASH0_REG (0x40)
#define EMAC_RX_HASH1_REG (0x44)
#define EMAC_RX_STA_REG (0x48)
#define EMAC_RX_IO_DATA_REG (0x4c)
#define EMAC_RX_IO_DATA_LEN(x) (x & 0xffff)
#define EMAC_RX_IO_DATA_STATUS(x) ((x >> 16) & 0xffff)
#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7)
#define EMAC_RX_FBC_REG (0x50)
#define EMAC_INT_CTL_REG (0x54)
#define EMAC_INT_STA_REG (0x58)
#define EMAC_MAC_CTL0_REG (0x5c)
#define EMAC_MAC_CTL0_RX_FLOW_CTL_EN (1 << 2)
#define EMAC_MAC_CTL0_TX_FLOW_CTL_EN (1 << 3)
#define EMAC_MAC_CTL0_SOFT_RESET (1 << 15)
#define EMAC_MAC_CTL1_REG (0x60)
#define EMAC_MAC_CTL1_DUPLEX_EN (1 << 0)
#define EMAC_MAC_CTL1_LEN_CHECK_EN (1 << 1)
#define EMAC_MAC_CTL1_HUGE_FRAME_EN (1 << 2)
#define EMAC_MAC_CTL1_DELAYED_CRC_EN (1 << 3)
#define EMAC_MAC_CTL1_CRC_EN (1 << 4)
#define EMAC_MAC_CTL1_PAD_EN (1 << 5)
#define EMAC_MAC_CTL1_PAD_CRC_EN (1 << 6)
#define EMAC_MAC_CTL1_AD_SHORT_FRAME_EN (1 << 7)
#define EMAC_MAC_CTL1_BACKOFF_DIS (1 << 12)
#define EMAC_MAC_IPGT_REG (0x64)
#define EMAC_MAC_IPGT_HALF_DUPLEX (0x12)
#define EMAC_MAC_IPGT_FULL_DUPLEX (0x15)
#define EMAC_MAC_IPGR_REG (0x68)
#define EMAC_MAC_IPGR_IPG1 (0x0c)
#define EMAC_MAC_IPGR_IPG2 (0x12)
#define EMAC_MAC_CLRT_REG (0x6c)
#define EMAC_MAC_CLRT_COLLISION_WINDOW (0x37)
#define EMAC_MAC_CLRT_RM (0x0f)
#define EMAC_MAC_MAXF_REG (0x70)
#define EMAC_MAC_SUPP_REG (0x74)
#define EMAC_MAC_TEST_REG (0x78)
#define EMAC_MAC_MCFG_REG (0x7c)
#define EMAC_MAC_A0_REG (0x98)
#define EMAC_MAC_A1_REG (0x9c)
#define EMAC_MAC_A2_REG (0xa0)
#define EMAC_SAFX_L_REG0 (0xa4)
#define EMAC_SAFX_H_REG0 (0xa8)
#define EMAC_SAFX_L_REG1 (0xac)
#define EMAC_SAFX_H_REG1 (0xb0)
#define EMAC_SAFX_L_REG2 (0xb4)
#define EMAC_SAFX_H_REG2 (0xb8)
#define EMAC_SAFX_L_REG3 (0xbc)
#define EMAC_SAFX_H_REG3 (0xc0)
#define EMAC_PHY_DUPLEX (1 << 8)
#define EMAC_EEPROM_MAGIC (0x444d394b)
#define EMAC_UNDOCUMENTED_MAGIC (0x0143414d)
#endif /* _SUN4I_EMAC_H_ */

View file

@ -0,0 +1,48 @@
#
# Alteon network device configuration
#
config NET_VENDOR_ALTEON
bool "Alteon devices"
default y
depends on PCI
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Alteon cards. If you say Y, you will be asked for
your specific card in the following questions.
if NET_VENDOR_ALTEON
config ACENIC
tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support"
depends on PCI
---help---
Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear
GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet
adapter. The driver allows for using the Jumbo Frame option (9000
bytes/frame) however it requires that your switches can handle this
as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig
line.
To compile this driver as a module, choose M here: the
module will be called acenic.
config ACENIC_OMIT_TIGON_I
bool "Omit support for old Tigon I based AceNICs"
depends on ACENIC
---help---
Say Y here if you only have Tigon II based AceNICs and want to leave
out support for the older Tigon I based cards which are no longer
being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B
version)). This will reduce the size of the driver object by
app. 100KB. If you are not sure whether your card is a Tigon I or a
Tigon II, say N here.
The safe and default value for this is N.
endif # NET_VENDOR_ALTEON

View file

@ -0,0 +1,5 @@
#
# Makefile for the Alteon network device drivers.
#
obj-$(CONFIG_ACENIC) += acenic.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,790 @@
#ifndef _ACENIC_H_
#define _ACENIC_H_
#include <linux/interrupt.h>
/*
* Generate TX index update each time, when TX ring is closed.
* Normally, this is not useful, because results in more dma (and irqs
* without TX_COAL_INTS_ONLY).
*/
#define USE_TX_COAL_NOW 0
/*
* Addressing:
*
* The Tigon uses 64-bit host addresses, regardless of their actual
* length, and it expects a big-endian format. For 32 bit systems the
* upper 32 bits of the address are simply ignored (zero), however for
* little endian 64 bit systems (Alpha) this looks strange with the
* two parts of the address word being swapped.
*
* The addresses are split in two 32 bit words for all architectures
* as some of them are in PCI shared memory and it is necessary to use
* readl/writel to access them.
*
* The addressing code is derived from Pete Wyckoff's work, but
* modified to deal properly with readl/writel usage.
*/
struct ace_regs {
u32 pad0[16]; /* PCI control registers */
u32 HostCtrl; /* 0x40 */
u32 LocalCtrl;
u32 pad1[2];
u32 MiscCfg; /* 0x50 */
u32 pad2[2];
u32 PciState;
u32 pad3[2]; /* 0x60 */
u32 WinBase;
u32 WinData;
u32 pad4[12]; /* 0x70 */
u32 DmaWriteState; /* 0xa0 */
u32 pad5[3];
u32 DmaReadState; /* 0xb0 */
u32 pad6[26];
u32 AssistState;
u32 pad7[8]; /* 0x120 */
u32 CpuCtrl; /* 0x140 */
u32 Pc;
u32 pad8[3];
u32 SramAddr; /* 0x154 */
u32 SramData;
u32 pad9[49];
u32 MacRxState; /* 0x220 */
u32 pad10[7];
u32 CpuBCtrl; /* 0x240 */
u32 PcB;
u32 pad11[3];
u32 SramBAddr; /* 0x254 */
u32 SramBData;
u32 pad12[105];
u32 pad13[32]; /* 0x400 */
u32 Stats[32];
u32 Mb0Hi; /* 0x500 */
u32 Mb0Lo;
u32 Mb1Hi;
u32 CmdPrd;
u32 Mb2Hi;
u32 TxPrd;
u32 Mb3Hi;
u32 RxStdPrd;
u32 Mb4Hi;
u32 RxJumboPrd;
u32 Mb5Hi;
u32 RxMiniPrd;
u32 Mb6Hi;
u32 Mb6Lo;
u32 Mb7Hi;
u32 Mb7Lo;
u32 Mb8Hi;
u32 Mb8Lo;
u32 Mb9Hi;
u32 Mb9Lo;
u32 MbAHi;
u32 MbALo;
u32 MbBHi;
u32 MbBLo;
u32 MbCHi;
u32 MbCLo;
u32 MbDHi;
u32 MbDLo;
u32 MbEHi;
u32 MbELo;
u32 MbFHi;
u32 MbFLo;
u32 pad14[32];
u32 MacAddrHi; /* 0x600 */
u32 MacAddrLo;
u32 InfoPtrHi;
u32 InfoPtrLo;
u32 MultiCastHi; /* 0x610 */
u32 MultiCastLo;
u32 ModeStat;
u32 DmaReadCfg;
u32 DmaWriteCfg; /* 0x620 */
u32 TxBufRat;
u32 EvtCsm;
u32 CmdCsm;
u32 TuneRxCoalTicks;/* 0x630 */
u32 TuneTxCoalTicks;
u32 TuneStatTicks;
u32 TuneMaxTxDesc;
u32 TuneMaxRxDesc; /* 0x640 */
u32 TuneTrace;
u32 TuneLink;
u32 TuneFastLink;
u32 TracePtr; /* 0x650 */
u32 TraceStrt;
u32 TraceLen;
u32 IfIdx;
u32 IfMtu; /* 0x660 */
u32 MaskInt;
u32 GigLnkState;
u32 FastLnkState;
u32 pad16[4]; /* 0x670 */
u32 RxRetCsm; /* 0x680 */
u32 pad17[31];
u32 CmdRng[64]; /* 0x700 */
u32 Window[0x200];
};
typedef struct {
u32 addrhi;
u32 addrlo;
} aceaddr;
#define ACE_WINDOW_SIZE 0x800
#define ACE_JUMBO_MTU 9000
#define ACE_STD_MTU 1500
#define ACE_TRACE_SIZE 0x8000
/*
* Host control register bits.
*/
#define IN_INT 0x01
#define CLR_INT 0x02
#define HW_RESET 0x08
#define BYTE_SWAP 0x10
#define WORD_SWAP 0x20
#define MASK_INTS 0x40
/*
* Local control register bits.
*/
#define EEPROM_DATA_IN 0x800000
#define EEPROM_DATA_OUT 0x400000
#define EEPROM_WRITE_ENABLE 0x200000
#define EEPROM_CLK_OUT 0x100000
#define EEPROM_BASE 0xa0000000
#define EEPROM_WRITE_SELECT 0xa0
#define EEPROM_READ_SELECT 0xa1
#define SRAM_BANK_512K 0x200
/*
* udelay() values for when clocking the eeprom
*/
#define ACE_SHORT_DELAY 2
#define ACE_LONG_DELAY 4
/*
* Misc Config bits
*/
#define SYNC_SRAM_TIMING 0x100000
/*
* CPU state bits.
*/
#define CPU_RESET 0x01
#define CPU_TRACE 0x02
#define CPU_PROM_FAILED 0x10
#define CPU_HALT 0x00010000
#define CPU_HALTED 0xffff0000
/*
* PCI State bits.
*/
#define DMA_READ_MAX_4 0x04
#define DMA_READ_MAX_16 0x08
#define DMA_READ_MAX_32 0x0c
#define DMA_READ_MAX_64 0x10
#define DMA_READ_MAX_128 0x14
#define DMA_READ_MAX_256 0x18
#define DMA_READ_MAX_1K 0x1c
#define DMA_WRITE_MAX_4 0x20
#define DMA_WRITE_MAX_16 0x40
#define DMA_WRITE_MAX_32 0x60
#define DMA_WRITE_MAX_64 0x80
#define DMA_WRITE_MAX_128 0xa0
#define DMA_WRITE_MAX_256 0xc0
#define DMA_WRITE_MAX_1K 0xe0
#define DMA_READ_WRITE_MASK 0xfc
#define MEM_READ_MULTIPLE 0x00020000
#define PCI_66MHZ 0x00080000
#define PCI_32BIT 0x00100000
#define DMA_WRITE_ALL_ALIGN 0x00800000
#define READ_CMD_MEM 0x06000000
#define WRITE_CMD_MEM 0x70000000
/*
* Mode status
*/
#define ACE_BYTE_SWAP_BD 0x02
#define ACE_WORD_SWAP_BD 0x04 /* not actually used */
#define ACE_WARN 0x08
#define ACE_BYTE_SWAP_DMA 0x10
#define ACE_NO_JUMBO_FRAG 0x200
#define ACE_FATAL 0x40000000
/*
* DMA config
*/
#define DMA_THRESH_1W 0x10
#define DMA_THRESH_2W 0x20
#define DMA_THRESH_4W 0x40
#define DMA_THRESH_8W 0x80
#define DMA_THRESH_16W 0x100
#define DMA_THRESH_32W 0x0 /* not described in doc, but exists. */
/*
* Tuning parameters
*/
#define TICKS_PER_SEC 1000000
/*
* Link bits
*/
#define LNK_PREF 0x00008000
#define LNK_10MB 0x00010000
#define LNK_100MB 0x00020000
#define LNK_1000MB 0x00040000
#define LNK_FULL_DUPLEX 0x00080000
#define LNK_HALF_DUPLEX 0x00100000
#define LNK_TX_FLOW_CTL_Y 0x00200000
#define LNK_NEG_ADVANCED 0x00400000
#define LNK_RX_FLOW_CTL_Y 0x00800000
#define LNK_NIC 0x01000000
#define LNK_JAM 0x02000000
#define LNK_JUMBO 0x04000000
#define LNK_ALTEON 0x08000000
#define LNK_NEG_FCTL 0x10000000
#define LNK_NEGOTIATE 0x20000000
#define LNK_ENABLE 0x40000000
#define LNK_UP 0x80000000
/*
* Event definitions
*/
#define EVT_RING_ENTRIES 256
#define EVT_RING_SIZE (EVT_RING_ENTRIES * sizeof(struct event))
struct event {
#ifdef __LITTLE_ENDIAN_BITFIELD
u32 idx:12;
u32 code:12;
u32 evt:8;
#else
u32 evt:8;
u32 code:12;
u32 idx:12;
#endif
u32 pad;
};
/*
* Events
*/
#define E_FW_RUNNING 0x01
#define E_STATS_UPDATED 0x04
#define E_STATS_UPDATE 0x04
#define E_LNK_STATE 0x06
#define E_C_LINK_UP 0x01
#define E_C_LINK_DOWN 0x02
#define E_C_LINK_10_100 0x03
#define E_ERROR 0x07
#define E_C_ERR_INVAL_CMD 0x01
#define E_C_ERR_UNIMP_CMD 0x02
#define E_C_ERR_BAD_CFG 0x03
#define E_MCAST_LIST 0x08
#define E_C_MCAST_ADDR_ADD 0x01
#define E_C_MCAST_ADDR_DEL 0x02
#define E_RESET_JUMBO_RNG 0x09
/*
* Commands
*/
#define CMD_RING_ENTRIES 64
struct cmd {
#ifdef __LITTLE_ENDIAN_BITFIELD
u32 idx:12;
u32 code:12;
u32 evt:8;
#else
u32 evt:8;
u32 code:12;
u32 idx:12;
#endif
};
#define C_HOST_STATE 0x01
#define C_C_STACK_UP 0x01
#define C_C_STACK_DOWN 0x02
#define C_FDR_FILTERING 0x02
#define C_C_FDR_FILT_ENABLE 0x01
#define C_C_FDR_FILT_DISABLE 0x02
#define C_SET_RX_PRD_IDX 0x03
#define C_UPDATE_STATS 0x04
#define C_RESET_JUMBO_RNG 0x05
#define C_ADD_MULTICAST_ADDR 0x08
#define C_DEL_MULTICAST_ADDR 0x09
#define C_SET_PROMISC_MODE 0x0a
#define C_C_PROMISC_ENABLE 0x01
#define C_C_PROMISC_DISABLE 0x02
#define C_LNK_NEGOTIATION 0x0b
#define C_C_NEGOTIATE_BOTH 0x00
#define C_C_NEGOTIATE_GIG 0x01
#define C_C_NEGOTIATE_10_100 0x02
#define C_SET_MAC_ADDR 0x0c
#define C_CLEAR_PROFILE 0x0d
#define C_SET_MULTICAST_MODE 0x0e
#define C_C_MCAST_ENABLE 0x01
#define C_C_MCAST_DISABLE 0x02
#define C_CLEAR_STATS 0x0f
#define C_SET_RX_JUMBO_PRD_IDX 0x10
#define C_REFRESH_STATS 0x11
/*
* Descriptor flags
*/
#define BD_FLG_TCP_UDP_SUM 0x01
#define BD_FLG_IP_SUM 0x02
#define BD_FLG_END 0x04
#define BD_FLG_MORE 0x08
#define BD_FLG_JUMBO 0x10
#define BD_FLG_UCAST 0x20
#define BD_FLG_MCAST 0x40
#define BD_FLG_BCAST 0x60
#define BD_FLG_TYP_MASK 0x60
#define BD_FLG_IP_FRAG 0x80
#define BD_FLG_IP_FRAG_END 0x100
#define BD_FLG_VLAN_TAG 0x200
#define BD_FLG_FRAME_ERROR 0x400
#define BD_FLG_COAL_NOW 0x800
#define BD_FLG_MINI 0x1000
/*
* Ring Control block flags
*/
#define RCB_FLG_TCP_UDP_SUM 0x01
#define RCB_FLG_IP_SUM 0x02
#define RCB_FLG_NO_PSEUDO_HDR 0x08
#define RCB_FLG_VLAN_ASSIST 0x10
#define RCB_FLG_COAL_INT_ONLY 0x20
#define RCB_FLG_TX_HOST_RING 0x40
#define RCB_FLG_IEEE_SNAP_SUM 0x80
#define RCB_FLG_EXT_RX_BD 0x100
#define RCB_FLG_RNG_DISABLE 0x200
/*
* TX ring - maximum TX ring entries for Tigon I's is 128
*/
#define MAX_TX_RING_ENTRIES 256
#define TIGON_I_TX_RING_ENTRIES 128
#define TX_RING_SIZE (MAX_TX_RING_ENTRIES * sizeof(struct tx_desc))
#define TX_RING_BASE 0x3800
struct tx_desc{
aceaddr addr;
u32 flagsize;
#if 0
/*
* This is in PCI shared mem and must be accessed with readl/writel
* real layout is:
*/
#if __LITTLE_ENDIAN
u16 flags;
u16 size;
u16 vlan;
u16 reserved;
#else
u16 size;
u16 flags;
u16 reserved;
u16 vlan;
#endif
#endif
u32 vlanres;
};
#define RX_STD_RING_ENTRIES 512
#define RX_STD_RING_SIZE (RX_STD_RING_ENTRIES * sizeof(struct rx_desc))
#define RX_JUMBO_RING_ENTRIES 256
#define RX_JUMBO_RING_SIZE (RX_JUMBO_RING_ENTRIES *sizeof(struct rx_desc))
#define RX_MINI_RING_ENTRIES 1024
#define RX_MINI_RING_SIZE (RX_MINI_RING_ENTRIES *sizeof(struct rx_desc))
#define RX_RETURN_RING_ENTRIES 2048
#define RX_RETURN_RING_SIZE (RX_MAX_RETURN_RING_ENTRIES * \
sizeof(struct rx_desc))
struct rx_desc{
aceaddr addr;
#ifdef __LITTLE_ENDIAN
u16 size;
u16 idx;
#else
u16 idx;
u16 size;
#endif
#ifdef __LITTLE_ENDIAN
u16 flags;
u16 type;
#else
u16 type;
u16 flags;
#endif
#ifdef __LITTLE_ENDIAN
u16 tcp_udp_csum;
u16 ip_csum;
#else
u16 ip_csum;
u16 tcp_udp_csum;
#endif
#ifdef __LITTLE_ENDIAN
u16 vlan;
u16 err_flags;
#else
u16 err_flags;
u16 vlan;
#endif
u32 reserved;
u32 opague;
};
/*
* This struct is shared with the NIC firmware.
*/
struct ring_ctrl {
aceaddr rngptr;
#ifdef __LITTLE_ENDIAN
u16 flags;
u16 max_len;
#else
u16 max_len;
u16 flags;
#endif
u32 pad;
};
struct ace_mac_stats {
u32 excess_colls;
u32 coll_1;
u32 coll_2;
u32 coll_3;
u32 coll_4;
u32 coll_5;
u32 coll_6;
u32 coll_7;
u32 coll_8;
u32 coll_9;
u32 coll_10;
u32 coll_11;
u32 coll_12;
u32 coll_13;
u32 coll_14;
u32 coll_15;
u32 late_coll;
u32 defers;
u32 crc_err;
u32 underrun;
u32 crs_err;
u32 pad[3];
u32 drop_ula;
u32 drop_mc;
u32 drop_fc;
u32 drop_space;
u32 coll;
u32 kept_bc;
u32 kept_mc;
u32 kept_uc;
};
struct ace_info {
union {
u32 stats[256];
} s;
struct ring_ctrl evt_ctrl;
struct ring_ctrl cmd_ctrl;
struct ring_ctrl tx_ctrl;
struct ring_ctrl rx_std_ctrl;
struct ring_ctrl rx_jumbo_ctrl;
struct ring_ctrl rx_mini_ctrl;
struct ring_ctrl rx_return_ctrl;
aceaddr evt_prd_ptr;
aceaddr rx_ret_prd_ptr;
aceaddr tx_csm_ptr;
aceaddr stats2_ptr;
};
struct ring_info {
struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(mapping);
};
/*
* Funny... As soon as we add maplen on alpha, it starts to work
* much slower. Hmm... is it because struct does not fit to one cacheline?
* So, split tx_ring_info.
*/
struct tx_ring_info {
struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(mapping);
DEFINE_DMA_UNMAP_LEN(maplen);
};
/*
* struct ace_skb holding the rings of skb's. This is an awful lot of
* pointers, but I don't see any other smart mode to do this in an
* efficient manner ;-(
*/
struct ace_skb
{
struct tx_ring_info tx_skbuff[MAX_TX_RING_ENTRIES];
struct ring_info rx_std_skbuff[RX_STD_RING_ENTRIES];
struct ring_info rx_mini_skbuff[RX_MINI_RING_ENTRIES];
struct ring_info rx_jumbo_skbuff[RX_JUMBO_RING_ENTRIES];
};
/*
* Struct private for the AceNIC.
*
* Elements are grouped so variables used by the tx handling goes
* together, and will go into the same cache lines etc. in order to
* avoid cache line contention between the rx and tx handling on SMP.
*
* Frequently accessed variables are put at the beginning of the
* struct to help the compiler generate better/shorter code.
*/
struct ace_private
{
struct ace_info *info;
struct ace_regs __iomem *regs; /* register base */
struct ace_skb *skb;
dma_addr_t info_dma; /* 32/64 bit */
int version, link;
int promisc, mcast_all;
/*
* TX elements
*/
struct tx_desc *tx_ring;
u32 tx_prd;
volatile u32 tx_ret_csm;
int tx_ring_entries;
/*
* RX elements
*/
unsigned long std_refill_busy
__attribute__ ((aligned (SMP_CACHE_BYTES)));
unsigned long mini_refill_busy, jumbo_refill_busy;
atomic_t cur_rx_bufs;
atomic_t cur_mini_bufs;
atomic_t cur_jumbo_bufs;
u32 rx_std_skbprd, rx_mini_skbprd, rx_jumbo_skbprd;
u32 cur_rx;
struct rx_desc *rx_std_ring;
struct rx_desc *rx_jumbo_ring;
struct rx_desc *rx_mini_ring;
struct rx_desc *rx_return_ring;
int tasklet_pending, jumbo;
struct tasklet_struct ace_tasklet;
struct event *evt_ring;
volatile u32 *evt_prd, *rx_ret_prd, *tx_csm;
dma_addr_t tx_ring_dma; /* 32/64 bit */
dma_addr_t rx_ring_base_dma;
dma_addr_t evt_ring_dma;
dma_addr_t evt_prd_dma, rx_ret_prd_dma, tx_csm_dma;
unsigned char *trace_buf;
struct pci_dev *pdev;
struct net_device *next;
volatile int fw_running;
int board_idx;
u16 pci_command;
u8 pci_latency;
const char *name;
#ifdef INDEX_DEBUG
spinlock_t debug_lock
__attribute__ ((aligned (SMP_CACHE_BYTES)));
u32 last_tx, last_std_rx, last_mini_rx;
#endif
int pci_using_dac;
u8 firmware_major;
u8 firmware_minor;
u8 firmware_fix;
u32 firmware_start;
};
#define TX_RESERVED MAX_SKB_FRAGS
static inline int tx_space (struct ace_private *ap, u32 csm, u32 prd)
{
return (csm - prd - 1) & (ACE_TX_RING_ENTRIES(ap) - 1);
}
#define tx_free(ap) tx_space((ap)->tx_ret_csm, (ap)->tx_prd, ap)
#define tx_ring_full(ap, csm, prd) (tx_space(ap, csm, prd) <= TX_RESERVED)
static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr)
{
u64 baddr = (u64) addr;
aa->addrlo = baddr & 0xffffffff;
aa->addrhi = baddr >> 32;
wmb();
}
static inline void ace_set_txprd(struct ace_regs __iomem *regs,
struct ace_private *ap, u32 value)
{
#ifdef INDEX_DEBUG
unsigned long flags;
spin_lock_irqsave(&ap->debug_lock, flags);
writel(value, &regs->TxPrd);
if (value == ap->last_tx)
printk(KERN_ERR "AceNIC RACE ALERT! writing identical value "
"to tx producer (%i)\n", value);
ap->last_tx = value;
spin_unlock_irqrestore(&ap->debug_lock, flags);
#else
writel(value, &regs->TxPrd);
#endif
wmb();
}
static inline void ace_mask_irq(struct net_device *dev)
{
struct ace_private *ap = netdev_priv(dev);
struct ace_regs __iomem *regs = ap->regs;
if (ACE_IS_TIGON_I(ap))
writel(1, &regs->MaskInt);
else
writel(readl(&regs->HostCtrl) | MASK_INTS, &regs->HostCtrl);
ace_sync_irq(dev->irq);
}
static inline void ace_unmask_irq(struct net_device *dev)
{
struct ace_private *ap = netdev_priv(dev);
struct ace_regs __iomem *regs = ap->regs;
if (ACE_IS_TIGON_I(ap))
writel(0, &regs->MaskInt);
else
writel(readl(&regs->HostCtrl) & ~MASK_INTS, &regs->HostCtrl);
}
/*
* Prototypes
*/
static int ace_init(struct net_device *dev);
static void ace_load_std_rx_ring(struct net_device *dev, int nr_bufs);
static void ace_load_mini_rx_ring(struct net_device *dev, int nr_bufs);
static void ace_load_jumbo_rx_ring(struct net_device *dev, int nr_bufs);
static irqreturn_t ace_interrupt(int irq, void *dev_id);
static int ace_load_firmware(struct net_device *dev);
static int ace_open(struct net_device *dev);
static netdev_tx_t ace_start_xmit(struct sk_buff *skb,
struct net_device *dev);
static int ace_close(struct net_device *dev);
static void ace_tasklet(unsigned long dev);
static void ace_dump_trace(struct ace_private *ap);
static void ace_set_multicast_list(struct net_device *dev);
static int ace_change_mtu(struct net_device *dev, int new_mtu);
static int ace_set_mac_addr(struct net_device *dev, void *p);
static void ace_set_rxtx_parms(struct net_device *dev, int jumbo);
static int ace_allocate_descriptors(struct net_device *dev);
static void ace_free_descriptors(struct net_device *dev);
static void ace_init_cleanup(struct net_device *dev);
static struct net_device_stats *ace_get_stats(struct net_device *dev);
static int read_eeprom_byte(struct net_device *dev, unsigned long offset);
#endif /* _ACENIC_H_ */

View file

@ -0,0 +1,9 @@
config ALTERA_TSE
tristate "Altera Triple-Speed Ethernet MAC support"
depends on HAS_DMA
select PHYLIB
---help---
This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
To compile this driver as a module, choose M here. The module
will be called alteratse.

View file

@ -0,0 +1,8 @@
#
# Makefile for the Altera device drivers.
#
obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
altera_msgdma.o altera_sgdma.o altera_utils.o
ccflags-y += -D__CHECK_ENDIAN__

View file

@ -0,0 +1,206 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/netdevice.h>
#include "altera_utils.h"
#include "altera_tse.h"
#include "altera_msgdmahw.h"
#include "altera_msgdma.h"
/* No initialization work to do for MSGDMA */
int msgdma_initialize(struct altera_tse_private *priv)
{
return 0;
}
void msgdma_uninitialize(struct altera_tse_private *priv)
{
}
void msgdma_start_rxdma(struct altera_tse_private *priv)
{
}
void msgdma_reset(struct altera_tse_private *priv)
{
int counter;
/* Reset Rx mSGDMA */
csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr,
msgdma_csroffs(status));
csrwr32(MSGDMA_CSR_CTL_RESET, priv->rx_dma_csr,
msgdma_csroffs(control));
counter = 0;
while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
if (tse_bit_is_clear(priv->rx_dma_csr, msgdma_csroffs(status),
MSGDMA_CSR_STAT_RESETTING))
break;
udelay(1);
}
if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
netif_warn(priv, drv, priv->dev,
"TSE Rx mSGDMA resetting bit never cleared!\n");
/* clear all status bits */
csrwr32(MSGDMA_CSR_STAT_MASK, priv->rx_dma_csr, msgdma_csroffs(status));
/* Reset Tx mSGDMA */
csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr,
msgdma_csroffs(status));
csrwr32(MSGDMA_CSR_CTL_RESET, priv->tx_dma_csr,
msgdma_csroffs(control));
counter = 0;
while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
if (tse_bit_is_clear(priv->tx_dma_csr, msgdma_csroffs(status),
MSGDMA_CSR_STAT_RESETTING))
break;
udelay(1);
}
if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
netif_warn(priv, drv, priv->dev,
"TSE Tx mSGDMA resetting bit never cleared!\n");
/* clear all status bits */
csrwr32(MSGDMA_CSR_STAT_MASK, priv->tx_dma_csr, msgdma_csroffs(status));
}
void msgdma_disable_rxirq(struct altera_tse_private *priv)
{
tse_clear_bit(priv->rx_dma_csr, msgdma_csroffs(control),
MSGDMA_CSR_CTL_GLOBAL_INTR);
}
void msgdma_enable_rxirq(struct altera_tse_private *priv)
{
tse_set_bit(priv->rx_dma_csr, msgdma_csroffs(control),
MSGDMA_CSR_CTL_GLOBAL_INTR);
}
void msgdma_disable_txirq(struct altera_tse_private *priv)
{
tse_clear_bit(priv->tx_dma_csr, msgdma_csroffs(control),
MSGDMA_CSR_CTL_GLOBAL_INTR);
}
void msgdma_enable_txirq(struct altera_tse_private *priv)
{
tse_set_bit(priv->tx_dma_csr, msgdma_csroffs(control),
MSGDMA_CSR_CTL_GLOBAL_INTR);
}
void msgdma_clear_rxirq(struct altera_tse_private *priv)
{
csrwr32(MSGDMA_CSR_STAT_IRQ, priv->rx_dma_csr, msgdma_csroffs(status));
}
void msgdma_clear_txirq(struct altera_tse_private *priv)
{
csrwr32(MSGDMA_CSR_STAT_IRQ, priv->tx_dma_csr, msgdma_csroffs(status));
}
/* return 0 to indicate transmit is pending */
int msgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
{
csrwr32(lower_32_bits(buffer->dma_addr), priv->tx_dma_desc,
msgdma_descroffs(read_addr_lo));
csrwr32(upper_32_bits(buffer->dma_addr), priv->tx_dma_desc,
msgdma_descroffs(read_addr_hi));
csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_lo));
csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(write_addr_hi));
csrwr32(buffer->len, priv->tx_dma_desc, msgdma_descroffs(len));
csrwr32(0, priv->tx_dma_desc, msgdma_descroffs(burst_seq_num));
csrwr32(MSGDMA_DESC_TX_STRIDE, priv->tx_dma_desc,
msgdma_descroffs(stride));
csrwr32(MSGDMA_DESC_CTL_TX_SINGLE, priv->tx_dma_desc,
msgdma_descroffs(control));
return 0;
}
u32 msgdma_tx_completions(struct altera_tse_private *priv)
{
u32 ready = 0;
u32 inuse;
u32 status;
/* Get number of sent descriptors */
inuse = csrrd32(priv->tx_dma_csr, msgdma_csroffs(rw_fill_level))
& 0xffff;
if (inuse) { /* Tx FIFO is not empty */
ready = priv->tx_prod - priv->tx_cons - inuse - 1;
} else {
/* Check for buffered last packet */
status = csrrd32(priv->tx_dma_csr, msgdma_csroffs(status));
if (status & MSGDMA_CSR_STAT_BUSY)
ready = priv->tx_prod - priv->tx_cons - 1;
else
ready = priv->tx_prod - priv->tx_cons;
}
return ready;
}
/* Put buffer to the mSGDMA RX FIFO
*/
void msgdma_add_rx_desc(struct altera_tse_private *priv,
struct tse_buffer *rxbuffer)
{
u32 len = priv->rx_dma_buf_sz;
dma_addr_t dma_addr = rxbuffer->dma_addr;
u32 control = (MSGDMA_DESC_CTL_END_ON_EOP
| MSGDMA_DESC_CTL_END_ON_LEN
| MSGDMA_DESC_CTL_TR_COMP_IRQ
| MSGDMA_DESC_CTL_EARLY_IRQ
| MSGDMA_DESC_CTL_TR_ERR_IRQ
| MSGDMA_DESC_CTL_GO);
csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_lo));
csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(read_addr_hi));
csrwr32(lower_32_bits(dma_addr), priv->rx_dma_desc,
msgdma_descroffs(write_addr_lo));
csrwr32(upper_32_bits(dma_addr), priv->rx_dma_desc,
msgdma_descroffs(write_addr_hi));
csrwr32(len, priv->rx_dma_desc, msgdma_descroffs(len));
csrwr32(0, priv->rx_dma_desc, msgdma_descroffs(burst_seq_num));
csrwr32(0x00010001, priv->rx_dma_desc, msgdma_descroffs(stride));
csrwr32(control, priv->rx_dma_desc, msgdma_descroffs(control));
}
/* status is returned on upper 16 bits,
* length is returned in lower 16 bits
*/
u32 msgdma_rx_status(struct altera_tse_private *priv)
{
u32 rxstatus = 0;
u32 pktlength;
u32 pktstatus;
if (csrrd32(priv->rx_dma_csr, msgdma_csroffs(resp_fill_level))
& 0xffff) {
pktlength = csrrd32(priv->rx_dma_resp,
msgdma_respoffs(bytes_transferred));
pktstatus = csrrd32(priv->rx_dma_resp,
msgdma_respoffs(status));
rxstatus = pktstatus;
rxstatus = rxstatus << 16;
rxstatus |= (pktlength & 0xffff);
}
return rxstatus;
}

View file

@ -0,0 +1,35 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ALTERA_MSGDMA_H__
#define __ALTERA_MSGDMA_H__
void msgdma_reset(struct altera_tse_private *);
void msgdma_enable_txirq(struct altera_tse_private *);
void msgdma_enable_rxirq(struct altera_tse_private *);
void msgdma_disable_rxirq(struct altera_tse_private *);
void msgdma_disable_txirq(struct altera_tse_private *);
void msgdma_clear_rxirq(struct altera_tse_private *);
void msgdma_clear_txirq(struct altera_tse_private *);
u32 msgdma_tx_completions(struct altera_tse_private *);
void msgdma_add_rx_desc(struct altera_tse_private *, struct tse_buffer *);
int msgdma_tx_buffer(struct altera_tse_private *, struct tse_buffer *);
u32 msgdma_rx_status(struct altera_tse_private *);
int msgdma_initialize(struct altera_tse_private *);
void msgdma_uninitialize(struct altera_tse_private *);
void msgdma_start_rxdma(struct altera_tse_private *);
#endif /* __ALTERA_MSGDMA_H__ */

View file

@ -0,0 +1,162 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ALTERA_MSGDMAHW_H__
#define __ALTERA_MSGDMAHW_H__
/* mSGDMA extended descriptor format
*/
struct msgdma_extended_desc {
u32 read_addr_lo; /* data buffer source address low bits */
u32 write_addr_lo; /* data buffer destination address low bits */
u32 len; /* the number of bytes to transfer
* per descriptor
*/
u32 burst_seq_num; /* bit 31:24 write burst
* bit 23:16 read burst
* bit 15:0 sequence number
*/
u32 stride; /* bit 31:16 write stride
* bit 15:0 read stride
*/
u32 read_addr_hi; /* data buffer source address high bits */
u32 write_addr_hi; /* data buffer destination address high bits */
u32 control; /* characteristics of the transfer */
};
/* mSGDMA descriptor control field bit definitions
*/
#define MSGDMA_DESC_CTL_SET_CH(x) ((x) & 0xff)
#define MSGDMA_DESC_CTL_GEN_SOP BIT(8)
#define MSGDMA_DESC_CTL_GEN_EOP BIT(9)
#define MSGDMA_DESC_CTL_PARK_READS BIT(10)
#define MSGDMA_DESC_CTL_PARK_WRITES BIT(11)
#define MSGDMA_DESC_CTL_END_ON_EOP BIT(12)
#define MSGDMA_DESC_CTL_END_ON_LEN BIT(13)
#define MSGDMA_DESC_CTL_TR_COMP_IRQ BIT(14)
#define MSGDMA_DESC_CTL_EARLY_IRQ BIT(15)
#define MSGDMA_DESC_CTL_TR_ERR_IRQ (0xff << 16)
#define MSGDMA_DESC_CTL_EARLY_DONE BIT(24)
/* Writing 1 to the go bit commits the entire descriptor into the
* descriptor FIFO(s)
*/
#define MSGDMA_DESC_CTL_GO BIT(31)
/* Tx buffer control flags
*/
#define MSGDMA_DESC_CTL_TX_FIRST (MSGDMA_DESC_CTL_GEN_SOP | \
MSGDMA_DESC_CTL_TR_ERR_IRQ | \
MSGDMA_DESC_CTL_GO)
#define MSGDMA_DESC_CTL_TX_MIDDLE (MSGDMA_DESC_CTL_TR_ERR_IRQ | \
MSGDMA_DESC_CTL_GO)
#define MSGDMA_DESC_CTL_TX_LAST (MSGDMA_DESC_CTL_GEN_EOP | \
MSGDMA_DESC_CTL_TR_COMP_IRQ | \
MSGDMA_DESC_CTL_TR_ERR_IRQ | \
MSGDMA_DESC_CTL_GO)
#define MSGDMA_DESC_CTL_TX_SINGLE (MSGDMA_DESC_CTL_GEN_SOP | \
MSGDMA_DESC_CTL_GEN_EOP | \
MSGDMA_DESC_CTL_TR_COMP_IRQ | \
MSGDMA_DESC_CTL_TR_ERR_IRQ | \
MSGDMA_DESC_CTL_GO)
#define MSGDMA_DESC_CTL_RX_SINGLE (MSGDMA_DESC_CTL_END_ON_EOP | \
MSGDMA_DESC_CTL_END_ON_LEN | \
MSGDMA_DESC_CTL_TR_COMP_IRQ | \
MSGDMA_DESC_CTL_EARLY_IRQ | \
MSGDMA_DESC_CTL_TR_ERR_IRQ | \
MSGDMA_DESC_CTL_GO)
/* mSGDMA extended descriptor stride definitions
*/
#define MSGDMA_DESC_TX_STRIDE (0x00010001)
#define MSGDMA_DESC_RX_STRIDE (0x00010001)
/* mSGDMA dispatcher control and status register map
*/
struct msgdma_csr {
u32 status; /* Read/Clear */
u32 control; /* Read/Write */
u32 rw_fill_level; /* bit 31:16 - write fill level
* bit 15:0 - read fill level
*/
u32 resp_fill_level; /* bit 15:0 */
u32 rw_seq_num; /* bit 31:16 - write sequence number
* bit 15:0 - read sequence number
*/
u32 pad[3]; /* reserved */
};
/* mSGDMA CSR status register bit definitions
*/
#define MSGDMA_CSR_STAT_BUSY BIT(0)
#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY BIT(1)
#define MSGDMA_CSR_STAT_DESC_BUF_FULL BIT(2)
#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY BIT(3)
#define MSGDMA_CSR_STAT_RESP_BUF_FULL BIT(4)
#define MSGDMA_CSR_STAT_STOPPED BIT(5)
#define MSGDMA_CSR_STAT_RESETTING BIT(6)
#define MSGDMA_CSR_STAT_STOPPED_ON_ERR BIT(7)
#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY BIT(8)
#define MSGDMA_CSR_STAT_IRQ BIT(9)
#define MSGDMA_CSR_STAT_MASK 0x3FF
#define MSGDMA_CSR_STAT_MASK_WITHOUT_IRQ 0x1FF
#define MSGDMA_CSR_STAT_BUSY_GET(v) GET_BIT_VALUE(v, 0)
#define MSGDMA_CSR_STAT_DESC_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 1)
#define MSGDMA_CSR_STAT_DESC_BUF_FULL_GET(v) GET_BIT_VALUE(v, 2)
#define MSGDMA_CSR_STAT_RESP_BUF_EMPTY_GET(v) GET_BIT_VALUE(v, 3)
#define MSGDMA_CSR_STAT_RESP_BUF_FULL_GET(v) GET_BIT_VALUE(v, 4)
#define MSGDMA_CSR_STAT_STOPPED_GET(v) GET_BIT_VALUE(v, 5)
#define MSGDMA_CSR_STAT_RESETTING_GET(v) GET_BIT_VALUE(v, 6)
#define MSGDMA_CSR_STAT_STOPPED_ON_ERR_GET(v) GET_BIT_VALUE(v, 7)
#define MSGDMA_CSR_STAT_STOPPED_ON_EARLY_GET(v) GET_BIT_VALUE(v, 8)
#define MSGDMA_CSR_STAT_IRQ_GET(v) GET_BIT_VALUE(v, 9)
/* mSGDMA CSR control register bit definitions
*/
#define MSGDMA_CSR_CTL_STOP BIT(0)
#define MSGDMA_CSR_CTL_RESET BIT(1)
#define MSGDMA_CSR_CTL_STOP_ON_ERR BIT(2)
#define MSGDMA_CSR_CTL_STOP_ON_EARLY BIT(3)
#define MSGDMA_CSR_CTL_GLOBAL_INTR BIT(4)
#define MSGDMA_CSR_CTL_STOP_DESCS BIT(5)
/* mSGDMA CSR fill level bits
*/
#define MSGDMA_CSR_WR_FILL_LEVEL_GET(v) (((v) & 0xffff0000) >> 16)
#define MSGDMA_CSR_RD_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
#define MSGDMA_CSR_RESP_FILL_LEVEL_GET(v) ((v) & 0x0000ffff)
/* mSGDMA response register map
*/
struct msgdma_response {
u32 bytes_transferred;
u32 status;
};
#define msgdma_respoffs(a) (offsetof(struct msgdma_response, a))
#define msgdma_csroffs(a) (offsetof(struct msgdma_csr, a))
#define msgdma_descroffs(a) (offsetof(struct msgdma_extended_desc, a))
/* mSGDMA response register bit definitions
*/
#define MSGDMA_RESP_EARLY_TERM BIT(8)
#define MSGDMA_RESP_ERR_MASK 0xFF
#endif /* __ALTERA_MSGDMA_H__*/

View file

@ -0,0 +1,540 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/list.h>
#include "altera_utils.h"
#include "altera_tse.h"
#include "altera_sgdmahw.h"
#include "altera_sgdma.h"
static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc,
struct sgdma_descrip __iomem *ndesc,
dma_addr_t ndesc_phys,
dma_addr_t raddr,
dma_addr_t waddr,
u16 length,
int generate_eop,
int rfixed,
int wfixed);
static int sgdma_async_write(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc);
static int sgdma_async_read(struct altera_tse_private *priv);
static dma_addr_t
sgdma_txphysaddr(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc);
static dma_addr_t
sgdma_rxphysaddr(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc);
static int sgdma_txbusy(struct altera_tse_private *priv);
static int sgdma_rxbusy(struct altera_tse_private *priv);
static void
queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer);
static void
queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer);
static struct tse_buffer *
dequeue_tx(struct altera_tse_private *priv);
static struct tse_buffer *
dequeue_rx(struct altera_tse_private *priv);
static struct tse_buffer *
queue_rx_peekhead(struct altera_tse_private *priv);
int sgdma_initialize(struct altera_tse_private *priv)
{
priv->txctrlreg = SGDMA_CTRLREG_ILASTD |
SGDMA_CTRLREG_INTEN;
priv->rxctrlreg = SGDMA_CTRLREG_IDESCRIP |
SGDMA_CTRLREG_INTEN |
SGDMA_CTRLREG_ILASTD;
priv->sgdmadesclen = sizeof(struct sgdma_descrip);
INIT_LIST_HEAD(&priv->txlisthd);
INIT_LIST_HEAD(&priv->rxlisthd);
priv->rxdescphys = (dma_addr_t) 0;
priv->txdescphys = (dma_addr_t) 0;
priv->rxdescphys = dma_map_single(priv->device,
(void __force *)priv->rx_dma_desc,
priv->rxdescmem, DMA_BIDIRECTIONAL);
if (dma_mapping_error(priv->device, priv->rxdescphys)) {
sgdma_uninitialize(priv);
netdev_err(priv->dev, "error mapping rx descriptor memory\n");
return -EINVAL;
}
priv->txdescphys = dma_map_single(priv->device,
(void __force *)priv->tx_dma_desc,
priv->txdescmem, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, priv->txdescphys)) {
sgdma_uninitialize(priv);
netdev_err(priv->dev, "error mapping tx descriptor memory\n");
return -EINVAL;
}
/* Initialize descriptor memory to all 0's, sync memory to cache */
memset_io(priv->tx_dma_desc, 0, priv->txdescmem);
memset_io(priv->rx_dma_desc, 0, priv->rxdescmem);
dma_sync_single_for_device(priv->device, priv->txdescphys,
priv->txdescmem, DMA_TO_DEVICE);
dma_sync_single_for_device(priv->device, priv->rxdescphys,
priv->rxdescmem, DMA_TO_DEVICE);
return 0;
}
void sgdma_uninitialize(struct altera_tse_private *priv)
{
if (priv->rxdescphys)
dma_unmap_single(priv->device, priv->rxdescphys,
priv->rxdescmem, DMA_BIDIRECTIONAL);
if (priv->txdescphys)
dma_unmap_single(priv->device, priv->txdescphys,
priv->txdescmem, DMA_TO_DEVICE);
}
/* This function resets the SGDMA controller and clears the
* descriptor memory used for transmits and receives.
*/
void sgdma_reset(struct altera_tse_private *priv)
{
/* Initialize descriptor memory to 0 */
memset_io(priv->tx_dma_desc, 0, priv->txdescmem);
memset_io(priv->rx_dma_desc, 0, priv->rxdescmem);
csrwr32(SGDMA_CTRLREG_RESET, priv->tx_dma_csr, sgdma_csroffs(control));
csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control));
csrwr32(SGDMA_CTRLREG_RESET, priv->rx_dma_csr, sgdma_csroffs(control));
csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control));
}
/* For SGDMA, interrupts remain enabled after initially enabling,
* so no need to provide implementations for abstract enable
* and disable
*/
void sgdma_enable_rxirq(struct altera_tse_private *priv)
{
}
void sgdma_enable_txirq(struct altera_tse_private *priv)
{
}
void sgdma_disable_rxirq(struct altera_tse_private *priv)
{
}
void sgdma_disable_txirq(struct altera_tse_private *priv)
{
}
void sgdma_clear_rxirq(struct altera_tse_private *priv)
{
tse_set_bit(priv->rx_dma_csr, sgdma_csroffs(control),
SGDMA_CTRLREG_CLRINT);
}
void sgdma_clear_txirq(struct altera_tse_private *priv)
{
tse_set_bit(priv->tx_dma_csr, sgdma_csroffs(control),
SGDMA_CTRLREG_CLRINT);
}
/* transmits buffer through SGDMA. Returns number of buffers
* transmitted, 0 if not possible.
*
* tx_lock is held by the caller
*/
int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
{
struct sgdma_descrip __iomem *descbase =
(struct sgdma_descrip __iomem *)priv->tx_dma_desc;
struct sgdma_descrip __iomem *cdesc = &descbase[0];
struct sgdma_descrip __iomem *ndesc = &descbase[1];
/* wait 'til the tx sgdma is ready for the next transmit request */
if (sgdma_txbusy(priv))
return 0;
sgdma_setup_descrip(cdesc, /* current descriptor */
ndesc, /* next descriptor */
sgdma_txphysaddr(priv, ndesc),
buffer->dma_addr, /* address of packet to xmit */
0, /* write addr 0 for tx dma */
buffer->len, /* length of packet */
SGDMA_CONTROL_EOP, /* Generate EOP */
0, /* read fixed */
SGDMA_CONTROL_WR_FIXED); /* Generate SOP */
sgdma_async_write(priv, cdesc);
/* enqueue the request to the pending transmit queue */
queue_tx(priv, buffer);
return 1;
}
/* tx_lock held to protect access to queued tx list
*/
u32 sgdma_tx_completions(struct altera_tse_private *priv)
{
u32 ready = 0;
if (!sgdma_txbusy(priv) &&
((csrrd8(priv->tx_dma_desc, sgdma_descroffs(control))
& SGDMA_CONTROL_HW_OWNED) == 0) &&
(dequeue_tx(priv))) {
ready = 1;
}
return ready;
}
void sgdma_start_rxdma(struct altera_tse_private *priv)
{
sgdma_async_read(priv);
}
void sgdma_add_rx_desc(struct altera_tse_private *priv,
struct tse_buffer *rxbuffer)
{
queue_rx(priv, rxbuffer);
}
/* status is returned on upper 16 bits,
* length is returned in lower 16 bits
*/
u32 sgdma_rx_status(struct altera_tse_private *priv)
{
struct sgdma_descrip __iomem *base =
(struct sgdma_descrip __iomem *)priv->rx_dma_desc;
struct sgdma_descrip __iomem *desc = NULL;
struct tse_buffer *rxbuffer = NULL;
unsigned int rxstatus = 0;
u32 sts = csrrd32(priv->rx_dma_csr, sgdma_csroffs(status));
desc = &base[0];
if (sts & SGDMA_STSREG_EOP) {
unsigned int pktlength = 0;
unsigned int pktstatus = 0;
dma_sync_single_for_cpu(priv->device,
priv->rxdescphys,
priv->sgdmadesclen,
DMA_FROM_DEVICE);
pktlength = csrrd16(desc, sgdma_descroffs(bytes_xferred));
pktstatus = csrrd8(desc, sgdma_descroffs(status));
rxstatus = pktstatus & ~SGDMA_STATUS_EOP;
rxstatus = rxstatus << 16;
rxstatus |= (pktlength & 0xffff);
if (rxstatus) {
csrwr8(0, desc, sgdma_descroffs(status));
rxbuffer = dequeue_rx(priv);
if (rxbuffer == NULL)
netdev_info(priv->dev,
"sgdma rx and rx queue empty!\n");
/* Clear control */
csrwr32(0, priv->rx_dma_csr, sgdma_csroffs(control));
/* clear status */
csrwr32(0xf, priv->rx_dma_csr, sgdma_csroffs(status));
/* kick the rx sgdma after reaping this descriptor */
sgdma_async_read(priv);
} else {
/* If the SGDMA indicated an end of packet on recv,
* then it's expected that the rxstatus from the
* descriptor is non-zero - meaning a valid packet
* with a nonzero length, or an error has been
* indicated. if not, then all we can do is signal
* an error and return no packet received. Most likely
* there is a system design error, or an error in the
* underlying kernel (cache or cache management problem)
*/
netdev_err(priv->dev,
"SGDMA RX Error Info: %x, %x, %x\n",
sts, csrrd8(desc, sgdma_descroffs(status)),
rxstatus);
}
} else if (sts == 0) {
sgdma_async_read(priv);
}
return rxstatus;
}
/* Private functions */
static void sgdma_setup_descrip(struct sgdma_descrip __iomem *desc,
struct sgdma_descrip __iomem *ndesc,
dma_addr_t ndesc_phys,
dma_addr_t raddr,
dma_addr_t waddr,
u16 length,
int generate_eop,
int rfixed,
int wfixed)
{
/* Clear the next descriptor as not owned by hardware */
u32 ctrl = csrrd8(ndesc, sgdma_descroffs(control));
ctrl &= ~SGDMA_CONTROL_HW_OWNED;
csrwr8(ctrl, ndesc, sgdma_descroffs(control));
ctrl = SGDMA_CONTROL_HW_OWNED;
ctrl |= generate_eop;
ctrl |= rfixed;
ctrl |= wfixed;
/* Channel is implicitly zero, initialized to 0 by default */
csrwr32(lower_32_bits(raddr), desc, sgdma_descroffs(raddr));
csrwr32(lower_32_bits(waddr), desc, sgdma_descroffs(waddr));
csrwr32(0, desc, sgdma_descroffs(pad1));
csrwr32(0, desc, sgdma_descroffs(pad2));
csrwr32(lower_32_bits(ndesc_phys), desc, sgdma_descroffs(next));
csrwr8(ctrl, desc, sgdma_descroffs(control));
csrwr8(0, desc, sgdma_descroffs(status));
csrwr8(0, desc, sgdma_descroffs(wburst));
csrwr8(0, desc, sgdma_descroffs(rburst));
csrwr16(length, desc, sgdma_descroffs(bytes));
csrwr16(0, desc, sgdma_descroffs(bytes_xferred));
}
/* If hardware is busy, don't restart async read.
* if status register is 0 - meaning initial state, restart async read,
* probably for the first time when populating a receive buffer.
* If read status indicate not busy and a status, restart the async
* DMA read.
*/
static int sgdma_async_read(struct altera_tse_private *priv)
{
struct sgdma_descrip __iomem *descbase =
(struct sgdma_descrip __iomem *)priv->rx_dma_desc;
struct sgdma_descrip __iomem *cdesc = &descbase[0];
struct sgdma_descrip __iomem *ndesc = &descbase[1];
struct tse_buffer *rxbuffer = NULL;
if (!sgdma_rxbusy(priv)) {
rxbuffer = queue_rx_peekhead(priv);
if (rxbuffer == NULL) {
netdev_err(priv->dev, "no rx buffers available\n");
return 0;
}
sgdma_setup_descrip(cdesc, /* current descriptor */
ndesc, /* next descriptor */
sgdma_rxphysaddr(priv, ndesc),
0, /* read addr 0 for rx dma */
rxbuffer->dma_addr, /* write addr for rx dma */
0, /* read 'til EOP */
0, /* EOP: NA for rx dma */
0, /* read fixed: NA for rx dma */
0); /* SOP: NA for rx DMA */
dma_sync_single_for_device(priv->device,
priv->rxdescphys,
priv->sgdmadesclen,
DMA_TO_DEVICE);
csrwr32(lower_32_bits(sgdma_rxphysaddr(priv, cdesc)),
priv->rx_dma_csr,
sgdma_csroffs(next_descrip));
csrwr32((priv->rxctrlreg | SGDMA_CTRLREG_START),
priv->rx_dma_csr,
sgdma_csroffs(control));
return 1;
}
return 0;
}
static int sgdma_async_write(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc)
{
if (sgdma_txbusy(priv))
return 0;
/* clear control and status */
csrwr32(0, priv->tx_dma_csr, sgdma_csroffs(control));
csrwr32(0x1f, priv->tx_dma_csr, sgdma_csroffs(status));
dma_sync_single_for_device(priv->device, priv->txdescphys,
priv->sgdmadesclen, DMA_TO_DEVICE);
csrwr32(lower_32_bits(sgdma_txphysaddr(priv, desc)),
priv->tx_dma_csr,
sgdma_csroffs(next_descrip));
csrwr32((priv->txctrlreg | SGDMA_CTRLREG_START),
priv->tx_dma_csr,
sgdma_csroffs(control));
return 1;
}
static dma_addr_t
sgdma_txphysaddr(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc)
{
dma_addr_t paddr = priv->txdescmem_busaddr;
uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->tx_dma_desc;
return (dma_addr_t)((uintptr_t)paddr + offs);
}
static dma_addr_t
sgdma_rxphysaddr(struct altera_tse_private *priv,
struct sgdma_descrip __iomem *desc)
{
dma_addr_t paddr = priv->rxdescmem_busaddr;
uintptr_t offs = (uintptr_t)desc - (uintptr_t)priv->rx_dma_desc;
return (dma_addr_t)((uintptr_t)paddr + offs);
}
#define list_remove_head(list, entry, type, member) \
do { \
entry = NULL; \
if (!list_empty(list)) { \
entry = list_entry((list)->next, type, member); \
list_del_init(&entry->member); \
} \
} while (0)
#define list_peek_head(list, entry, type, member) \
do { \
entry = NULL; \
if (!list_empty(list)) { \
entry = list_entry((list)->next, type, member); \
} \
} while (0)
/* adds a tse_buffer to the tail of a tx buffer list.
* assumes the caller is managing and holding a mutual exclusion
* primitive to avoid simultaneous pushes/pops to the list.
*/
static void
queue_tx(struct altera_tse_private *priv, struct tse_buffer *buffer)
{
list_add_tail(&buffer->lh, &priv->txlisthd);
}
/* adds a tse_buffer to the tail of a rx buffer list
* assumes the caller is managing and holding a mutual exclusion
* primitive to avoid simultaneous pushes/pops to the list.
*/
static void
queue_rx(struct altera_tse_private *priv, struct tse_buffer *buffer)
{
list_add_tail(&buffer->lh, &priv->rxlisthd);
}
/* dequeues a tse_buffer from the transmit buffer list, otherwise
* returns NULL if empty.
* assumes the caller is managing and holding a mutual exclusion
* primitive to avoid simultaneous pushes/pops to the list.
*/
static struct tse_buffer *
dequeue_tx(struct altera_tse_private *priv)
{
struct tse_buffer *buffer = NULL;
list_remove_head(&priv->txlisthd, buffer, struct tse_buffer, lh);
return buffer;
}
/* dequeues a tse_buffer from the receive buffer list, otherwise
* returns NULL if empty
* assumes the caller is managing and holding a mutual exclusion
* primitive to avoid simultaneous pushes/pops to the list.
*/
static struct tse_buffer *
dequeue_rx(struct altera_tse_private *priv)
{
struct tse_buffer *buffer = NULL;
list_remove_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
return buffer;
}
/* dequeues a tse_buffer from the receive buffer list, otherwise
* returns NULL if empty
* assumes the caller is managing and holding a mutual exclusion
* primitive to avoid simultaneous pushes/pops to the list while the
* head is being examined.
*/
static struct tse_buffer *
queue_rx_peekhead(struct altera_tse_private *priv)
{
struct tse_buffer *buffer = NULL;
list_peek_head(&priv->rxlisthd, buffer, struct tse_buffer, lh);
return buffer;
}
/* check and return rx sgdma status without polling
*/
static int sgdma_rxbusy(struct altera_tse_private *priv)
{
return csrrd32(priv->rx_dma_csr, sgdma_csroffs(status))
& SGDMA_STSREG_BUSY;
}
/* waits for the tx sgdma to finish it's current operation, returns 0
* when it transitions to nonbusy, returns 1 if the operation times out
*/
static int sgdma_txbusy(struct altera_tse_private *priv)
{
int delay = 0;
/* if DMA is busy, wait for current transactino to finish */
while ((csrrd32(priv->tx_dma_csr, sgdma_csroffs(status))
& SGDMA_STSREG_BUSY) && (delay++ < 100))
udelay(1);
if (csrrd32(priv->tx_dma_csr, sgdma_csroffs(status))
& SGDMA_STSREG_BUSY) {
netdev_err(priv->dev, "timeout waiting for tx dma\n");
return 1;
}
return 0;
}

View file

@ -0,0 +1,36 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ALTERA_SGDMA_H__
#define __ALTERA_SGDMA_H__
void sgdma_reset(struct altera_tse_private *);
void sgdma_enable_txirq(struct altera_tse_private *);
void sgdma_enable_rxirq(struct altera_tse_private *);
void sgdma_disable_rxirq(struct altera_tse_private *);
void sgdma_disable_txirq(struct altera_tse_private *);
void sgdma_clear_rxirq(struct altera_tse_private *);
void sgdma_clear_txirq(struct altera_tse_private *);
int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *);
u32 sgdma_tx_completions(struct altera_tse_private *);
void sgdma_add_rx_desc(struct altera_tse_private *priv, struct tse_buffer *);
void sgdma_status(struct altera_tse_private *);
u32 sgdma_rx_status(struct altera_tse_private *);
int sgdma_initialize(struct altera_tse_private *);
void sgdma_uninitialize(struct altera_tse_private *);
void sgdma_start_rxdma(struct altera_tse_private *);
#endif /* __ALTERA_SGDMA_H__ */

View file

@ -0,0 +1,126 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ALTERA_SGDMAHW_H__
#define __ALTERA_SGDMAHW_H__
/* SGDMA descriptor structure */
struct sgdma_descrip {
u32 raddr; /* address of data to be read */
u32 pad1;
u32 waddr;
u32 pad2;
u32 next;
u32 pad3;
u16 bytes;
u8 rburst;
u8 wburst;
u16 bytes_xferred; /* 16 bits, bytes xferred */
/* bit 0: error
* bit 1: length error
* bit 2: crc error
* bit 3: truncated error
* bit 4: phy error
* bit 5: collision error
* bit 6: reserved
* bit 7: status eop for recv case
*/
u8 status;
/* bit 0: eop
* bit 1: read_fixed
* bit 2: write fixed
* bits 3,4,5,6: Channel (always 0)
* bit 7: hardware owned
*/
u8 control;
} __packed;
#define SGDMA_STATUS_ERR BIT(0)
#define SGDMA_STATUS_LENGTH_ERR BIT(1)
#define SGDMA_STATUS_CRC_ERR BIT(2)
#define SGDMA_STATUS_TRUNC_ERR BIT(3)
#define SGDMA_STATUS_PHY_ERR BIT(4)
#define SGDMA_STATUS_COLL_ERR BIT(5)
#define SGDMA_STATUS_EOP BIT(7)
#define SGDMA_CONTROL_EOP BIT(0)
#define SGDMA_CONTROL_RD_FIXED BIT(1)
#define SGDMA_CONTROL_WR_FIXED BIT(2)
/* Channel is always 0, so just zero initialize it */
#define SGDMA_CONTROL_HW_OWNED BIT(7)
/* SGDMA register space */
struct sgdma_csr {
/* bit 0: error
* bit 1: eop
* bit 2: descriptor completed
* bit 3: chain completed
* bit 4: busy
* remainder reserved
*/
u32 status;
u32 pad1[3];
/* bit 0: interrupt on error
* bit 1: interrupt on eop
* bit 2: interrupt after every descriptor
* bit 3: interrupt after last descrip in a chain
* bit 4: global interrupt enable
* bit 5: starts descriptor processing
* bit 6: stop core on dma error
* bit 7: interrupt on max descriptors
* bits 8-15: max descriptors to generate interrupt
* bit 16: Software reset
* bit 17: clears owned by hardware if 0, does not clear otherwise
* bit 18: enables descriptor polling mode
* bit 19-26: clocks before polling again
* bit 27-30: reserved
* bit 31: clear interrupt
*/
u32 control;
u32 pad2[3];
u32 next_descrip;
u32 pad3[3];
};
#define sgdma_csroffs(a) (offsetof(struct sgdma_csr, a))
#define sgdma_descroffs(a) (offsetof(struct sgdma_descrip, a))
#define SGDMA_STSREG_ERR BIT(0) /* Error */
#define SGDMA_STSREG_EOP BIT(1) /* EOP */
#define SGDMA_STSREG_DESCRIP BIT(2) /* Descriptor completed */
#define SGDMA_STSREG_CHAIN BIT(3) /* Chain completed */
#define SGDMA_STSREG_BUSY BIT(4) /* Controller busy */
#define SGDMA_CTRLREG_IOE BIT(0) /* Interrupt on error */
#define SGDMA_CTRLREG_IOEOP BIT(1) /* Interrupt on EOP */
#define SGDMA_CTRLREG_IDESCRIP BIT(2) /* Interrupt after every descriptor */
#define SGDMA_CTRLREG_ILASTD BIT(3) /* Interrupt after last descriptor */
#define SGDMA_CTRLREG_INTEN BIT(4) /* Global Interrupt enable */
#define SGDMA_CTRLREG_START BIT(5) /* starts descriptor processing */
#define SGDMA_CTRLREG_STOPERR BIT(6) /* stop on dma error */
#define SGDMA_CTRLREG_INTMAX BIT(7) /* Interrupt on max descriptors */
#define SGDMA_CTRLREG_RESET BIT(16)/* Software reset */
#define SGDMA_CTRLREG_COBHW BIT(17)/* Clears owned by hardware */
#define SGDMA_CTRLREG_POLL BIT(18)/* enables descriptor polling mode */
#define SGDMA_CTRLREG_CLRINT BIT(31)/* Clears interrupt */
#endif /* __ALTERA_SGDMAHW_H__ */

View file

@ -0,0 +1,537 @@
/* Altera Triple-Speed Ethernet MAC driver
* Copyright (C) 2008-2014 Altera Corporation. All rights reserved
*
* Contributors:
* Dalon Westergreen
* Thomas Chou
* Ian Abbott
* Yuriy Kozlov
* Tobias Klauser
* Andriy Smolskyy
* Roman Bulgakov
* Dmytro Mytarchuk
* Matthew Gerlach
*
* Original driver contributed by SLS.
* Major updates contributed by GlobalLogic
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ALTERA_TSE_H__
#define __ALTERA_TSE_H__
#define ALTERA_TSE_RESOURCE_NAME "altera_tse"
#include <linux/bitops.h>
#include <linux/if_vlan.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000
#define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in
* bytes
*/
/* Rx FIFO default settings */
#define ALTERA_TSE_RX_SECTION_EMPTY 16
#define ALTERA_TSE_RX_SECTION_FULL 0
#define ALTERA_TSE_RX_ALMOST_EMPTY 8
#define ALTERA_TSE_RX_ALMOST_FULL 8
/* Tx FIFO default settings */
#define ALTERA_TSE_TX_SECTION_EMPTY 16
#define ALTERA_TSE_TX_SECTION_FULL 0
#define ALTERA_TSE_TX_ALMOST_EMPTY 8
#define ALTERA_TSE_TX_ALMOST_FULL 3
/* MAC function configuration default settings */
#define ALTERA_TSE_TX_IPG_LENGTH 12
#define ALTERA_TSE_PAUSE_QUANTA 0xffff
#define GET_BIT_VALUE(v, bit) (((v) >> (bit)) & 0x1)
/* MAC Command_Config Register Bit Definitions
*/
#define MAC_CMDCFG_TX_ENA BIT(0)
#define MAC_CMDCFG_RX_ENA BIT(1)
#define MAC_CMDCFG_XON_GEN BIT(2)
#define MAC_CMDCFG_ETH_SPEED BIT(3)
#define MAC_CMDCFG_PROMIS_EN BIT(4)
#define MAC_CMDCFG_PAD_EN BIT(5)
#define MAC_CMDCFG_CRC_FWD BIT(6)
#define MAC_CMDCFG_PAUSE_FWD BIT(7)
#define MAC_CMDCFG_PAUSE_IGNORE BIT(8)
#define MAC_CMDCFG_TX_ADDR_INS BIT(9)
#define MAC_CMDCFG_HD_ENA BIT(10)
#define MAC_CMDCFG_EXCESS_COL BIT(11)
#define MAC_CMDCFG_LATE_COL BIT(12)
#define MAC_CMDCFG_SW_RESET BIT(13)
#define MAC_CMDCFG_MHASH_SEL BIT(14)
#define MAC_CMDCFG_LOOP_ENA BIT(15)
#define MAC_CMDCFG_TX_ADDR_SEL(v) (((v) & 0x7) << 16)
#define MAC_CMDCFG_MAGIC_ENA BIT(19)
#define MAC_CMDCFG_SLEEP BIT(20)
#define MAC_CMDCFG_WAKEUP BIT(21)
#define MAC_CMDCFG_XOFF_GEN BIT(22)
#define MAC_CMDCFG_CNTL_FRM_ENA BIT(23)
#define MAC_CMDCFG_NO_LGTH_CHECK BIT(24)
#define MAC_CMDCFG_ENA_10 BIT(25)
#define MAC_CMDCFG_RX_ERR_DISC BIT(26)
#define MAC_CMDCFG_DISABLE_READ_TIMEOUT BIT(27)
#define MAC_CMDCFG_CNT_RESET BIT(31)
#define MAC_CMDCFG_TX_ENA_GET(v) GET_BIT_VALUE(v, 0)
#define MAC_CMDCFG_RX_ENA_GET(v) GET_BIT_VALUE(v, 1)
#define MAC_CMDCFG_XON_GEN_GET(v) GET_BIT_VALUE(v, 2)
#define MAC_CMDCFG_ETH_SPEED_GET(v) GET_BIT_VALUE(v, 3)
#define MAC_CMDCFG_PROMIS_EN_GET(v) GET_BIT_VALUE(v, 4)
#define MAC_CMDCFG_PAD_EN_GET(v) GET_BIT_VALUE(v, 5)
#define MAC_CMDCFG_CRC_FWD_GET(v) GET_BIT_VALUE(v, 6)
#define MAC_CMDCFG_PAUSE_FWD_GET(v) GET_BIT_VALUE(v, 7)
#define MAC_CMDCFG_PAUSE_IGNORE_GET(v) GET_BIT_VALUE(v, 8)
#define MAC_CMDCFG_TX_ADDR_INS_GET(v) GET_BIT_VALUE(v, 9)
#define MAC_CMDCFG_HD_ENA_GET(v) GET_BIT_VALUE(v, 10)
#define MAC_CMDCFG_EXCESS_COL_GET(v) GET_BIT_VALUE(v, 11)
#define MAC_CMDCFG_LATE_COL_GET(v) GET_BIT_VALUE(v, 12)
#define MAC_CMDCFG_SW_RESET_GET(v) GET_BIT_VALUE(v, 13)
#define MAC_CMDCFG_MHASH_SEL_GET(v) GET_BIT_VALUE(v, 14)
#define MAC_CMDCFG_LOOP_ENA_GET(v) GET_BIT_VALUE(v, 15)
#define MAC_CMDCFG_TX_ADDR_SEL_GET(v) (((v) >> 16) & 0x7)
#define MAC_CMDCFG_MAGIC_ENA_GET(v) GET_BIT_VALUE(v, 19)
#define MAC_CMDCFG_SLEEP_GET(v) GET_BIT_VALUE(v, 20)
#define MAC_CMDCFG_WAKEUP_GET(v) GET_BIT_VALUE(v, 21)
#define MAC_CMDCFG_XOFF_GEN_GET(v) GET_BIT_VALUE(v, 22)
#define MAC_CMDCFG_CNTL_FRM_ENA_GET(v) GET_BIT_VALUE(v, 23)
#define MAC_CMDCFG_NO_LGTH_CHECK_GET(v) GET_BIT_VALUE(v, 24)
#define MAC_CMDCFG_ENA_10_GET(v) GET_BIT_VALUE(v, 25)
#define MAC_CMDCFG_RX_ERR_DISC_GET(v) GET_BIT_VALUE(v, 26)
#define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27)
#define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31)
/* MDIO registers within MAC register Space
*/
struct altera_tse_mdio {
u32 control; /* PHY device operation control register */
u32 status; /* PHY device operation status register */
u32 phy_id1; /* Bits 31:16 of PHY identifier */
u32 phy_id2; /* Bits 15:0 of PHY identifier */
u32 auto_negotiation_advertisement; /* Auto-negotiation
* advertisement
* register
*/
u32 remote_partner_base_page_ability;
u32 reg6;
u32 reg7;
u32 reg8;
u32 reg9;
u32 rega;
u32 regb;
u32 regc;
u32 regd;
u32 rege;
u32 regf;
u32 reg10;
u32 reg11;
u32 reg12;
u32 reg13;
u32 reg14;
u32 reg15;
u32 reg16;
u32 reg17;
u32 reg18;
u32 reg19;
u32 reg1a;
u32 reg1b;
u32 reg1c;
u32 reg1d;
u32 reg1e;
u32 reg1f;
};
/* MAC register Space. Note that some of these registers may or may not be
* present depending upon options chosen by the user when the core was
* configured and built. Please consult the Altera Triple Speed Ethernet User
* Guide for details.
*/
struct altera_tse_mac {
/* Bits 15:0: MegaCore function revision (0x0800). Bit 31:16: Customer
* specific revision
*/
u32 megacore_revision;
/* Provides a memory location for user applications to test the device
* memory operation.
*/
u32 scratch_pad;
/* The host processor uses this register to control and configure the
* MAC block
*/
u32 command_config;
/* 32-bit primary MAC address word 0 bits 0 to 31 of the primary
* MAC address
*/
u32 mac_addr_0;
/* 32-bit primary MAC address word 1 bits 32 to 47 of the primary
* MAC address
*/
u32 mac_addr_1;
/* 14-bit maximum frame length. The MAC receive logic */
u32 frm_length;
/* The pause quanta is used in each pause frame sent to a remote
* Ethernet device, in increments of 512 Ethernet bit times
*/
u32 pause_quanta;
/* 12-bit receive FIFO section-empty threshold */
u32 rx_section_empty;
/* 12-bit receive FIFO section-full threshold */
u32 rx_section_full;
/* 12-bit transmit FIFO section-empty threshold */
u32 tx_section_empty;
/* 12-bit transmit FIFO section-full threshold */
u32 tx_section_full;
/* 12-bit receive FIFO almost-empty threshold */
u32 rx_almost_empty;
/* 12-bit receive FIFO almost-full threshold */
u32 rx_almost_full;
/* 12-bit transmit FIFO almost-empty threshold */
u32 tx_almost_empty;
/* 12-bit transmit FIFO almost-full threshold */
u32 tx_almost_full;
/* MDIO address of PHY Device 0. Bits 0 to 4 hold a 5-bit PHY address */
u32 mdio_phy0_addr;
/* MDIO address of PHY Device 1. Bits 0 to 4 hold a 5-bit PHY address */
u32 mdio_phy1_addr;
/* Bit[15:0]—16-bit holdoff quanta */
u32 holdoff_quant;
/* only if 100/1000 BaseX PCS, reserved otherwise */
u32 reserved1[5];
/* Minimum IPG between consecutive transmit frame in terms of bytes */
u32 tx_ipg_length;
/* IEEE 802.3 oEntity Managed Object Support */
/* The MAC addresses */
u32 mac_id_1;
u32 mac_id_2;
/* Number of frames transmitted without error including pause frames */
u32 frames_transmitted_ok;
/* Number of frames received without error including pause frames */
u32 frames_received_ok;
/* Number of frames received with a CRC error */
u32 frames_check_sequence_errors;
/* Frame received with an alignment error */
u32 alignment_errors;
/* Sum of payload and padding octets of frames transmitted without
* error
*/
u32 octets_transmitted_ok;
/* Sum of payload and padding octets of frames received without error */
u32 octets_received_ok;
/* IEEE 802.3 oPausedEntity Managed Object Support */
/* Number of transmitted pause frames */
u32 tx_pause_mac_ctrl_frames;
/* Number of Received pause frames */
u32 rx_pause_mac_ctrl_frames;
/* IETF MIB (MIB-II) Object Support */
/* Number of frames received with error */
u32 if_in_errors;
/* Number of frames transmitted with error */
u32 if_out_errors;
/* Number of valid received unicast frames */
u32 if_in_ucast_pkts;
/* Number of valid received multicasts frames (without pause) */
u32 if_in_multicast_pkts;
/* Number of valid received broadcast frames */
u32 if_in_broadcast_pkts;
u32 if_out_discards;
/* The number of valid unicast frames transmitted */
u32 if_out_ucast_pkts;
/* The number of valid multicast frames transmitted,
* excluding pause frames
*/
u32 if_out_multicast_pkts;
u32 if_out_broadcast_pkts;
/* IETF RMON MIB Object Support */
/* Counts the number of dropped packets due to internal errors
* of the MAC client.
*/
u32 ether_stats_drop_events;
/* Total number of bytes received. Good and bad frames. */
u32 ether_stats_octets;
/* Total number of packets received. Counts good and bad packets. */
u32 ether_stats_pkts;
/* Number of packets received with less than 64 bytes. */
u32 ether_stats_undersize_pkts;
/* The number of frames received that are longer than the
* value configured in the frm_length register
*/
u32 ether_stats_oversize_pkts;
/* Number of received packet with 64 bytes */
u32 ether_stats_pkts_64_octets;
/* Frames (good and bad) with 65 to 127 bytes */
u32 ether_stats_pkts_65to127_octets;
/* Frames (good and bad) with 128 to 255 bytes */
u32 ether_stats_pkts_128to255_octets;
/* Frames (good and bad) with 256 to 511 bytes */
u32 ether_stats_pkts_256to511_octets;
/* Frames (good and bad) with 512 to 1023 bytes */
u32 ether_stats_pkts_512to1023_octets;
/* Frames (good and bad) with 1024 to 1518 bytes */
u32 ether_stats_pkts_1024to1518_octets;
/* Any frame length from 1519 to the maximum length configured in the
* frm_length register, if it is greater than 1518
*/
u32 ether_stats_pkts_1519tox_octets;
/* Too long frames with CRC error */
u32 ether_stats_jabbers;
/* Too short frames with CRC error */
u32 ether_stats_fragments;
u32 reserved2;
/* FIFO control register */
u32 tx_cmd_stat;
u32 rx_cmd_stat;
/* Extended Statistics Counters */
u32 msb_octets_transmitted_ok;
u32 msb_octets_received_ok;
u32 msb_ether_stats_octets;
u32 reserved3;
/* Multicast address resolution table, mapped in the controller address
* space
*/
u32 hash_table[64];
/* Registers 0 to 31 within PHY device 0/1 connected to the MDIO PHY
* management interface
*/
struct altera_tse_mdio mdio_phy0;
struct altera_tse_mdio mdio_phy1;
/* 4 Supplemental MAC Addresses */
u32 supp_mac_addr_0_0;
u32 supp_mac_addr_0_1;
u32 supp_mac_addr_1_0;
u32 supp_mac_addr_1_1;
u32 supp_mac_addr_2_0;
u32 supp_mac_addr_2_1;
u32 supp_mac_addr_3_0;
u32 supp_mac_addr_3_1;
u32 reserved4[8];
/* IEEE 1588v2 Feature */
u32 tx_period;
u32 tx_adjust_fns;
u32 tx_adjust_ns;
u32 rx_period;
u32 rx_adjust_fns;
u32 rx_adjust_ns;
u32 reserved5[42];
};
#define tse_csroffs(a) (offsetof(struct altera_tse_mac, a))
/* Transmit and Receive Command Registers Bit Definitions
*/
#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC BIT(17)
#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 BIT(18)
#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 BIT(25)
/* Wrapper around a pointer to a socket buffer,
* so a DMA handle can be stored along with the buffer
*/
struct tse_buffer {
struct list_head lh;
struct sk_buff *skb;
dma_addr_t dma_addr;
u32 len;
int mapped_as_page;
};
struct altera_tse_private;
#define ALTERA_DTYPE_SGDMA 1
#define ALTERA_DTYPE_MSGDMA 2
/* standard DMA interface for SGDMA and MSGDMA */
struct altera_dmaops {
int altera_dtype;
int dmamask;
void (*reset_dma)(struct altera_tse_private *);
void (*enable_txirq)(struct altera_tse_private *);
void (*enable_rxirq)(struct altera_tse_private *);
void (*disable_txirq)(struct altera_tse_private *);
void (*disable_rxirq)(struct altera_tse_private *);
void (*clear_txirq)(struct altera_tse_private *);
void (*clear_rxirq)(struct altera_tse_private *);
int (*tx_buffer)(struct altera_tse_private *, struct tse_buffer *);
u32 (*tx_completions)(struct altera_tse_private *);
void (*add_rx_desc)(struct altera_tse_private *, struct tse_buffer *);
u32 (*get_rx_status)(struct altera_tse_private *);
int (*init_dma)(struct altera_tse_private *);
void (*uninit_dma)(struct altera_tse_private *);
void (*start_rxdma)(struct altera_tse_private *);
};
/* This structure is private to each device.
*/
struct altera_tse_private {
struct net_device *dev;
struct device *device;
struct napi_struct napi;
/* MAC address space */
struct altera_tse_mac __iomem *mac_dev;
/* TSE Revision */
u32 revision;
/* mSGDMA Rx Dispatcher address space */
void __iomem *rx_dma_csr;
void __iomem *rx_dma_desc;
void __iomem *rx_dma_resp;
/* mSGDMA Tx Dispatcher address space */
void __iomem *tx_dma_csr;
void __iomem *tx_dma_desc;
/* Rx buffers queue */
struct tse_buffer *rx_ring;
u32 rx_cons;
u32 rx_prod;
u32 rx_ring_size;
u32 rx_dma_buf_sz;
/* Tx ring buffer */
struct tse_buffer *tx_ring;
u32 tx_prod;
u32 tx_cons;
u32 tx_ring_size;
/* Interrupts */
u32 tx_irq;
u32 rx_irq;
/* RX/TX MAC FIFO configs */
u32 tx_fifo_depth;
u32 rx_fifo_depth;
u32 max_mtu;
/* Hash filter settings */
u32 hash_filter;
u32 added_unicast;
/* Descriptor memory info for managing SGDMA */
u32 txdescmem;
u32 rxdescmem;
dma_addr_t rxdescmem_busaddr;
dma_addr_t txdescmem_busaddr;
u32 txctrlreg;
u32 rxctrlreg;
dma_addr_t rxdescphys;
dma_addr_t txdescphys;
size_t sgdmadesclen;
struct list_head txlisthd;
struct list_head rxlisthd;
/* MAC command_config register protection */
spinlock_t mac_cfg_lock;
/* Tx path protection */
spinlock_t tx_lock;
/* Rx DMA & interrupt control protection */
spinlock_t rxdma_irq_lock;
/* PHY */
int phy_addr; /* PHY's MDIO address, -1 for autodetection */
phy_interface_t phy_iface;
struct mii_bus *mdio;
struct phy_device *phydev;
int oldspeed;
int oldduplex;
int oldlink;
/* ethtool msglvl option */
u32 msg_enable;
struct altera_dmaops *dmaops;
};
/* Function prototypes
*/
void altera_tse_set_ethtool_ops(struct net_device *);
static inline
u32 csrrd32(void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
return readl(paddr);
}
static inline
u16 csrrd16(void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
return readw(paddr);
}
static inline
u8 csrrd8(void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
return readb(paddr);
}
static inline
void csrwr32(u32 val, void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
writel(val, paddr);
}
static inline
void csrwr16(u16 val, void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
writew(val, paddr);
}
static inline
void csrwr8(u8 val, void __iomem *mac, size_t offs)
{
void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
writeb(val, paddr);
}
#endif /* __ALTERA_TSE_H__ */

View file

@ -0,0 +1,275 @@
/* Ethtool support for Altera Triple-Speed Ethernet MAC driver
* Copyright (C) 2008-2014 Altera Corporation. All rights reserved
*
* Contributors:
* Dalon Westergreen
* Thomas Chou
* Ian Abbott
* Yuriy Kozlov
* Tobias Klauser
* Andriy Smolskyy
* Roman Bulgakov
* Dmytro Mytarchuk
*
* Original driver contributed by SLS.
* Major updates contributed by GlobalLogic
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/ethtool.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "altera_tse.h"
#define TSE_STATS_LEN 31
#define TSE_NUM_REGS 128
static char const stat_gstrings[][ETH_GSTRING_LEN] = {
"tx_packets",
"rx_packets",
"rx_crc_errors",
"rx_align_errors",
"tx_bytes",
"rx_bytes",
"tx_pause",
"rx_pause",
"rx_errors",
"tx_errors",
"rx_unicast",
"rx_multicast",
"rx_broadcast",
"tx_discards",
"tx_unicast",
"tx_multicast",
"tx_broadcast",
"ether_drops",
"rx_total_bytes",
"rx_total_packets",
"rx_undersize",
"rx_oversize",
"rx_64_bytes",
"rx_65_127_bytes",
"rx_128_255_bytes",
"rx_256_511_bytes",
"rx_512_1023_bytes",
"rx_1024_1518_bytes",
"rx_gte_1519_bytes",
"rx_jabbers",
"rx_runts",
};
static void tse_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct altera_tse_private *priv = netdev_priv(dev);
u32 rev = ioread32(&priv->mac_dev->megacore_revision);
strcpy(info->driver, "altera_tse");
strcpy(info->version, "v8.0");
snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "v%d.%d",
rev & 0xFFFF, (rev & 0xFFFF0000) >> 16);
sprintf(info->bus_info, "platform");
}
/* Fill in a buffer with the strings which correspond to the
* stats
*/
static void tse_gstrings(struct net_device *dev, u32 stringset, u8 *buf)
{
memcpy(buf, stat_gstrings, TSE_STATS_LEN * ETH_GSTRING_LEN);
}
static void tse_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
u64 *buf)
{
struct altera_tse_private *priv = netdev_priv(dev);
u64 ext;
buf[0] = csrrd32(priv->mac_dev,
tse_csroffs(frames_transmitted_ok));
buf[1] = csrrd32(priv->mac_dev,
tse_csroffs(frames_received_ok));
buf[2] = csrrd32(priv->mac_dev,
tse_csroffs(frames_check_sequence_errors));
buf[3] = csrrd32(priv->mac_dev,
tse_csroffs(alignment_errors));
/* Extended aOctetsTransmittedOK counter */
ext = (u64) csrrd32(priv->mac_dev,
tse_csroffs(msb_octets_transmitted_ok)) << 32;
ext |= csrrd32(priv->mac_dev,
tse_csroffs(octets_transmitted_ok));
buf[4] = ext;
/* Extended aOctetsReceivedOK counter */
ext = (u64) csrrd32(priv->mac_dev,
tse_csroffs(msb_octets_received_ok)) << 32;
ext |= csrrd32(priv->mac_dev,
tse_csroffs(octets_received_ok));
buf[5] = ext;
buf[6] = csrrd32(priv->mac_dev,
tse_csroffs(tx_pause_mac_ctrl_frames));
buf[7] = csrrd32(priv->mac_dev,
tse_csroffs(rx_pause_mac_ctrl_frames));
buf[8] = csrrd32(priv->mac_dev,
tse_csroffs(if_in_errors));
buf[9] = csrrd32(priv->mac_dev,
tse_csroffs(if_out_errors));
buf[10] = csrrd32(priv->mac_dev,
tse_csroffs(if_in_ucast_pkts));
buf[11] = csrrd32(priv->mac_dev,
tse_csroffs(if_in_multicast_pkts));
buf[12] = csrrd32(priv->mac_dev,
tse_csroffs(if_in_broadcast_pkts));
buf[13] = csrrd32(priv->mac_dev,
tse_csroffs(if_out_discards));
buf[14] = csrrd32(priv->mac_dev,
tse_csroffs(if_out_ucast_pkts));
buf[15] = csrrd32(priv->mac_dev,
tse_csroffs(if_out_multicast_pkts));
buf[16] = csrrd32(priv->mac_dev,
tse_csroffs(if_out_broadcast_pkts));
buf[17] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_drop_events));
/* Extended etherStatsOctets counter */
ext = (u64) csrrd32(priv->mac_dev,
tse_csroffs(msb_ether_stats_octets)) << 32;
ext |= csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_octets));
buf[18] = ext;
buf[19] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts));
buf[20] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_undersize_pkts));
buf[21] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_oversize_pkts));
buf[22] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_64_octets));
buf[23] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_65to127_octets));
buf[24] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_128to255_octets));
buf[25] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_256to511_octets));
buf[26] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_512to1023_octets));
buf[27] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_1024to1518_octets));
buf[28] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_pkts_1519tox_octets));
buf[29] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_jabbers));
buf[30] = csrrd32(priv->mac_dev,
tse_csroffs(ether_stats_fragments));
}
static int tse_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return TSE_STATS_LEN;
default:
return -EOPNOTSUPP;
}
}
static u32 tse_get_msglevel(struct net_device *dev)
{
struct altera_tse_private *priv = netdev_priv(dev);
return priv->msg_enable;
}
static void tse_set_msglevel(struct net_device *dev, uint32_t data)
{
struct altera_tse_private *priv = netdev_priv(dev);
priv->msg_enable = data;
}
static int tse_reglen(struct net_device *dev)
{
return TSE_NUM_REGS * sizeof(u32);
}
static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *regbuf)
{
int i;
struct altera_tse_private *priv = netdev_priv(dev);
u32 *buf = regbuf;
/* Set version to a known value, so ethtool knows
* how to do any special formatting of this data.
* This version number will need to change if and
* when this register table is changed.
*
* version[31:0] = 1: Dump the first 128 TSE Registers
* Upper bits are all 0 by default
*
* Upper 16-bits will indicate feature presence for
* Ethtool register decoding in future version.
*/
regs->version = 1;
for (i = 0; i < TSE_NUM_REGS; i++)
buf[i] = csrrd32(priv->mac_dev, i * 4);
}
static int tse_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct altera_tse_private *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
if (phydev == NULL)
return -ENODEV;
return phy_ethtool_gset(phydev, cmd);
}
static int tse_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct altera_tse_private *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
if (phydev == NULL)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
static const struct ethtool_ops tse_ethtool_ops = {
.get_drvinfo = tse_get_drvinfo,
.get_regs_len = tse_reglen,
.get_regs = tse_get_regs,
.get_link = ethtool_op_get_link,
.get_settings = tse_get_settings,
.set_settings = tse_set_settings,
.get_strings = tse_gstrings,
.get_sset_count = tse_sset_count,
.get_ethtool_stats = tse_fill_stats,
.get_msglevel = tse_get_msglevel,
.set_msglevel = tse_set_msglevel,
};
void altera_tse_set_ethtool_ops(struct net_device *netdev)
{
netdev->ethtool_ops = &tse_ethtool_ops;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "altera_tse.h"
#include "altera_utils.h"
void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask)
{
u32 value = csrrd32(ioaddr, offs);
value |= bit_mask;
csrwr32(value, ioaddr, offs);
}
void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask)
{
u32 value = csrrd32(ioaddr, offs);
value &= ~bit_mask;
csrwr32(value, ioaddr, offs);
}
int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask)
{
u32 value = csrrd32(ioaddr, offs);
return (value & bit_mask) ? 1 : 0;
}
int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask)
{
u32 value = csrrd32(ioaddr, offs);
return (value & bit_mask) ? 0 : 1;
}

View file

@ -0,0 +1,27 @@
/* Altera TSE SGDMA and MSGDMA Linux driver
* Copyright (C) 2014 Altera Corporation. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#ifndef __ALTERA_UTILS_H__
#define __ALTERA_UTILS_H__
void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask);
int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask);
#endif /* __ALTERA_UTILS_H__*/

View file

@ -0,0 +1,666 @@
/*
* 7990.c -- LANCE ethernet IC generic routines.
* This is an attempt to separate out the bits of various ethernet
* drivers that are common because they all use the AMD 7990 LANCE
* (Local Area Network Controller for Ethernet) chip.
*
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
*
* Most of this stuff was obtained by looking at other LANCE drivers,
* in particular a2065.[ch]. The AMD C-LANCE datasheet was also helpful.
* NB: this was made easy by the fact that Jes Sorensen had cleaned up
* most of a2025 and sunlance with the aim of merging them, so the
* common code was pretty obvious.
*/
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/route.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <asm/irq.h>
/* Used for the temporal inet entries and routing */
#include <linux/socket.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/pgtable.h>
#ifdef CONFIG_HP300
#include <asm/blinken.h>
#endif
#include "7990.h"
#define WRITERAP(lp, x) out_be16(lp->base + LANCE_RAP, (x))
#define WRITERDP(lp, x) out_be16(lp->base + LANCE_RDP, (x))
#define READRDP(lp) in_be16(lp->base + LANCE_RDP)
#if defined(CONFIG_HPLANCE) || defined(CONFIG_HPLANCE_MODULE)
#include "hplance.h"
#undef WRITERAP
#undef WRITERDP
#undef READRDP
#if defined(CONFIG_MVME147_NET) || defined(CONFIG_MVME147_NET_MODULE)
/* Lossage Factor Nine, Mr Sulu. */
#define WRITERAP(lp, x) (lp->writerap(lp, x))
#define WRITERDP(lp, x) (lp->writerdp(lp, x))
#define READRDP(lp) (lp->readrdp(lp))
#else
/* These inlines can be used if only CONFIG_HPLANCE is defined */
static inline void WRITERAP(struct lance_private *lp, __u16 value)
{
do {
out_be16(lp->base + HPLANCE_REGOFF + LANCE_RAP, value);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
}
static inline void WRITERDP(struct lance_private *lp, __u16 value)
{
do {
out_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP, value);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
}
static inline __u16 READRDP(struct lance_private *lp)
{
__u16 value;
do {
value = in_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
return value;
}
#endif
#endif /* CONFIG_HPLANCE || CONFIG_HPLANCE_MODULE */
/* debugging output macros, various flavours */
/* #define TEST_HITS */
#ifdef UNDEF
#define PRINT_RINGS() \
do { \
int t; \
for (t = 0; t < RX_RING_SIZE; t++) { \
printk("R%d: @(%02X %04X) len %04X, mblen %04X, bits %02X\n", \
t, ib->brx_ring[t].rmd1_hadr, ib->brx_ring[t].rmd0, \
ib->brx_ring[t].length, \
ib->brx_ring[t].mblength, ib->brx_ring[t].rmd1_bits); \
} \
for (t = 0; t < TX_RING_SIZE; t++) { \
printk("T%d: @(%02X %04X) len %04X, misc %04X, bits %02X\n", \
t, ib->btx_ring[t].tmd1_hadr, ib->btx_ring[t].tmd0, \
ib->btx_ring[t].length, \
ib->btx_ring[t].misc, ib->btx_ring[t].tmd1_bits); \
} \
} while (0)
#else
#define PRINT_RINGS()
#endif
/* Load the CSR registers. The LANCE has to be STOPped when we do this! */
static void load_csrs(struct lance_private *lp)
{
volatile struct lance_init_block *aib = lp->lance_init_block;
int leptr;
leptr = LANCE_ADDR(aib);
WRITERAP(lp, LE_CSR1); /* load address of init block */
WRITERDP(lp, leptr & 0xFFFF);
WRITERAP(lp, LE_CSR2);
WRITERDP(lp, leptr >> 16);
WRITERAP(lp, LE_CSR3);
WRITERDP(lp, lp->busmaster_regval); /* set byteswap/ALEctrl/byte ctrl */
/* Point back to csr0 */
WRITERAP(lp, LE_CSR0);
}
/* #define to 0 or 1 appropriately */
#define DEBUG_IRING 0
/* Set up the Lance Rx and Tx rings and the init block */
static void lance_init_ring(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_init_block *aib; /* for LANCE_ADDR computations */
int leptr;
int i;
aib = lp->lance_init_block;
lp->rx_new = lp->tx_new = 0;
lp->rx_old = lp->tx_old = 0;
ib->mode = LE_MO_PROM; /* normal, enable Tx & Rx */
/* Copy the ethernet address to the lance init block
* Notice that we do a byteswap if we're big endian.
* [I think this is the right criterion; at least, sunlance,
* a2065 and atarilance do the byteswap and lance.c (PC) doesn't.
* However, the datasheet says that the BSWAP bit doesn't affect
* the init block, so surely it should be low byte first for
* everybody? Um.]
* We could define the ib->physaddr as three 16bit values and
* use (addr[1] << 8) | addr[0] & co, but this is more efficient.
*/
#ifdef __BIG_ENDIAN
ib->phys_addr[0] = dev->dev_addr[1];
ib->phys_addr[1] = dev->dev_addr[0];
ib->phys_addr[2] = dev->dev_addr[3];
ib->phys_addr[3] = dev->dev_addr[2];
ib->phys_addr[4] = dev->dev_addr[5];
ib->phys_addr[5] = dev->dev_addr[4];
#else
for (i = 0; i < 6; i++)
ib->phys_addr[i] = dev->dev_addr[i];
#endif
if (DEBUG_IRING)
printk("TX rings:\n");
lp->tx_full = 0;
/* Setup the Tx ring entries */
for (i = 0; i < (1 << lp->lance_log_tx_bufs); i++) {
leptr = LANCE_ADDR(&aib->tx_buf[i][0]);
ib->btx_ring[i].tmd0 = leptr;
ib->btx_ring[i].tmd1_hadr = leptr >> 16;
ib->btx_ring[i].tmd1_bits = 0;
ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */
ib->btx_ring[i].misc = 0;
if (DEBUG_IRING)
printk("%d: 0x%8.8x\n", i, leptr);
}
/* Setup the Rx ring entries */
if (DEBUG_IRING)
printk("RX rings:\n");
for (i = 0; i < (1 << lp->lance_log_rx_bufs); i++) {
leptr = LANCE_ADDR(&aib->rx_buf[i][0]);
ib->brx_ring[i].rmd0 = leptr;
ib->brx_ring[i].rmd1_hadr = leptr >> 16;
ib->brx_ring[i].rmd1_bits = LE_R1_OWN;
/* 0xf000 == bits that must be one (reserved, presumably) */
ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000;
ib->brx_ring[i].mblength = 0;
if (DEBUG_IRING)
printk("%d: 0x%8.8x\n", i, leptr);
}
/* Setup the initialization block */
/* Setup rx descriptor pointer */
leptr = LANCE_ADDR(&aib->brx_ring);
ib->rx_len = (lp->lance_log_rx_bufs << 13) | (leptr >> 16);
ib->rx_ptr = leptr;
if (DEBUG_IRING)
printk("RX ptr: %8.8x\n", leptr);
/* Setup tx descriptor pointer */
leptr = LANCE_ADDR(&aib->btx_ring);
ib->tx_len = (lp->lance_log_tx_bufs << 13) | (leptr >> 16);
ib->tx_ptr = leptr;
if (DEBUG_IRING)
printk("TX ptr: %8.8x\n", leptr);
/* Clear the multicast filter */
ib->filter[0] = 0;
ib->filter[1] = 0;
PRINT_RINGS();
}
/* LANCE must be STOPped before we do this, too... */
static int init_restart_lance(struct lance_private *lp)
{
int i;
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_INIT);
/* Need a hook here for sunlance ledma stuff */
/* Wait for the lance to complete initialization */
for (i = 0; (i < 100) && !(READRDP(lp) & (LE_C0_ERR | LE_C0_IDON)); i++)
barrier();
if ((i == 100) || (READRDP(lp) & LE_C0_ERR)) {
printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, READRDP(lp));
return -1;
}
/* Clear IDON by writing a "1", enable interrupts and start lance */
WRITERDP(lp, LE_C0_IDON);
WRITERDP(lp, LE_C0_INEA | LE_C0_STRT);
return 0;
}
static int lance_reset(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
int status;
/* Stop the lance */
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STOP);
load_csrs(lp);
lance_init_ring(dev);
dev->trans_start = jiffies; /* prevent tx timeout */
status = init_restart_lance(lp);
#ifdef DEBUG_DRIVER
printk("Lance restart=%d\n", status);
#endif
return status;
}
static int lance_rx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_rx_desc *rd;
unsigned char bits;
#ifdef TEST_HITS
int i;
#endif
#ifdef TEST_HITS
printk("[");
for (i = 0; i < RX_RING_SIZE; i++) {
if (i == lp->rx_new)
printk("%s",
ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "_" : "X");
else
printk("%s",
ib->brx_ring[i].rmd1_bits & LE_R1_OWN ? "." : "1");
}
printk("]");
#endif
#ifdef CONFIG_HP300
blinken_leds(0x40, 0);
#endif
WRITERDP(lp, LE_C0_RINT | LE_C0_INEA); /* ack Rx int, reenable ints */
for (rd = &ib->brx_ring[lp->rx_new]; /* For each Rx ring we own... */
!((bits = rd->rmd1_bits) & LE_R1_OWN);
rd = &ib->brx_ring[lp->rx_new]) {
/* We got an incomplete frame? */
if ((bits & LE_R1_POK) != LE_R1_POK) {
dev->stats.rx_over_errors++;
dev->stats.rx_errors++;
continue;
} else if (bits & LE_R1_ERR) {
/* Count only the end frame as a rx error,
* not the beginning
*/
if (bits & LE_R1_BUF)
dev->stats.rx_fifo_errors++;
if (bits & LE_R1_CRC)
dev->stats.rx_crc_errors++;
if (bits & LE_R1_OFL)
dev->stats.rx_over_errors++;
if (bits & LE_R1_FRA)
dev->stats.rx_frame_errors++;
if (bits & LE_R1_EOP)
dev->stats.rx_errors++;
} else {
int len = (rd->mblength & 0xfff) - 4;
struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
if (!skb) {
dev->stats.rx_dropped++;
rd->mblength = 0;
rd->rmd1_bits = LE_R1_OWN;
lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
return 0;
}
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
skb_copy_to_linear_data(skb,
(unsigned char *)&(ib->rx_buf[lp->rx_new][0]),
len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
/* Return the packet to the pool */
rd->mblength = 0;
rd->rmd1_bits = LE_R1_OWN;
lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
}
return 0;
}
static int lance_tx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_tx_desc *td;
int i, j;
int status;
#ifdef CONFIG_HP300
blinken_leds(0x80, 0);
#endif
/* csr0 is 2f3 */
WRITERDP(lp, LE_C0_TINT | LE_C0_INEA);
/* csr0 is 73 */
j = lp->tx_old;
for (i = j; i != lp->tx_new; i = j) {
td = &ib->btx_ring[i];
/* If we hit a packet not owned by us, stop */
if (td->tmd1_bits & LE_T1_OWN)
break;
if (td->tmd1_bits & LE_T1_ERR) {
status = td->misc;
dev->stats.tx_errors++;
if (status & LE_T3_RTY)
dev->stats.tx_aborted_errors++;
if (status & LE_T3_LCOL)
dev->stats.tx_window_errors++;
if (status & LE_T3_CLOS) {
dev->stats.tx_carrier_errors++;
if (lp->auto_select) {
lp->tpe = 1 - lp->tpe;
printk("%s: Carrier Lost, trying %s\n",
dev->name,
lp->tpe ? "TPE" : "AUI");
/* Stop the lance */
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STOP);
lance_init_ring(dev);
load_csrs(lp);
init_restart_lance(lp);
return 0;
}
}
/* buffer errors and underflows turn off the transmitter */
/* Restart the adapter */
if (status & (LE_T3_BUF|LE_T3_UFL)) {
dev->stats.tx_fifo_errors++;
printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
dev->name);
/* Stop the lance */
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STOP);
lance_init_ring(dev);
load_csrs(lp);
init_restart_lance(lp);
return 0;
}
} else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
/*
* So we don't count the packet more than once.
*/
td->tmd1_bits &= ~(LE_T1_POK);
/* One collision before packet was sent. */
if (td->tmd1_bits & LE_T1_EONE)
dev->stats.collisions++;
/* More than one collision, be optimistic. */
if (td->tmd1_bits & LE_T1_EMORE)
dev->stats.collisions += 2;
dev->stats.tx_packets++;
}
j = (j + 1) & lp->tx_ring_mod_mask;
}
lp->tx_old = j;
WRITERDP(lp, LE_C0_TINT | LE_C0_INEA);
return 0;
}
static irqreturn_t
lance_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
struct lance_private *lp = netdev_priv(dev);
int csr0;
spin_lock(&lp->devlock);
WRITERAP(lp, LE_CSR0); /* LANCE Controller Status */
csr0 = READRDP(lp);
PRINT_RINGS();
if (!(csr0 & LE_C0_INTR)) { /* Check if any interrupt has */
spin_unlock(&lp->devlock);
return IRQ_NONE; /* been generated by the Lance. */
}
/* Acknowledge all the interrupt sources ASAP */
WRITERDP(lp, csr0 & ~(LE_C0_INEA|LE_C0_TDMD|LE_C0_STOP|LE_C0_STRT|LE_C0_INIT));
if ((csr0 & LE_C0_ERR)) {
/* Clear the error condition */
WRITERDP(lp, LE_C0_BABL|LE_C0_ERR|LE_C0_MISS|LE_C0_INEA);
}
if (csr0 & LE_C0_RINT)
lance_rx(dev);
if (csr0 & LE_C0_TINT)
lance_tx(dev);
/* Log misc errors. */
if (csr0 & LE_C0_BABL)
dev->stats.tx_errors++; /* Tx babble. */
if (csr0 & LE_C0_MISS)
dev->stats.rx_errors++; /* Missed a Rx frame. */
if (csr0 & LE_C0_MERR) {
printk("%s: Bus master arbitration failure, status %4.4x.\n",
dev->name, csr0);
/* Restart the chip. */
WRITERDP(lp, LE_C0_STRT);
}
if (lp->tx_full && netif_queue_stopped(dev) && (TX_BUFFS_AVAIL >= 0)) {
lp->tx_full = 0;
netif_wake_queue(dev);
}
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_MERR|LE_C0_IDON|LE_C0_INEA);
spin_unlock(&lp->devlock);
return IRQ_HANDLED;
}
int lance_open(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
int res;
/* Install the Interrupt handler. Or we could shunt this out to specific drivers? */
if (request_irq(lp->irq, lance_interrupt, IRQF_SHARED, lp->name, dev))
return -EAGAIN;
res = lance_reset(dev);
spin_lock_init(&lp->devlock);
netif_start_queue(dev);
return res;
}
EXPORT_SYMBOL_GPL(lance_open);
int lance_close(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
netif_stop_queue(dev);
/* Stop the LANCE */
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STOP);
free_irq(lp->irq, dev);
return 0;
}
EXPORT_SYMBOL_GPL(lance_close);
void lance_tx_timeout(struct net_device *dev)
{
printk("lance_tx_timeout\n");
lance_reset(dev);
dev->trans_start = jiffies; /* prevent tx timeout */
netif_wake_queue(dev);
}
EXPORT_SYMBOL_GPL(lance_tx_timeout);
int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
int entry, skblen, len;
static int outs;
unsigned long flags;
if (!TX_BUFFS_AVAIL)
return NETDEV_TX_LOCKED;
netif_stop_queue(dev);
skblen = skb->len;
#ifdef DEBUG_DRIVER
/* dump the packet */
{
int i;
for (i = 0; i < 64; i++) {
if ((i % 16) == 0)
printk("\n");
printk("%2.2x ", skb->data[i]);
}
}
#endif
len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
entry = lp->tx_new & lp->tx_ring_mod_mask;
ib->btx_ring[entry].length = (-len) | 0xf000;
ib->btx_ring[entry].misc = 0;
if (skb->len < ETH_ZLEN)
memset((void *)&ib->tx_buf[entry][0], 0, ETH_ZLEN);
skb_copy_from_linear_data(skb, (void *)&ib->tx_buf[entry][0], skblen);
/* Now, give the packet to the lance */
ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
lp->tx_new = (lp->tx_new + 1) & lp->tx_ring_mod_mask;
outs++;
/* Kick the lance: transmit now */
WRITERDP(lp, LE_C0_INEA | LE_C0_TDMD);
dev_consume_skb_any(skb);
spin_lock_irqsave(&lp->devlock, flags);
if (TX_BUFFS_AVAIL)
netif_start_queue(dev);
else
lp->tx_full = 1;
spin_unlock_irqrestore(&lp->devlock, flags);
return NETDEV_TX_OK;
}
EXPORT_SYMBOL_GPL(lance_start_xmit);
/* taken from the depca driver via a2065.c */
static void lance_load_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile u16 *mcast_table = (u16 *)&ib->filter;
struct netdev_hw_addr *ha;
u32 crc;
/* set all multicast bits */
if (dev->flags & IFF_ALLMULTI) {
ib->filter[0] = 0xffffffff;
ib->filter[1] = 0xffffffff;
return;
}
/* clear the multicast filter */
ib->filter[0] = 0;
ib->filter[1] = 0;
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
mcast_table[crc >> 4] |= 1 << (crc & 0xf);
}
}
void lance_set_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
int stopped;
stopped = netif_queue_stopped(dev);
if (!stopped)
netif_stop_queue(dev);
while (lp->tx_old != lp->tx_new)
schedule();
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STOP);
lance_init_ring(dev);
if (dev->flags & IFF_PROMISC) {
ib->mode |= LE_MO_PROM;
} else {
ib->mode &= ~LE_MO_PROM;
lance_load_multicast(dev);
}
load_csrs(lp);
init_restart_lance(lp);
if (!stopped)
netif_start_queue(dev);
}
EXPORT_SYMBOL_GPL(lance_set_multicast);
#ifdef CONFIG_NET_POLL_CONTROLLER
void lance_poll(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
spin_lock(&lp->devlock);
WRITERAP(lp, LE_CSR0);
WRITERDP(lp, LE_C0_STRT);
spin_unlock(&lp->devlock);
lance_interrupt(dev->irq, dev);
}
#endif
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,250 @@
/*
* 7990.h -- LANCE ethernet IC generic routines.
* This is an attempt to separate out the bits of various ethernet
* drivers that are common because they all use the AMD 7990 LANCE
* (Local Area Network Controller for Ethernet) chip.
*
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
*
* Most of this stuff was obtained by looking at other LANCE drivers,
* in particular a2065.[ch]. The AMD C-LANCE datasheet was also helpful.
*/
#ifndef _7990_H
#define _7990_H
/* The lance only has two register locations. We communicate mostly via memory. */
#define LANCE_RDP 0 /* Register Data Port */
#define LANCE_RAP 2 /* Register Address Port */
/* Transmit/receive ring definitions.
* We allow the specific drivers to override these defaults if they want to.
* NB: according to lance.c, increasing the number of buffers is a waste
* of space and reduces the chance that an upper layer will be able to
* reorder queued Tx packets based on priority. [Clearly there is a minimum
* limit too: too small and we drop rx packets and can't tx at full speed.]
* 4+4 seems to be the usual setting; the atarilance driver uses 3 and 5.
*/
/* Blast! This won't work. The problem is that we can't specify a default
* setting because that would cause the lance_init_block struct to be
* too long (and overflow the RAM on shared-memory cards like the HP LANCE.
*/
#ifndef LANCE_LOG_TX_BUFFERS
#define LANCE_LOG_TX_BUFFERS 1
#define LANCE_LOG_RX_BUFFERS 3
#endif
#define TX_RING_SIZE (1 << LANCE_LOG_TX_BUFFERS)
#define RX_RING_SIZE (1 << LANCE_LOG_RX_BUFFERS)
#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
#define PKT_BUFF_SIZE (1544)
#define RX_BUFF_SIZE PKT_BUFF_SIZE
#define TX_BUFF_SIZE PKT_BUFF_SIZE
/* Each receive buffer is described by a receive message descriptor (RMD) */
struct lance_rx_desc {
volatile unsigned short rmd0; /* low address of packet */
volatile unsigned char rmd1_bits; /* descriptor bits */
volatile unsigned char rmd1_hadr; /* high address of packet */
volatile short length; /* This length is 2s complement (negative)!
* Buffer length */
volatile unsigned short mblength; /* Actual number of bytes received */
};
/* Ditto for TMD: */
struct lance_tx_desc {
volatile unsigned short tmd0; /* low address of packet */
volatile unsigned char tmd1_bits; /* descriptor bits */
volatile unsigned char tmd1_hadr; /* high address of packet */
volatile short length; /* Length is 2s complement (negative)! */
volatile unsigned short misc;
};
/* There are three memory structures accessed by the LANCE:
* the initialization block, the receive and transmit descriptor rings,
* and the data buffers themselves. In fact we might as well put the
* init block,the Tx and Rx rings and the buffers together in memory:
*/
struct lance_init_block {
volatile unsigned short mode; /* Pre-set mode (reg. 15) */
volatile unsigned char phys_addr[6]; /* Physical ethernet address */
volatile unsigned filter[2]; /* Multicast filter (64 bits) */
/* Receive and transmit ring base, along with extra bits. */
volatile unsigned short rx_ptr; /* receive descriptor addr */
volatile unsigned short rx_len; /* receive len and high addr */
volatile unsigned short tx_ptr; /* transmit descriptor addr */
volatile unsigned short tx_len; /* transmit len and high addr */
/* The Tx and Rx ring entries must be aligned on 8-byte boundaries.
* This will be true if this whole struct is 8-byte aligned.
*/
volatile struct lance_tx_desc btx_ring[TX_RING_SIZE];
volatile struct lance_rx_desc brx_ring[RX_RING_SIZE];
volatile char tx_buf[TX_RING_SIZE][TX_BUFF_SIZE];
volatile char rx_buf[RX_RING_SIZE][RX_BUFF_SIZE];
/* we use this just to make the struct big enough that we can move its startaddr
* in order to force alignment to an eight byte boundary.
*/
};
/* This is where we keep all the stuff the driver needs to know about.
* I'm definitely unhappy about the mechanism for allowing specific
* drivers to add things...
*/
struct lance_private {
const char *name;
unsigned long base;
volatile struct lance_init_block *init_block; /* CPU address of RAM */
volatile struct lance_init_block *lance_init_block; /* LANCE address of RAM */
int rx_new, tx_new;
int rx_old, tx_old;
int lance_log_rx_bufs, lance_log_tx_bufs;
int rx_ring_mod_mask, tx_ring_mod_mask;
int tpe; /* TPE is selected */
int auto_select; /* cable-selection is by carrier */
unsigned short busmaster_regval;
unsigned int irq; /* IRQ to register */
/* This is because the HP LANCE is disgusting and you have to check
* a DIO-specific register every time you read/write the LANCE regs :-<
* [could we get away with making these some sort of macro?]
*/
void (*writerap)(void *, unsigned short);
void (*writerdp)(void *, unsigned short);
unsigned short (*readrdp)(void *);
spinlock_t devlock;
char tx_full;
};
/*
* Am7990 Control and Status Registers
*/
#define LE_CSR0 0x0000 /* LANCE Controller Status */
#define LE_CSR1 0x0001 /* IADR[15:0] (bit0==0 ie word aligned) */
#define LE_CSR2 0x0002 /* IADR[23:16] (high bits reserved) */
#define LE_CSR3 0x0003 /* Misc */
/*
* Bit definitions for CSR0 (LANCE Controller Status)
*/
#define LE_C0_ERR 0x8000 /* Error = BABL | CERR | MISS | MERR */
#define LE_C0_BABL 0x4000 /* Babble: Transmitted too many bits */
#define LE_C0_CERR 0x2000 /* No Heartbeat (10BASE-T) */
#define LE_C0_MISS 0x1000 /* Missed Frame (no rx buffer to put it in) */
#define LE_C0_MERR 0x0800 /* Memory Error */
#define LE_C0_RINT 0x0400 /* Receive Interrupt */
#define LE_C0_TINT 0x0200 /* Transmit Interrupt */
#define LE_C0_IDON 0x0100 /* Initialization Done */
#define LE_C0_INTR 0x0080 /* Interrupt Flag
= BABL | MISS | MERR | RINT | TINT | IDON */
#define LE_C0_INEA 0x0040 /* Interrupt Enable */
#define LE_C0_RXON 0x0020 /* Receive On */
#define LE_C0_TXON 0x0010 /* Transmit On */
#define LE_C0_TDMD 0x0008 /* Transmit Demand */
#define LE_C0_STOP 0x0004 /* Stop */
#define LE_C0_STRT 0x0002 /* Start */
#define LE_C0_INIT 0x0001 /* Initialize */
/*
* Bit definitions for CSR3
*/
#define LE_C3_BSWP 0x0004 /* Byte Swap (on for big endian byte order) */
#define LE_C3_ACON 0x0002 /* ALE Control (on for active low ALE) */
#define LE_C3_BCON 0x0001 /* Byte Control */
/*
* Mode Flags
*/
#define LE_MO_PROM 0x8000 /* Promiscuous Mode */
/* these next ones 0x4000 -- 0x0080 are not available on the LANCE 7990,
* but they are in NetBSD's am7990.h, presumably for backwards-compatible chips
*/
#define LE_MO_DRCVBC 0x4000 /* disable receive broadcast */
#define LE_MO_DRCVPA 0x2000 /* disable physical address detection */
#define LE_MO_DLNKTST 0x1000 /* disable link status */
#define LE_MO_DAPC 0x0800 /* disable automatic polarity correction */
#define LE_MO_MENDECL 0x0400 /* MENDEC loopback mode */
#define LE_MO_LRTTSEL 0x0200 /* lower RX threshold / TX mode selection */
#define LE_MO_PSEL1 0x0100 /* port selection bit1 */
#define LE_MO_PSEL0 0x0080 /* port selection bit0 */
/* and this one is from the C-LANCE data sheet... */
#define LE_MO_EMBA 0x0080 /* Enable Modified Backoff Algorithm
(C-LANCE, not original LANCE) */
#define LE_MO_INTL 0x0040 /* Internal Loopback */
#define LE_MO_DRTY 0x0020 /* Disable Retry */
#define LE_MO_FCOLL 0x0010 /* Force Collision */
#define LE_MO_DXMTFCS 0x0008 /* Disable Transmit CRC */
#define LE_MO_LOOP 0x0004 /* Loopback Enable */
#define LE_MO_DTX 0x0002 /* Disable Transmitter */
#define LE_MO_DRX 0x0001 /* Disable Receiver */
/*
* Receive Flags
*/
#define LE_R1_OWN 0x80 /* LANCE owns the descriptor */
#define LE_R1_ERR 0x40 /* Error */
#define LE_R1_FRA 0x20 /* Framing Error */
#define LE_R1_OFL 0x10 /* Overflow Error */
#define LE_R1_CRC 0x08 /* CRC Error */
#define LE_R1_BUF 0x04 /* Buffer Error */
#define LE_R1_SOP 0x02 /* Start of Packet */
#define LE_R1_EOP 0x01 /* End of Packet */
#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
/*
* Transmit Flags
*/
#define LE_T1_OWN 0x80 /* LANCE owns the descriptor */
#define LE_T1_ERR 0x40 /* Error */
#define LE_T1_RES 0x20 /* Reserved, LANCE writes this with a zero */
#define LE_T1_EMORE 0x10 /* More than one retry needed */
#define LE_T1_EONE 0x08 /* One retry needed */
#define LE_T1_EDEF 0x04 /* Deferred */
#define LE_T1_SOP 0x02 /* Start of Packet */
#define LE_T1_EOP 0x01 /* End of Packet */
#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
/*
* Error Flags
*/
#define LE_T3_BUF 0x8000 /* Buffer Error */
#define LE_T3_UFL 0x4000 /* Underflow Error */
#define LE_T3_LCOL 0x1000 /* Late Collision */
#define LE_T3_CLOS 0x0800 /* Loss of Carrier */
#define LE_T3_RTY 0x0400 /* Retry Error */
#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry */
/* Miscellaneous useful macros */
#define TX_BUFFS_AVAIL ((lp->tx_old <= lp->tx_new) ? \
lp->tx_old + lp->tx_ring_mod_mask - lp->tx_new : \
lp->tx_old - lp->tx_new - 1)
/* The LANCE only uses 24 bit addresses. This does the obvious thing. */
#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
/* Now the prototypes we export */
int lance_open(struct net_device *dev);
int lance_close(struct net_device *dev);
int lance_start_xmit(struct sk_buff *skb, struct net_device *dev);
void lance_set_multicast(struct net_device *dev);
void lance_tx_timeout(struct net_device *dev);
#ifdef CONFIG_NET_POLL_CONTROLLER
void lance_poll(struct net_device *dev);
#endif
#endif /* ndef _7990_H */

View file

@ -0,0 +1,205 @@
#
# AMD network device configuration
#
config NET_VENDOR_AMD
bool "AMD devices"
default y
depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
(ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA || ARM64
---help---
If you have a network (Ethernet) chipset belonging to this class,
say Y.
Note that the answer to this question does not directly affect
the kernel: saying N will just case the configurator to skip all
the questions regarding AMD chipsets. If you say Y, you will be asked
for your specific chipset/driver in the following questions.
if NET_VENDOR_AMD
config A2065
tristate "A2065 support"
depends on ZORRO
select CRC32
---help---
If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise,
say N.
To compile this driver as a module, choose M here: the module
will be called a2065.
config AMD8111_ETH
tristate "AMD 8111 (new PCI LANCE) support"
depends on PCI
select CRC32
select MII
---help---
If you have an AMD 8111-based PCI LANCE ethernet card,
answer Y here and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called amd8111e.
config LANCE
tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
depends on ISA && ISA_DMA_API
---help---
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. Some LinkSys cards are
of this type.
To compile this driver as a module, choose M here: the module
will be called lance. This is recommended.
config PCNET32
tristate "AMD PCnet32 PCI support"
depends on PCI
select CRC32
select MII
---help---
If you have a PCnet32 or PCnetPCI based network (Ethernet) card,
answer Y here and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called pcnet32.
config ARIADNE
tristate "Ariadne support"
depends on ZORRO
---help---
If you have a Village Tronic Ariadne Ethernet adapter, say Y.
Otherwise, say N.
To compile this driver as a module, choose M here: the module
will be called ariadne.
config ARM_AM79C961A
bool "ARM EBSA110 AM79C961A support"
depends on 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 ATARILANCE
tristate "Atari LANCE support"
depends on ATARI
---help---
Say Y to include support for several Atari Ethernet adapters based
on the AMD LANCE chipset: RieblCard (with or without battery), or
PAMCard VME (also the version by Rhotron, with different addresses).
config DECLANCE
tristate "DEC LANCE ethernet controller support"
depends on MACH_DECSTATION
select CRC32
---help---
This driver is for the series of Ethernet controllers produced by
DEC (now Compaq) based on the AMD LANCE chipset, including the
DEPCA series. (This chipset is better known via the NE2100 cards.)
config HPLANCE
bool "HP on-board LANCE support"
depends on DIO
select CRC32
---help---
If you want to use the builtin "LANCE" Ethernet controller on an
HP300 machine, say Y here.
config MIPS_AU1X00_ENET
tristate "MIPS AU1000 Ethernet support"
depends on MIPS_ALCHEMY
select PHYLIB
select CRC32
---help---
If you have an Alchemy Semi AU1X00 based system
say Y. Otherwise, say N.
config MVME147_NET
tristate "MVME147 (LANCE) Ethernet support"
depends on MVME147
select CRC32
---help---
Support for the on-board Ethernet interface on the Motorola MVME147
single-board computer. Say Y here to include the
driver for this chip in your kernel.
To compile this driver as a module, choose M here.
config PCMCIA_NMCLAN
tristate "New Media PCMCIA support"
depends on PCMCIA
help
Say Y here if you intend to attach a New Media Ethernet or LiveWire
PCMCIA (PC-card) Ethernet card to your computer.
To compile this driver as a module, choose M here: the module will be
called nmclan_cs. If unsure, say N.
config NI65
tristate "NI6510 support"
depends on ISA && ISA_DMA_API
---help---
If you have a network (Ethernet) card of this type, say Y and read
the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
To compile this driver as a module, choose M here. The module
will be called ni65.
config SUN3LANCE
tristate "Sun3/Sun3x on-board LANCE support"
depends on (SUN3 || SUN3X)
---help---
Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80)
featured an AMD LANCE 10Mbit Ethernet controller on board; say Y
here to compile in the Linux driver for this and enable Ethernet.
General Linux information on the Sun 3 and 3x series (now
discontinued) is at
<http://www.angelfire.com/ca2/tech68k/sun3.html>.
If you're not building a kernel for a Sun 3, say N.
config SUNLANCE
tristate "Sun LANCE support"
depends on SBUS
select CRC32
---help---
This driver supports the "le" interface present on all 32-bit Sparc
systems, on some older Ultra systems and as an Sbus option. These
cards are based on the AMD LANCE chipset, which is better known
via the NE2100 cards.
To compile this driver as a module, choose M here: the module
will be called sunlance.
config AMD_XGBE
tristate "AMD 10GbE Ethernet driver"
depends on OF_NET
select PHYLIB
select AMD_XGBE_PHY
select BITREVERSE
select CRC32
select PTP_1588_CLOCK
---help---
This driver supports the AMD 10GbE Ethernet device found on an
AMD SoC.
To compile this driver as a module, choose M here: the module
will be called amd-xgbe.
config AMD_XGBE_DCB
bool "Data Center Bridging (DCB) support"
default n
depends on AMD_XGBE && DCB
---help---
Say Y here to enable Data Center Bridging (DCB) support in the
driver.
If unsure, say N.
endif # NET_VENDOR_AMD

View file

@ -0,0 +1,20 @@
#
# Makefile for the AMD network device drivers.
#
obj-$(CONFIG_A2065) += a2065.o
obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
obj-$(CONFIG_ARIADNE) += ariadne.o
obj-$(CONFIG_ATARILANCE) += atarilance.o
obj-$(CONFIG_DECLANCE) += declance.o
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
obj-$(CONFIG_LANCE) += lance.o
obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
obj-$(CONFIG_NI65) += ni65.o
obj-$(CONFIG_PCNET32) += pcnet32.o
obj-$(CONFIG_SUN3LANCE) += sun3lance.o
obj-$(CONFIG_SUNLANCE) += sunlance.o
obj-$(CONFIG_AMD_XGBE) += xgbe/

View file

@ -0,0 +1,783 @@
/*
* Amiga Linux/68k A2065 Ethernet Driver
*
* (C) Copyright 1995-2003 by Geert Uytterhoeven <geert@linux-m68k.org>
*
* Fixes and tips by:
* - Janos Farkas (CHEXUM@sparta.banki.hu)
* - Jes Degn Soerensen (jds@kom.auc.dk)
* - Matt Domsch (Matt_Domsch@dell.com)
*
* ----------------------------------------------------------------------------
*
* This program is based on
*
* ariadne.?: Amiga Linux/68k Ariadne Ethernet Driver
* (C) Copyright 1995 by Geert Uytterhoeven,
* Peter De Schrijver
*
* lance.c: An AMD LANCE ethernet driver for linux.
* Written 1993-94 by Donald Becker.
*
* Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
* Advanced Micro Devices
* Publication #16907, Rev. B, Amendment/0, May 1994
*
* ----------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ----------------------------------------------------------------------------
*
* The A2065 is a Zorro-II board made by Commodore/Ameristar. It contains:
*
* - an Am7990 Local Area Network Controller for Ethernet (LANCE) with
* both 10BASE-2 (thin coax) and AUI (DB-15) connectors
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/*#define DEBUG*/
/*#define TEST_HITS*/
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/zorro.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include "a2065.h"
/* Transmit/Receive Ring Definitions */
#define LANCE_LOG_TX_BUFFERS (2)
#define LANCE_LOG_RX_BUFFERS (4)
#define TX_RING_SIZE (1 << LANCE_LOG_TX_BUFFERS)
#define RX_RING_SIZE (1 << LANCE_LOG_RX_BUFFERS)
#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
#define PKT_BUF_SIZE (1544)
#define RX_BUFF_SIZE PKT_BUF_SIZE
#define TX_BUFF_SIZE PKT_BUF_SIZE
/* Layout of the Lance's RAM Buffer */
struct lance_init_block {
unsigned short mode; /* Pre-set mode (reg. 15) */
unsigned char phys_addr[6]; /* Physical ethernet address */
unsigned filter[2]; /* Multicast filter. */
/* Receive and transmit ring base, along with extra bits. */
unsigned short rx_ptr; /* receive descriptor addr */
unsigned short rx_len; /* receive len and high addr */
unsigned short tx_ptr; /* transmit descriptor addr */
unsigned short tx_len; /* transmit len and high addr */
/* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
struct lance_rx_desc brx_ring[RX_RING_SIZE];
struct lance_tx_desc btx_ring[TX_RING_SIZE];
char rx_buf[RX_RING_SIZE][RX_BUFF_SIZE];
char tx_buf[TX_RING_SIZE][TX_BUFF_SIZE];
};
/* Private Device Data */
struct lance_private {
char *name;
volatile struct lance_regs *ll;
volatile struct lance_init_block *init_block; /* Hosts view */
volatile struct lance_init_block *lance_init_block; /* Lance view */
int rx_new, tx_new;
int rx_old, tx_old;
int lance_log_rx_bufs, lance_log_tx_bufs;
int rx_ring_mod_mask, tx_ring_mod_mask;
int tpe; /* cable-selection is TPE */
int auto_select; /* cable-selection by carrier */
unsigned short busmaster_regval;
#ifdef CONFIG_SUNLANCE
struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */
int burst_sizes; /* ledma SBus burst sizes */
#endif
struct timer_list multicast_timer;
};
#define LANCE_ADDR(x) ((int)(x) & ~0xff000000)
/* Load the CSR registers */
static void load_csrs(struct lance_private *lp)
{
volatile struct lance_regs *ll = lp->ll;
volatile struct lance_init_block *aib = lp->lance_init_block;
int leptr = LANCE_ADDR(aib);
ll->rap = LE_CSR1;
ll->rdp = (leptr & 0xFFFF);
ll->rap = LE_CSR2;
ll->rdp = leptr >> 16;
ll->rap = LE_CSR3;
ll->rdp = lp->busmaster_regval;
/* Point back to csr0 */
ll->rap = LE_CSR0;
}
/* Setup the Lance Rx and Tx rings */
static void lance_init_ring(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_init_block *aib = lp->lance_init_block;
/* for LANCE_ADDR computations */
int leptr;
int i;
/* Lock out other processes while setting up hardware */
netif_stop_queue(dev);
lp->rx_new = lp->tx_new = 0;
lp->rx_old = lp->tx_old = 0;
ib->mode = 0;
/* Copy the ethernet address to the lance init block
* Note that on the sparc you need to swap the ethernet address.
*/
ib->phys_addr[0] = dev->dev_addr[1];
ib->phys_addr[1] = dev->dev_addr[0];
ib->phys_addr[2] = dev->dev_addr[3];
ib->phys_addr[3] = dev->dev_addr[2];
ib->phys_addr[4] = dev->dev_addr[5];
ib->phys_addr[5] = dev->dev_addr[4];
/* Setup the Tx ring entries */
netdev_dbg(dev, "TX rings:\n");
for (i = 0; i <= 1 << lp->lance_log_tx_bufs; i++) {
leptr = LANCE_ADDR(&aib->tx_buf[i][0]);
ib->btx_ring[i].tmd0 = leptr;
ib->btx_ring[i].tmd1_hadr = leptr >> 16;
ib->btx_ring[i].tmd1_bits = 0;
ib->btx_ring[i].length = 0xf000; /* The ones required by tmd2 */
ib->btx_ring[i].misc = 0;
if (i < 3)
netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
}
/* Setup the Rx ring entries */
netdev_dbg(dev, "RX rings:\n");
for (i = 0; i < 1 << lp->lance_log_rx_bufs; i++) {
leptr = LANCE_ADDR(&aib->rx_buf[i][0]);
ib->brx_ring[i].rmd0 = leptr;
ib->brx_ring[i].rmd1_hadr = leptr >> 16;
ib->brx_ring[i].rmd1_bits = LE_R1_OWN;
ib->brx_ring[i].length = -RX_BUFF_SIZE | 0xf000;
ib->brx_ring[i].mblength = 0;
if (i < 3)
netdev_dbg(dev, "%d: 0x%08x\n", i, leptr);
}
/* Setup the initialization block */
/* Setup rx descriptor pointer */
leptr = LANCE_ADDR(&aib->brx_ring);
ib->rx_len = (lp->lance_log_rx_bufs << 13) | (leptr >> 16);
ib->rx_ptr = leptr;
netdev_dbg(dev, "RX ptr: %08x\n", leptr);
/* Setup tx descriptor pointer */
leptr = LANCE_ADDR(&aib->btx_ring);
ib->tx_len = (lp->lance_log_tx_bufs << 13) | (leptr >> 16);
ib->tx_ptr = leptr;
netdev_dbg(dev, "TX ptr: %08x\n", leptr);
/* Clear the multicast filter */
ib->filter[0] = 0;
ib->filter[1] = 0;
}
static int init_restart_lance(struct lance_private *lp)
{
volatile struct lance_regs *ll = lp->ll;
int i;
ll->rap = LE_CSR0;
ll->rdp = LE_C0_INIT;
/* Wait for the lance to complete initialization */
for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++)
barrier();
if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
pr_err("unopened after %d ticks, csr0=%04x\n", i, ll->rdp);
return -EIO;
}
/* Clear IDON by writing a "1", enable interrupts and start lance */
ll->rdp = LE_C0_IDON;
ll->rdp = LE_C0_INEA | LE_C0_STRT;
return 0;
}
static int lance_rx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_regs *ll = lp->ll;
volatile struct lance_rx_desc *rd;
unsigned char bits;
#ifdef TEST_HITS
int i;
char buf[RX_RING_SIZE + 1];
for (i = 0; i < RX_RING_SIZE; i++) {
char r1_own = ib->brx_ring[i].rmd1_bits & LE_R1_OWN;
if (i == lp->rx_new)
buf[i] = r1_own ? '_' : 'X';
else
buf[i] = r1_own ? '.' : '1';
}
buf[RX_RING_SIZE] = 0;
pr_debug("RxRing TestHits: [%s]\n", buf);
#endif
ll->rdp = LE_C0_RINT | LE_C0_INEA;
for (rd = &ib->brx_ring[lp->rx_new];
!((bits = rd->rmd1_bits) & LE_R1_OWN);
rd = &ib->brx_ring[lp->rx_new]) {
/* We got an incomplete frame? */
if ((bits & LE_R1_POK) != LE_R1_POK) {
dev->stats.rx_over_errors++;
dev->stats.rx_errors++;
continue;
} else if (bits & LE_R1_ERR) {
/* Count only the end frame as a rx error,
* not the beginning
*/
if (bits & LE_R1_BUF)
dev->stats.rx_fifo_errors++;
if (bits & LE_R1_CRC)
dev->stats.rx_crc_errors++;
if (bits & LE_R1_OFL)
dev->stats.rx_over_errors++;
if (bits & LE_R1_FRA)
dev->stats.rx_frame_errors++;
if (bits & LE_R1_EOP)
dev->stats.rx_errors++;
} else {
int len = (rd->mblength & 0xfff) - 4;
struct sk_buff *skb = netdev_alloc_skb(dev, len + 2);
if (!skb) {
dev->stats.rx_dropped++;
rd->mblength = 0;
rd->rmd1_bits = LE_R1_OWN;
lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
return 0;
}
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, len); /* make room */
skb_copy_to_linear_data(skb,
(unsigned char *)&ib->rx_buf[lp->rx_new][0],
len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
/* Return the packet to the pool */
rd->mblength = 0;
rd->rmd1_bits = LE_R1_OWN;
lp->rx_new = (lp->rx_new + 1) & lp->rx_ring_mod_mask;
}
return 0;
}
static int lance_tx(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_regs *ll = lp->ll;
volatile struct lance_tx_desc *td;
int i, j;
int status;
/* csr0 is 2f3 */
ll->rdp = LE_C0_TINT | LE_C0_INEA;
/* csr0 is 73 */
j = lp->tx_old;
for (i = j; i != lp->tx_new; i = j) {
td = &ib->btx_ring[i];
/* If we hit a packet not owned by us, stop */
if (td->tmd1_bits & LE_T1_OWN)
break;
if (td->tmd1_bits & LE_T1_ERR) {
status = td->misc;
dev->stats.tx_errors++;
if (status & LE_T3_RTY)
dev->stats.tx_aborted_errors++;
if (status & LE_T3_LCOL)
dev->stats.tx_window_errors++;
if (status & LE_T3_CLOS) {
dev->stats.tx_carrier_errors++;
if (lp->auto_select) {
lp->tpe = 1 - lp->tpe;
netdev_err(dev, "Carrier Lost, trying %s\n",
lp->tpe ? "TPE" : "AUI");
/* Stop the lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
lance_init_ring(dev);
load_csrs(lp);
init_restart_lance(lp);
return 0;
}
}
/* buffer errors and underflows turn off
* the transmitter, so restart the adapter
*/
if (status & (LE_T3_BUF | LE_T3_UFL)) {
dev->stats.tx_fifo_errors++;
netdev_err(dev, "Tx: ERR_BUF|ERR_UFL, restarting\n");
/* Stop the lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
lance_init_ring(dev);
load_csrs(lp);
init_restart_lance(lp);
return 0;
}
} else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
/* So we don't count the packet more than once. */
td->tmd1_bits &= ~(LE_T1_POK);
/* One collision before packet was sent. */
if (td->tmd1_bits & LE_T1_EONE)
dev->stats.collisions++;
/* More than one collision, be optimistic. */
if (td->tmd1_bits & LE_T1_EMORE)
dev->stats.collisions += 2;
dev->stats.tx_packets++;
}
j = (j + 1) & lp->tx_ring_mod_mask;
}
lp->tx_old = j;
ll->rdp = LE_C0_TINT | LE_C0_INEA;
return 0;
}
static int lance_tx_buffs_avail(struct lance_private *lp)
{
if (lp->tx_old <= lp->tx_new)
return lp->tx_old + lp->tx_ring_mod_mask - lp->tx_new;
return lp->tx_old - lp->tx_new - 1;
}
static irqreturn_t lance_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
int csr0;
ll->rap = LE_CSR0; /* LANCE Controller Status */
csr0 = ll->rdp;
if (!(csr0 & LE_C0_INTR)) /* Check if any interrupt has */
return IRQ_NONE; /* been generated by the Lance. */
/* Acknowledge all the interrupt sources ASAP */
ll->rdp = csr0 & ~(LE_C0_INEA | LE_C0_TDMD | LE_C0_STOP | LE_C0_STRT |
LE_C0_INIT);
if (csr0 & LE_C0_ERR) {
/* Clear the error condition */
ll->rdp = LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | LE_C0_INEA;
}
if (csr0 & LE_C0_RINT)
lance_rx(dev);
if (csr0 & LE_C0_TINT)
lance_tx(dev);
/* Log misc errors. */
if (csr0 & LE_C0_BABL)
dev->stats.tx_errors++; /* Tx babble. */
if (csr0 & LE_C0_MISS)
dev->stats.rx_errors++; /* Missed a Rx frame. */
if (csr0 & LE_C0_MERR) {
netdev_err(dev, "Bus master arbitration failure, status %04x\n",
csr0);
/* Restart the chip. */
ll->rdp = LE_C0_STRT;
}
if (netif_queue_stopped(dev) && lance_tx_buffs_avail(lp) > 0)
netif_wake_queue(dev);
ll->rap = LE_CSR0;
ll->rdp = (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR |
LE_C0_IDON | LE_C0_INEA);
return IRQ_HANDLED;
}
static int lance_open(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
int ret;
/* Stop the Lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
/* Install the Interrupt handler */
ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, IRQF_SHARED,
dev->name, dev);
if (ret)
return ret;
load_csrs(lp);
lance_init_ring(dev);
netif_start_queue(dev);
return init_restart_lance(lp);
}
static int lance_close(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
netif_stop_queue(dev);
del_timer_sync(&lp->multicast_timer);
/* Stop the card */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
free_irq(IRQ_AMIGA_PORTS, dev);
return 0;
}
static inline int lance_reset(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
int status;
/* Stop the lance */
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
load_csrs(lp);
lance_init_ring(dev);
dev->trans_start = jiffies; /* prevent tx timeout */
netif_start_queue(dev);
status = init_restart_lance(lp);
netdev_dbg(dev, "Lance restart=%d\n", status);
return status;
}
static void lance_tx_timeout(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
netdev_err(dev, "transmit timed out, status %04x, reset\n", ll->rdp);
lance_reset(dev);
netif_wake_queue(dev);
}
static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_regs *ll = lp->ll;
volatile struct lance_init_block *ib = lp->init_block;
int entry, skblen;
int status = NETDEV_TX_OK;
unsigned long flags;
if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
skblen = max_t(unsigned, skb->len, ETH_ZLEN);
local_irq_save(flags);
if (!lance_tx_buffs_avail(lp)) {
local_irq_restore(flags);
return NETDEV_TX_LOCKED;
}
#ifdef DEBUG
/* dump the packet */
print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
16, 1, skb->data, 64, true);
#endif
entry = lp->tx_new & lp->tx_ring_mod_mask;
ib->btx_ring[entry].length = (-skblen) | 0xf000;
ib->btx_ring[entry].misc = 0;
skb_copy_from_linear_data(skb, (void *)&ib->tx_buf[entry][0], skblen);
/* Now, give the packet to the lance */
ib->btx_ring[entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
lp->tx_new = (lp->tx_new+1) & lp->tx_ring_mod_mask;
dev->stats.tx_bytes += skblen;
if (lance_tx_buffs_avail(lp) <= 0)
netif_stop_queue(dev);
/* Kick the lance: transmit now */
ll->rdp = LE_C0_INEA | LE_C0_TDMD;
dev_kfree_skb(skb);
local_irq_restore(flags);
return status;
}
/* taken from the depca driver */
static void lance_load_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile u16 *mcast_table = (u16 *)&ib->filter;
struct netdev_hw_addr *ha;
u32 crc;
/* set all multicast bits */
if (dev->flags & IFF_ALLMULTI) {
ib->filter[0] = 0xffffffff;
ib->filter[1] = 0xffffffff;
return;
}
/* clear the multicast filter */
ib->filter[0] = 0;
ib->filter[1] = 0;
/* Add addresses */
netdev_for_each_mc_addr(ha, dev) {
crc = ether_crc_le(6, ha->addr);
crc = crc >> 26;
mcast_table[crc >> 4] |= 1 << (crc & 0xf);
}
}
static void lance_set_multicast(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
volatile struct lance_init_block *ib = lp->init_block;
volatile struct lance_regs *ll = lp->ll;
if (!netif_running(dev))
return;
if (lp->tx_old != lp->tx_new) {
mod_timer(&lp->multicast_timer, jiffies + 4);
netif_wake_queue(dev);
return;
}
netif_stop_queue(dev);
ll->rap = LE_CSR0;
ll->rdp = LE_C0_STOP;
lance_init_ring(dev);
if (dev->flags & IFF_PROMISC) {
ib->mode |= LE_MO_PROM;
} else {
ib->mode &= ~LE_MO_PROM;
lance_load_multicast(dev);
}
load_csrs(lp);
init_restart_lance(lp);
netif_wake_queue(dev);
}
static int a2065_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent);
static void a2065_remove_one(struct zorro_dev *z);
static struct zorro_device_id a2065_zorro_tbl[] = {
{ ZORRO_PROD_CBM_A2065_1 },
{ ZORRO_PROD_CBM_A2065_2 },
{ ZORRO_PROD_AMERISTAR_A2065 },
{ 0 }
};
MODULE_DEVICE_TABLE(zorro, a2065_zorro_tbl);
static struct zorro_driver a2065_driver = {
.name = "a2065",
.id_table = a2065_zorro_tbl,
.probe = a2065_init_one,
.remove = a2065_remove_one,
};
static const struct net_device_ops lance_netdev_ops = {
.ndo_open = lance_open,
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_set_rx_mode = lance_set_multicast,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
};
static int a2065_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent)
{
struct net_device *dev;
struct lance_private *priv;
unsigned long board = z->resource.start;
unsigned long base_addr = board + A2065_LANCE;
unsigned long mem_start = board + A2065_RAM;
struct resource *r1, *r2;
u32 serial;
int err;
r1 = request_mem_region(base_addr, sizeof(struct lance_regs),
"Am7990");
if (!r1)
return -EBUSY;
r2 = request_mem_region(mem_start, A2065_RAM_SIZE, "RAM");
if (!r2) {
release_mem_region(base_addr, sizeof(struct lance_regs));
return -EBUSY;
}
dev = alloc_etherdev(sizeof(struct lance_private));
if (dev == NULL) {
release_mem_region(base_addr, sizeof(struct lance_regs));
release_mem_region(mem_start, A2065_RAM_SIZE);
return -ENOMEM;
}
priv = netdev_priv(dev);
r1->name = dev->name;
r2->name = dev->name;
serial = be32_to_cpu(z->rom.er_SerialNumber);
dev->dev_addr[0] = 0x00;
if (z->id != ZORRO_PROD_AMERISTAR_A2065) { /* Commodore */
dev->dev_addr[1] = 0x80;
dev->dev_addr[2] = 0x10;
} else { /* Ameristar */
dev->dev_addr[1] = 0x00;
dev->dev_addr[2] = 0x9f;
}
dev->dev_addr[3] = (serial >> 16) & 0xff;
dev->dev_addr[4] = (serial >> 8) & 0xff;
dev->dev_addr[5] = serial & 0xff;
dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr);
dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start);
dev->mem_end = dev->mem_start + A2065_RAM_SIZE;
priv->ll = (volatile struct lance_regs *)dev->base_addr;
priv->init_block = (struct lance_init_block *)dev->mem_start;
priv->lance_init_block = (struct lance_init_block *)A2065_RAM;
priv->auto_select = 0;
priv->busmaster_regval = LE_C3_BSWP;
priv->lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
priv->lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
priv->rx_ring_mod_mask = RX_RING_MOD_MASK;
priv->tx_ring_mod_mask = TX_RING_MOD_MASK;
dev->netdev_ops = &lance_netdev_ops;
dev->watchdog_timeo = 5*HZ;
dev->dma = 0;
init_timer(&priv->multicast_timer);
priv->multicast_timer.data = (unsigned long) dev;
priv->multicast_timer.function =
(void (*)(unsigned long))lance_set_multicast;
err = register_netdev(dev);
if (err) {
release_mem_region(base_addr, sizeof(struct lance_regs));
release_mem_region(mem_start, A2065_RAM_SIZE);
free_netdev(dev);
return err;
}
zorro_set_drvdata(z, dev);
netdev_info(dev, "A2065 at 0x%08lx, Ethernet Address %pM\n",
board, dev->dev_addr);
return 0;
}
static void a2065_remove_one(struct zorro_dev *z)
{
struct net_device *dev = zorro_get_drvdata(z);
unregister_netdev(dev);
release_mem_region(ZTWO_PADDR(dev->base_addr),
sizeof(struct lance_regs));
release_mem_region(ZTWO_PADDR(dev->mem_start), A2065_RAM_SIZE);
free_netdev(dev);
}
static int __init a2065_init_module(void)
{
return zorro_register_driver(&a2065_driver);
}
static void __exit a2065_cleanup_module(void)
{
zorro_unregister_driver(&a2065_driver);
}
module_init(a2065_init_module);
module_exit(a2065_cleanup_module);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,173 @@
/*
* Amiga Linux/68k A2065 Ethernet Driver
*
* (C) Copyright 1995 by Geert Uytterhoeven <geert@linux-m68k.org>
*
* ---------------------------------------------------------------------------
*
* This program is based on
*
* ariadne.?: Amiga Linux/68k Ariadne Ethernet Driver
* (C) Copyright 1995 by Geert Uytterhoeven,
* Peter De Schrijver
*
* lance.c: An AMD LANCE ethernet driver for linux.
* Written 1993-94 by Donald Becker.
*
* Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
* Advanced Micro Devices
* Publication #16907, Rev. B, Amendment/0, May 1994
*
* ---------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ---------------------------------------------------------------------------
*
* The A2065 is a Zorro-II board made by Commodore/Ameristar. It contains:
*
* - an Am7990 Local Area Network Controller for Ethernet (LANCE) with
* both 10BASE-2 (thin coax) and AUI (DB-15) connectors
*/
/*
* Am7990 Local Area Network Controller for Ethernet (LANCE)
*/
struct lance_regs {
unsigned short rdp; /* Register Data Port */
unsigned short rap; /* Register Address Port */
};
/*
* Am7990 Control and Status Registers
*/
#define LE_CSR0 0x0000 /* LANCE Controller Status */
#define LE_CSR1 0x0001 /* IADR[15:0] */
#define LE_CSR2 0x0002 /* IADR[23:16] */
#define LE_CSR3 0x0003 /* Misc */
/*
* Bit definitions for CSR0 (LANCE Controller Status)
*/
#define LE_C0_ERR 0x8000 /* Error */
#define LE_C0_BABL 0x4000 /* Babble: Transmitted too many bits */
#define LE_C0_CERR 0x2000 /* No Heartbeat (10BASE-T) */
#define LE_C0_MISS 0x1000 /* Missed Frame */
#define LE_C0_MERR 0x0800 /* Memory Error */
#define LE_C0_RINT 0x0400 /* Receive Interrupt */
#define LE_C0_TINT 0x0200 /* Transmit Interrupt */
#define LE_C0_IDON 0x0100 /* Initialization Done */
#define LE_C0_INTR 0x0080 /* Interrupt Flag */
#define LE_C0_INEA 0x0040 /* Interrupt Enable */
#define LE_C0_RXON 0x0020 /* Receive On */
#define LE_C0_TXON 0x0010 /* Transmit On */
#define LE_C0_TDMD 0x0008 /* Transmit Demand */
#define LE_C0_STOP 0x0004 /* Stop */
#define LE_C0_STRT 0x0002 /* Start */
#define LE_C0_INIT 0x0001 /* Initialize */
/*
* Bit definitions for CSR3
*/
#define LE_C3_BSWP 0x0004 /* Byte Swap
(on for big endian byte order) */
#define LE_C3_ACON 0x0002 /* ALE Control
(on for active low ALE) */
#define LE_C3_BCON 0x0001 /* Byte Control */
/*
* Mode Flags
*/
#define LE_MO_PROM 0x8000 /* Promiscuous Mode */
#define LE_MO_INTL 0x0040 /* Internal Loopback */
#define LE_MO_DRTY 0x0020 /* Disable Retry */
#define LE_MO_FCOLL 0x0010 /* Force Collision */
#define LE_MO_DXMTFCS 0x0008 /* Disable Transmit CRC */
#define LE_MO_LOOP 0x0004 /* Loopback Enable */
#define LE_MO_DTX 0x0002 /* Disable Transmitter */
#define LE_MO_DRX 0x0001 /* Disable Receiver */
struct lance_rx_desc {
unsigned short rmd0; /* low address of packet */
unsigned char rmd1_bits; /* descriptor bits */
unsigned char rmd1_hadr; /* high address of packet */
short length; /* This length is 2s complement (negative)!
* Buffer length
*/
unsigned short mblength; /* Aactual number of bytes received */
};
struct lance_tx_desc {
unsigned short tmd0; /* low address of packet */
unsigned char tmd1_bits; /* descriptor bits */
unsigned char tmd1_hadr; /* high address of packet */
short length; /* Length is 2s complement (negative)! */
unsigned short misc;
};
/*
* Receive Flags
*/
#define LE_R1_OWN 0x80 /* LANCE owns the descriptor */
#define LE_R1_ERR 0x40 /* Error */
#define LE_R1_FRA 0x20 /* Framing Error */
#define LE_R1_OFL 0x10 /* Overflow Error */
#define LE_R1_CRC 0x08 /* CRC Error */
#define LE_R1_BUF 0x04 /* Buffer Error */
#define LE_R1_SOP 0x02 /* Start of Packet */
#define LE_R1_EOP 0x01 /* End of Packet */
#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */
/*
* Transmit Flags
*/
#define LE_T1_OWN 0x80 /* LANCE owns the descriptor */
#define LE_T1_ERR 0x40 /* Error */
#define LE_T1_RES 0x20 /* Reserved,
LANCE writes this with a zero */
#define LE_T1_EMORE 0x10 /* More than one retry needed */
#define LE_T1_EONE 0x08 /* One retry needed */
#define LE_T1_EDEF 0x04 /* Deferred */
#define LE_T1_SOP 0x02 /* Start of Packet */
#define LE_T1_EOP 0x01 /* End of Packet */
#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */
/*
* Error Flags
*/
#define LE_T3_BUF 0x8000 /* Buffer Error */
#define LE_T3_UFL 0x4000 /* Underflow Error */
#define LE_T3_LCOL 0x1000 /* Late Collision */
#define LE_T3_CLOS 0x0800 /* Loss of Carrier */
#define LE_T3_RTY 0x0400 /* Retry Error */
#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry */
/*
* A2065 Expansion Board Structure
*/
#define A2065_LANCE 0x4000
#define A2065_RAM 0x8000
#define A2065_RAM_SIZE 0x8000

View file

@ -0,0 +1,768 @@
/*
* linux/drivers/net/ethernet/amd/am79c961a.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 <linux/io.h>
#include <mach/hardware.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 volatile(
"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 volatile(
"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 volatile(
"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 volatile(
"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 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) {
register unsigned int tmp asm("r2"), tmp2 asm("r3");
asm volatile(
"ldm%?ia %0!, {%1, %2}"
: "+r" (buf), "=&r" (tmp), "=&r" (tmp2));
length -= 8;
asm volatile(
"str%?h %1, [%0], #4\n\t"
"mov%? %1, %1, lsr #16\n\t"
"str%?h %1, [%0], #4\n\t"
"str%?h %2, [%0], #4\n\t"
"mov%? %2, %2, lsr #16\n\t"
"str%?h %2, [%0], #4"
: "+r" (offset), "=&r" (tmp), "=&r" (tmp2));
}
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 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) {
register unsigned int tmp asm("r2"), tmp2 asm("r3"), tmp3;
asm volatile(
"ldr%?h %2, [%0], #4\n\t"
"ldr%?h %4, [%0], #4\n\t"
"ldr%?h %3, [%0], #4\n\t"
"orr%? %2, %2, %4, lsl #16\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_mc_hash(char *addr, u16 *hash)
{
int idx, bit;
u32 crc;
crc = ether_crc_le(ETH_ALEN, addr);
idx = crc >> 30;
bit = (crc >> 26) & 15;
hash[idx] |= 1 << bit;
}
static unsigned int am79c961_get_rx_mode(struct net_device *dev, u16 *hash)
{
unsigned int mode = MODE_PORT_10BT;
if (dev->flags & IFF_PROMISC) {
mode |= MODE_PROMISC;
memset(hash, 0xff, 4 * sizeof(*hash));
} else if (dev->flags & IFF_ALLMULTI) {
memset(hash, 0xff, 4 * sizeof(*hash));
} else {
struct netdev_hw_addr *ha;
memset(hash, 0, 4 * sizeof(*hash));
netdev_for_each_mc_addr(ha, dev)
am79c961_mc_hash(ha->addr, hash);
}
return mode;
}
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;
u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
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, multi_hash[i - LADRL]);
for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
write_rreg (dev->base_addr, MODE, mode);
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;
unsigned long flags;
spin_lock_irqsave(&priv->chip_lock, flags);
lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
spin_unlock_irqrestore(&priv->chip_lock, flags);
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;
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;
}
/*
* 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;
u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
int i, stopped;
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 < ARRAY_SIZE(multi_hash); 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);
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_consume_skb_any(skb);
return NETDEV_TX_OK;
}
/*
* 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);
dev->stats.rx_errors++;
if (status & RMD_ERR) {
if (status & RMD_FRAM)
dev->stats.rx_frame_errors++;
if (status & RMD_CRC)
dev->stats.rx_crc_errors++;
} else if (status & RMD_STP)
dev->stats.rx_length_errors++;
continue;
}
len = am_readword(dev, hdraddr + 6);
skb = netdev_alloc_skb(dev, len + 2);
if (skb) {
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->stats.rx_bytes += len;
dev->stats.rx_packets++;
} else {
am_writeword (dev, hdraddr + 2, RMD_OWN);
dev->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;
dev->stats.tx_errors++;
status2 = am_readword (dev, hdraddr + 6);
/*
* Clear the error byte
*/
am_writeword (dev, hdraddr + 6, 0);
if (status2 & TST_RTRY)
dev->stats.collisions += 16;
if (status2 & TST_LCOL)
dev->stats.tx_window_errors++;
if (status2 & TST_LCAR)
dev->stats.tx_carrier_errors++;
if (status2 & TST_UFLO)
dev->stats.tx_fifo_errors++;
continue;
}
dev->stats.tx_packets++;
len = am_readword (dev, hdraddr + 4);
dev->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;
dev->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);
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 const struct net_device_ops am79c961_netdev_ops = {
.ndo_open = am79c961_open,
.ndo_stop = am79c961_close,
.ndo_start_xmit = am79c961_sendpacket,
.ndo_set_rx_mode = am79c961_setmulticastlist,
.ndo_tx_timeout = am79c961_timeout,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = am79c961_poll_controller,
#endif
};
static int 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;
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
ret = -ENODEV;
goto nodev;
}
dev->irq = ret;
ret = -ENODEV;
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->netdev_ops = &am79c961_netdev_ops;
ret = register_netdev(dev);
if (ret == 0) {
printk(KERN_INFO "%s: ether address %pM\n",
dev->name, dev->dev_addr);
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);

View file

@ -0,0 +1,145 @@
/*
* linux/drivers/net/ethernet/amd/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 {
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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,813 @@
/*
* Advanced Micro Devices Inc. AMD8111E Linux Network Driver
* Copyright (C) 2003 Advanced Micro Devices
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
Module Name:
amd8111e.h
Abstract:
AMD8111 based 10/100 Ethernet Controller driver definitions.
Environment:
Kernel Mode
Revision History:
3.0.0
Initial Revision.
3.0.1
*/
#ifndef _AMD811E_H
#define _AMD811E_H
/* Command style register access
Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the value bit that specifies the value that will be written into the selected bits of register.
eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered.
*/
/* Offset for Memory Mapped Registers. */
/* 32 bit registers */
#define ASF_STAT 0x00 /* ASF status register */
#define CHIPID 0x04 /* Chip ID regsiter */
#define MIB_DATA 0x10 /* MIB data register */
#define MIB_ADDR 0x14 /* MIB address register */
#define STAT0 0x30 /* Status0 register */
#define INT0 0x38 /* Interrupt0 register */
#define INTEN0 0x40 /* Interrupt0 enable register*/
#define CMD0 0x48 /* Command0 register */
#define CMD2 0x50 /* Command2 register */
#define CMD3 0x54 /* Command3 resiter */
#define CMD7 0x64 /* Command7 register */
#define CTRL1 0x6C /* Control1 register */
#define CTRL2 0x70 /* Control2 register */
#define XMT_RING_LIMIT 0x7C /* Transmit ring limit register */
#define AUTOPOLL0 0x88 /* Auto-poll0 register */
#define AUTOPOLL1 0x8A /* Auto-poll1 register */
#define AUTOPOLL2 0x8C /* Auto-poll2 register */
#define AUTOPOLL3 0x8E /* Auto-poll3 register */
#define AUTOPOLL4 0x90 /* Auto-poll4 register */
#define AUTOPOLL5 0x92 /* Auto-poll5 register */
#define AP_VALUE 0x98 /* Auto-poll value register */
#define DLY_INT_A 0xA8 /* Group A delayed interrupt register */
#define DLY_INT_B 0xAC /* Group B delayed interrupt register */
#define FLOW_CONTROL 0xC8 /* Flow control register */
#define PHY_ACCESS 0xD0 /* PHY access register */
#define STVAL 0xD8 /* Software timer value register */
#define XMT_RING_BASE_ADDR0 0x100 /* Transmit ring0 base addr register */
#define XMT_RING_BASE_ADDR1 0x108 /* Transmit ring1 base addr register */
#define XMT_RING_BASE_ADDR2 0x110 /* Transmit ring2 base addr register */
#define XMT_RING_BASE_ADDR3 0x118 /* Transmit ring2 base addr register */
#define RCV_RING_BASE_ADDR0 0x120 /* Transmit ring0 base addr register */
#define PMAT0 0x190 /* OnNow pattern register0 */
#define PMAT1 0x194 /* OnNow pattern register1 */
/* 16bit registers */
#define XMT_RING_LEN0 0x140 /* Transmit Ring0 length register */
#define XMT_RING_LEN1 0x144 /* Transmit Ring1 length register */
#define XMT_RING_LEN2 0x148 /* Transmit Ring2 length register */
#define XMT_RING_LEN3 0x14C /* Transmit Ring3 length register */
#define RCV_RING_LEN0 0x150 /* Receive Ring0 length register */
#define SRAM_SIZE 0x178 /* SRAM size register */
#define SRAM_BOUNDARY 0x17A /* SRAM boundary register */
/* 48bit register */
#define PADR 0x160 /* Physical address register */
#define IFS1 0x18C /* Inter-frame spacing Part1 register */
#define IFS 0x18D /* Inter-frame spacing register */
#define IPG 0x18E /* Inter-frame gap register */
/* 64bit register */
#define LADRF 0x168 /* Logical address filter register */
/* Register Bit Definitions */
typedef enum {
ASF_INIT_DONE = (1 << 1),
ASF_INIT_PRESENT = (1 << 0),
}STAT_ASF_BITS;
typedef enum {
MIB_CMD_ACTIVE = (1 << 15 ),
MIB_RD_CMD = (1 << 13 ),
MIB_CLEAR = (1 << 12 ),
MIB_ADDRESS = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)|
(1 << 4) | (1 << 5),
}MIB_ADDR_BITS;
typedef enum {
PMAT_DET = (1 << 12),
MP_DET = (1 << 11),
LC_DET = (1 << 10),
SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7),
FULL_DPLX = (1 << 6),
LINK_STATS = (1 << 5),
AUTONEG_COMPLETE = (1 << 4),
MIIPD = (1 << 3),
RX_SUSPENDED = (1 << 2),
TX_SUSPENDED = (1 << 1),
RUNNING = (1 << 0),
}STAT0_BITS;
#define PHY_SPEED_10 0x2
#define PHY_SPEED_100 0x3
/* INT0 0x38, 32bit register */
typedef enum {
INTR = (1 << 31),
PCSINT = (1 << 28),
LCINT = (1 << 27),
APINT5 = (1 << 26),
APINT4 = (1 << 25),
APINT3 = (1 << 24),
TINT_SUM = (1 << 23),
APINT2 = (1 << 22),
APINT1 = (1 << 21),
APINT0 = (1 << 20),
MIIPDTINT = (1 << 19),
MCCINT = (1 << 17),
MREINT = (1 << 16),
RINT_SUM = (1 << 15),
SPNDINT = (1 << 14),
MPINT = (1 << 13),
SINT = (1 << 12),
TINT3 = (1 << 11),
TINT2 = (1 << 10),
TINT1 = (1 << 9),
TINT0 = (1 << 8),
UINT = (1 << 7),
STINT = (1 << 4),
RINT0 = (1 << 0),
}INT0_BITS;
typedef enum {
VAL3 = (1 << 31), /* VAL bit for byte 3 */
VAL2 = (1 << 23), /* VAL bit for byte 2 */
VAL1 = (1 << 15), /* VAL bit for byte 1 */
VAL0 = (1 << 7), /* VAL bit for byte 0 */
}VAL_BITS;
typedef enum {
/* VAL3 */
LCINTEN = (1 << 27),
APINT5EN = (1 << 26),
APINT4EN = (1 << 25),
APINT3EN = (1 << 24),
/* VAL2 */
APINT2EN = (1 << 22),
APINT1EN = (1 << 21),
APINT0EN = (1 << 20),
MIIPDTINTEN = (1 << 19),
MCCIINTEN = (1 << 18),
MCCINTEN = (1 << 17),
MREINTEN = (1 << 16),
/* VAL1 */
SPNDINTEN = (1 << 14),
MPINTEN = (1 << 13),
TINTEN3 = (1 << 11),
SINTEN = (1 << 12),
TINTEN2 = (1 << 10),
TINTEN1 = (1 << 9),
TINTEN0 = (1 << 8),
/* VAL0 */
STINTEN = (1 << 4),
RINTEN0 = (1 << 0),
INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */
}INTEN0_BITS;
typedef enum {
/* VAL2 */
RDMD0 = (1 << 16),
/* VAL1 */
TDMD3 = (1 << 11),
TDMD2 = (1 << 10),
TDMD1 = (1 << 9),
TDMD0 = (1 << 8),
/* VAL0 */
UINTCMD = (1 << 6),
RX_FAST_SPND = (1 << 5),
TX_FAST_SPND = (1 << 4),
RX_SPND = (1 << 3),
TX_SPND = (1 << 2),
INTREN = (1 << 1),
RUN = (1 << 0),
CMD0_CLEAR = 0x000F0F7F, /* Command style register */
}CMD0_BITS;
typedef enum {
/* VAL3 */
CONDUIT_MODE = (1 << 29),
/* VAL2 */
RPA = (1 << 19),
DRCVPA = (1 << 18),
DRCVBC = (1 << 17),
PROM = (1 << 16),
/* VAL1 */
ASTRP_RCV = (1 << 13),
RCV_DROP0 = (1 << 12),
EMBA = (1 << 11),
DXMT2PD = (1 << 10),
LTINTEN = (1 << 9),
DXMTFCS = (1 << 8),
/* VAL0 */
APAD_XMT = (1 << 6),
DRTY = (1 << 5),
INLOOP = (1 << 4),
EXLOOP = (1 << 3),
REX_RTRY = (1 << 2),
REX_UFLO = (1 << 1),
REX_LCOL = (1 << 0),
CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */
}CMD2_BITS;
typedef enum {
/* VAL3 */
ASF_INIT_DONE_ALIAS = (1 << 29),
/* VAL2 */
JUMBO = (1 << 21),
VSIZE = (1 << 20),
VLONLY = (1 << 19),
VL_TAG_DEL = (1 << 18),
/* VAL1 */
EN_PMGR = (1 << 14),
INTLEVEL = (1 << 13),
FORCE_FULL_DUPLEX = (1 << 12),
FORCE_LINK_STATUS = (1 << 11),
APEP = (1 << 10),
MPPLBA = (1 << 9),
/* VAL0 */
RESET_PHY_PULSE = (1 << 2),
RESET_PHY = (1 << 1),
PHY_RST_POL = (1 << 0),
}CMD3_BITS;
typedef enum {
/* VAL0 */
PMAT_SAVE_MATCH = (1 << 4),
PMAT_MODE = (1 << 3),
MPEN_SW = (1 << 1),
LCMODE_SW = (1 << 0),
CMD7_CLEAR = 0x0000001B /* Command style register */
}CMD7_BITS;
typedef enum {
RESET_PHY_WIDTH = (0xF << 16) | (0xF<< 20), /* 0x00FF0000 */
XMTSP_MASK = (1 << 9) | (1 << 8), /* 9:8 */
XMTSP_128 = (1 << 9), /* 9 */
XMTSP_64 = (1 << 8),
CACHE_ALIGN = (1 << 4),
BURST_LIMIT_MASK = (0xF << 0 ),
CTRL1_DEFAULT = 0x00010111,
}CTRL1_BITS;
typedef enum {
FMDC_MASK = (1 << 9)|(1 << 8), /* 9:8 */
XPHYRST = (1 << 7),
XPHYANE = (1 << 6),
XPHYFD = (1 << 5),
XPHYSP = (1 << 4) | (1 << 3), /* 4:3 */
APDW_MASK = (1 << 2) | (1 << 1) | (1 << 0), /* 2:0 */
}CTRL2_BITS;
/* XMT_RING_LIMIT 0x7C, 32bit register */
typedef enum {
XMT_RING2_LIMIT = (0xFF << 16), /* 23:16 */
XMT_RING1_LIMIT = (0xFF << 8), /* 15:8 */
XMT_RING0_LIMIT = (0xFF << 0), /* 7:0 */
}XMT_RING_LIMIT_BITS;
typedef enum {
AP_REG0_EN = (1 << 15),
AP_REG0_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PHY0_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL0_BITS;
/* AUTOPOLL1 0x8A, 16bit register */
typedef enum {
AP_REG1_EN = (1 << 15),
AP_REG1_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PRE_SUP1 = (1 << 6),
AP_PHY1_DFLT = (1 << 5),
AP_PHY1_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL1_BITS;
typedef enum {
AP_REG2_EN = (1 << 15),
AP_REG2_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PRE_SUP2 = (1 << 6),
AP_PHY2_DFLT = (1 << 5),
AP_PHY2_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL2_BITS;
typedef enum {
AP_REG3_EN = (1 << 15),
AP_REG3_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PRE_SUP3 = (1 << 6),
AP_PHY3_DFLT = (1 << 5),
AP_PHY3_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL3_BITS;
typedef enum {
AP_REG4_EN = (1 << 15),
AP_REG4_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PRE_SUP4 = (1 << 6),
AP_PHY4_DFLT = (1 << 5),
AP_PHY4_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL4_BITS;
typedef enum {
AP_REG5_EN = (1 << 15),
AP_REG5_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */
AP_PRE_SUP5 = (1 << 6),
AP_PHY5_DFLT = (1 << 5),
AP_PHY5_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */
}AUTOPOLL5_BITS;
/* AP_VALUE 0x98, 32bit ragister */
typedef enum {
AP_VAL_ACTIVE = (1 << 31),
AP_VAL_RD_CMD = ( 1 << 29),
AP_ADDR = (1 << 18)|(1 << 17)|(1 << 16), /* 18:16 */
AP_VAL = (0xF << 0) | (0xF << 4) |( 0xF << 8) |
(0xF << 12), /* 15:0 */
}AP_VALUE_BITS;
typedef enum {
DLY_INT_A_R3 = (1 << 31),
DLY_INT_A_R2 = (1 << 30),
DLY_INT_A_R1 = (1 << 29),
DLY_INT_A_R0 = (1 << 28),
DLY_INT_A_T3 = (1 << 27),
DLY_INT_A_T2 = (1 << 26),
DLY_INT_A_T1 = (1 << 25),
DLY_INT_A_T0 = ( 1 << 24),
EVENT_COUNT_A = (0xF << 16) | (0x1 << 20),/* 20:16 */
MAX_DELAY_TIME_A = (0xF << 0) | (0xF << 4) | (1 << 8)|
(1 << 9) | (1 << 10), /* 10:0 */
}DLY_INT_A_BITS;
typedef enum {
DLY_INT_B_R3 = (1 << 31),
DLY_INT_B_R2 = (1 << 30),
DLY_INT_B_R1 = (1 << 29),
DLY_INT_B_R0 = (1 << 28),
DLY_INT_B_T3 = (1 << 27),
DLY_INT_B_T2 = (1 << 26),
DLY_INT_B_T1 = (1 << 25),
DLY_INT_B_T0 = ( 1 << 24),
EVENT_COUNT_B = (0xF << 16) | (0x1 << 20),/* 20:16 */
MAX_DELAY_TIME_B = (0xF << 0) | (0xF << 4) | (1 << 8)|
(1 << 9) | (1 << 10), /* 10:0 */
}DLY_INT_B_BITS;
/* FLOW_CONTROL 0xC8, 32bit register */
typedef enum {
PAUSE_LEN_CHG = (1 << 30),
FTPE = (1 << 22),
FRPE = (1 << 21),
NAPA = (1 << 20),
NPA = (1 << 19),
FIXP = ( 1 << 18),
FCCMD = ( 1 << 16),
PAUSE_LEN = (0xF << 0) | (0xF << 4) |( 0xF << 8) | (0xF << 12), /* 15:0 */
}FLOW_CONTROL_BITS;
/* PHY_ ACCESS 0xD0, 32bit register */
typedef enum {
PHY_CMD_ACTIVE = (1 << 31),
PHY_WR_CMD = (1 << 30),
PHY_RD_CMD = (1 << 29),
PHY_RD_ERR = (1 << 28),
PHY_PRE_SUP = (1 << 27),
PHY_ADDR = (1 << 21) | (1 << 22) | (1 << 23)|
(1 << 24) |(1 << 25),/* 25:21 */
PHY_REG_ADDR = (1 << 16) | (1 << 17) | (1 << 18)| (1 << 19) | (1 << 20),/* 20:16 */
PHY_DATA = (0xF << 0)|(0xF << 4) |(0xF << 8)|
(0xF << 12),/* 15:0 */
}PHY_ACCESS_BITS;
/* PMAT0 0x190, 32bit register */
typedef enum {
PMR_ACTIVE = (1 << 31),
PMR_WR_CMD = (1 << 30),
PMR_RD_CMD = (1 << 29),
PMR_BANK = (1 <<28),
PMR_ADDR = (0xF << 16)|(1 << 20)|(1 << 21)|
(1 << 22),/* 22:16 */
PMR_B4 = (0xF << 0) | (0xF << 4),/* 15:0 */
}PMAT0_BITS;
/* PMAT1 0x194, 32bit register */
typedef enum {
PMR_B3 = (0xF << 24) | (0xF <<28),/* 31:24 */
PMR_B2 = (0xF << 16) |(0xF << 20),/* 23:16 */
PMR_B1 = (0xF << 8) | (0xF <<12), /* 15:8 */
PMR_B0 = (0xF << 0)|(0xF << 4),/* 7:0 */
}PMAT1_BITS;
/************************************************************************/
/* */
/* MIB counter definitions */
/* */
/************************************************************************/
#define rcv_miss_pkts 0x00
#define rcv_octets 0x01
#define rcv_broadcast_pkts 0x02
#define rcv_multicast_pkts 0x03
#define rcv_undersize_pkts 0x04
#define rcv_oversize_pkts 0x05
#define rcv_fragments 0x06
#define rcv_jabbers 0x07
#define rcv_unicast_pkts 0x08
#define rcv_alignment_errors 0x09
#define rcv_fcs_errors 0x0A
#define rcv_good_octets 0x0B
#define rcv_mac_ctrl 0x0C
#define rcv_flow_ctrl 0x0D
#define rcv_pkts_64_octets 0x0E
#define rcv_pkts_65to127_octets 0x0F
#define rcv_pkts_128to255_octets 0x10
#define rcv_pkts_256to511_octets 0x11
#define rcv_pkts_512to1023_octets 0x12
#define rcv_pkts_1024to1518_octets 0x13
#define rcv_unsupported_opcode 0x14
#define rcv_symbol_errors 0x15
#define rcv_drop_pkts_ring1 0x16
#define rcv_drop_pkts_ring2 0x17
#define rcv_drop_pkts_ring3 0x18
#define rcv_drop_pkts_ring4 0x19
#define rcv_jumbo_pkts 0x1A
#define xmt_underrun_pkts 0x20
#define xmt_octets 0x21
#define xmt_packets 0x22
#define xmt_broadcast_pkts 0x23
#define xmt_multicast_pkts 0x24
#define xmt_collisions 0x25
#define xmt_unicast_pkts 0x26
#define xmt_one_collision 0x27
#define xmt_multiple_collision 0x28
#define xmt_deferred_transmit 0x29
#define xmt_late_collision 0x2A
#define xmt_excessive_defer 0x2B
#define xmt_loss_carrier 0x2C
#define xmt_excessive_collision 0x2D
#define xmt_back_pressure 0x2E
#define xmt_flow_ctrl 0x2F
#define xmt_pkts_64_octets 0x30
#define xmt_pkts_65to127_octets 0x31
#define xmt_pkts_128to255_octets 0x32
#define xmt_pkts_256to511_octets 0x33
#define xmt_pkts_512to1023_octets 0x34
#define xmt_pkts_1024to1518_octet 0x35
#define xmt_oversize_pkts 0x36
#define xmt_jumbo_pkts 0x37
/* Driver definitions */
#define PCI_VENDOR_ID_AMD 0x1022
#define PCI_DEVICE_ID_AMD8111E_7462 0x7462
#define MAX_UNITS 8 /* Maximum number of devices possible */
#define NUM_TX_BUFFERS 32 /* Number of transmit buffers */
#define NUM_RX_BUFFERS 32 /* Number of receive buffers */
#define TX_BUFF_MOD_MASK 31 /* (NUM_TX_BUFFERS -1) */
#define RX_BUFF_MOD_MASK 31 /* (NUM_RX_BUFFERS -1) */
#define NUM_TX_RING_DR 32
#define NUM_RX_RING_DR 32
#define TX_RING_DR_MOD_MASK 31 /* (NUM_TX_RING_DR -1) */
#define RX_RING_DR_MOD_MASK 31 /* (NUM_RX_RING_DR -1) */
#define MAX_FILTER_SIZE 64 /* Maximum multicast address */
#define AMD8111E_MIN_MTU 60
#define AMD8111E_MAX_MTU 9000
#define PKT_BUFF_SZ 1536
#define MIN_PKT_LEN 60
#define AMD8111E_TX_TIMEOUT (3 * HZ)/* 3 sec */
#define SOFT_TIMER_FREQ 0xBEBC /* 0.5 sec */
#define DELAY_TIMER_CONV 50 /* msec to 10 usec conversion.
Only 500 usec resolution */
#define OPTION_VLAN_ENABLE 0x0001
#define OPTION_JUMBO_ENABLE 0x0002
#define OPTION_MULTICAST_ENABLE 0x0004
#define OPTION_WOL_ENABLE 0x0008
#define OPTION_WAKE_MAGIC_ENABLE 0x0010
#define OPTION_WAKE_PHY_ENABLE 0x0020
#define OPTION_INTR_COAL_ENABLE 0x0040
#define OPTION_DYN_IPG_ENABLE 0x0080
#define PHY_REG_ADDR_MASK 0x1f
/* ipg parameters */
#define DEFAULT_IPG 0x60
#define IFS1_DELTA 36
#define IPG_CONVERGE_JIFFIES (HZ/2)
#define IPG_STABLE_TIME 5
#define MIN_IPG 96
#define MAX_IPG 255
#define IPG_STEP 16
#define CSTATE 1
#define SSTATE 2
/* Assume contoller gets data 10 times the maximum processing time */
#define REPEAT_CNT 10
/* amd8111e decriptor flag definitions */
typedef enum {
OWN_BIT = (1 << 15),
ADD_FCS_BIT = (1 << 13),
LTINT_BIT = (1 << 12),
STP_BIT = (1 << 9),
ENP_BIT = (1 << 8),
KILL_BIT = (1 << 6),
TCC_VLAN_INSERT = (1 << 1),
TCC_VLAN_REPLACE = (1 << 1) |( 1<< 0),
}TX_FLAG_BITS;
typedef enum {
ERR_BIT = (1 << 14),
FRAM_BIT = (1 << 13),
OFLO_BIT = (1 << 12),
CRC_BIT = (1 << 11),
PAM_BIT = (1 << 6),
LAFM_BIT = (1 << 5),
BAM_BIT = (1 << 4),
TT_VLAN_TAGGED = (1 << 3) |(1 << 2),/* 0x000 */
TT_PRTY_TAGGED = (1 << 3),/* 0x0008 */
}RX_FLAG_BITS;
#define RESET_RX_FLAGS 0x0000
#define TT_MASK 0x000c
#define TCC_MASK 0x0003
/* driver ioctl parameters */
#define AMD8111E_REG_DUMP_LEN 13*sizeof(u32)
/* amd8111e desriptor format */
struct amd8111e_tx_dr{
__le16 buff_count; /* Size of the buffer pointed by this descriptor */
__le16 tx_flags;
__le16 tag_ctrl_info;
__le16 tag_ctrl_cmd;
__le32 buff_phy_addr;
__le32 reserved;
};
struct amd8111e_rx_dr{
__le32 reserved;
__le16 msg_count; /* Received message len */
__le16 tag_ctrl_info;
__le16 buff_count; /* Len of the buffer pointed by descriptor. */
__le16 rx_flags;
__le32 buff_phy_addr;
};
struct amd8111e_link_config{
#define SPEED_INVALID 0xffff
#define DUPLEX_INVALID 0xff
#define AUTONEG_INVALID 0xff
unsigned long orig_phy_option;
u16 speed;
u8 duplex;
u8 autoneg;
u8 reserved; /* 32bit alignment */
};
enum coal_type{
NO_COALESCE,
LOW_COALESCE,
MEDIUM_COALESCE,
HIGH_COALESCE,
};
enum coal_mode{
RX_INTR_COAL,
TX_INTR_COAL,
DISABLE_COAL,
ENABLE_COAL,
};
#define MAX_TIMEOUT 40
#define MAX_EVENT_COUNT 31
struct amd8111e_coalesce_conf{
unsigned int rx_timeout;
unsigned int rx_event_count;
unsigned long rx_packets;
unsigned long rx_prev_packets;
unsigned long rx_bytes;
unsigned long rx_prev_bytes;
unsigned int rx_coal_type;
unsigned int tx_timeout;
unsigned int tx_event_count;
unsigned long tx_packets;
unsigned long tx_prev_packets;
unsigned long tx_bytes;
unsigned long tx_prev_bytes;
unsigned int tx_coal_type;
};
struct ipg_info{
unsigned int ipg_state;
unsigned int ipg;
unsigned int current_ipg;
unsigned int col_cnt;
unsigned int diff_col_cnt;
unsigned int timer_tick;
unsigned int prev_ipg;
struct timer_list ipg_timer;
};
struct amd8111e_priv{
struct amd8111e_tx_dr* tx_ring;
struct amd8111e_rx_dr* rx_ring;
dma_addr_t tx_ring_dma_addr; /* tx descriptor ring base address */
dma_addr_t rx_ring_dma_addr; /* rx descriptor ring base address */
const char *name;
struct pci_dev *pci_dev; /* Ptr to the associated pci_dev */
struct net_device* amd8111e_net_dev; /* ptr to associated net_device */
/* Transmit and receive skbs */
struct sk_buff *tx_skbuff[NUM_TX_BUFFERS];
struct sk_buff *rx_skbuff[NUM_RX_BUFFERS];
/* Transmit and receive dma mapped addr */
dma_addr_t tx_dma_addr[NUM_TX_BUFFERS];
dma_addr_t rx_dma_addr[NUM_RX_BUFFERS];
/* Reg memory mapped address */
void __iomem *mmio;
struct napi_struct napi;
spinlock_t lock; /* Guard lock */
unsigned long rx_idx, tx_idx; /* The next free ring entry */
unsigned long tx_complete_idx;
unsigned long tx_ring_complete_idx;
unsigned long tx_ring_idx;
unsigned int rx_buff_len; /* Buffer length of rx buffers */
int options; /* Options enabled/disabled for the device */
unsigned long ext_phy_option;
int ext_phy_addr;
u32 ext_phy_id;
struct amd8111e_link_config link_config;
int pm_cap;
struct net_device *next;
int mii;
struct mii_if_info mii_if;
char opened;
unsigned int drv_rx_errors;
struct amd8111e_coalesce_conf coal_conf;
struct ipg_info ipg_data;
};
/* kernel provided writeq does not write 64 bits into the amd8111e device register instead writes only higher 32bits data into lower 32bits of the register.
BUG? */
#define amd8111e_writeq(_UlData,_memMap) \
writel(*(u32*)(&_UlData), _memMap); \
writel(*(u32*)((u8*)(&_UlData)+4), _memMap+4)
/* maps the external speed options to internal value */
typedef enum {
SPEED_AUTONEG,
SPEED10_HALF,
SPEED10_FULL,
SPEED100_HALF,
SPEED100_FULL,
}EXT_PHY_OPTION;
static int card_idx;
static int speed_duplex[MAX_UNITS] = { 0, };
static bool coalesce[MAX_UNITS] = { [ 0 ... MAX_UNITS-1] = true };
static bool dynamic_ipg[MAX_UNITS] = { [ 0 ... MAX_UNITS-1] = false };
static unsigned int chip_version;
#endif /* _AMD8111E_H */

View file

@ -0,0 +1,792 @@
/*
* Amiga Linux/m68k Ariadne Ethernet Driver
*
* © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org)
* Peter De Schrijver (p2@mind.be)
*
* ---------------------------------------------------------------------------
*
* This program is based on
*
* lance.c: An AMD LANCE ethernet driver for linux.
* Written 1993-94 by Donald Becker.
*
* Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
* Advanced Micro Devices
* Publication #16907, Rev. B, Amendment/0, May 1994
*
* MC68230: Parallel Interface/Timer (PI/T)
* Motorola Semiconductors, December, 1983
*
* ---------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ---------------------------------------------------------------------------
*
* The Ariadne is a Zorro-II board made by Village Tronic. It contains:
*
* - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
* 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
*
* - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/*#define DEBUG*/
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/zorro.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
#include "ariadne.h"
#ifdef ARIADNE_DEBUG
int ariadne_debug = ARIADNE_DEBUG;
#else
int ariadne_debug = 1;
#endif
/* Macros to Fix Endianness problems */
/* Swap the Bytes in a WORD */
#define swapw(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00))
/* Get the Low BYTE in a WORD */
#define lowb(x) (x & 0xff)
/* Get the Swapped High WORD in a LONG */
#define swhighw(x) ((((x) >> 8) & 0xff00) | (((x) >> 24) & 0x00ff))
/* Get the Swapped Low WORD in a LONG */
#define swloww(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff))
/* Transmit/Receive Ring Definitions */
#define TX_RING_SIZE 5
#define RX_RING_SIZE 16
#define PKT_BUF_SIZE 1520
/* Private Device Data */
struct ariadne_private {
volatile struct TDRE *tx_ring[TX_RING_SIZE];
volatile struct RDRE *rx_ring[RX_RING_SIZE];
volatile u_short *tx_buff[TX_RING_SIZE];
volatile u_short *rx_buff[RX_RING_SIZE];
int cur_tx, cur_rx; /* The next free ring entry */
int dirty_tx; /* The ring entries to be free()ed */
char tx_full;
};
/* Structure Created in the Ariadne's RAM Buffer */
struct lancedata {
struct TDRE tx_ring[TX_RING_SIZE];
struct RDRE rx_ring[RX_RING_SIZE];
u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)];
};
static void memcpyw(volatile u_short *dest, u_short *src, int len)
{
while (len >= 2) {
*(dest++) = *(src++);
len -= 2;
}
if (len == 1)
*dest = (*(u_char *)src) << 8;
}
static void ariadne_init_ring(struct net_device *dev)
{
struct ariadne_private *priv = netdev_priv(dev);
volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start;
int i;
netif_stop_queue(dev);
priv->tx_full = 0;
priv->cur_rx = priv->cur_tx = 0;
priv->dirty_tx = 0;
/* Set up TX Ring */
for (i = 0; i < TX_RING_SIZE; i++) {
volatile struct TDRE *t = &lancedata->tx_ring[i];
t->TMD0 = swloww(ARIADNE_RAM +
offsetof(struct lancedata, tx_buff[i]));
t->TMD1 = swhighw(ARIADNE_RAM +
offsetof(struct lancedata, tx_buff[i])) |
TF_STP | TF_ENP;
t->TMD2 = swapw((u_short)-PKT_BUF_SIZE);
t->TMD3 = 0;
priv->tx_ring[i] = &lancedata->tx_ring[i];
priv->tx_buff[i] = lancedata->tx_buff[i];
netdev_dbg(dev, "TX Entry %2d at %p, Buf at %p\n",
i, &lancedata->tx_ring[i], lancedata->tx_buff[i]);
}
/* Set up RX Ring */
for (i = 0; i < RX_RING_SIZE; i++) {
volatile struct RDRE *r = &lancedata->rx_ring[i];
r->RMD0 = swloww(ARIADNE_RAM +
offsetof(struct lancedata, rx_buff[i]));
r->RMD1 = swhighw(ARIADNE_RAM +
offsetof(struct lancedata, rx_buff[i])) |
RF_OWN;
r->RMD2 = swapw((u_short)-PKT_BUF_SIZE);
r->RMD3 = 0x0000;
priv->rx_ring[i] = &lancedata->rx_ring[i];
priv->rx_buff[i] = lancedata->rx_buff[i];
netdev_dbg(dev, "RX Entry %2d at %p, Buf at %p\n",
i, &lancedata->rx_ring[i], lancedata->rx_buff[i]);
}
}
static int ariadne_rx(struct net_device *dev)
{
struct ariadne_private *priv = netdev_priv(dev);
int entry = priv->cur_rx % RX_RING_SIZE;
int i;
/* If we own the next entry, it's a new packet. Send it up */
while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) {
int status = lowb(priv->rx_ring[entry]->RMD1);
if (status != (RF_STP | RF_ENP)) { /* There was an error */
/* There is a tricky error noted by
* John Murphy <murf@perftech.com> to Russ Nelson:
* Even with full-sized buffers it's possible for a
* jabber packet to use two buffers, with only the
* last correctly noting the error
*/
/* Only count a general error at the end of a packet */
if (status & RF_ENP)
dev->stats.rx_errors++;
if (status & RF_FRAM)
dev->stats.rx_frame_errors++;
if (status & RF_OFLO)
dev->stats.rx_over_errors++;
if (status & RF_CRC)
dev->stats.rx_crc_errors++;
if (status & RF_BUFF)
dev->stats.rx_fifo_errors++;
priv->rx_ring[entry]->RMD1 &= 0xff00 | RF_STP | RF_ENP;
} else {
/* Malloc up new buffer, compatible with net-3 */
short pkt_len = swapw(priv->rx_ring[entry]->RMD3);
struct sk_buff *skb;
skb = netdev_alloc_skb(dev, pkt_len + 2);
if (skb == NULL) {
for (i = 0; i < RX_RING_SIZE; i++)
if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN)
break;
if (i > RX_RING_SIZE - 2) {
dev->stats.rx_dropped++;
priv->rx_ring[entry]->RMD1 |= RF_OWN;
priv->cur_rx++;
}
break;
}
skb_reserve(skb, 2); /* 16 byte align */
skb_put(skb, pkt_len); /* Make room */
skb_copy_to_linear_data(skb,
(const void *)priv->rx_buff[entry],
pkt_len);
skb->protocol = eth_type_trans(skb, dev);
netdev_dbg(dev, "RX pkt type 0x%04x from %pM to %pM data %p len %u\n",
((u_short *)skb->data)[6],
skb->data + 6, skb->data,
skb->data, skb->len);
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
priv->rx_ring[entry]->RMD1 |= RF_OWN;
entry = (++priv->cur_rx) % RX_RING_SIZE;
}
priv->cur_rx = priv->cur_rx % RX_RING_SIZE;
/* We should check that at least two ring entries are free.
* If not, we should free one and mark stats->rx_dropped++
*/
return 0;
}
static irqreturn_t ariadne_interrupt(int irq, void *data)
{
struct net_device *dev = (struct net_device *)data;
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
struct ariadne_private *priv;
int csr0, boguscnt;
int handled = 0;
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
if (!(lance->RDP & INTR)) /* Check if any interrupt has been */
return IRQ_NONE; /* generated by the board */
priv = netdev_priv(dev);
boguscnt = 10;
while ((csr0 = lance->RDP) & (ERR | RINT | TINT) && --boguscnt >= 0) {
/* Acknowledge all of the current interrupt sources ASAP */
lance->RDP = csr0 & ~(INEA | TDMD | STOP | STRT | INIT);
#ifdef DEBUG
if (ariadne_debug > 5) {
netdev_dbg(dev, "interrupt csr0=%#02x new csr=%#02x [",
csr0, lance->RDP);
if (csr0 & INTR)
pr_cont(" INTR");
if (csr0 & INEA)
pr_cont(" INEA");
if (csr0 & RXON)
pr_cont(" RXON");
if (csr0 & TXON)
pr_cont(" TXON");
if (csr0 & TDMD)
pr_cont(" TDMD");
if (csr0 & STOP)
pr_cont(" STOP");
if (csr0 & STRT)
pr_cont(" STRT");
if (csr0 & INIT)
pr_cont(" INIT");
if (csr0 & ERR)
pr_cont(" ERR");
if (csr0 & BABL)
pr_cont(" BABL");
if (csr0 & CERR)
pr_cont(" CERR");
if (csr0 & MISS)
pr_cont(" MISS");
if (csr0 & MERR)
pr_cont(" MERR");
if (csr0 & RINT)
pr_cont(" RINT");
if (csr0 & TINT)
pr_cont(" TINT");
if (csr0 & IDON)
pr_cont(" IDON");
pr_cont(" ]\n");
}
#endif
if (csr0 & RINT) { /* Rx interrupt */
handled = 1;
ariadne_rx(dev);
}
if (csr0 & TINT) { /* Tx-done interrupt */
int dirty_tx = priv->dirty_tx;
handled = 1;
while (dirty_tx < priv->cur_tx) {
int entry = dirty_tx % TX_RING_SIZE;
int status = lowb(priv->tx_ring[entry]->TMD1);
if (status & TF_OWN)
break; /* It still hasn't been Txed */
priv->tx_ring[entry]->TMD1 &= 0xff00;
if (status & TF_ERR) {
/* There was an major error, log it */
int err_status = priv->tx_ring[entry]->TMD3;
dev->stats.tx_errors++;
if (err_status & EF_RTRY)
dev->stats.tx_aborted_errors++;
if (err_status & EF_LCAR)
dev->stats.tx_carrier_errors++;
if (err_status & EF_LCOL)
dev->stats.tx_window_errors++;
if (err_status & EF_UFLO) {
/* Ackk! On FIFO errors the Tx unit is turned off! */
dev->stats.tx_fifo_errors++;
/* Remove this verbosity later! */
netdev_err(dev, "Tx FIFO error! Status %04x\n",
csr0);
/* Restart the chip */
lance->RDP = STRT;
}
} else {
if (status & (TF_MORE | TF_ONE))
dev->stats.collisions++;
dev->stats.tx_packets++;
}
dirty_tx++;
}
#ifndef final_version
if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) {
netdev_err(dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n",
dirty_tx, priv->cur_tx,
priv->tx_full);
dirty_tx += TX_RING_SIZE;
}
#endif
if (priv->tx_full && netif_queue_stopped(dev) &&
dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) {
/* The ring is no longer full */
priv->tx_full = 0;
netif_wake_queue(dev);
}
priv->dirty_tx = dirty_tx;
}
/* Log misc errors */
if (csr0 & BABL) {
handled = 1;
dev->stats.tx_errors++; /* Tx babble */
}
if (csr0 & MISS) {
handled = 1;
dev->stats.rx_errors++; /* Missed a Rx frame */
}
if (csr0 & MERR) {
handled = 1;
netdev_err(dev, "Bus master arbitration failure, status %04x\n",
csr0);
/* Restart the chip */
lance->RDP = STRT;
}
}
/* Clear any other interrupt, and set interrupt enable */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA | BABL | CERR | MISS | MERR | IDON;
if (ariadne_debug > 4)
netdev_dbg(dev, "exiting interrupt, csr%d=%#04x\n",
lance->RAP, lance->RDP);
return IRQ_RETVAL(handled);
}
static int ariadne_open(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
u_short in;
u_long version;
int i;
/* Reset the LANCE */
in = lance->Reset;
/* Stop the LANCE */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = STOP;
/* Check the LANCE version */
lance->RAP = CSR88; /* Chip ID */
version = swapw(lance->RDP);
lance->RAP = CSR89; /* Chip ID */
version |= swapw(lance->RDP) << 16;
if ((version & 0x00000fff) != 0x00000003) {
pr_warn("Couldn't find AMD Ethernet Chip\n");
return -EAGAIN;
}
if ((version & 0x0ffff000) != 0x00003000) {
pr_warn("Couldn't find Am79C960 (Wrong part number = %ld)\n",
(version & 0x0ffff000) >> 12);
return -EAGAIN;
}
netdev_dbg(dev, "Am79C960 (PCnet-ISA) Revision %ld\n",
(version & 0xf0000000) >> 28);
ariadne_init_ring(dev);
/* Miscellaneous Stuff */
lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */
lance->RDP = 0x0000;
lance->RAP = CSR4; /* Test and Features Control */
lance->RDP = DPOLL | APAD_XMT | MFCOM | RCVCCOM | TXSTRTM | JABM;
/* Set the Multicast Table */
lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */
lance->RDP = 0x0000;
lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */
lance->RDP = 0x0000;
lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */
lance->RDP = 0x0000;
lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */
lance->RDP = 0x0000;
/* Set the Ethernet Hardware Address */
lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */
lance->RDP = ((u_short *)&dev->dev_addr[0])[0];
lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */
lance->RDP = ((u_short *)&dev->dev_addr[0])[1];
lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */
lance->RDP = ((u_short *)&dev->dev_addr[0])[2];
/* Set the Init Block Mode */
lance->RAP = CSR15; /* Mode Register */
lance->RDP = 0x0000;
/* Set the Transmit Descriptor Ring Pointer */
lance->RAP = CSR30; /* Base Address of Transmit Ring */
lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
lance->RAP = CSR31; /* Base Address of transmit Ring */
lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, tx_ring));
/* Set the Receive Descriptor Ring Pointer */
lance->RAP = CSR24; /* Base Address of Receive Ring */
lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
lance->RAP = CSR25; /* Base Address of Receive Ring */
lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, rx_ring));
/* Set the Number of RX and TX Ring Entries */
lance->RAP = CSR76; /* Receive Ring Length */
lance->RDP = swapw(((u_short)-RX_RING_SIZE));
lance->RAP = CSR78; /* Transmit Ring Length */
lance->RDP = swapw(((u_short)-TX_RING_SIZE));
/* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */
lance->RAP = ISACSR2; /* Miscellaneous Configuration */
lance->IDP = ASEL;
/* LED Control */
lance->RAP = ISACSR5; /* LED1 Status */
lance->IDP = PSE|XMTE;
lance->RAP = ISACSR6; /* LED2 Status */
lance->IDP = PSE|COLE;
lance->RAP = ISACSR7; /* LED3 Status */
lance->IDP = PSE|RCVE;
netif_start_queue(dev);
i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED,
dev->name, dev);
if (i)
return i;
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA | STRT;
return 0;
}
static int ariadne_close(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
netif_stop_queue(dev);
lance->RAP = CSR112; /* Missed Frame Count */
dev->stats.rx_missed_errors = swapw(lance->RDP);
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
if (ariadne_debug > 1) {
netdev_dbg(dev, "Shutting down ethercard, status was %02x\n",
lance->RDP);
netdev_dbg(dev, "%lu packets missed\n",
dev->stats.rx_missed_errors);
}
/* We stop the LANCE here -- it occasionally polls memory if we don't */
lance->RDP = STOP;
free_irq(IRQ_AMIGA_PORTS, dev);
return 0;
}
static inline void ariadne_reset(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = STOP;
ariadne_init_ring(dev);
lance->RDP = INEA | STRT;
netif_start_queue(dev);
}
static void ariadne_tx_timeout(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
netdev_err(dev, "transmit timed out, status %04x, resetting\n",
lance->RDP);
ariadne_reset(dev);
netif_wake_queue(dev);
}
static netdev_tx_t ariadne_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ariadne_private *priv = netdev_priv(dev);
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
int entry;
unsigned long flags;
int len = skb->len;
#if 0
if (ariadne_debug > 3) {
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
netdev_dbg(dev, "%s: csr0 %04x\n", __func__, lance->RDP);
lance->RDP = 0x0000;
}
#endif
/* FIXME: is the 79C960 new enough to do its own padding right ? */
if (skb->len < ETH_ZLEN) {
if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
len = ETH_ZLEN;
}
/* Fill in a Tx ring entry */
netdev_dbg(dev, "TX pkt type 0x%04x from %pM to %pM data %p len %u\n",
((u_short *)skb->data)[6],
skb->data + 6, skb->data,
skb->data, skb->len);
local_irq_save(flags);
entry = priv->cur_tx % TX_RING_SIZE;
/* Caution: the write order is important here, set the base address with
the "ownership" bits last */
priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len);
priv->tx_ring[entry]->TMD3 = 0x0000;
memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len);
#ifdef DEBUG
print_hex_dump(KERN_DEBUG, "tx_buff: ", DUMP_PREFIX_OFFSET, 16, 1,
(void *)priv->tx_buff[entry],
skb->len > 64 ? 64 : skb->len, true);
#endif
priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1 & 0xff00)
| TF_OWN | TF_STP | TF_ENP;
dev_kfree_skb(skb);
priv->cur_tx++;
if ((priv->cur_tx >= TX_RING_SIZE) &&
(priv->dirty_tx >= TX_RING_SIZE)) {
netdev_dbg(dev, "*** Subtracting TX_RING_SIZE from cur_tx (%d) and dirty_tx (%d)\n",
priv->cur_tx, priv->dirty_tx);
priv->cur_tx -= TX_RING_SIZE;
priv->dirty_tx -= TX_RING_SIZE;
}
dev->stats.tx_bytes += len;
/* Trigger an immediate send poll */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA | TDMD;
if (lowb(priv->tx_ring[(entry + 1) % TX_RING_SIZE]->TMD1) != 0) {
netif_stop_queue(dev);
priv->tx_full = 1;
}
local_irq_restore(flags);
return NETDEV_TX_OK;
}
static struct net_device_stats *ariadne_get_stats(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
short saved_addr;
unsigned long flags;
local_irq_save(flags);
saved_addr = lance->RAP;
lance->RAP = CSR112; /* Missed Frame Count */
dev->stats.rx_missed_errors = swapw(lance->RDP);
lance->RAP = saved_addr;
local_irq_restore(flags);
return &dev->stats;
}
/* Set or clear the multicast filter for this adaptor.
* num_addrs == -1 Promiscuous mode, receive all packets
* num_addrs == 0 Normal mode, clear multicast list
* num_addrs > 0 Multicast mode, receive normal and MC packets,
* and do best-effort filtering.
*/
static void set_multicast_list(struct net_device *dev)
{
volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr;
if (!netif_running(dev))
return;
netif_stop_queue(dev);
/* We take the simple way out and always enable promiscuous mode */
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = STOP; /* Temporarily stop the lance */
ariadne_init_ring(dev);
if (dev->flags & IFF_PROMISC) {
lance->RAP = CSR15; /* Mode Register */
lance->RDP = PROM; /* Set promiscuous mode */
} else {
short multicast_table[4];
int num_addrs = netdev_mc_count(dev);
int i;
/* We don't use the multicast table,
* but rely on upper-layer filtering
*/
memset(multicast_table, (num_addrs == 0) ? 0 : -1,
sizeof(multicast_table));
for (i = 0; i < 4; i++) {
lance->RAP = CSR8 + (i << 8);
/* Logical Address Filter */
lance->RDP = swapw(multicast_table[i]);
}
lance->RAP = CSR15; /* Mode Register */
lance->RDP = 0x0000; /* Unset promiscuous mode */
}
lance->RAP = CSR0; /* PCnet-ISA Controller Status */
lance->RDP = INEA | STRT | IDON;/* Resume normal operation */
netif_wake_queue(dev);
}
static void ariadne_remove_one(struct zorro_dev *z)
{
struct net_device *dev = zorro_get_drvdata(z);
unregister_netdev(dev);
release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960));
release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE);
free_netdev(dev);
}
static struct zorro_device_id ariadne_zorro_tbl[] = {
{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE },
{ 0 }
};
MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl);
static const struct net_device_ops ariadne_netdev_ops = {
.ndo_open = ariadne_open,
.ndo_stop = ariadne_close,
.ndo_start_xmit = ariadne_start_xmit,
.ndo_tx_timeout = ariadne_tx_timeout,
.ndo_get_stats = ariadne_get_stats,
.ndo_set_rx_mode = set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
};
static int ariadne_init_one(struct zorro_dev *z,
const struct zorro_device_id *ent)
{
unsigned long board = z->resource.start;
unsigned long base_addr = board + ARIADNE_LANCE;
unsigned long mem_start = board + ARIADNE_RAM;
struct resource *r1, *r2;
struct net_device *dev;
u32 serial;
int err;
r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960");
if (!r1)
return -EBUSY;
r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM");
if (!r2) {
release_mem_region(base_addr, sizeof(struct Am79C960));
return -EBUSY;
}
dev = alloc_etherdev(sizeof(struct ariadne_private));
if (dev == NULL) {
release_mem_region(base_addr, sizeof(struct Am79C960));
release_mem_region(mem_start, ARIADNE_RAM_SIZE);
return -ENOMEM;
}
r1->name = dev->name;
r2->name = dev->name;
serial = be32_to_cpu(z->rom.er_SerialNumber);
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x60;
dev->dev_addr[2] = 0x30;
dev->dev_addr[3] = (serial >> 16) & 0xff;
dev->dev_addr[4] = (serial >> 8) & 0xff;
dev->dev_addr[5] = serial & 0xff;
dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr);
dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start);
dev->mem_end = dev->mem_start + ARIADNE_RAM_SIZE;
dev->netdev_ops = &ariadne_netdev_ops;
dev->watchdog_timeo = 5 * HZ;
err = register_netdev(dev);
if (err) {
release_mem_region(base_addr, sizeof(struct Am79C960));
release_mem_region(mem_start, ARIADNE_RAM_SIZE);
free_netdev(dev);
return err;
}
zorro_set_drvdata(z, dev);
netdev_info(dev, "Ariadne at 0x%08lx, Ethernet Address %pM\n",
board, dev->dev_addr);
return 0;
}
static struct zorro_driver ariadne_driver = {
.name = "ariadne",
.id_table = ariadne_zorro_tbl,
.probe = ariadne_init_one,
.remove = ariadne_remove_one,
};
static int __init ariadne_init_module(void)
{
return zorro_register_driver(&ariadne_driver);
}
static void __exit ariadne_cleanup_module(void)
{
zorro_unregister_driver(&ariadne_driver);
}
module_init(ariadne_init_module);
module_exit(ariadne_cleanup_module);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,415 @@
/*
* Amiga Linux/m68k Ariadne Ethernet Driver
*
* © Copyright 1995 by Geert Uytterhoeven (geert@linux-m68k.org)
* Peter De Schrijver
* (Peter.DeSchrijver@linux.cc.kuleuven.ac.be)
*
* ----------------------------------------------------------------------------------
*
* This program is based on
*
* lance.c: An AMD LANCE ethernet driver for linux.
* Written 1993-94 by Donald Becker.
*
* Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller
* Advanced Micro Devices
* Publication #16907, Rev. B, Amendment/0, May 1994
*
* MC68230: Parallel Interface/Timer (PI/T)
* Motorola Semiconductors, December, 1983
*
* ----------------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
* ----------------------------------------------------------------------------------
*
* The Ariadne is a Zorro-II board made by Village Tronic. It contains:
*
* - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both
* 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors
*
* - an MC68230 Parallel Interface/Timer configured as 2 parallel ports
*/
/*
* Am79C960 PCnet-ISA
*/
struct Am79C960 {
volatile u_short AddressPROM[8];
/* IEEE Address PROM (Unused in the Ariadne) */
volatile u_short RDP; /* Register Data Port */
volatile u_short RAP; /* Register Address Port */
volatile u_short Reset; /* Reset Chip on Read Access */
volatile u_short IDP; /* ISACSR Data Port */
};
/*
* Am79C960 Control and Status Registers
*
* These values are already swap()ed!!
*
* Only registers marked with a `-' are intended for network software
* access
*/
#define CSR0 0x0000 /* - PCnet-ISA Controller Status */
#define CSR1 0x0100 /* - IADR[15:0] */
#define CSR2 0x0200 /* - IADR[23:16] */
#define CSR3 0x0300 /* - Interrupt Masks and Deferral Control */
#define CSR4 0x0400 /* - Test and Features Control */
#define CSR6 0x0600 /* RCV/XMT Descriptor Table Length */
#define CSR8 0x0800 /* - Logical Address Filter, LADRF[15:0] */
#define CSR9 0x0900 /* - Logical Address Filter, LADRF[31:16] */
#define CSR10 0x0a00 /* - Logical Address Filter, LADRF[47:32] */
#define CSR11 0x0b00 /* - Logical Address Filter, LADRF[63:48] */
#define CSR12 0x0c00 /* - Physical Address Register, PADR[15:0] */
#define CSR13 0x0d00 /* - Physical Address Register, PADR[31:16] */
#define CSR14 0x0e00 /* - Physical Address Register, PADR[47:32] */
#define CSR15 0x0f00 /* - Mode Register */
#define CSR16 0x1000 /* Initialization Block Address Lower */
#define CSR17 0x1100 /* Initialization Block Address Upper */
#define CSR18 0x1200 /* Current Receive Buffer Address */
#define CSR19 0x1300 /* Current Receive Buffer Address */
#define CSR20 0x1400 /* Current Transmit Buffer Address */
#define CSR21 0x1500 /* Current Transmit Buffer Address */
#define CSR22 0x1600 /* Next Receive Buffer Address */
#define CSR23 0x1700 /* Next Receive Buffer Address */
#define CSR24 0x1800 /* - Base Address of Receive Ring */
#define CSR25 0x1900 /* - Base Address of Receive Ring */
#define CSR26 0x1a00 /* Next Receive Descriptor Address */
#define CSR27 0x1b00 /* Next Receive Descriptor Address */
#define CSR28 0x1c00 /* Current Receive Descriptor Address */
#define CSR29 0x1d00 /* Current Receive Descriptor Address */
#define CSR30 0x1e00 /* - Base Address of Transmit Ring */
#define CSR31 0x1f00 /* - Base Address of transmit Ring */
#define CSR32 0x2000 /* Next Transmit Descriptor Address */
#define CSR33 0x2100 /* Next Transmit Descriptor Address */
#define CSR34 0x2200 /* Current Transmit Descriptor Address */
#define CSR35 0x2300 /* Current Transmit Descriptor Address */
#define CSR36 0x2400 /* Next Next Receive Descriptor Address */
#define CSR37 0x2500 /* Next Next Receive Descriptor Address */
#define CSR38 0x2600 /* Next Next Transmit Descriptor Address */
#define CSR39 0x2700 /* Next Next Transmit Descriptor Address */
#define CSR40 0x2800 /* Current Receive Status and Byte Count */
#define CSR41 0x2900 /* Current Receive Status and Byte Count */
#define CSR42 0x2a00 /* Current Transmit Status and Byte Count */
#define CSR43 0x2b00 /* Current Transmit Status and Byte Count */
#define CSR44 0x2c00 /* Next Receive Status and Byte Count */
#define CSR45 0x2d00 /* Next Receive Status and Byte Count */
#define CSR46 0x2e00 /* Poll Time Counter */
#define CSR47 0x2f00 /* Polling Interval */
#define CSR48 0x3000 /* Temporary Storage */
#define CSR49 0x3100 /* Temporary Storage */
#define CSR50 0x3200 /* Temporary Storage */
#define CSR51 0x3300 /* Temporary Storage */
#define CSR52 0x3400 /* Temporary Storage */
#define CSR53 0x3500 /* Temporary Storage */
#define CSR54 0x3600 /* Temporary Storage */
#define CSR55 0x3700 /* Temporary Storage */
#define CSR56 0x3800 /* Temporary Storage */
#define CSR57 0x3900 /* Temporary Storage */
#define CSR58 0x3a00 /* Temporary Storage */
#define CSR59 0x3b00 /* Temporary Storage */
#define CSR60 0x3c00 /* Previous Transmit Descriptor Address */
#define CSR61 0x3d00 /* Previous Transmit Descriptor Address */
#define CSR62 0x3e00 /* Previous Transmit Status and Byte Count */
#define CSR63 0x3f00 /* Previous Transmit Status and Byte Count */
#define CSR64 0x4000 /* Next Transmit Buffer Address */
#define CSR65 0x4100 /* Next Transmit Buffer Address */
#define CSR66 0x4200 /* Next Transmit Status and Byte Count */
#define CSR67 0x4300 /* Next Transmit Status and Byte Count */
#define CSR68 0x4400 /* Transmit Status Temporary Storage */
#define CSR69 0x4500 /* Transmit Status Temporary Storage */
#define CSR70 0x4600 /* Temporary Storage */
#define CSR71 0x4700 /* Temporary Storage */
#define CSR72 0x4800 /* Receive Ring Counter */
#define CSR74 0x4a00 /* Transmit Ring Counter */
#define CSR76 0x4c00 /* - Receive Ring Length */
#define CSR78 0x4e00 /* - Transmit Ring Length */
#define CSR80 0x5000 /* - Burst and FIFO Threshold Control */
#define CSR82 0x5200 /* - Bus Activity Timer */
#define CSR84 0x5400 /* DMA Address */
#define CSR85 0x5500 /* DMA Address */
#define CSR86 0x5600 /* Buffer Byte Counter */
#define CSR88 0x5800 /* - Chip ID */
#define CSR89 0x5900 /* - Chip ID */
#define CSR92 0x5c00 /* Ring Length Conversion */
#define CSR94 0x5e00 /* Transmit Time Domain Reflectometry Count */
#define CSR96 0x6000 /* Bus Interface Scratch Register 0 */
#define CSR97 0x6100 /* Bus Interface Scratch Register 0 */
#define CSR98 0x6200 /* Bus Interface Scratch Register 1 */
#define CSR99 0x6300 /* Bus Interface Scratch Register 1 */
#define CSR104 0x6800 /* SWAP */
#define CSR105 0x6900 /* SWAP */
#define CSR108 0x6c00 /* Buffer Management Scratch */
#define CSR109 0x6d00 /* Buffer Management Scratch */
#define CSR112 0x7000 /* - Missed Frame Count */
#define CSR114 0x7200 /* - Receive Collision Count */
#define CSR124 0x7c00 /* - Buffer Management Unit Test */
/*
* Am79C960 ISA Control and Status Registers
*
* These values are already swap()ed!!
*/
#define ISACSR0 0x0000 /* Master Mode Read Active */
#define ISACSR1 0x0100 /* Master Mode Write Active */
#define ISACSR2 0x0200 /* Miscellaneous Configuration */
#define ISACSR4 0x0400 /* LED0 Status (Link Integrity) */
#define ISACSR5 0x0500 /* LED1 Status */
#define ISACSR6 0x0600 /* LED2 Status */
#define ISACSR7 0x0700 /* LED3 Status */
/*
* Bit definitions for CSR0 (PCnet-ISA Controller Status)
*
* These values are already swap()ed!!
*/
#define ERR 0x0080 /* Error */
#define BABL 0x0040 /* Babble: Transmitted too many bits */
#define CERR 0x0020 /* No Heartbeat (10BASE-T) */
#define MISS 0x0010 /* Missed Frame */
#define MERR 0x0008 /* Memory Error */
#define RINT 0x0004 /* Receive Interrupt */
#define TINT 0x0002 /* Transmit Interrupt */
#define IDON 0x0001 /* Initialization Done */
#define INTR 0x8000 /* Interrupt Flag */
#define INEA 0x4000 /* Interrupt Enable */
#define RXON 0x2000 /* Receive On */
#define TXON 0x1000 /* Transmit On */
#define TDMD 0x0800 /* Transmit Demand */
#define STOP 0x0400 /* Stop */
#define STRT 0x0200 /* Start */
#define INIT 0x0100 /* Initialize */
/*
* Bit definitions for CSR3 (Interrupt Masks and Deferral Control)
*
* These values are already swap()ed!!
*/
#define BABLM 0x0040 /* Babble Mask */
#define MISSM 0x0010 /* Missed Frame Mask */
#define MERRM 0x0008 /* Memory Error Mask */
#define RINTM 0x0004 /* Receive Interrupt Mask */
#define TINTM 0x0002 /* Transmit Interrupt Mask */
#define IDONM 0x0001 /* Initialization Done Mask */
#define DXMT2PD 0x1000 /* Disable Transmit Two Part Deferral */
#define EMBA 0x0800 /* Enable Modified Back-off Algorithm */
/*
* Bit definitions for CSR4 (Test and Features Control)
*
* These values are already swap()ed!!
*/
#define ENTST 0x0080 /* Enable Test Mode */
#define DMAPLUS 0x0040 /* Disable Burst Transaction Counter */
#define TIMER 0x0020 /* Timer Enable Register */
#define DPOLL 0x0010 /* Disable Transmit Polling */
#define APAD_XMT 0x0008 /* Auto Pad Transmit */
#define ASTRP_RCV 0x0004 /* Auto Pad Stripping */
#define MFCO 0x0002 /* Missed Frame Counter Overflow Interrupt */
#define MFCOM 0x0001 /* Missed Frame Counter Overflow Mask */
#define RCVCCO 0x2000 /* Receive Collision Counter Overflow Interrupt */
#define RCVCCOM 0x1000 /* Receive Collision Counter Overflow Mask */
#define TXSTRT 0x0800 /* Transmit Start Status */
#define TXSTRTM 0x0400 /* Transmit Start Mask */
#define JAB 0x0200 /* Jabber Error */
#define JABM 0x0100 /* Jabber Error Mask */
/*
* Bit definitions for CSR15 (Mode Register)
*
* These values are already swap()ed!!
*/
#define PROM 0x0080 /* Promiscuous Mode */
#define DRCVBC 0x0040 /* Disable Receive Broadcast */
#define DRCVPA 0x0020 /* Disable Receive Physical Address */
#define DLNKTST 0x0010 /* Disable Link Status */
#define DAPC 0x0008 /* Disable Automatic Polarity Correction */
#define MENDECL 0x0004 /* MENDEC Loopback Mode */
#define LRTTSEL 0x0002 /* Low Receive Threshold/Transmit Mode Select */
#define PORTSEL1 0x0001 /* Port Select Bits */
#define PORTSEL2 0x8000 /* Port Select Bits */
#define INTL 0x4000 /* Internal Loopback */
#define DRTY 0x2000 /* Disable Retry */
#define FCOLL 0x1000 /* Force Collision */
#define DXMTFCS 0x0800 /* Disable Transmit CRC */
#define LOOP 0x0400 /* Loopback Enable */
#define DTX 0x0200 /* Disable Transmitter */
#define DRX 0x0100 /* Disable Receiver */
/*
* Bit definitions for ISACSR2 (Miscellaneous Configuration)
*
* These values are already swap()ed!!
*/
#define ASEL 0x0200 /* Media Interface Port Auto Select */
/*
* Bit definitions for ISACSR5-7 (LED1-3 Status)
*
* These values are already swap()ed!!
*/
#define LEDOUT 0x0080 /* Current LED Status */
#define PSE 0x8000 /* Pulse Stretcher Enable */
#define XMTE 0x1000 /* Enable Transmit Status Signal */
#define RVPOLE 0x0800 /* Enable Receive Polarity Signal */
#define RCVE 0x0400 /* Enable Receive Status Signal */
#define JABE 0x0200 /* Enable Jabber Signal */
#define COLE 0x0100 /* Enable Collision Signal */
/*
* Receive Descriptor Ring Entry
*/
struct RDRE {
volatile u_short RMD0; /* LADR[15:0] */
volatile u_short RMD1; /* HADR[23:16] | Receive Flags */
volatile u_short RMD2; /* Buffer Byte Count (two's complement) */
volatile u_short RMD3; /* Message Byte Count */
};
/*
* Transmit Descriptor Ring Entry
*/
struct TDRE {
volatile u_short TMD0; /* LADR[15:0] */
volatile u_short TMD1; /* HADR[23:16] | Transmit Flags */
volatile u_short TMD2; /* Buffer Byte Count (two's complement) */
volatile u_short TMD3; /* Error Flags */
};
/*
* Receive Flags
*/
#define RF_OWN 0x0080 /* PCnet-ISA controller owns the descriptor */
#define RF_ERR 0x0040 /* Error */
#define RF_FRAM 0x0020 /* Framing Error */
#define RF_OFLO 0x0010 /* Overflow Error */
#define RF_CRC 0x0008 /* CRC Error */
#define RF_BUFF 0x0004 /* Buffer Error */
#define RF_STP 0x0002 /* Start of Packet */
#define RF_ENP 0x0001 /* End of Packet */
/*
* Transmit Flags
*/
#define TF_OWN 0x0080 /* PCnet-ISA controller owns the descriptor */
#define TF_ERR 0x0040 /* Error */
#define TF_ADD_FCS 0x0020 /* Controls FCS Generation */
#define TF_MORE 0x0010 /* More than one retry needed */
#define TF_ONE 0x0008 /* One retry needed */
#define TF_DEF 0x0004 /* Deferred */
#define TF_STP 0x0002 /* Start of Packet */
#define TF_ENP 0x0001 /* End of Packet */
/*
* Error Flags
*/
#define EF_BUFF 0x0080 /* Buffer Error */
#define EF_UFLO 0x0040 /* Underflow Error */
#define EF_LCOL 0x0010 /* Late Collision */
#define EF_LCAR 0x0008 /* Loss of Carrier */
#define EF_RTRY 0x0004 /* Retry Error */
#define EF_TDR 0xff03 /* Time Domain Reflectometry */
/*
* MC68230 Parallel Interface/Timer
*/
struct MC68230 {
volatile u_char PGCR; /* Port General Control Register */
u_char Pad1[1];
volatile u_char PSRR; /* Port Service Request Register */
u_char Pad2[1];
volatile u_char PADDR; /* Port A Data Direction Register */
u_char Pad3[1];
volatile u_char PBDDR; /* Port B Data Direction Register */
u_char Pad4[1];
volatile u_char PCDDR; /* Port C Data Direction Register */
u_char Pad5[1];
volatile u_char PIVR; /* Port Interrupt Vector Register */
u_char Pad6[1];
volatile u_char PACR; /* Port A Control Register */
u_char Pad7[1];
volatile u_char PBCR; /* Port B Control Register */
u_char Pad8[1];
volatile u_char PADR; /* Port A Data Register */
u_char Pad9[1];
volatile u_char PBDR; /* Port B Data Register */
u_char Pad10[1];
volatile u_char PAAR; /* Port A Alternate Register */
u_char Pad11[1];
volatile u_char PBAR; /* Port B Alternate Register */
u_char Pad12[1];
volatile u_char PCDR; /* Port C Data Register */
u_char Pad13[1];
volatile u_char PSR; /* Port Status Register */
u_char Pad14[5];
volatile u_char TCR; /* Timer Control Register */
u_char Pad15[1];
volatile u_char TIVR; /* Timer Interrupt Vector Register */
u_char Pad16[3];
volatile u_char CPRH; /* Counter Preload Register (High) */
u_char Pad17[1];
volatile u_char CPRM; /* Counter Preload Register (Mid) */
u_char Pad18[1];
volatile u_char CPRL; /* Counter Preload Register (Low) */
u_char Pad19[3];
volatile u_char CNTRH; /* Count Register (High) */
u_char Pad20[1];
volatile u_char CNTRM; /* Count Register (Mid) */
u_char Pad21[1];
volatile u_char CNTRL; /* Count Register (Low) */
u_char Pad22[1];
volatile u_char TSR; /* Timer Status Register */
u_char Pad23[11];
};
/*
* Ariadne Expansion Board Structure
*/
#define ARIADNE_LANCE 0x360
#define ARIADNE_PIT 0x1000
#define ARIADNE_BOOTPROM 0x4000 /* I guess it's here :-) */
#define ARIADNE_BOOTPROM_SIZE 0x4000
#define ARIADNE_RAM 0x8000 /* Always access WORDs!! */
#define ARIADNE_RAM_SIZE 0x8000

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
/*
*
* Alchemy Au1x00 ethernet driver include file
*
* Author: Pete Popov <ppopov@mvista.com>
*
* Copyright 2001 MontaVista Software Inc.
*
* ########################################################################
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
* ########################################################################
*
*
*/
#define MAC_IOSIZE 0x10000
#define NUM_RX_DMA 4 /* Au1x00 has 4 rx hardware descriptors */
#define NUM_TX_DMA 4 /* Au1x00 has 4 tx hardware descriptors */
#define NUM_RX_BUFFS 4
#define NUM_TX_BUFFS 4
#define MAX_BUF_SIZE 2048
#define ETH_TX_TIMEOUT (HZ/4)
#define MAC_MIN_PKT_SIZE 64
#define MULTICAST_FILTER_LIMIT 64
/*
* Data Buffer Descriptor. Data buffers must be aligned on 32 byte
* boundary for both, receive and transmit.
*/
struct db_dest {
struct db_dest *pnext;
u32 *vaddr;
dma_addr_t dma_addr;
};
/*
* The transmit and receive descriptors are memory
* mapped registers.
*/
struct tx_dma {
u32 status;
u32 buff_stat;
u32 len;
u32 pad;
};
struct rx_dma {
u32 status;
u32 buff_stat;
u32 pad[2];
};
/*
* MAC control registers, memory mapped.
*/
struct mac_reg {
u32 control;
u32 mac_addr_high;
u32 mac_addr_low;
u32 multi_hash_high;
u32 multi_hash_low;
u32 mii_control;
u32 mii_data;
u32 flow_control;
u32 vlan1_tag;
u32 vlan2_tag;
};
struct au1000_private {
struct db_dest *pDBfree;
struct db_dest db[NUM_RX_BUFFS+NUM_TX_BUFFS];
struct rx_dma *rx_dma_ring[NUM_RX_DMA];
struct tx_dma *tx_dma_ring[NUM_TX_DMA];
struct db_dest *rx_db_inuse[NUM_RX_DMA];
struct db_dest *tx_db_inuse[NUM_TX_DMA];
u32 rx_head;
u32 tx_head;
u32 tx_tail;
u32 tx_full;
int mac_id;
int mac_enabled; /* whether MAC is currently enabled and running
* (req. for mdio)
*/
int old_link; /* used by au1000_adjust_link */
int old_speed;
int old_duplex;
struct phy_device *phy_dev;
struct mii_bus *mii_bus;
/* PHY configuration */
int phy_static_config;
int phy_search_highest_addr;
int phy1_search_mac0;
int phy_addr;
int phy_busid;
int phy_irq;
/* These variables are just for quick access
* to certain regs addresses.
*/
struct mac_reg *mac; /* mac registers */
u32 *enable; /* address of MAC Enable Register */
void __iomem *macdma; /* base of MAC DMA port */
u32 vaddr; /* virtual address of rx/tx buffers */
dma_addr_t dma_addr; /* dma address of rx/tx buffers */
spinlock_t lock; /* Serialise access to device */
u32 msg_enable;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
/* hplance.c : the Linux/hp300/lance ethernet driver
*
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
* Based on the Sun Lance driver and the NetBSD HP Lance driver
* Uses the generic 7990.c LANCE code.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/errno.h>
/* Used for the temporal inet entries and routing */
#include <linux/socket.h>
#include <linux/route.h>
#include <linux/dio.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include "hplance.h"
/* We have 16392 bytes of RAM for the init block and buffers. This places
* an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
* buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
*/
#define LANCE_LOG_TX_BUFFERS 1
#define LANCE_LOG_RX_BUFFERS 3
#include "7990.h" /* use generic LANCE code */
/* Our private data structure */
struct hplance_private {
struct lance_private lance;
};
/* function prototypes... This is easy because all the grot is in the
* generic LANCE support. All we have to support is probing for boards,
* plus board-specific init, open and close actions.
* Oh, and we need to tell the generic code how to read and write LANCE registers...
*/
static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent);
static void hplance_init(struct net_device *dev, struct dio_dev *d);
static void hplance_remove_one(struct dio_dev *d);
static void hplance_writerap(void *priv, unsigned short value);
static void hplance_writerdp(void *priv, unsigned short value);
static unsigned short hplance_readrdp(void *priv);
static int hplance_open(struct net_device *dev);
static int hplance_close(struct net_device *dev);
static struct dio_device_id hplance_dio_tbl[] = {
{ DIO_ID_LAN },
{ 0 }
};
static struct dio_driver hplance_driver = {
.name = "hplance",
.id_table = hplance_dio_tbl,
.probe = hplance_init_one,
.remove = hplance_remove_one,
};
static const struct net_device_ops hplance_netdev_ops = {
.ndo_open = hplance_open,
.ndo_stop = hplance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_set_rx_mode = lance_set_multicast,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = lance_poll,
#endif
};
/* Find all the HP Lance boards and initialise them... */
static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent)
{
struct net_device *dev;
int err = -ENOMEM;
dev = alloc_etherdev(sizeof(struct hplance_private));
if (!dev)
goto out;
err = -EBUSY;
if (!request_mem_region(dio_resource_start(d),
dio_resource_len(d), d->name))
goto out_free_netdev;
hplance_init(dev, d);
err = register_netdev(dev);
if (err)
goto out_release_mem_region;
dio_set_drvdata(d, dev);
printk(KERN_INFO "%s: %s; select code %d, addr %pM, irq %d\n",
dev->name, d->name, d->scode, dev->dev_addr, d->ipl);
return 0;
out_release_mem_region:
release_mem_region(dio_resource_start(d), dio_resource_len(d));
out_free_netdev:
free_netdev(dev);
out:
return err;
}
static void hplance_remove_one(struct dio_dev *d)
{
struct net_device *dev = dio_get_drvdata(d);
unregister_netdev(dev);
release_mem_region(dio_resource_start(d), dio_resource_len(d));
free_netdev(dev);
}
/* Initialise a single lance board at the given DIO device */
static void hplance_init(struct net_device *dev, struct dio_dev *d)
{
unsigned long va = (d->resource.start + DIO_VIRADDRBASE);
struct hplance_private *lp;
int i;
/* reset the board */
out_8(va + DIO_IDOFF, 0xff);
udelay(100); /* ariba! ariba! udelay! udelay! */
/* Fill the dev fields */
dev->base_addr = va;
dev->netdev_ops = &hplance_netdev_ops;
dev->dma = 0;
for (i = 0; i < 6; i++) {
/* The NVRAM holds our ethernet address, one nibble per byte,
* at bytes NVRAMOFF+1,3,5,7,9...
*/
dev->dev_addr[i] = ((in_8(va + HPLANCE_NVRAMOFF + i*4 + 1) & 0xF) << 4)
| (in_8(va + HPLANCE_NVRAMOFF + i*4 + 3) & 0xF);
}
lp = netdev_priv(dev);
lp->lance.name = d->name;
lp->lance.base = va;
lp->lance.init_block = (struct lance_init_block *)(va + HPLANCE_MEMOFF); /* CPU addr */
lp->lance.lance_init_block = NULL; /* LANCE addr of same RAM */
lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */
lp->lance.irq = d->ipl;
lp->lance.writerap = hplance_writerap;
lp->lance.writerdp = hplance_writerdp;
lp->lance.readrdp = hplance_readrdp;
lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
}
/* This is disgusting. We have to check the DIO status register for ack every
* time we read or write the LANCE registers.
*/
static void hplance_writerap(void *priv, unsigned short value)
{
struct lance_private *lp = (struct lance_private *)priv;
do {
out_be16(lp->base + HPLANCE_REGOFF + LANCE_RAP, value);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
}
static void hplance_writerdp(void *priv, unsigned short value)
{
struct lance_private *lp = (struct lance_private *)priv;
do {
out_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP, value);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
}
static unsigned short hplance_readrdp(void *priv)
{
struct lance_private *lp = (struct lance_private *)priv;
__u16 value;
do {
value = in_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP);
} while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0);
return value;
}
static int hplance_open(struct net_device *dev)
{
int status;
struct lance_private *lp = netdev_priv(dev);
status = lance_open(dev); /* call generic lance open code */
if (status)
return status;
/* enable interrupts at board level. */
out_8(lp->base + HPLANCE_STATUS, LE_IE);
return 0;
}
static int hplance_close(struct net_device *dev)
{
struct lance_private *lp = netdev_priv(dev);
out_8(lp->base + HPLANCE_STATUS, 0); /* disable interrupts at boardlevel */
lance_close(dev);
return 0;
}
static int __init hplance_init_module(void)
{
return dio_register_driver(&hplance_driver);
}
static void __exit hplance_cleanup_module(void)
{
dio_unregister_driver(&hplance_driver);
}
module_init(hplance_init_module);
module_exit(hplance_cleanup_module);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,26 @@
/* Random defines and structures for the HP Lance driver.
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
* Based on the Sun Lance driver and the NetBSD HP Lance driver
*/
/* Registers */
#define HPLANCE_ID 0x01 /* DIO register: ID byte */
#define HPLANCE_STATUS 0x03 /* DIO register: interrupt enable/status */
/* Control and status bits for the status register */
#define LE_IE 0x80 /* interrupt enable */
#define LE_IR 0x40 /* interrupt requested */
#define LE_LOCK 0x08 /* lock status register */
#define LE_ACK 0x04 /* ack of lock */
#define LE_JAB 0x02 /* loss of tx clock (???) */
/* We can also extract the IPL from the status register with the standard
* DIO_IPL(hplance) macro, or using dio_scodetoipl()
*/
/* These are the offsets for the DIO regs (hplance_reg), lance_ioreg,
* memory and NVRAM:
*/
#define HPLANCE_IDOFF 0 /* board baseaddr */
#define HPLANCE_REGOFF 0x4000 /* lance registers */
#define HPLANCE_MEMOFF 0x8000 /* struct lance_init_block */
#define HPLANCE_NVRAMOFF 0xC008 /* etheraddress as one *nibble* per byte */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
/* mvme147.c : the Linux/mvme147/lance ethernet driver
*
* Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
* Based on the Sun Lance driver and the NetBSD HP Lance driver
* Uses the generic 7990.c LANCE code.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gfp.h>
/* Used for the temporal inet entries and routing */
#include <linux/socket.h>
#include <linux/route.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/mvme147hw.h>
/* We have 32K of RAM for the init block and buffers. This places
* an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
* buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
*/
#define LANCE_LOG_TX_BUFFERS 1
#define LANCE_LOG_RX_BUFFERS 3
#include "7990.h" /* use generic LANCE code */
/* Our private data structure */
struct m147lance_private {
struct lance_private lance;
unsigned long ram;
};
/* function prototypes... This is easy because all the grot is in the
* generic LANCE support. All we have to support is probing for boards,
* plus board-specific init, open and close actions.
* Oh, and we need to tell the generic code how to read and write LANCE registers...
*/
static int m147lance_open(struct net_device *dev);
static int m147lance_close(struct net_device *dev);
static void m147lance_writerap(struct lance_private *lp, unsigned short value);
static void m147lance_writerdp(struct lance_private *lp, unsigned short value);
static unsigned short m147lance_readrdp(struct lance_private *lp);
typedef void (*writerap_t)(void *, unsigned short);
typedef void (*writerdp_t)(void *, unsigned short);
typedef unsigned short (*readrdp_t)(void *);
static const struct net_device_ops lance_netdev_ops = {
.ndo_open = m147lance_open,
.ndo_stop = m147lance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_set_rx_mode = lance_set_multicast,
.ndo_tx_timeout = lance_tx_timeout,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
/* Initialise the one and only on-board 7990 */
struct net_device * __init mvme147lance_probe(int unit)
{
struct net_device *dev;
static int called;
static const char name[] = "MVME147 LANCE";
struct m147lance_private *lp;
u_long *addr;
u_long address;
int err;
if (!MACH_IS_MVME147 || called)
return ERR_PTR(-ENODEV);
called++;
dev = alloc_etherdev(sizeof(struct m147lance_private));
if (!dev)
return ERR_PTR(-ENOMEM);
if (unit >= 0)
sprintf(dev->name, "eth%d", unit);
/* Fill the dev fields */
dev->base_addr = (unsigned long)MVME147_LANCE_BASE;
dev->netdev_ops = &lance_netdev_ops;
dev->dma = 0;
addr = (u_long *)ETHERNET_ADDRESS;
address = *addr;
dev->dev_addr[0] = 0x08;
dev->dev_addr[1] = 0x00;
dev->dev_addr[2] = 0x3e;
address = address >> 8;
dev->dev_addr[5] = address&0xff;
address = address >> 8;
dev->dev_addr[4] = address&0xff;
address = address >> 8;
dev->dev_addr[3] = address&0xff;
printk("%s: MVME147 at 0x%08lx, irq %d, Hardware Address %pM\n",
dev->name, dev->base_addr, MVME147_LANCE_IRQ,
dev->dev_addr);
lp = netdev_priv(dev);
lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 32K */
if (!lp->ram) {
printk("%s: No memory for LANCE buffers\n", dev->name);
free_netdev(dev);
return ERR_PTR(-ENOMEM);
}
lp->lance.name = name;
lp->lance.base = dev->base_addr;
lp->lance.init_block = (struct lance_init_block *)(lp->ram); /* CPU addr */
lp->lance.lance_init_block = (struct lance_init_block *)(lp->ram); /* LANCE addr of same RAM */
lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */
lp->lance.irq = MVME147_LANCE_IRQ;
lp->lance.writerap = (writerap_t)m147lance_writerap;
lp->lance.writerdp = (writerdp_t)m147lance_writerdp;
lp->lance.readrdp = (readrdp_t)m147lance_readrdp;
lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
err = register_netdev(dev);
if (err) {
free_pages(lp->ram, 3);
free_netdev(dev);
return ERR_PTR(err);
}
return dev;
}
static void m147lance_writerap(struct lance_private *lp, unsigned short value)
{
out_be16(lp->base + LANCE_RAP, value);
}
static void m147lance_writerdp(struct lance_private *lp, unsigned short value)
{
out_be16(lp->base + LANCE_RDP, value);
}
static unsigned short m147lance_readrdp(struct lance_private *lp)
{
return in_be16(lp->base + LANCE_RDP);
}
static int m147lance_open(struct net_device *dev)
{
int status;
status = lance_open(dev); /* call generic lance open code */
if (status)
return status;
/* enable interrupts at board level. */
m147_pcc->lan_cntrl = 0; /* clear the interrupts (if any) */
m147_pcc->lan_cntrl = 0x08 | 0x04; /* Enable irq 4 */
return 0;
}
static int m147lance_close(struct net_device *dev)
{
/* disable interrupts at boardlevel */
m147_pcc->lan_cntrl = 0x0; /* disable interrupts */
lance_close(dev);
return 0;
}
#ifdef MODULE
MODULE_LICENSE("GPL");
static struct net_device *dev_mvme147_lance;
int __init init_module(void)
{
dev_mvme147_lance = mvme147lance_probe(-1);
return PTR_ERR_OR_ZERO(dev_mvme147_lance);
}
void __exit cleanup_module(void)
{
struct m147lance_private *lp = netdev_priv(dev_mvme147_lance);
unregister_netdev(dev_mvme147_lance);
free_pages(lp->ram, 3);
free_netdev(dev_mvme147_lance);
}
#endif /* MODULE */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,121 @@
/* am7990 (lance) definitions
*
* This is an extension to the Linux operating system, and is covered by
* same GNU General Public License that covers that work.
*
* Michael Hipp
* email: mhipp@student.uni-tuebingen.de
*
* sources: (mail me or ask archie if you need them)
* crynwr-packet-driver
*/
/*
* Control and Status Register 0 (CSR0) bit definitions
* (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
*
*/
#define CSR0_ERR 0x8000 /* Error summary (R) */
#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
#define CSR0_CERR 0x2000 /* Collision Error (RC) */
#define CSR0_MISS 0x1000 /* Missed packet (RC) */
#define CSR0_MERR 0x0800 /* Memory Error (RC) */
#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
#define CSR0_RXON 0x0020 /* Receiver on (R) */
#define CSR0_TXON 0x0010 /* Transmitter on (R) */
#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
#define CSR0_STOP 0x0004 /* Stop (RS) */
#define CSR0_STRT 0x0002 /* Start (RS) */
#define CSR0_INIT 0x0001 /* Initialize (RS) */
#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
/*
* Initialization Block Mode operation Bit Definitions.
*/
#define M_PROM 0x8000 /* Promiscuous Mode */
#define M_INTL 0x0040 /* Internal Loopback */
#define M_DRTY 0x0020 /* Disable Retry */
#define M_COLL 0x0010 /* Force Collision */
#define M_DTCR 0x0008 /* Disable Transmit CRC) */
#define M_LOOP 0x0004 /* Loopback */
#define M_DTX 0x0002 /* Disable the Transmitter */
#define M_DRX 0x0001 /* Disable the Receiver */
/*
* Receive message descriptor bit definitions.
*/
#define RCV_OWN 0x80 /* owner bit 0 = host, 1 = lance */
#define RCV_ERR 0x40 /* Error Summary */
#define RCV_FRAM 0x20 /* Framing Error */
#define RCV_OFLO 0x10 /* Overflow Error */
#define RCV_CRC 0x08 /* CRC Error */
#define RCV_BUF_ERR 0x04 /* Buffer Error */
#define RCV_START 0x02 /* Start of Packet */
#define RCV_END 0x01 /* End of Packet */
/*
* Transmit message descriptor bit definitions.
*/
#define XMIT_OWN 0x80 /* owner bit 0 = host, 1 = lance */
#define XMIT_ERR 0x40 /* Error Summary */
#define XMIT_RETRY 0x10 /* more the 1 retry needed to Xmit */
#define XMIT_1_RETRY 0x08 /* one retry needed to Xmit */
#define XMIT_DEF 0x04 /* Deferred */
#define XMIT_START 0x02 /* Start of Packet */
#define XMIT_END 0x01 /* End of Packet */
/*
* transmit status (2) (valid if XMIT_ERR == 1)
*/
#define XMIT_TDRMASK 0x03ff /* time-domain-reflectometer-value */
#define XMIT_RTRY 0x0400 /* Failed after 16 retransmissions */
#define XMIT_LCAR 0x0800 /* Loss of Carrier */
#define XMIT_LCOL 0x1000 /* Late collision */
#define XMIT_RESERV 0x2000 /* Reserved */
#define XMIT_UFLO 0x4000 /* Underflow (late memory) */
#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */
struct init_block {
unsigned short mode;
unsigned char eaddr[6];
unsigned char filter[8];
/* bit 29-31: number of rmd's (power of 2) */
u32 rrp; /* receive ring pointer (align 8) */
/* bit 29-31: number of tmd's (power of 2) */
u32 trp; /* transmit ring pointer (align 8) */
};
struct rmd { /* Receive Message Descriptor */
union {
volatile u32 buffer;
struct {
volatile unsigned char dummy[3];
volatile unsigned char status;
} s;
} u;
volatile short blen;
volatile unsigned short mlen;
};
struct tmd {
union {
volatile u32 buffer;
struct {
volatile unsigned char dummy[3];
volatile unsigned char status;
} s;
} u;
volatile unsigned short blen;
volatile unsigned short status2;
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,956 @@
/* sun3lance.c: Ethernet driver for SUN3 Lance chip */
/*
Sun3 Lance ethernet driver, by Sam Creasey (sammy@users.qual.net).
This driver is a part of the linux kernel, and is thus distributed
under the GNU General Public License.
The values used in LANCE_OBIO and LANCE_IRQ seem to be empirically
true for the correct IRQ and address of the lance registers. They
have not been widely tested, however. What we probably need is a
"proper" way to search for a device in the sun3's prom, but, alas,
linux has no such thing.
This driver is largely based on atarilance.c, by Roman Hodek. Other
sources of inspiration were the NetBSD sun3 am7990 driver, and the
linux sparc lance driver (sunlance.c).
There are more assumptions made throughout this driver, it almost
certainly still needs work, but it does work at least for RARP/BOOTP and
mounting the root NFS filesystem.
*/
static char *version = "sun3lance.c: v1.2 1/12/2001 Sam Creasey (sammy@sammy.net)\n";
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
#include <asm/cacheflush.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/dvma.h>
#include <asm/idprom.h>
#include <asm/machines.h>
#ifdef CONFIG_SUN3
#include <asm/sun3mmu.h>
#else
#include <asm/sun3xprom.h>
#endif
/* sun3/60 addr/irq for the lance chip. If your sun is different,
change this. */
#define LANCE_OBIO 0x120000
#define LANCE_IRQ IRQ_AUTO_3
/* Debug level:
* 0 = silent, print only serious errors
* 1 = normal, print error messages
* 2 = debug, print debug infos
* 3 = debug, print even more debug infos (packet data)
*/
#define LANCE_DEBUG 0
#ifdef LANCE_DEBUG
static int lance_debug = LANCE_DEBUG;
#else
static int lance_debug = 1;
#endif
module_param(lance_debug, int, 0);
MODULE_PARM_DESC(lance_debug, "SUN3 Lance debug level (0-3)");
MODULE_LICENSE("GPL");
#define DPRINTK(n,a) \
do { \
if (lance_debug >= n) \
printk a; \
} while( 0 )
/* we're only using 32k of memory, so we use 4 TX
buffers and 16 RX buffers. These values are expressed as log2. */
#define TX_LOG_RING_SIZE 3
#define RX_LOG_RING_SIZE 5
/* These are the derived values */
#define TX_RING_SIZE (1 << TX_LOG_RING_SIZE)
#define TX_RING_LEN_BITS (TX_LOG_RING_SIZE << 5)
#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
#define RX_RING_SIZE (1 << RX_LOG_RING_SIZE)
#define RX_RING_LEN_BITS (RX_LOG_RING_SIZE << 5)
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
/* Definitions for packet buffer access: */
#define PKT_BUF_SZ 1544
/* Get the address of a packet buffer corresponding to a given buffer head */
#define PKTBUF_ADDR(head) (void *)((unsigned long)(MEM) | (head)->base)
/* The LANCE Rx and Tx ring descriptors. */
struct lance_rx_head {
unsigned short base; /* Low word of base addr */
volatile unsigned char flag;
unsigned char base_hi; /* High word of base addr (unused) */
short buf_length; /* This length is 2s complement! */
volatile short msg_length; /* This length is "normal". */
};
struct lance_tx_head {
unsigned short base; /* Low word of base addr */
volatile unsigned char flag;
unsigned char base_hi; /* High word of base addr (unused) */
short length; /* Length is 2s complement! */
volatile short misc;
};
/* The LANCE initialization block, described in databook. */
struct lance_init_block {
unsigned short mode; /* Pre-set mode */
unsigned char hwaddr[6]; /* Physical ethernet address */
unsigned int filter[2]; /* Multicast filter (unused). */
/* Receive and transmit ring base, along with length bits. */
unsigned short rdra;
unsigned short rlen;
unsigned short tdra;
unsigned short tlen;
unsigned short pad[4]; /* is thie needed? */
};
/* The whole layout of the Lance shared memory */
struct lance_memory {
struct lance_init_block init;
struct lance_tx_head tx_head[TX_RING_SIZE];
struct lance_rx_head rx_head[RX_RING_SIZE];
char rx_data[RX_RING_SIZE][PKT_BUF_SZ];
char tx_data[TX_RING_SIZE][PKT_BUF_SZ];
};
/* The driver's private device structure */
struct lance_private {
volatile unsigned short *iobase;
struct lance_memory *mem;
int new_rx, new_tx; /* The next free ring entry */
int old_tx, old_rx; /* ring entry to be processed */
/* These two must be longs for set_bit() */
long tx_full;
long lock;
};
/* I/O register access macros */
#define MEM lp->mem
#define DREG lp->iobase[0]
#define AREG lp->iobase[1]
#define REGA(a) (*( AREG = (a), &DREG ))
/* Definitions for the Lance */
/* tx_head flags */
#define TMD1_ENP 0x01 /* end of packet */
#define TMD1_STP 0x02 /* start of packet */
#define TMD1_DEF 0x04 /* deferred */
#define TMD1_ONE 0x08 /* one retry needed */
#define TMD1_MORE 0x10 /* more than one retry needed */
#define TMD1_ERR 0x40 /* error summary */
#define TMD1_OWN 0x80 /* ownership (set: chip owns) */
#define TMD1_OWN_CHIP TMD1_OWN
#define TMD1_OWN_HOST 0
/* tx_head misc field */
#define TMD3_TDR 0x03FF /* Time Domain Reflectometry counter */
#define TMD3_RTRY 0x0400 /* failed after 16 retries */
#define TMD3_LCAR 0x0800 /* carrier lost */
#define TMD3_LCOL 0x1000 /* late collision */
#define TMD3_UFLO 0x4000 /* underflow (late memory) */
#define TMD3_BUFF 0x8000 /* buffering error (no ENP) */
/* rx_head flags */
#define RMD1_ENP 0x01 /* end of packet */
#define RMD1_STP 0x02 /* start of packet */
#define RMD1_BUFF 0x04 /* buffer error */
#define RMD1_CRC 0x08 /* CRC error */
#define RMD1_OFLO 0x10 /* overflow */
#define RMD1_FRAM 0x20 /* framing error */
#define RMD1_ERR 0x40 /* error summary */
#define RMD1_OWN 0x80 /* ownership (set: ship owns) */
#define RMD1_OWN_CHIP RMD1_OWN
#define RMD1_OWN_HOST 0
/* register names */
#define CSR0 0 /* mode/status */
#define CSR1 1 /* init block addr (low) */
#define CSR2 2 /* init block addr (high) */
#define CSR3 3 /* misc */
#define CSR8 8 /* address filter */
#define CSR15 15 /* promiscuous mode */
/* CSR0 */
/* (R=readable, W=writeable, S=set on write, C=clear on write) */
#define CSR0_INIT 0x0001 /* initialize (RS) */
#define CSR0_STRT 0x0002 /* start (RS) */
#define CSR0_STOP 0x0004 /* stop (RS) */
#define CSR0_TDMD 0x0008 /* transmit demand (RS) */
#define CSR0_TXON 0x0010 /* transmitter on (R) */
#define CSR0_RXON 0x0020 /* receiver on (R) */
#define CSR0_INEA 0x0040 /* interrupt enable (RW) */
#define CSR0_INTR 0x0080 /* interrupt active (R) */
#define CSR0_IDON 0x0100 /* initialization done (RC) */
#define CSR0_TINT 0x0200 /* transmitter interrupt (RC) */
#define CSR0_RINT 0x0400 /* receiver interrupt (RC) */
#define CSR0_MERR 0x0800 /* memory error (RC) */
#define CSR0_MISS 0x1000 /* missed frame (RC) */
#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) (RC) */
#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits (RC) */
#define CSR0_ERR 0x8000 /* error (RC) */
/* CSR3 */
#define CSR3_BCON 0x0001 /* byte control */
#define CSR3_ACON 0x0002 /* ALE control */
#define CSR3_BSWP 0x0004 /* byte swap (1=big endian) */
/***************************** Prototypes *****************************/
static int lance_probe( struct net_device *dev);
static int lance_open( struct net_device *dev );
static void lance_init_ring( struct net_device *dev );
static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev );
static irqreturn_t lance_interrupt( int irq, void *dev_id);
static int lance_rx( struct net_device *dev );
static int lance_close( struct net_device *dev );
static void set_multicast_list( struct net_device *dev );
/************************* End of Prototypes **************************/
struct net_device * __init sun3lance_probe(int unit)
{
struct net_device *dev;
static int found;
int err = -ENODEV;
if (!MACH_IS_SUN3 && !MACH_IS_SUN3X)
return ERR_PTR(-ENODEV);
/* check that this machine has an onboard lance */
switch(idprom->id_machtype) {
case SM_SUN3|SM_3_50:
case SM_SUN3|SM_3_60:
case SM_SUN3X|SM_3_80:
/* these machines have lance */
break;
default:
return ERR_PTR(-ENODEV);
}
if (found)
return ERR_PTR(-ENODEV);
dev = alloc_etherdev(sizeof(struct lance_private));
if (!dev)
return ERR_PTR(-ENOMEM);
if (unit >= 0) {
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
}
if (!lance_probe(dev))
goto out;
err = register_netdev(dev);
if (err)
goto out1;
found = 1;
return dev;
out1:
#ifdef CONFIG_SUN3
iounmap((void __iomem *)dev->base_addr);
#endif
out:
free_netdev(dev);
return ERR_PTR(err);
}
static const struct net_device_ops lance_netdev_ops = {
.ndo_open = lance_open,
.ndo_stop = lance_close,
.ndo_start_xmit = lance_start_xmit,
.ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = NULL,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
};
static int __init lance_probe( struct net_device *dev)
{
unsigned long ioaddr;
struct lance_private *lp;
int i;
static int did_version;
volatile unsigned short *ioaddr_probe;
unsigned short tmp1, tmp2;
#ifdef CONFIG_SUN3
ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE);
if (!ioaddr)
return 0;
#else
ioaddr = SUN3X_LANCE;
#endif
/* test to see if there's really a lance here */
/* (CSRO_INIT shouldn't be readable) */
ioaddr_probe = (volatile unsigned short *)ioaddr;
tmp1 = ioaddr_probe[0];
tmp2 = ioaddr_probe[1];
ioaddr_probe[1] = CSR0;
ioaddr_probe[0] = CSR0_INIT | CSR0_STOP;
if(ioaddr_probe[0] != CSR0_STOP) {
ioaddr_probe[0] = tmp1;
ioaddr_probe[1] = tmp2;
#ifdef CONFIG_SUN3
iounmap((void __iomem *)ioaddr);
#endif
return 0;
}
lp = netdev_priv(dev);
/* XXX - leak? */
MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000);
if (MEM == NULL) {
#ifdef CONFIG_SUN3
iounmap((void __iomem *)ioaddr);
#endif
printk(KERN_WARNING "SUN3 Lance couldn't allocate DVMA memory\n");
return 0;
}
lp->iobase = (volatile unsigned short *)ioaddr;
dev->base_addr = (unsigned long)ioaddr; /* informational only */
REGA(CSR0) = CSR0_STOP;
if (request_irq(LANCE_IRQ, lance_interrupt, 0, "SUN3 Lance", dev) < 0) {
#ifdef CONFIG_SUN3
iounmap((void __iomem *)ioaddr);
#endif
dvma_free((void *)MEM);
printk(KERN_WARNING "SUN3 Lance unable to allocate IRQ\n");
return 0;
}
dev->irq = (unsigned short)LANCE_IRQ;
printk("%s: SUN3 Lance at io %#lx, mem %#lx, irq %d, hwaddr ",
dev->name,
(unsigned long)ioaddr,
(unsigned long)MEM,
dev->irq);
/* copy in the ethernet address from the prom */
for(i = 0; i < 6 ; i++)
dev->dev_addr[i] = idprom->id_ethaddr[i];
/* tell the card it's ether address, bytes swapped */
MEM->init.hwaddr[0] = dev->dev_addr[1];
MEM->init.hwaddr[1] = dev->dev_addr[0];
MEM->init.hwaddr[2] = dev->dev_addr[3];
MEM->init.hwaddr[3] = dev->dev_addr[2];
MEM->init.hwaddr[4] = dev->dev_addr[5];
MEM->init.hwaddr[5] = dev->dev_addr[4];
printk("%pM\n", dev->dev_addr);
MEM->init.mode = 0x0000;
MEM->init.filter[0] = 0x00000000;
MEM->init.filter[1] = 0x00000000;
MEM->init.rdra = dvma_vtob(MEM->rx_head);
MEM->init.rlen = (RX_LOG_RING_SIZE << 13) |
(dvma_vtob(MEM->rx_head) >> 16);
MEM->init.tdra = dvma_vtob(MEM->tx_head);
MEM->init.tlen = (TX_LOG_RING_SIZE << 13) |
(dvma_vtob(MEM->tx_head) >> 16);
DPRINTK(2, ("initaddr: %08lx rx_ring: %08lx tx_ring: %08lx\n",
dvma_vtob(&(MEM->init)), dvma_vtob(MEM->rx_head),
(dvma_vtob(MEM->tx_head))));
if (did_version++ == 0)
printk( version );
dev->netdev_ops = &lance_netdev_ops;
// KLUDGE -- REMOVE ME
set_bit(__LINK_STATE_PRESENT, &dev->state);
return 1;
}
static int lance_open( struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
int i;
DPRINTK( 2, ( "%s: lance_open()\n", dev->name ));
REGA(CSR0) = CSR0_STOP;
lance_init_ring(dev);
/* From now on, AREG is kept to point to CSR0 */
REGA(CSR0) = CSR0_INIT;
i = 1000000;
while (--i > 0)
if (DREG & CSR0_IDON)
break;
if (i <= 0 || (DREG & CSR0_ERR)) {
DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n",
dev->name, i, DREG ));
DREG = CSR0_STOP;
return -EIO;
}
DREG = CSR0_IDON | CSR0_STRT | CSR0_INEA;
netif_start_queue(dev);
DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG ));
return 0;
}
/* Initialize the LANCE Rx and Tx rings. */
static void lance_init_ring( struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
int i;
lp->lock = 0;
lp->tx_full = 0;
lp->new_rx = lp->new_tx = 0;
lp->old_rx = lp->old_tx = 0;
for( i = 0; i < TX_RING_SIZE; i++ ) {
MEM->tx_head[i].base = dvma_vtob(MEM->tx_data[i]);
MEM->tx_head[i].flag = 0;
MEM->tx_head[i].base_hi =
(dvma_vtob(MEM->tx_data[i])) >>16;
MEM->tx_head[i].length = 0;
MEM->tx_head[i].misc = 0;
}
for( i = 0; i < RX_RING_SIZE; i++ ) {
MEM->rx_head[i].base = dvma_vtob(MEM->rx_data[i]);
MEM->rx_head[i].flag = RMD1_OWN_CHIP;
MEM->rx_head[i].base_hi =
(dvma_vtob(MEM->rx_data[i])) >> 16;
MEM->rx_head[i].buf_length = -PKT_BUF_SZ | 0xf000;
MEM->rx_head[i].msg_length = 0;
}
/* tell the card it's ether address, bytes swapped */
MEM->init.hwaddr[0] = dev->dev_addr[1];
MEM->init.hwaddr[1] = dev->dev_addr[0];
MEM->init.hwaddr[2] = dev->dev_addr[3];
MEM->init.hwaddr[3] = dev->dev_addr[2];
MEM->init.hwaddr[4] = dev->dev_addr[5];
MEM->init.hwaddr[5] = dev->dev_addr[4];
MEM->init.mode = 0x0000;
MEM->init.filter[0] = 0x00000000;
MEM->init.filter[1] = 0x00000000;
MEM->init.rdra = dvma_vtob(MEM->rx_head);
MEM->init.rlen = (RX_LOG_RING_SIZE << 13) |
(dvma_vtob(MEM->rx_head) >> 16);
MEM->init.tdra = dvma_vtob(MEM->tx_head);
MEM->init.tlen = (TX_LOG_RING_SIZE << 13) |
(dvma_vtob(MEM->tx_head) >> 16);
/* tell the lance the address of its init block */
REGA(CSR1) = dvma_vtob(&(MEM->init));
REGA(CSR2) = dvma_vtob(&(MEM->init)) >> 16;
#ifdef CONFIG_SUN3X
REGA(CSR3) = CSR3_BSWP | CSR3_ACON | CSR3_BCON;
#else
REGA(CSR3) = CSR3_BSWP;
#endif
}
static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
int entry, len;
struct lance_tx_head *head;
unsigned long flags;
DPRINTK( 1, ( "%s: transmit start.\n",
dev->name));
/* Transmitter timeout, serious problems. */
if (netif_queue_stopped(dev)) {
int tickssofar = jiffies - dev_trans_start(dev);
if (tickssofar < HZ/5)
return NETDEV_TX_BUSY;
DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n",
dev->name, DREG ));
DREG = CSR0_STOP;
/*
* Always set BSWP after a STOP as STOP puts it back into
* little endian mode.
*/
REGA(CSR3) = CSR3_BSWP;
dev->stats.tx_errors++;
if(lance_debug >= 2) {
int i;
printk("Ring data: old_tx %d new_tx %d%s new_rx %d\n",
lp->old_tx, lp->new_tx,
lp->tx_full ? " (full)" : "",
lp->new_rx );
for( i = 0 ; i < RX_RING_SIZE; i++ )
printk( "rx #%d: base=%04x blen=%04x mlen=%04x\n",
i, MEM->rx_head[i].base,
-MEM->rx_head[i].buf_length,
MEM->rx_head[i].msg_length);
for( i = 0 ; i < TX_RING_SIZE; i++ )
printk("tx #%d: base=%04x len=%04x misc=%04x\n",
i, MEM->tx_head[i].base,
-MEM->tx_head[i].length,
MEM->tx_head[i].misc );
}
lance_init_ring(dev);
REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT;
netif_start_queue(dev);
return NETDEV_TX_OK;
}
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
/* Block a timer-based transmit from overlapping with us by
stopping the queue for a bit... */
netif_stop_queue(dev);
if (test_and_set_bit( 0, (void*)&lp->lock ) != 0) {
printk( "%s: tx queue lock!.\n", dev->name);
/* don't clear dev->tbusy flag. */
return NETDEV_TX_BUSY;
}
AREG = CSR0;
DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
dev->name, DREG ));
#ifdef CONFIG_SUN3X
/* this weirdness doesn't appear on sun3... */
if(!(DREG & CSR0_INIT)) {
DPRINTK( 1, ("INIT not set, reinitializing...\n"));
REGA( CSR0 ) = CSR0_STOP;
lance_init_ring(dev);
REGA( CSR0 ) = CSR0_INIT | CSR0_STRT;
}
#endif
/* Fill in a Tx ring entry */
#if 0
if (lance_debug >= 2) {
printk( "%s: TX pkt %d type 0x%04x"
" from %s to %s"
" data at 0x%08x len %d\n",
dev->name, lp->new_tx, ((u_short *)skb->data)[6],
DEV_ADDR(&skb->data[6]), DEV_ADDR(skb->data),
(int)skb->data, (int)skb->len );
}
#endif
/* We're not prepared for the int until the last flags are set/reset.
* And the int may happen already after setting the OWN_CHIP... */
local_irq_save(flags);
/* Mask to ring buffer boundary. */
entry = lp->new_tx;
head = &(MEM->tx_head[entry]);
/* Caution: the write order is important here, set the "ownership" bits
* last.
*/
/* the sun3's lance needs it's buffer padded to the minimum
size */
len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
// head->length = -len;
head->length = (-len) | 0xf000;
head->misc = 0;
skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len);
if (len != skb->len)
memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len);
head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
lp->new_tx = (lp->new_tx + 1) & TX_RING_MOD_MASK;
dev->stats.tx_bytes += skb->len;
/* Trigger an immediate send poll. */
REGA(CSR0) = CSR0_INEA | CSR0_TDMD | CSR0_STRT;
AREG = CSR0;
DPRINTK( 2, ( "%s: lance_start_xmit() exiting, csr0 %4.4x.\n",
dev->name, DREG ));
dev_kfree_skb(skb);
lp->lock = 0;
if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) ==
TMD1_OWN_HOST)
netif_start_queue(dev);
local_irq_restore(flags);
return NETDEV_TX_OK;
}
/* The LANCE interrupt handler. */
static irqreturn_t lance_interrupt( int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct lance_private *lp = netdev_priv(dev);
int csr0;
static int in_interrupt;
if (dev == NULL) {
DPRINTK( 1, ( "lance_interrupt(): invalid dev_id\n" ));
return IRQ_NONE;
}
if (in_interrupt)
DPRINTK( 2, ( "%s: Re-entering the interrupt handler.\n", dev->name ));
in_interrupt = 1;
still_more:
flush_cache_all();
AREG = CSR0;
csr0 = DREG;
/* ack interrupts */
DREG = csr0 & (CSR0_TINT | CSR0_RINT | CSR0_IDON);
/* clear errors */
if(csr0 & CSR0_ERR)
DREG = CSR0_BABL | CSR0_MERR | CSR0_CERR | CSR0_MISS;
DPRINTK( 2, ( "%s: interrupt csr0=%04x new csr=%04x.\n",
dev->name, csr0, DREG ));
if (csr0 & CSR0_TINT) { /* Tx-done interrupt */
int old_tx = lp->old_tx;
// if(lance_debug >= 3) {
// int i;
//
// printk("%s: tx int\n", dev->name);
//
// for(i = 0; i < TX_RING_SIZE; i++)
// printk("ring %d flag=%04x\n", i,
// MEM->tx_head[i].flag);
// }
while( old_tx != lp->new_tx) {
struct lance_tx_head *head = &(MEM->tx_head[old_tx]);
DPRINTK(3, ("on tx_ring %d\n", old_tx));
if (head->flag & TMD1_OWN_CHIP)
break; /* It still hasn't been Txed */
if (head->flag & TMD1_ERR) {
int status = head->misc;
dev->stats.tx_errors++;
if (status & TMD3_RTRY) dev->stats.tx_aborted_errors++;
if (status & TMD3_LCAR) dev->stats.tx_carrier_errors++;
if (status & TMD3_LCOL) dev->stats.tx_window_errors++;
if (status & (TMD3_UFLO | TMD3_BUFF)) {
dev->stats.tx_fifo_errors++;
printk("%s: Tx FIFO error\n",
dev->name);
REGA(CSR0) = CSR0_STOP;
REGA(CSR3) = CSR3_BSWP;
lance_init_ring(dev);
REGA(CSR0) = CSR0_STRT | CSR0_INEA;
return IRQ_HANDLED;
}
} else if(head->flag & (TMD1_ENP | TMD1_STP)) {
head->flag &= ~(TMD1_ENP | TMD1_STP);
if(head->flag & (TMD1_ONE | TMD1_MORE))
dev->stats.collisions++;
dev->stats.tx_packets++;
DPRINTK(3, ("cleared tx ring %d\n", old_tx));
}
old_tx = (old_tx +1) & TX_RING_MOD_MASK;
}
lp->old_tx = old_tx;
}
if (netif_queue_stopped(dev)) {
/* The ring is no longer full, clear tbusy. */
netif_start_queue(dev);
netif_wake_queue(dev);
}
if (csr0 & CSR0_RINT) /* Rx interrupt */
lance_rx( dev );
/* Log misc errors. */
if (csr0 & CSR0_BABL) dev->stats.tx_errors++; /* Tx babble. */
if (csr0 & CSR0_MISS) dev->stats.rx_errors++; /* Missed a Rx frame. */
if (csr0 & CSR0_MERR) {
DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), "
"status %04x.\n", dev->name, csr0 ));
/* Restart the chip. */
REGA(CSR0) = CSR0_STOP;
REGA(CSR3) = CSR3_BSWP;
lance_init_ring(dev);
REGA(CSR0) = CSR0_STRT | CSR0_INEA;
}
/* Clear any other interrupt, and set interrupt enable. */
// DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR |
// CSR0_IDON | CSR0_INEA;
REGA(CSR0) = CSR0_INEA;
if(DREG & (CSR0_RINT | CSR0_TINT)) {
DPRINTK(2, ("restarting interrupt, csr0=%#04x\n", DREG));
goto still_more;
}
DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n",
dev->name, DREG ));
in_interrupt = 0;
return IRQ_HANDLED;
}
/* get packet, toss into skbuff */
static int lance_rx( struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
int entry = lp->new_rx;
/* If we own the next entry, it's a new packet. Send it up. */
while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) {
struct lance_rx_head *head = &(MEM->rx_head[entry]);
int status = head->flag;
if (status != (RMD1_ENP|RMD1_STP)) { /* There was an error. */
/* There is a tricky error noted by John Murphy,
<murf@perftech.com> to Russ Nelson: Even with
full-sized buffers it's possible for a jabber packet to use two
buffers, with only the last correctly noting the error. */
if (status & RMD1_ENP) /* Only count a general error at the */
dev->stats.rx_errors++; /* end of a packet.*/
if (status & RMD1_FRAM) dev->stats.rx_frame_errors++;
if (status & RMD1_OFLO) dev->stats.rx_over_errors++;
if (status & RMD1_CRC) dev->stats.rx_crc_errors++;
if (status & RMD1_BUFF) dev->stats.rx_fifo_errors++;
head->flag &= (RMD1_ENP|RMD1_STP);
} else {
/* Malloc up new buffer, compatible with net-3. */
// short pkt_len = head->msg_length;// & 0xfff;
short pkt_len = (head->msg_length & 0xfff) - 4;
struct sk_buff *skb;
if (pkt_len < 60) {
printk( "%s: Runt packet!\n", dev->name );
dev->stats.rx_errors++;
}
else {
skb = netdev_alloc_skb(dev, pkt_len + 2);
if (skb == NULL) {
dev->stats.rx_dropped++;
head->msg_length = 0;
head->flag |= RMD1_OWN_CHIP;
lp->new_rx = (lp->new_rx+1) &
RX_RING_MOD_MASK;
}
#if 0
if (lance_debug >= 3) {
u_char *data = PKTBUF_ADDR(head);
printk("%s: RX pkt %d type 0x%04x"
" from %pM to %pM",
dev->name, lp->new_tx, ((u_short *)data)[6],
&data[6], data);
printk(" data %02x %02x %02x %02x %02x %02x %02x %02x "
"len %d at %08x\n",
data[15], data[16], data[17], data[18],
data[19], data[20], data[21], data[22],
pkt_len, data);
}
#endif
if (lance_debug >= 3) {
u_char *data = PKTBUF_ADDR(head);
printk( "%s: RX pkt %d type 0x%04x len %d\n ", dev->name, entry, ((u_short *)data)[6], pkt_len);
}
skb_reserve( skb, 2 ); /* 16 byte align */
skb_put( skb, pkt_len ); /* Make room */
skb_copy_to_linear_data(skb,
PKTBUF_ADDR(head),
pkt_len);
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
}
// head->buf_length = -PKT_BUF_SZ | 0xf000;
head->msg_length = 0;
head->flag = RMD1_OWN_CHIP;
entry = lp->new_rx = (lp->new_rx +1) & RX_RING_MOD_MASK;
}
/* From lance.c (Donald Becker): */
/* We should check that at least two ring entries are free.
If not, we should free one and mark stats->rx_dropped++. */
return 0;
}
static int lance_close( struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
netif_stop_queue(dev);
AREG = CSR0;
DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n",
dev->name, DREG ));
/* We stop the LANCE here -- it occasionally polls
memory if we don't. */
DREG = CSR0_STOP;
return 0;
}
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
best-effort filtering.
*/
/* completely untested on a sun3 */
static void set_multicast_list( struct net_device *dev )
{
struct lance_private *lp = netdev_priv(dev);
if(netif_queue_stopped(dev))
/* Only possible if board is already started */
return;
/* We take the simple way out and always enable promiscuous mode. */
DREG = CSR0_STOP; /* Temporarily stop the lance. */
if (dev->flags & IFF_PROMISC) {
/* Log any net taps. */
DPRINTK( 3, ( "%s: Promiscuous mode enabled.\n", dev->name ));
REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */
} else {
short multicast_table[4];
int num_addrs = netdev_mc_count(dev);
int i;
/* We don't use the multicast table, but rely on upper-layer
* filtering. */
memset( multicast_table, (num_addrs == 0) ? 0 : -1,
sizeof(multicast_table) );
for( i = 0; i < 4; i++ )
REGA( CSR8+i ) = multicast_table[i];
REGA( CSR15 ) = 0; /* Unset promiscuous mode */
}
/*
* Always set BSWP after a STOP as STOP puts it back into
* little endian mode.
*/
REGA( CSR3 ) = CSR3_BSWP;
/* Resume normal operation and reset AREG to CSR0 */
REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT;
}
#ifdef MODULE
static struct net_device *sun3lance_dev;
int __init init_module(void)
{
sun3lance_dev = sun3lance_probe(-1);
return PTR_ERR_OR_ZERO(sun3lance_dev);
}
void __exit cleanup_module(void)
{
unregister_netdev(sun3lance_dev);
#ifdef CONFIG_SUN3
iounmap((void __iomem *)sun3lance_dev->base_addr);
#endif
free_netdev(sun3lance_dev);
}
#endif /* MODULE */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \
xgbe-ptp.o
amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o
amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/*
* AMD 10Gb Ethernet driver
*
* This file is available to you under your choice of the following two
* licenses:
*
* License 1: GPLv2
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
*
* This file is free software; you may copy, redistribute 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.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*
* License 2: Modified BSD
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Advanced Micro Devices, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/netdevice.h>
#include <net/dcbnl.h>
#include "xgbe.h"
#include "xgbe-common.h"
static int xgbe_dcb_ieee_getets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
/* Set number of supported traffic classes */
ets->ets_cap = pdata->hw_feat.tc_cnt;
if (pdata->ets) {
ets->cbs = pdata->ets->cbs;
memcpy(ets->tc_tx_bw, pdata->ets->tc_tx_bw,
sizeof(ets->tc_tx_bw));
memcpy(ets->tc_tsa, pdata->ets->tc_tsa,
sizeof(ets->tc_tsa));
memcpy(ets->prio_tc, pdata->ets->prio_tc,
sizeof(ets->prio_tc));
}
return 0;
}
static int xgbe_dcb_ieee_setets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
unsigned int i, tc_ets, tc_ets_weight;
tc_ets = 0;
tc_ets_weight = 0;
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
DBGPR(" TC%u: tx_bw=%hhu, rx_bw=%hhu, tsa=%hhu\n", i,
ets->tc_tx_bw[i], ets->tc_rx_bw[i], ets->tc_tsa[i]);
DBGPR(" PRIO%u: TC=%hhu\n", i, ets->prio_tc[i]);
if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) &&
(i >= pdata->hw_feat.tc_cnt))
return -EINVAL;
if (ets->prio_tc[i] >= pdata->hw_feat.tc_cnt)
return -EINVAL;
switch (ets->tc_tsa[i]) {
case IEEE_8021QAZ_TSA_STRICT:
break;
case IEEE_8021QAZ_TSA_ETS:
tc_ets = 1;
tc_ets_weight += ets->tc_tx_bw[i];
break;
default:
return -EINVAL;
}
}
/* Weights must add up to 100% */
if (tc_ets && (tc_ets_weight != 100))
return -EINVAL;
if (!pdata->ets) {
pdata->ets = devm_kzalloc(pdata->dev, sizeof(*pdata->ets),
GFP_KERNEL);
if (!pdata->ets)
return -ENOMEM;
}
memcpy(pdata->ets, ets, sizeof(*pdata->ets));
pdata->hw_if.config_dcb_tc(pdata);
return 0;
}
static int xgbe_dcb_ieee_getpfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
/* Set number of supported PFC traffic classes */
pfc->pfc_cap = pdata->hw_feat.tc_cnt;
if (pdata->pfc) {
pfc->pfc_en = pdata->pfc->pfc_en;
pfc->mbc = pdata->pfc->mbc;
pfc->delay = pdata->pfc->delay;
}
return 0;
}
static int xgbe_dcb_ieee_setpfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
DBGPR(" cap=%hhu, en=%hhx, mbc=%hhu, delay=%hhu\n",
pfc->pfc_cap, pfc->pfc_en, pfc->mbc, pfc->delay);
if (!pdata->pfc) {
pdata->pfc = devm_kzalloc(pdata->dev, sizeof(*pdata->pfc),
GFP_KERNEL);
if (!pdata->pfc)
return -ENOMEM;
}
memcpy(pdata->pfc, pfc, sizeof(*pdata->pfc));
pdata->hw_if.config_dcb_pfc(pdata);
return 0;
}
static u8 xgbe_dcb_getdcbx(struct net_device *netdev)
{
return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
}
static u8 xgbe_dcb_setdcbx(struct net_device *netdev, u8 dcbx)
{
u8 support = xgbe_dcb_getdcbx(netdev);
DBGPR(" DCBX=%#hhx\n", dcbx);
if (dcbx & ~support)
return 1;
if ((dcbx & support) != support)
return 1;
return 0;
}
static const struct dcbnl_rtnl_ops xgbe_dcbnl_ops = {
/* IEEE 802.1Qaz std */
.ieee_getets = xgbe_dcb_ieee_getets,
.ieee_setets = xgbe_dcb_ieee_setets,
.ieee_getpfc = xgbe_dcb_ieee_getpfc,
.ieee_setpfc = xgbe_dcb_ieee_setpfc,
/* DCBX configuration */
.getdcbx = xgbe_dcb_getdcbx,
.setdcbx = xgbe_dcb_setdcbx,
};
const struct dcbnl_rtnl_ops *xgbe_get_dcbnl_ops(void)
{
return &xgbe_dcbnl_ops;
}

View file

@ -0,0 +1,373 @@
/*
* AMD 10Gb Ethernet driver
*
* This file is available to you under your choice of the following two
* licenses:
*
* License 1: GPLv2
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
*
* This file is free software; you may copy, redistribute 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.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*
* License 2: Modified BSD
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Advanced Micro Devices, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "xgbe.h"
#include "xgbe-common.h"
static ssize_t xgbe_common_read(char __user *buffer, size_t count,
loff_t *ppos, unsigned int value)
{
char *buf;
ssize_t len;
if (*ppos != 0)
return 0;
buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static ssize_t xgbe_common_write(const char __user *buffer, size_t count,
loff_t *ppos, unsigned int *value)
{
char workarea[32];
ssize_t len;
int ret;
if (*ppos != 0)
return 0;
if (count >= sizeof(workarea))
return -ENOSPC;
len = simple_write_to_buffer(workarea, sizeof(workarea) - 1, ppos,
buffer, count);
if (len < 0)
return len;
workarea[len] = '\0';
ret = kstrtouint(workarea, 16, value);
if (ret)
return -EIO;
return len;
}
static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xgmac_reg);
}
static ssize_t xgmac_reg_addr_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_write(buffer, count, ppos,
&pdata->debugfs_xgmac_reg);
}
static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
unsigned int value;
value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg);
return xgbe_common_read(buffer, count, ppos, value);
}
static ssize_t xgmac_reg_value_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
unsigned int value;
ssize_t len;
len = xgbe_common_write(buffer, count, ppos, &value);
if (len < 0)
return len;
XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value);
return len;
}
static const struct file_operations xgmac_reg_addr_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = xgmac_reg_addr_read,
.write = xgmac_reg_addr_write,
};
static const struct file_operations xgmac_reg_value_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = xgmac_reg_value_read,
.write = xgmac_reg_value_write,
};
static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_mmd);
}
static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_write(buffer, count, ppos,
&pdata->debugfs_xpcs_mmd);
}
static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_reg);
}
static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
return xgbe_common_write(buffer, count, ppos,
&pdata->debugfs_xpcs_reg);
}
static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
unsigned int value;
value = XMDIO_READ(pdata, pdata->debugfs_xpcs_mmd,
pdata->debugfs_xpcs_reg);
return xgbe_common_read(buffer, count, ppos, value);
}
static ssize_t xpcs_reg_value_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct xgbe_prv_data *pdata = filp->private_data;
unsigned int value;
ssize_t len;
len = xgbe_common_write(buffer, count, ppos, &value);
if (len < 0)
return len;
XMDIO_WRITE(pdata, pdata->debugfs_xpcs_mmd, pdata->debugfs_xpcs_reg,
value);
return len;
}
static const struct file_operations xpcs_mmd_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = xpcs_mmd_read,
.write = xpcs_mmd_write,
};
static const struct file_operations xpcs_reg_addr_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = xpcs_reg_addr_read,
.write = xpcs_reg_addr_write,
};
static const struct file_operations xpcs_reg_value_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = xpcs_reg_value_read,
.write = xpcs_reg_value_write,
};
void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
{
struct dentry *pfile;
char *buf;
/* Set defaults */
pdata->debugfs_xgmac_reg = 0;
pdata->debugfs_xpcs_mmd = 1;
pdata->debugfs_xpcs_reg = 0;
buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
if (pdata->xgbe_debugfs == NULL) {
netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
return;
}
pfile = debugfs_create_file("xgmac_register", 0600,
pdata->xgbe_debugfs, pdata,
&xgmac_reg_addr_fops);
if (!pfile)
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
pfile = debugfs_create_file("xgmac_register_value", 0600,
pdata->xgbe_debugfs, pdata,
&xgmac_reg_value_fops);
if (!pfile)
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
pfile = debugfs_create_file("xpcs_mmd", 0600,
pdata->xgbe_debugfs, pdata,
&xpcs_mmd_fops);
if (!pfile)
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
pfile = debugfs_create_file("xpcs_register", 0600,
pdata->xgbe_debugfs, pdata,
&xpcs_reg_addr_fops);
if (!pfile)
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
pfile = debugfs_create_file("xpcs_register_value", 0600,
pdata->xgbe_debugfs, pdata,
&xpcs_reg_value_fops);
if (!pfile)
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
kfree(buf);
}
void xgbe_debugfs_exit(struct xgbe_prv_data *pdata)
{
debugfs_remove_recursive(pdata->xgbe_debugfs);
pdata->xgbe_debugfs = NULL;
}

View file

@ -0,0 +1,562 @@
/*
* AMD 10Gb Ethernet driver
*
* This file is available to you under your choice of the following two
* licenses:
*
* License 1: GPLv2
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
*
* This file is free software; you may copy, redistribute 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.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*
* License 2: Modified BSD
*
* Copyright (c) 2014 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Advanced Micro Devices, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
* Inc. unless otherwise expressly agreed to in writing between Synopsys
* and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product
* under any End User Software License Agreement or Agreement for Licensed
* Product with Synopsys or any supplement thereto. Permission is hereby
* granted, free of charge, to any person obtaining a copy of this software
* annotated with this license and the Software, to deal in the Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "xgbe.h"
#include "xgbe-common.h"
static void xgbe_unmap_skb(struct xgbe_prv_data *, struct xgbe_ring_data *);
static void xgbe_free_ring(struct xgbe_prv_data *pdata,
struct xgbe_ring *ring)
{
struct xgbe_ring_data *rdata;
unsigned int i;
if (!ring)
return;
if (ring->rdata) {
for (i = 0; i < ring->rdesc_count; i++) {
rdata = XGBE_GET_DESC_DATA(ring, i);
xgbe_unmap_skb(pdata, rdata);
}
kfree(ring->rdata);
ring->rdata = NULL;
}
if (ring->rdesc) {
dma_free_coherent(pdata->dev,
(sizeof(struct xgbe_ring_desc) *
ring->rdesc_count),
ring->rdesc, ring->rdesc_dma);
ring->rdesc = NULL;
}
}
static void xgbe_free_ring_resources(struct xgbe_prv_data *pdata)
{
struct xgbe_channel *channel;
unsigned int i;
DBGPR("-->xgbe_free_ring_resources\n");
channel = pdata->channel;
for (i = 0; i < pdata->channel_count; i++, channel++) {
xgbe_free_ring(pdata, channel->tx_ring);
xgbe_free_ring(pdata, channel->rx_ring);
}
DBGPR("<--xgbe_free_ring_resources\n");
}
static int xgbe_init_ring(struct xgbe_prv_data *pdata,
struct xgbe_ring *ring, unsigned int rdesc_count)
{
DBGPR("-->xgbe_init_ring\n");
if (!ring)
return 0;
/* Descriptors */
ring->rdesc_count = rdesc_count;
ring->rdesc = dma_alloc_coherent(pdata->dev,
(sizeof(struct xgbe_ring_desc) *
rdesc_count), &ring->rdesc_dma,
GFP_KERNEL);
if (!ring->rdesc)
return -ENOMEM;
/* Descriptor information */
ring->rdata = kcalloc(rdesc_count, sizeof(struct xgbe_ring_data),
GFP_KERNEL);
if (!ring->rdata)
return -ENOMEM;
DBGPR(" rdesc=0x%p, rdesc_dma=0x%llx, rdata=0x%p\n",
ring->rdesc, ring->rdesc_dma, ring->rdata);
DBGPR("<--xgbe_init_ring\n");
return 0;
}
static int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata)
{
struct xgbe_channel *channel;
unsigned int i;
int ret;
DBGPR("-->xgbe_alloc_ring_resources\n");
channel = pdata->channel;
for (i = 0; i < pdata->channel_count; i++, channel++) {
DBGPR(" %s - tx_ring:\n", channel->name);
ret = xgbe_init_ring(pdata, channel->tx_ring,
pdata->tx_desc_count);
if (ret) {
netdev_alert(pdata->netdev,
"error initializing Tx ring\n");
goto err_ring;
}
DBGPR(" %s - rx_ring:\n", channel->name);
ret = xgbe_init_ring(pdata, channel->rx_ring,
pdata->rx_desc_count);
if (ret) {
netdev_alert(pdata->netdev,
"error initializing Tx ring\n");
goto err_ring;
}
}
DBGPR("<--xgbe_alloc_ring_resources\n");
return 0;
err_ring:
xgbe_free_ring_resources(pdata);
return ret;
}
static void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata)
{
struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct xgbe_channel *channel;
struct xgbe_ring *ring;
struct xgbe_ring_data *rdata;
struct xgbe_ring_desc *rdesc;
dma_addr_t rdesc_dma;
unsigned int i, j;
DBGPR("-->xgbe_wrapper_tx_descriptor_init\n");
channel = pdata->channel;
for (i = 0; i < pdata->channel_count; i++, channel++) {
ring = channel->tx_ring;
if (!ring)
break;
rdesc = ring->rdesc;
rdesc_dma = ring->rdesc_dma;
for (j = 0; j < ring->rdesc_count; j++) {
rdata = XGBE_GET_DESC_DATA(ring, j);
rdata->rdesc = rdesc;
rdata->rdesc_dma = rdesc_dma;
rdesc++;
rdesc_dma += sizeof(struct xgbe_ring_desc);
}
ring->cur = 0;
ring->dirty = 0;
ring->tx.queue_stopped = 0;
hw_if->tx_desc_init(channel);
}
DBGPR("<--xgbe_wrapper_tx_descriptor_init\n");
}
static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
{
struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct xgbe_channel *channel;
struct xgbe_ring *ring;
struct xgbe_ring_desc *rdesc;
struct xgbe_ring_data *rdata;
dma_addr_t rdesc_dma, skb_dma;
struct sk_buff *skb = NULL;
unsigned int i, j;
DBGPR("-->xgbe_wrapper_rx_descriptor_init\n");
channel = pdata->channel;
for (i = 0; i < pdata->channel_count; i++, channel++) {
ring = channel->rx_ring;
if (!ring)
break;
rdesc = ring->rdesc;
rdesc_dma = ring->rdesc_dma;
for (j = 0; j < ring->rdesc_count; j++) {
rdata = XGBE_GET_DESC_DATA(ring, j);
rdata->rdesc = rdesc;
rdata->rdesc_dma = rdesc_dma;
/* Allocate skb & assign to each rdesc */
skb = dev_alloc_skb(pdata->rx_buf_size);
if (skb == NULL)
break;
skb_dma = dma_map_single(pdata->dev, skb->data,
pdata->rx_buf_size,
DMA_FROM_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
netdev_alert(pdata->netdev,
"failed to do the dma map\n");
dev_kfree_skb_any(skb);
break;
}
rdata->skb = skb;
rdata->skb_dma = skb_dma;
rdata->skb_dma_len = pdata->rx_buf_size;
rdesc++;
rdesc_dma += sizeof(struct xgbe_ring_desc);
}
ring->cur = 0;
ring->dirty = 0;
ring->rx.realloc_index = 0;
ring->rx.realloc_threshold = 0;
hw_if->rx_desc_init(channel);
}
DBGPR("<--xgbe_wrapper_rx_descriptor_init\n");
}
static void xgbe_unmap_skb(struct xgbe_prv_data *pdata,
struct xgbe_ring_data *rdata)
{
if (rdata->skb_dma) {
if (rdata->mapped_as_page) {
dma_unmap_page(pdata->dev, rdata->skb_dma,
rdata->skb_dma_len, DMA_TO_DEVICE);
} else {
dma_unmap_single(pdata->dev, rdata->skb_dma,
rdata->skb_dma_len, DMA_TO_DEVICE);
}
rdata->skb_dma = 0;
rdata->skb_dma_len = 0;
}
if (rdata->skb) {
dev_kfree_skb_any(rdata->skb);
rdata->skb = NULL;
}
rdata->tso_header = 0;
rdata->len = 0;
rdata->interrupt = 0;
rdata->mapped_as_page = 0;
if (rdata->state_saved) {
rdata->state_saved = 0;
rdata->state.incomplete = 0;
rdata->state.context_next = 0;
rdata->state.skb = NULL;
rdata->state.len = 0;
rdata->state.error = 0;
}
}
static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb)
{
struct xgbe_prv_data *pdata = channel->pdata;
struct xgbe_ring *ring = channel->tx_ring;
struct xgbe_ring_data *rdata;
struct xgbe_packet_data *packet;
struct skb_frag_struct *frag;
dma_addr_t skb_dma;
unsigned int start_index, cur_index;
unsigned int offset, tso, vlan, datalen, len;
unsigned int i;
DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur);
offset = 0;
start_index = ring->cur;
cur_index = ring->cur;
packet = &ring->packet_data;
packet->rdesc_count = 0;
packet->length = 0;
tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
TSO_ENABLE);
vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
VLAN_CTAG);
/* Save space for a context descriptor if needed */
if ((tso && (packet->mss != ring->tx.cur_mss)) ||
(vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag)))
cur_index++;
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
if (tso) {
DBGPR(" TSO packet\n");
/* Map the TSO header */
skb_dma = dma_map_single(pdata->dev, skb->data,
packet->header_len, DMA_TO_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
netdev_alert(pdata->netdev, "dma_map_single failed\n");
goto err_out;
}
rdata->skb_dma = skb_dma;
rdata->skb_dma_len = packet->header_len;
rdata->tso_header = 1;
offset = packet->header_len;
packet->length += packet->header_len;
cur_index++;
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
}
/* Map the (remainder of the) packet */
for (datalen = skb_headlen(skb) - offset; datalen; ) {
len = min_t(unsigned int, datalen, XGBE_TX_MAX_BUF_SIZE);
skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
DMA_TO_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
netdev_alert(pdata->netdev, "dma_map_single failed\n");
goto err_out;
}
rdata->skb_dma = skb_dma;
rdata->skb_dma_len = len;
DBGPR(" skb data: index=%u, dma=0x%llx, len=%u\n",
cur_index, skb_dma, len);
datalen -= len;
offset += len;
packet->length += len;
cur_index++;
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
}
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
DBGPR(" mapping frag %u\n", i);
frag = &skb_shinfo(skb)->frags[i];
offset = 0;
for (datalen = skb_frag_size(frag); datalen; ) {
len = min_t(unsigned int, datalen,
XGBE_TX_MAX_BUF_SIZE);
skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
len, DMA_TO_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
netdev_alert(pdata->netdev,
"skb_frag_dma_map failed\n");
goto err_out;
}
rdata->skb_dma = skb_dma;
rdata->skb_dma_len = len;
rdata->mapped_as_page = 1;
DBGPR(" skb data: index=%u, dma=0x%llx, len=%u\n",
cur_index, skb_dma, len);
datalen -= len;
offset += len;
packet->length += len;
cur_index++;
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
}
}
/* Save the skb address in the last entry */
rdata->skb = skb;
/* Save the number of descriptor entries used */
packet->rdesc_count = cur_index - start_index;
DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count);
return packet->rdesc_count;
err_out:
while (start_index < cur_index) {
rdata = XGBE_GET_DESC_DATA(ring, start_index++);
xgbe_unmap_skb(pdata, rdata);
}
DBGPR("<--xgbe_map_tx_skb: count=0\n");
return 0;
}
static void xgbe_realloc_skb(struct xgbe_channel *channel)
{
struct xgbe_prv_data *pdata = channel->pdata;
struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct xgbe_ring *ring = channel->rx_ring;
struct xgbe_ring_data *rdata;
struct sk_buff *skb = NULL;
dma_addr_t skb_dma;
int i;
DBGPR("-->xgbe_realloc_skb: rx_ring->rx.realloc_index = %u\n",
ring->rx.realloc_index);
for (i = 0; i < ring->dirty; i++) {
rdata = XGBE_GET_DESC_DATA(ring, ring->rx.realloc_index);
/* Reset rdata values */
xgbe_unmap_skb(pdata, rdata);
/* Allocate skb & assign to each rdesc */
skb = dev_alloc_skb(pdata->rx_buf_size);
if (skb == NULL)
break;
skb_dma = dma_map_single(pdata->dev, skb->data,
pdata->rx_buf_size, DMA_FROM_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
netdev_alert(pdata->netdev,
"failed to do the dma map\n");
dev_kfree_skb_any(skb);
break;
}
rdata->skb = skb;
rdata->skb_dma = skb_dma;
rdata->skb_dma_len = pdata->rx_buf_size;
hw_if->rx_desc_reset(rdata);
ring->rx.realloc_index++;
}
ring->dirty = 0;
DBGPR("<--xgbe_realloc_skb\n");
}
void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
{
DBGPR("-->xgbe_init_function_ptrs_desc\n");
desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
desc_if->free_ring_resources = xgbe_free_ring_resources;
desc_if->map_tx_skb = xgbe_map_tx_skb;
desc_if->realloc_skb = xgbe_realloc_skb;
desc_if->unmap_skb = xgbe_unmap_skb;
desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
DBGPR("<--xgbe_init_function_ptrs_desc\n");
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more