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

[PATCH] resurrect the 3c589_cs pcmcia

parent a0e8dc91
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
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;
...@@ -833,6 +838,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -833,6 +838,7 @@ 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;
} }
...@@ -928,8 +935,9 @@ static void media_check(u_long arg) ...@@ -928,8 +935,9 @@ static void media_check(u_long arg)
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