blob: eca3d20fd087cc886f5d70d72039e4af627c2d48 [file] [log] [blame]
/* lance.c: An AMD LANCE ethernet driver for linux. */
/*
Written 1993 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 Public License,
incorporated herein by reference.
This driver is for the Allied Telesis AT1500, and should work with
NE2100 clones.
The author may be reached as becker@super.org or
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/
static char *version = "lance.c:v0.12 9/3/93 becker@super.org\n";
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
/*#include <linux/interrupt.h>*/
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/dma.h>
/*#include <asm/system.h>*/
#include "dev.h"
#include "iow.h"
#include "eth.h"
#include "skbuff.h"
#include "arp.h"
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c, should be in a *.h file. */
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);
extern struct device *irq2dev_map[16];
#endif
#ifdef LANCE_DEBUG
int lance_debug = LANCE_DEBUG;
#else
int lance_debug = 1;
#endif
#ifndef DEFAULT_DMA
#define DEFAULT_DMA 5
#endif
/* Bitfield in the high bits of init block ring buffer. */
#define RING_LEN_BITS 0x80000000
#define RING_MOD_MASK 0x0f
#define RING_SIZE 16
/* Offsets from base I/O address. */
#define LANCE_DATA 0x10
#define LANCE_ADDR 0x12
#define LANCE_RESET 0x14
#define LANCE_BUS_IF 0x16
#define LANCE_TOTAL_SIZE 0x18
/* The LANCE Rx and Tx ring descriptors. */
struct lance_rx_head {
int base;
short buf_length; /* This length is 2's complement (negative)! */
short msg_length; /* This length is "normal". */
};
struct lance_tx_head {
int base;
short length; /* Length is 2's complement (negative)! */
short misc;
};
/* The LANCE initialization block, described in databook. */
struct lance_init {
unsigned short mode; /* Pre-set mode (reg. 15) */
unsigned char phys_addr[6]; /* Physical ethernet address */
unsigned filter[2]; /* Multicast filter (unused). */
/* Receive and transmit ring base, along with extra bits. */
unsigned rx_ring; /* Tx and Rx ring base pointers */
unsigned tx_ring;
};
struct lance_private {
/* These must aligned on 8-byte boundaries. */
struct lance_rx_head rx_ring[RING_SIZE];
struct lance_tx_head tx_ring[RING_SIZE];
struct lance_init init_block;
int cur_rx, cur_tx; /* The next free ring entry */
int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
int dma;
struct enet_statistics stats;
int pad0, pad1; /* Used for alignment */
};
/* We need a ethercard low-memory allocation scheme for for bus-master or
DMA ethercards if they are to work >16M memory systems. This is a
temporary solution to the lack of one, but it limits us to a single
AT1500 and <16M. Bummer. */
#define PKT_BUF_SZ 1550
static char rx_buffs[PKT_BUF_SZ][RING_SIZE];
int at1500_probe1(struct device *dev);
static int lance_open(struct device *dev);
static void lance_init_ring(struct device *dev);
static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
static int lance_rx(struct device *dev);
static void lance_interrupt(int reg_ptr);
static int lance_close(struct device *dev);
static struct enet_statistics *lance_get_stats(struct device *dev);
#ifdef notdef
static struct sigaction lance_sigaction = { &lance_interrupt, 0, 0, NULL, };
#endif
int at1500_probe(struct device *dev)
{
int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
int base_addr = dev->base_addr;
if (base_addr < 0)
return ENXIO; /* Don't probe at all. */
if (base_addr > 0x100) /* Check a single specified location. */
return at1500_probe1(dev);
/* First probe for the ethercard ID, 0x57, and then look for a LANCE
chip. */
for (port = &ports[0]; *port; port++) {
int probe_addr = *port;
short temp;
#ifdef HAVE_PORTRESERVE
if (check_region(probe_addr, LANCE_TOTAL_SIZE))
continue;
#endif
if (inb(probe_addr + 14) != 0x57
|| inb(probe_addr + 15) != 0x57)
continue;
/* Reset the LANCE. Un-Reset needed only for the real NE2100. */
temp = inw(probe_addr+LANCE_RESET); /* Reset the LANCE */
outw(temp, probe_addr+LANCE_RESET); /* "Un-reset" */
outw(0x0000, probe_addr+LANCE_ADDR); /* Switch to window 0 */
if (inw(probe_addr+LANCE_DATA) != 0x0004)
continue;
dev->base_addr = probe_addr;
if (at1500_probe1(dev) == 0)
return 0;
}
dev->base_addr = base_addr;
return ENODEV; /* ENODEV would be more accurate. */
}
int
at1500_probe1(struct device *dev)
{
struct lance_private *lp;
short ioaddr = dev->base_addr;
int i;
printk("%s: LANCE at %#3x, address", dev->name, ioaddr);
/* There is a 16 byte station address PROM at the base address.
The first six bytes are the station address. */
for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
/* Reset the LANCE */
inw(ioaddr+LANCE_RESET);
/* Make up a LANCE-specific-data structure. */
dev->priv = kmalloc(sizeof(struct lance_private), GFP_KERNEL);
/* Align on 8-byte boundary. */
dev->priv = (void *)(((int)dev->priv + 7) & ~0x07);
if ((int)dev->priv & 0xff000000 || (int) rx_buffs & 0xff000000) {
printk(" disabled (buff %#x > 16M).\n", (int)rx_buffs);
return -ENOMEM;
}
memset(dev->priv, 0, sizeof(struct lance_private));
lp = (struct lance_private *)dev->priv;
if ((int)(lp->rx_ring) & 0x07)
printk("%s: LANCE Rx and Tx rings not on even boundary.\n",
dev->name);
/* Un-Reset the LANCE, needed only for the NE2100. */
outw(0, ioaddr+LANCE_RESET);
lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
for (i = 0; i < 6; i++)
lp->init_block.phys_addr[i] = dev->dev_addr[i];
lp->init_block.filter[0] = 0x00000000;
lp->init_block.filter[1] = 0x00000000;
lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
outw(0x0001, ioaddr+LANCE_ADDR);
outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
outw(0x0002, ioaddr+LANCE_ADDR);
outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
outw(0x0000, ioaddr+LANCE_ADDR);
/* To auto-IRQ we enable the initialization-done and DMA err,
interrupts. For now we will always get a DMA error. */
if (dev->irq < 2) {
autoirq_setup(0);
/* Trigger an initialization just for the interrupt. */
outw(0x0041, ioaddr+LANCE_DATA);
dev->irq = autoirq_report(1);
if (dev->irq)
printk(", using IRQ %d.\n", dev->irq);
else {
printk(", failed to detect IRQ line.\n");
return -EAGAIN;
}
} else
printk(" assigned IRQ %d.\n", dev->irq);
/* The DMA channel may be passed in on this parameter. */
dev->dma = dev->mem_start & 0x07;
#ifndef NE2100 /* The NE2100 might not understand */
/* Turn on auto-select of media (10baseT or BNC) so that the user
can watch the LEDs even if the board isn't opened. */
outw(0x0002, ioaddr+LANCE_ADDR);
outw(0x0002, ioaddr+LANCE_BUS_IF);
#endif
#ifdef HAVE_PORTRESERVE
snarf_region(ioaddr, LANCE_TOTAL_SIZE);
#endif
if (lance_debug > 0)
printk(version);
/* The LANCE-specific entries in the device structure. */
dev->open = &lance_open;
dev->hard_start_xmit = &lance_start_xmit;
dev->stop = &lance_close;
dev->get_stats = &lance_get_stats;
dev->mem_start = 0;
/* Fill in the generic field of the device structure. */
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = eth_rebuild_header;
dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
for (i = 0; i < dev->addr_len; i++) {
dev->broadcast[i]=0xff;
}
/* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return 0;
}
static int
lance_open(struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int ioaddr = dev->base_addr;
int i;
if (request_irq(dev->irq, &lance_interrupt)) {
return -EAGAIN;
}
lp->dma = dev->dma ? dev->dma : DEFAULT_DMA;
if (request_dma(lp->dma)) {
free_irq(dev->irq);
return -EAGAIN;
}
irq2dev_map[dev->irq] = dev;
/* Reset the LANCE */
inw(ioaddr+LANCE_RESET);
/* The DMA controller is used as a no-operation slave, "cascade mode". */
enable_dma(lp->dma);
set_dma_mode(lp->dma, DMA_MODE_CASCADE);
/* Un-Reset the LANCE, needed only for the NE2100. */
outw(0, ioaddr+LANCE_RESET);
#ifndef NE2100
/* This is really 79C960-specific, NE2100 might not understand */
/* Turn on auto-select of media (10baseT or BNC). */
outw(0x0002, ioaddr+LANCE_ADDR);
outw(0x0002, ioaddr+LANCE_BUS_IF);
#endif
if (lance_debug > 1)
printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
dev->name, dev->irq, lp->dma, (int) lp->tx_ring, (int) lp->rx_ring,
(int) &lp->init_block);
lance_init_ring(dev);
/* Re-initialize the LANCE, and start it when done. */
outw(0x0001, ioaddr+LANCE_ADDR);
outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
outw(0x0002, ioaddr+LANCE_ADDR);
outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
outw(0x0004, ioaddr+LANCE_ADDR);
outw(0x0d15, ioaddr+LANCE_DATA);
outw(0x0000, ioaddr+LANCE_ADDR);
outw(0x0001, ioaddr+LANCE_DATA);
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
i = 0;
while (i++ < 100)
if (inw(ioaddr+LANCE_DATA) & 0x0100)
break;
outw(0x0142, ioaddr+LANCE_DATA);
if (lance_debug > 2)
printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
dev->name, i, (int) &lp->init_block, inw(ioaddr+LANCE_DATA));
return 0; /* Always succeed */
}
/* Initialize the LANCE Rx and Tx rings. */
static void
lance_init_ring(struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
lp->cur_rx = lp->cur_tx = 0;
lp->dirty_rx = lp->dirty_tx = 0;
for (i = 0; i < RING_SIZE; i++) {
lp->rx_ring[i].base = (int) rx_buffs | (0x80000000 + i*PKT_BUF_SZ);
lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
lp->tx_ring[i].base = 0;
}
lp->init_block.mode = 0x0000;
for (i = 0; i < 6; i++)
lp->init_block.phys_addr[i] = dev->dev_addr[i];
lp->init_block.filter[0] = 0x00000000;
lp->init_block.filter[1] = 0x00000000;
lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
}
static int
lance_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int ioaddr = dev->base_addr;
/* Transmitter timeout, serious problems. */
if (dev->tbusy) {
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5)
return 1;
outw(0, ioaddr+LANCE_ADDR);
printk("%s: transmit timed out, status %4.4x, resetting.\n",
dev->name, inw(ioaddr+LANCE_DATA));
lance_init_ring(dev);
outw(0x43, ioaddr+LANCE_DATA);
dev->tbusy=0;
dev->trans_start = jiffies;
return 0;
}
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* Fill in the ethernet header. */
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
if (skb->len <= 0)
return 0;
if (lance_debug > 3) {
outw(0x0000, ioaddr+LANCE_ADDR);
printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
inw(ioaddr+LANCE_DATA));
outw(0x0000, ioaddr+LANCE_DATA);
}
/* Avoid timer-based retransmission conflicts. */
dev->tbusy=1;
/* This code is broken for >16M RAM systems.
There are two ways to fix it:
Keep around several static low-memory buffers, and deal with
the book-keeping (bad for small systems).
Make static Tx buffers that are optionally used.
(Even worse.)
*/
{ /* Fill in a Tx ring entry */
int entry = lp->cur_tx++;
entry &= RING_MOD_MASK; /* Ring buffer. */
/* Caution: the write order is important here. */
lp->tx_ring[entry].length = -skb->len;
/* This shouldn't be necessary... */
lp->tx_ring[entry].length =
-(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
lp->tx_ring[entry].misc = 0x0000;
lp->tx_ring[entry].base = (int)(skb+1) | 0x83000000;
/* Trigger an immediate send poll. */
outw(0x0000, ioaddr+LANCE_ADDR);
outw(0x0048, ioaddr+LANCE_DATA);
if (lance_debug > 4) {
unsigned char *pkt =
(unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
printk("%s: tx ring[%d], %#x, sk_buf %#x len %d.\n",
dev->name, entry, (int) &lp->tx_ring[entry],
lp->tx_ring[entry].base, -lp->tx_ring[entry].length);
printk("%s: Tx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n",
dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6],
pkt[7], pkt[8], pkt[11], pkt[12], pkt[13],
pkt[14], pkt[15]);
}
dev->trans_start = jiffies;
if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0)
dev->tbusy=0;
}
return 0;
}
/* The LANCE interrupt handler. */
static void
lance_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct lance_private *lp;
int csr0, ioaddr;
if (dev == NULL) {
printk ("lance_interrupt(): irq %d for unknown device.\n", irq);
return;
}
ioaddr = dev->base_addr;
lp = (struct lance_private *)dev->priv;
if (dev->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
outw(0x00, dev->base_addr + LANCE_ADDR);
csr0 = inw(dev->base_addr + LANCE_DATA);
/* Acknowledge all of the current interrupt sources ASAP. */
outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
if (lance_debug > 5)
printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
if (csr0 & 0x0400) /* Rx interrupt */
lance_rx(dev);
if (csr0 & 0x0200) { /* Tx-done interrupt */
int dirty_tx = lp->dirty_tx & RING_MOD_MASK;
if (lance_debug > 5)
printk("%s: Cleaning tx ring, dirty %d clean %d.\n",
dev->name, dirty_tx, (lp->cur_tx & RING_MOD_MASK));
/* This code is broken for >16M RAM systems. */
while (dirty_tx != (lp->cur_tx & RING_MOD_MASK)
&& lp->tx_ring[dirty_tx].base > 0) {
struct sk_buff *skb =
(struct sk_buff *)(lp->tx_ring[dirty_tx].base & 0x00ffffff);
unsigned short *tmdp = (unsigned short *)(&lp->tx_ring[dirty_tx]);
int status = lp->tx_ring[dirty_tx].base >> 24;
if (status & 0x40) { /* There was an major error, log it. */
int err_status = lp->tx_ring[dirty_tx].misc;
lp->stats.tx_errors++;
if (err_status & 0x0400) lp->stats.tx_aborted_errors++;
if (err_status & 0x0800) lp->stats.tx_carrier_errors++;
if (err_status & 0x1000) lp->stats.tx_window_errors++;
if (err_status & 0x4000) lp->stats.tx_fifo_errors++;
/* We should re-init() after the FIFO error. */
} else if (status & 0x18)
lp->stats.collisions++;
else
lp->stats.tx_packets++;
if (lance_debug > 5)
printk("%s: Tx done entry %d, %4.4x %4.4x %4.4x %4.4x.\n",
dev->name, dirty_tx,
tmdp[0], tmdp[1], tmdp[2], tmdp[3]);
if ((skb-1)->free)
kfree_skb (skb-1, FREE_WRITE);
dirty_tx = ++lp->dirty_tx & RING_MOD_MASK;
}
/* mark_bh(INET_BH); */
}
/* Clear the interrupts we've handled. */
outw(0x0000, dev->base_addr + LANCE_ADDR);
outw(0x7f40, dev->base_addr + LANCE_DATA);
if (lance_debug > 4)
printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
dev->name, inw(ioaddr + LANCE_ADDR),
inw(dev->base_addr + LANCE_DATA));
dev->interrupt = 0;
return;
}
static int
lance_rx(struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int entry = lp->cur_rx & RING_MOD_MASK;
/* If we own the next entry, it's a new packet. Send it up. */
while (lp->rx_ring[entry].base >= 0) {
int status = lp->rx_ring[entry].base >> 24;
if (status & 0x40) { /* There was an error. */
lp->stats.rx_errors++;
if (status & 0x20) lp->stats.rx_frame_errors++;
if (status & 0x10) lp->stats.rx_over_errors++;
if (status & 0x08) lp->stats.rx_crc_errors++;
if (status & 0x04) lp->stats.rx_fifo_errors++;
} else {
/* Malloc up new buffer, compatible with net-2e. */
short pkt_len = lp->rx_ring[entry].msg_length;
int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
skb = (struct sk_buff *) kmalloc(sksize, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, deferring packet.\n", dev->name);
lp->stats.rx_dropped++; /* Really, deferred. */
break;
}
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
memcpy((unsigned char *) (skb + 1),
(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
pkt_len);
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_s(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
lp->stats.rx_packets++;
}
lp->rx_ring[entry].base |= 0x80000000;
entry = (entry+1) & RING_MOD_MASK;
}
/* We should check that at least two ring entries are free. If not,
we should free one and mark stats->rx_dropped++. */
lp->cur_rx = entry;
return 0;
}
static int
lance_close(struct device *dev)
{
int ioaddr = dev->base_addr;
struct lance_private *lp = (struct lance_private *)dev->priv;
dev->start = 0;
dev->tbusy = 1;
outw(112, ioaddr+LANCE_ADDR);
lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
outw(0, ioaddr+LANCE_ADDR);
if (lance_debug > 1)
printk("%s: Shutting down ethercard, status was %2.2x.\n",
dev->name, inw(ioaddr+LANCE_DATA));
/* We stop the LANCE here -- it occasionally polls
memory if we don't. */
outw(0x0004, ioaddr+LANCE_DATA);
disable_dma(lp->dma);
free_irq(dev->irq);
free_dma(lp->dma);
irq2dev_map[dev->irq] = 0;
return 0;
}
static struct enet_statistics *
lance_get_stats(struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
short ioaddr = dev->base_addr;
short saved_addr;
cli();
saved_addr = inw(ioaddr+LANCE_ADDR);
outw(112, ioaddr+LANCE_ADDR);
lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
outw(saved_addr, ioaddr+LANCE_ADDR);
sti();
return &lp->stats;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c lance.c"
* End:
*/