Commit 48ccae24 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] ressurrect the 3c515 driver

parent 0bd4095a
/* 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux. */
/*
Written 1997-1998 by Donald Becker.
......@@ -13,17 +13,19 @@
Annapolis MD 21403
2/2/00- Added support for kernel-level ISAPnP
2000/2/2- Added support for kernel-level ISAPnP
by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
11/17/2001 - Added ethtool support (jgarzik)
2001/11/17 - Added ethtool support (jgarzik)
2002/10/28 - Locking updates for 2.5 (alan@redhat.com)
*/
#define DRV_NAME "3c515"
#define DRV_VERSION "0.99t"
#define DRV_RELDATE "17-Nov-2001"
#define DRV_VERSION "0.99t-ac"
#define DRV_RELDATE "28-Oct-2002"
static char *version =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " becker@scyld.com and others\n";
......@@ -84,12 +86,6 @@ static int max_interrupt_work = 20;
#define NEW_MULTICAST
#include <linux/delay.h>
/* Kernel version compatibility functions. */
#define RUN_AT(x) (jiffies + (x))
#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
MODULE_LICENSE("GPL");
......@@ -202,16 +198,13 @@ of 1.5K, but the changes to support 4.5K are minimal.
enum corkscrew_cmd {
TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
UpStall = 6 << 11, UpUnstall = (6 << 11) + 1,
DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3,
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,
StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable =
21 << 11,
UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
DownUnstall = (6 << 11) + 3, 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, StartDMAUp = 20 << 11,
StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
StatsDisable = 22 << 11, StopCoax = 23 << 11,
};
......@@ -265,11 +258,9 @@ enum Window3 { /* Window 3: MAC/config bits. */
union wn3_config {
int i;
struct w3_config_fields {
unsigned int ram_size:3, ram_width:1, ram_speed:2,
rom_size:2;
unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
int pad8:8;
unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1,
autoselect:1;
unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
int pad24:7;
} u;
};
......@@ -340,14 +331,14 @@ struct corkscrew_private {
full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
tx_full:1;
spinlock_t lock;
};
/* The action to take with a media selection timer tick.
Note that we deviate from the 3Com order by checking 10base2 before AUI.
*/
enum xcvr_types {
XCVR_10baseT =
0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
};
......@@ -577,27 +568,23 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
#ifdef MODULE
/* Allocate and fill new device structure. */
int dev_size = sizeof(struct net_device) +
sizeof(struct corkscrew_private) + 15; /* Pad for alignment */
int dev_size = sizeof(struct net_device) + sizeof(struct corkscrew_private) + 15; /* Pad for alignment */
dev = (struct net_device *) kmalloc(dev_size, GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, dev_size);
/* Align the Rx and Tx ring entries. */
dev->priv =
(void *) (((long) dev + sizeof(struct net_device) + 15) & ~15);
dev->priv = (void *) (((long) dev + sizeof(struct net_device) + 15) & ~15);
vp = (struct corkscrew_private *) dev->priv;
dev->base_addr = ioaddr;
dev->irq = irq;
dev->dma =
(product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
dev->init = corkscrew_probe1;
vp->product_name = "3c515";
vp->options = options;
if (options >= 0) {
vp->media_override =
((options & 7) == 2) ? 0 : options & 7;
vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
vp->full_duplex = (options & 8) ? 1 : 0;
vp->bus_master = (options & 16) ? 1 : 0;
} else {
......@@ -615,22 +602,19 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
}
#else /* not a MODULE */
/* Caution: quad-word alignment required for rings! */
dev->priv =
kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
dev->priv = kmalloc(sizeof(struct corkscrew_private), GFP_KERNEL);
if (!dev->priv)
return NULL;
memset(dev->priv, 0, sizeof(struct corkscrew_private));
dev = init_etherdev(dev, sizeof(struct corkscrew_private));
dev->base_addr = ioaddr;
dev->irq = irq;
dev->dma =
(product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0);
vp = (struct corkscrew_private *) dev->priv;
vp->product_name = "3c515";
vp->options = options;
if (options >= 0) {
vp->media_override =
((options & 7) == 2) ? 0 : options & 7;
vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
vp->full_duplex = (options & 8) ? 1 : 0;
vp->bus_master = (options & 16) ? 1 : 0;
} else {
......@@ -647,13 +631,13 @@ static struct net_device *corkscrew_found_device(struct net_device *dev,
static int corkscrew_probe1(struct net_device *dev)
{
int ioaddr = dev->base_addr;
struct corkscrew_private *vp =
(struct corkscrew_private *) dev->priv;
struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name,
vp->product_name, ioaddr);
printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
spin_lock_init(&vp->lock);
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
......@@ -769,7 +753,7 @@ static int corkscrew_open(struct net_device *dev)
dev->name, media_tbl[dev->if_port].name);
init_timer(&vp->timer);
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
vp->timer.data = (unsigned long) dev;
vp->timer.function = &corkscrew_timer; /* timer handler */
add_timer(&vp->timer);
......@@ -907,8 +891,7 @@ static void corkscrew_timer(unsigned long data)
{
#ifdef AUTOMEDIA
struct net_device *dev = (struct net_device *) data;
struct corkscrew_private *vp =
(struct corkscrew_private *) dev->priv;
struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
int ioaddr = dev->base_addr;
unsigned long flags;
int ok = 0;
......@@ -917,8 +900,9 @@ static void corkscrew_timer(unsigned long data)
printk("%s: Media selection timer tick happened, %s.\n",
dev->name, media_tbl[dev->if_port].name);
save_flags(flags);
cli(); {
spin_lock_irqsave(&vp->lock, flags);
{
int old_window = inw(ioaddr + EL3_CMD) >> 13;
int media_status;
EL3WINDOW(4);
......@@ -969,7 +953,7 @@ static void corkscrew_timer(unsigned long data)
printk("%s: Media selection failed, now trying %s port.\n",
dev->name,
media_tbl[dev->if_port].name);
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
add_timer(&vp->timer);
}
outw((media_status & ~(Media_10TP | Media_SQE)) |
......@@ -986,7 +970,8 @@ static void corkscrew_timer(unsigned long data)
}
EL3WINDOW(old_window);
}
restore_flags(flags);
spin_unlock_irqrestore(&vp->lock, flags);
if (corkscrew_debug > 1)
printk("%s: Media selection timer finished, %s.\n",
dev->name, media_tbl[dev->if_port].name);
......@@ -1055,8 +1040,7 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
if (vp->tx_full) /* No room to transmit with */
return 1;
if (vp->cur_tx != 0)
prev_entry =
&vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
else
prev_entry = NULL;
if (corkscrew_debug > 3)
......@@ -1069,23 +1053,21 @@ static int corkscrew_start_xmit(struct sk_buff *skb,
vp->tx_ring[entry].length = skb->len | 0x80000000;
vp->tx_ring[entry].status = skb->len | 0x80000000;
save_flags(flags);
cli();
spin_lock_irqsave(&vp->lock, flags);
outw(DownStall, ioaddr + EL3_CMD);
/* Wait for the stall to complete. */
for (i = 20; i >= 0; i--)
if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) ==
0) break;
if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
break;
if (prev_entry)
prev_entry->next =
isa_virt_to_bus(&vp->tx_ring[entry]);
prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
if (inl(ioaddr + DownListPtr) == 0) {
outl(isa_virt_to_bus(&vp->tx_ring[entry]),
ioaddr + DownListPtr);
queued_packet++;
}
outw(DownUnstall, ioaddr + EL3_CMD);
restore_flags(flags);
spin_unlock_irqrestore(&vp->lock, flags);
vp->cur_tx++;
if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
......@@ -1179,6 +1161,8 @@ static void corkscrew_interrupt(int irq, void *dev_id,
latency = inb(ioaddr + Timer);
lp = (struct corkscrew_private *) dev->priv;
spin_lock(&lp->lock);
status = inw(ioaddr + EL3_STATUS);
if (corkscrew_debug > 4)
......@@ -1193,6 +1177,7 @@ static void corkscrew_interrupt(int irq, void *dev_id,
printk(KERN_ERR "%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
dev->name, status, netif_running(dev));
free_irq(dev->irq, dev);
dev->irq = -1;
}
}
......@@ -1205,8 +1190,7 @@ static void corkscrew_interrupt(int irq, void *dev_id,
if (status & TxAvailable) {
if (corkscrew_debug > 5)
printk
(" TX room bit was handled.\n");
printk(" 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);
......@@ -1216,21 +1200,17 @@ static void corkscrew_interrupt(int irq, void *dev_id,
while (lp->cur_tx - dirty_tx > 0) {
int entry = dirty_tx % TX_RING_SIZE;
if (inl(ioaddr + DownListPtr) ==
isa_virt_to_bus(&lp->tx_ring[entry]))
if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
break; /* It still hasn't been processed. */
if (lp->tx_skbuff[entry]) {
dev_kfree_skb_irq(lp->
tx_skbuff
[entry]);
dev_kfree_skb_irq(lp->tx_skbuff[entry]);
lp->tx_skbuff[entry] = 0;
}
dirty_tx++;
}
lp->dirty_tx = dirty_tx;
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
if (lp->tx_full
&& (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
lp->tx_full = 0;
netif_wake_queue(dev);
}
......@@ -1255,23 +1235,19 @@ static void corkscrew_interrupt(int irq, void *dev_id,
if (status & StatsFull) { /* Empty statistics. */
static int DoneDidThat;
if (corkscrew_debug > 4)
printk("%s: Updating stats.\n",
dev->name);
printk("%s: Updating stats.\n", dev->name);
update_stats(ioaddr, dev);
/* DEBUG HACK: Disable statistics as an interrupt source. */
/* This occurs when we have the wrong media type! */
if (DoneDidThat == 0 &&
inw(ioaddr + EL3_STATUS) & StatsFull) {
if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
int win, reg;
printk("%s: Updating stats failed, disabling stats as an"
" interrupt source.\n",
dev->name);
" interrupt source.\n", dev->name);
for (win = 0; win < 8; win++) {
EL3WINDOW(win);
printk("\n Vortex window %d:", win);
for (reg = 0; reg < 16; reg++)
printk(" %2.2x",
inb(ioaddr + reg));
printk(" %2.2x", inb(ioaddr + reg));
}
EL3WINDOW(7);
outw(SetIntrEnb | TxAvailable |
......@@ -1297,20 +1273,19 @@ static void corkscrew_interrupt(int irq, void *dev_id,
"Disabling functions (%4.4x).\n", dev->name,
status, SetStatusEnb | ((~status) & 0x7FE));
/* Disable all pending interrupts. */
outw(SetStatusEnb | ((~status) & 0x7FE),
ioaddr + EL3_CMD);
outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
break;
}
/* Acknowledge the IRQ. */
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
} while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete));
} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
spin_unlock(&lp->lock);
if (corkscrew_debug > 4)
printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
status);
printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
}
static int corkscrew_rx(struct net_device *dev)
......@@ -1529,15 +1504,13 @@ static int corkscrew_close(struct net_device *dev)
static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
{
struct corkscrew_private *vp =
(struct corkscrew_private *) dev->priv;
struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
unsigned long flags;
if (netif_running(dev)) {
save_flags(flags);
cli();
spin_lock_irqsave(&vp->lock, flags);
update_stats(dev->base_addr, dev);
restore_flags(flags);
spin_unlock_irqrestore(&vp->lock, flags);
}
return &vp->stats;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment