Commit 645870ac authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] resurrect the 3c589_cs pcmcia

parent a0e8dc91
...@@ -14,11 +14,13 @@ ...@@ -14,11 +14,13 @@
distributed according to the terms of the GNU General Public License, distributed according to the terms of the GNU General Public License,
incorporated herein by reference. incorporated herein by reference.
Donald Becker may be reached at becker@scyld.com Donald Becker may be reached at becker@scyld.com
Updated for 2.5.x by Alan Cox <alan@redhat.com>
======================================================================*/ ======================================================================*/
#define DRV_NAME "3c589_cs" #define DRV_NAME "3c589_cs"
#define DRV_VERSION "1.162" #define DRV_VERSION "1.162-ac"
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -111,9 +113,10 @@ struct el3_private { ...@@ -111,9 +113,10 @@ struct el3_private {
struct net_device_stats stats; struct net_device_stats stats;
/* For transceiver monitoring */ /* For transceiver monitoring */
struct timer_list media; struct timer_list media;
u_short media_status; u16 media_status;
u_short fast_poll; u16 fast_poll;
u_long last_irq; unsigned long last_irq;
spinlock_t lock;
}; };
static char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; static char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
...@@ -148,13 +151,13 @@ DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)"; ...@@ -148,13 +151,13 @@ DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
/*====================================================================*/ /*====================================================================*/
static void tc589_config(dev_link_t *link); static void tc589_config(dev_link_t *link);
static void tc589_release(u_long arg); static void tc589_release(unsigned long arg);
static int tc589_event(event_t event, int priority, static int tc589_event(event_t event, int priority,
event_callback_args_t *args); event_callback_args_t *args);
static u_short read_eeprom(ioaddr_t ioaddr, int index); static u16 read_eeprom(ioaddr_t ioaddr, int index);
static void tc589_reset(struct net_device *dev); static void tc589_reset(struct net_device *dev);
static void media_check(u_long arg); static void media_check(unsigned long arg);
static int el3_config(struct net_device *dev, struct ifmap *map); static int el3_config(struct net_device *dev, struct ifmap *map);
static int el3_open(struct net_device *dev); static int el3_open(struct net_device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
...@@ -223,11 +226,13 @@ static dev_link_t *tc589_attach(void) ...@@ -223,11 +226,13 @@ static dev_link_t *tc589_attach(void)
lp = kmalloc(sizeof(*lp), GFP_KERNEL); lp = kmalloc(sizeof(*lp), GFP_KERNEL);
if (!lp) return NULL; if (!lp) return NULL;
memset(lp, 0, sizeof(*lp)); memset(lp, 0, sizeof(*lp));
spin_lock_init(&lp->lock);
link = &lp->link; dev = &lp->dev; link = &lp->link; dev = &lp->dev;
link->priv = dev->priv = link->irq.Instance = lp; link->priv = dev->priv = link->irq.Instance = lp;
link->release.function = &tc589_release; link->release.function = &tc589_release;
link->release.data = (u_long)link; link->release.data = (unsigned long)link;
link->io.NumPorts1 = 16; link->io.NumPorts1 = 16;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
...@@ -302,9 +307,9 @@ static void tc589_detach(dev_link_t *link) ...@@ -302,9 +307,9 @@ static void tc589_detach(dev_link_t *link)
if (*linkp == NULL) if (*linkp == NULL)
return; return;
del_timer(&link->release); del_timer_sync(&link->release);
if (link->state & DEV_CONFIG) { if (link->state & DEV_CONFIG) {
tc589_release((u_long)link); tc589_release((unsigned long)link);
if (link->state & DEV_STALE_CONFIG) { if (link->state & DEV_STALE_CONFIG) {
link->state |= DEV_STALE_LINK; link->state |= DEV_STALE_LINK;
return; return;
...@@ -340,14 +345,14 @@ static void tc589_config(dev_link_t *link) ...@@ -340,14 +345,14 @@ static void tc589_config(dev_link_t *link)
struct net_device *dev = &lp->dev; struct net_device *dev = &lp->dev;
tuple_t tuple; tuple_t tuple;
cisparse_t parse; cisparse_t parse;
u_short buf[32], *phys_addr; u16 buf[32], *phys_addr;
int last_fn, last_ret, i, j, multi = 0; int last_fn, last_ret, i, j, multi = 0;
ioaddr_t ioaddr; ioaddr_t ioaddr;
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
DEBUG(0, "3c589_config(0x%p)\n", link); DEBUG(0, "3c589_config(0x%p)\n", link);
phys_addr = (u_short *)dev->dev_addr; phys_addr = (u16 *)dev->dev_addr;
tuple.Attributes = 0; tuple.Attributes = 0;
tuple.DesiredTuple = CISTPL_CONFIG; tuple.DesiredTuple = CISTPL_CONFIG;
CS_CHECK(GetFirstTuple, handle, &tuple); CS_CHECK(GetFirstTuple, handle, &tuple);
...@@ -391,7 +396,7 @@ static void tc589_config(dev_link_t *link) ...@@ -391,7 +396,7 @@ static void tc589_config(dev_link_t *link)
dev->irq = link->irq.AssignedIRQ; dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1; dev->base_addr = link->io.BasePort1;
if (register_netdev(dev) != 0) { if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "3c589_cs: register_netdev() failed\n"); printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
goto failed; goto failed;
} }
...@@ -409,7 +414,7 @@ static void tc589_config(dev_link_t *link) ...@@ -409,7 +414,7 @@ static void tc589_config(dev_link_t *link)
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i)); phys_addr[i] = htons(read_eeprom(ioaddr, i));
if (phys_addr[0] == 0x6060) { if (phys_addr[0] == 0x6060) {
printk(KERN_NOTICE "3c589_cs: IO port conflict at 0x%03lx" printk(KERN_ERR "3c589_cs: IO port conflict at 0x%03lx"
"-0x%03lx\n", dev->base_addr, dev->base_addr+15); "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
goto failed; goto failed;
} }
...@@ -427,7 +432,7 @@ static void tc589_config(dev_link_t *link) ...@@ -427,7 +432,7 @@ static void tc589_config(dev_link_t *link)
if ((if_port >= 0) && (if_port <= 3)) if ((if_port >= 0) && (if_port <= 3))
dev->if_port = if_port; dev->if_port = if_port;
else else
printk(KERN_NOTICE "3c589_cs: invalid if_port requested\n"); printk(KERN_ERR "3c589_cs: invalid if_port requested\n");
printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ", printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ",
dev->name, (multi ? "562" : "589"), dev->base_addr, dev->name, (multi ? "562" : "589"), dev->base_addr,
...@@ -443,7 +448,7 @@ static void tc589_config(dev_link_t *link) ...@@ -443,7 +448,7 @@ static void tc589_config(dev_link_t *link)
cs_failed: cs_failed:
cs_error(link->handle, last_fn, last_ret); cs_error(link->handle, last_fn, last_ret);
failed: failed:
tc589_release((u_long)link); tc589_release((unsigned long)link);
return; return;
} /* tc589_config */ } /* tc589_config */
...@@ -456,7 +461,7 @@ static void tc589_config(dev_link_t *link) ...@@ -456,7 +461,7 @@ static void tc589_config(dev_link_t *link)
======================================================================*/ ======================================================================*/
static void tc589_release(u_long arg) static void tc589_release(unsigned long arg)
{ {
dev_link_t *link = (dev_link_t *)arg; dev_link_t *link = (dev_link_t *)arg;
...@@ -545,7 +550,7 @@ static void tc589_wait_for_completion(struct net_device *dev, int cmd) ...@@ -545,7 +550,7 @@ static void tc589_wait_for_completion(struct net_device *dev, int cmd)
while (--i > 0) while (--i > 0)
if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
if (i == 0) if (i == 0)
printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", printk(KERN_WARNING "%s: command 0x%04x did not complete!\n",
dev->name, cmd); dev->name, cmd);
} }
...@@ -553,7 +558,7 @@ static void tc589_wait_for_completion(struct net_device *dev, int cmd) ...@@ -553,7 +558,7 @@ static void tc589_wait_for_completion(struct net_device *dev, int cmd)
Read a word from the EEPROM using the regular EEPROM access register. Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero. Assume that we are in register window zero.
*/ */
static u_short read_eeprom(ioaddr_t ioaddr, int index) static u16 read_eeprom(ioaddr_t ioaddr, int index)
{ {
int i; int i;
outw(EEPROM_READ + index, ioaddr + 10); outw(EEPROM_READ + index, ioaddr + 10);
...@@ -741,7 +746,7 @@ static int el3_open(struct net_device *dev) ...@@ -741,7 +746,7 @@ static int el3_open(struct net_device *dev)
tc589_reset(dev); tc589_reset(dev);
lp->media.function = &media_check; lp->media.function = &media_check;
lp->media.data = (u_long)lp; lp->media.data = (unsigned long)lp;
lp->media.expires = jiffies + HZ; lp->media.expires = jiffies + HZ;
add_timer(&lp->media); add_timer(&lp->media);
...@@ -756,7 +761,7 @@ static void el3_tx_timeout(struct net_device *dev) ...@@ -756,7 +761,7 @@ static void el3_tx_timeout(struct net_device *dev)
struct el3_private *lp = (struct el3_private *)dev->priv; struct el3_private *lp = (struct el3_private *)dev->priv;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); printk(KERN_WARNING "%s: Transmit timed out!\n", dev->name);
dump_status(dev); dump_status(dev);
lp->stats.tx_errors++; lp->stats.tx_errors++;
dev->trans_start = jiffies; dev->trans_start = jiffies;
...@@ -832,7 +837,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -832,7 +837,8 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DEBUG(3, "%s: interrupt, status %4.4x.\n", DEBUG(3, "%s: interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS)); dev->name, inw(ioaddr + EL3_STATUS));
spin_lock(&lp->lock);
while ((status = inw(ioaddr + EL3_STATUS)) & while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) { (IntLatch | RxComplete | StatsFull)) {
if (!netif_device_present(dev) || if (!netif_device_present(dev) ||
...@@ -867,7 +873,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -867,7 +873,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
EL3WINDOW(4); EL3WINDOW(4);
fifo_diag = inw(ioaddr + 4); fifo_diag = inw(ioaddr + 4);
EL3WINDOW(1); EL3WINDOW(1);
printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic" printk(KERN_WARNING "%s: adapter failure, FIFO diagnostic"
" register %04x.\n", dev->name, fifo_diag); " register %04x.\n", dev->name, fifo_diag);
if (fifo_diag & 0x0400) { if (fifo_diag & 0x0400) {
/* Tx overrun */ /* Tx overrun */
...@@ -885,7 +891,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -885,7 +891,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
if (++i > 10) { if (++i > 10) {
printk(KERN_NOTICE "%s: infinite loop in interrupt, " printk(KERN_ERR "%s: infinite loop in interrupt, "
"status %4.4x.\n", dev->name, status); "status %4.4x.\n", dev->name, status);
/* Clear all interrupts */ /* Clear all interrupts */
outw(AckIntr | 0xFF, ioaddr + EL3_CMD); outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
...@@ -896,18 +902,19 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -896,18 +902,19 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
lp->last_irq = jiffies; lp->last_irq = jiffies;
spin_unlock(&lp->lock);
DEBUG(3, "%s: exiting interrupt, status %4.4x.\n", DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS)); dev->name, inw(ioaddr + EL3_STATUS));
return; return;
} }
static void media_check(u_long arg) static void media_check(unsigned long arg)
{ {
struct el3_private *lp = (struct el3_private *)(arg); struct el3_private *lp = (struct el3_private *)(arg);
struct net_device *dev = &lp->dev; struct net_device *dev = &lp->dev;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
u_short media, errs; u16 media, errs;
u_long flags; unsigned long flags;
if (!netif_device_present(dev)) goto reschedule; if (!netif_device_present(dev)) goto reschedule;
...@@ -917,7 +924,7 @@ static void media_check(u_long arg) ...@@ -917,7 +924,7 @@ static void media_check(u_long arg)
if ((inw(ioaddr + EL3_STATUS) & IntLatch) && if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
(inb(ioaddr + EL3_TIMER) == 0xff)) { (inb(ioaddr + EL3_TIMER) == 0xff)) {
if (!lp->fast_poll) if (!lp->fast_poll)
printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); printk(KERN_WARNING "%s: interrupt(s) dropped!\n", dev->name);
el3_interrupt(dev->irq, lp, NULL); el3_interrupt(dev->irq, lp, NULL);
lp->fast_poll = HZ; lp->fast_poll = HZ;
} }
...@@ -927,9 +934,10 @@ static void media_check(u_long arg) ...@@ -927,9 +934,10 @@ static void media_check(u_long arg)
add_timer(&lp->media); add_timer(&lp->media);
return; return;
} }
save_flags(flags); /* lp->lock guards the EL3 window. Window should always be 1 except
cli(); when the lock is held */
spin_lock_irqsave(&lp->lock, flags);
EL3WINDOW(4); EL3WINDOW(4);
media = inw(ioaddr+WN4_MEDIA) & 0xc810; media = inw(ioaddr+WN4_MEDIA) & 0xc810;
...@@ -974,7 +982,7 @@ static void media_check(u_long arg) ...@@ -974,7 +982,7 @@ static void media_check(u_long arg)
} }
EL3WINDOW(1); EL3WINDOW(1);
restore_flags(flags); spin_unlock_irqrestore(&lp->lock, flags);
reschedule: reschedule:
lp->media.expires = jiffies + HZ; lp->media.expires = jiffies + HZ;
...@@ -988,10 +996,9 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev) ...@@ -988,10 +996,9 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev)
dev_link_t *link = &lp->link; dev_link_t *link = &lp->link;
if (DEV_OK(link)) { if (DEV_OK(link)) {
save_flags(flags); spin_lock_irqsave(&lp->lock, flags);
cli();
update_stats(dev); update_stats(dev);
restore_flags(flags); spin_unlock_irqrestore(&lp->lock, flags);
} }
return &lp->stats; return &lp->stats;
} }
...@@ -1001,6 +1008,8 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev) ...@@ -1001,6 +1008,8 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev)
single-threaded if the device is active. This is expected to be a rare 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 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. 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) static void update_stats(struct net_device *dev)
{ {
...@@ -1079,7 +1088,7 @@ static int el3_rx(struct net_device *dev) ...@@ -1079,7 +1088,7 @@ static int el3_rx(struct net_device *dev)
tc589_wait_for_completion(dev, RxDiscard); tc589_wait_for_completion(dev, RxDiscard);
} }
if (worklimit == 0) if (worklimit == 0)
printk(KERN_NOTICE "%s: too much work in el3_rx!\n", dev->name); printk(KERN_WARNING "%s: too much work in el3_rx!\n", dev->name);
return 0; return 0;
} }
...@@ -1088,7 +1097,7 @@ static void set_multicast_list(struct net_device *dev) ...@@ -1088,7 +1097,7 @@ static void set_multicast_list(struct net_device *dev)
struct el3_private *lp = dev->priv; struct el3_private *lp = dev->priv;
dev_link_t *link = &lp->link; dev_link_t *link = &lp->link;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
u_short opts = SetRxFilter | RxStation | RxBroadcast; u16 opts = SetRxFilter | RxStation | RxBroadcast;
if (!(DEV_OK(link))) return; if (!(DEV_OK(link))) return;
if (dev->flags & IFF_PROMISC) if (dev->flags & IFF_PROMISC)
...@@ -1135,7 +1144,7 @@ static int el3_close(struct net_device *dev) ...@@ -1135,7 +1144,7 @@ static int el3_close(struct net_device *dev)
link->open--; link->open--;
netif_stop_queue(dev); netif_stop_queue(dev);
del_timer(&lp->media); del_timer_sync(&lp->media);
if (link->state & DEV_STALE_CONFIG) if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20); mod_timer(&link->release, jiffies + HZ/20);
...@@ -1152,8 +1161,7 @@ static int __init init_3c589_cs(void) ...@@ -1152,8 +1161,7 @@ static int __init init_3c589_cs(void)
DEBUG(0, "%s\n", version); DEBUG(0, "%s\n", version);
CardServices(GetCardServicesInfo, &serv); CardServices(GetCardServicesInfo, &serv);
if (serv.Revision != CS_RELEASE_CODE) { if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "3c589_cs: Card Services release " printk(KERN_ERR "3c589_cs: Card Services release does not match!\n");
"does not match!\n");
return -1; return -1;
} }
register_pccard_driver(&dev_info, &tc589_attach, &tc589_detach); register_pccard_driver(&dev_info, &tc589_attach, &tc589_detach);
......
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