summaryrefslogtreecommitdiffstats
path: root/drivers/net/pcmcia/pcnet_cs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/pcmcia/pcnet_cs.c')
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c287
1 files changed, 192 insertions, 95 deletions
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index 185644307..9eff09aab 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -11,7 +11,7 @@
Copyright (C) 1999 David A. Hinds -- dhinds@pcmcia.sourceforge.org
- pcnet_cs.c 1.112 2000/02/11 01:24:44
+ pcnet_cs.c 1.117 2000/05/04 01:29:47
The network driver code is based on Donald Becker's NE2000 code:
@@ -28,9 +28,9 @@
======================================================================*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
@@ -72,7 +72,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"pcnet_cs.c 1.112 2000/02/11 01:24:44 (David Hinds)";
+"pcnet_cs.c 1.117 2000/05/04 01:29:47 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
@@ -124,6 +124,7 @@ static int pcnet_event(event_t event, int priority,
event_callback_args_t *args);
static int pcnet_open(struct net_device *dev);
static int pcnet_close(struct net_device *dev);
+static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
static void ei_watchdog(u_long arg);
static void pcnet_reset_8390(struct net_device *dev);
@@ -142,16 +143,17 @@ static dev_link_t *dev_list;
/*====================================================================*/
typedef struct hw_info_t {
- u_long offset;
+ u_int offset;
u_char a0, a1, a2;
- u_long flags;
+ u_int flags;
} hw_info_t;
#define DELAY_OUTPUT 0x01
#define HAS_MISC_REG 0x02
#define USE_BIG_BUF 0x04
#define HAS_IBM_MISC 0x08
-#define IS_DL10019A 0x10
+#define IS_DL10019 0x10
+#define IS_AX88190 0x20
#define USE_SHMEM 0x80 /* autodetected */
static hw_info_t hw_info[] = {
@@ -212,25 +214,25 @@ static hw_info_t hw_info[] = {
{ /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
{ /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
{ /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
- { /* PCMCIA Technology OEM */ 0x01c8, 0xa0, 0x0c, 0 }
+ { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
};
#define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t))
static hw_info_t default_info =
{ /* Unknown NE2000 Clone */ 0x00, 0x00, 0x00, 0x00, 0 };
-static hw_info_t dl_fast_info =
-{ /* D-Link EtherFast */ 0x00, 0x00, 0x00, 0x00, IS_DL10019A };
+static hw_info_t dl10019_info =
+{ /* D-Link DL10019 chipset */ 0x00, 0x00, 0x00, 0x00, IS_DL10019 };
typedef struct pcnet_dev_t {
- struct net_device dev; /* so &dev == &pcnet_dev_t */
+ struct net_device dev; /* so &dev == &pcnet_dev_t */
dev_link_t link;
dev_node_t node;
- u_long flags;
+ u_int flags;
caddr_t base;
struct timer_list watchdog;
- int stale, state;
- u_short fast_poll;
+ int stale, fast_poll;
+ u_char link_status;
} pcnet_dev_t;
/*======================================================================
@@ -310,14 +312,11 @@ static dev_link_t *pcnet_attach(void)
link->conf.IntType = INT_MEMORY_AND_IO;
ethdev_init(dev);
- dev->name = info->node.dev_name;
dev->init = &pcnet_init;
dev->open = &pcnet_open;
dev->stop = &pcnet_close;
dev->set_config = &set_config;
- netif_stop_queue(dev);
-
/* Register with Card Services */
link->next = dev_list;
dev_list = link;
@@ -353,7 +352,6 @@ static void pcnet_detach(dev_link_t *link)
{
pcnet_dev_t *info = link->priv;
dev_link_t **linkp;
- long flags;
DEBUG(0, "pcnet_detach(0x%p)\n", link);
@@ -363,14 +361,7 @@ static void pcnet_detach(dev_link_t *link)
if (*linkp == NULL)
return;
- save_flags(flags);
- cli();
- if (link->state & DEV_RELEASE_PENDING) {
- del_timer(&link->release);
- link->state &= ~DEV_RELEASE_PENDING;
- }
- restore_flags(flags);
-
+ del_timer(&link->release);
if (link->state & DEV_CONFIG) {
pcnet_release((u_long)link);
if (link->state & DEV_STALE_CONFIG) {
@@ -392,27 +383,6 @@ static void pcnet_detach(dev_link_t *link)
/*======================================================================
- For the Linksys EtherFast 10/100 card
-
-======================================================================*/
-
-static hw_info_t *get_dl_fast(dev_link_t *link)
-{
- struct net_device *dev = link->priv;
- int i;
- u_char sum;
-
- for (sum = 0, i = 0x14; i < 0x1c; i++)
- sum += inb_p(dev->base_addr + i);
- if (sum != 0xff)
- return NULL;
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
- return &dl_fast_info;
-}
-
-/*======================================================================
-
This probes for a card's hardware address, for card types that
encode this information in their CIS.
@@ -471,13 +441,13 @@ static hw_info_t *get_hwinfo(dev_link_t *link)
static hw_info_t *get_prom(dev_link_t *link)
{
struct net_device *dev = link->priv;
- unsigned char prom[32];
- ioaddr_t ioaddr;
+ ioaddr_t ioaddr = dev->base_addr;
+ u_char prom[32];
int i, j;
/* This is lifted straight from drivers/net/ne.c */
struct {
- unsigned char value, offset;
+ u_char value, offset;
} program_seq[] = {
{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
@@ -494,8 +464,6 @@ static hw_info_t *get_prom(dev_link_t *link)
{E8390_RREAD+E8390_START, E8390_CMD},
};
- ioaddr = dev->base_addr;
-
pcnet_reset_8390(dev);
udelay(10000);
@@ -520,6 +488,58 @@ static hw_info_t *get_prom(dev_link_t *link)
/*======================================================================
+ For DL10019 based cards, like the Linksys EtherFast
+
+======================================================================*/
+
+static hw_info_t *get_dl10019(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+ u_char sum;
+
+ for (sum = 0, i = 0x14; i < 0x1c; i++)
+ sum += inb_p(dev->base_addr + i);
+ if (sum != 0xff)
+ return NULL;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
+ return &dl10019_info;
+}
+
+/*======================================================================
+
+ For Asix AX88190 based cards
+
+======================================================================*/
+
+static hw_info_t *get_ax88190(dev_link_t *link)
+{
+ struct net_device *dev = link->priv;
+ ioaddr_t ioaddr = dev->base_addr;
+ int i, j;
+
+ /* Not much of a test, but the alternatives are messy */
+ if (link->conf.ConfigBase != 0x03c0)
+ return NULL;
+
+ outb_p(0x01, EN0_DCFG); /* Set word-wide access. */
+ outb_p(0x00, EN0_RSARLO); /* DMA starting at 0x0400. */
+ outb_p(0x04, EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, E8390_CMD);
+
+ for (i = 0; i < 6; i += 2) {
+ j = inw(ioaddr + PCNET_DATAPORT);
+ dev->dev_addr[i] = j & 0xff;
+ dev->dev_addr[i+1] = j >> 8;
+ }
+ printk(KERN_INFO "pcnet_cs: sorry, the AX88190 chipset is not "
+ "supported.\n");
+ return NULL;
+}
+
+/*======================================================================
+
This should be totally unnecessary... but when we can't figure
out the hardware address any other way, we'll let the user hard
wire it when the module is initialized.
@@ -687,7 +707,6 @@ static void pcnet_config(dev_link_t *link)
} else {
dev->if_port = 0;
}
- netif_start_queue(dev);
if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n");
goto failed;
@@ -697,7 +716,9 @@ static void pcnet_config(dev_link_t *link)
if (hw_info == NULL)
hw_info = get_prom(link);
if (hw_info == NULL)
- hw_info = get_dl_fast(link);
+ hw_info = get_dl10019(link);
+ if (hw_info == NULL)
+ hw_info = get_ax88190(link);
if (hw_info == NULL)
hw_info = get_hwired(link);
@@ -733,11 +754,19 @@ static void pcnet_config(dev_link_t *link)
ei_status.word16 = 1;
ei_status.reset_8390 = &pcnet_reset_8390;
+ strcpy(info->node.dev_name, dev->name);
link->dev = &info->node;
link->state &= ~DEV_CONFIG_PENDING;
- printk(KERN_INFO "%s: NE2000 Compatible: io %#3lx, irq %d,",
- dev->name, dev->base_addr, dev->irq);
+ if (info->flags & IS_DL10019) {
+ dev->do_ioctl = &do_ioctl;
+ printk(KERN_INFO "%s: NE2000 (DL10019 rev %02x): ",
+ dev->name, inb(dev->base_addr + 0x1a));
+ } else if (info->flags & IS_AX88190) {
+ printk(KERN_INFO "%s: NE2000 (AX88190): ", dev->name);
+ } else
+ printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name);
+ printk("io %#3lx, irq %d,", dev->base_addr, dev->irq);
if (info->flags & USE_SHMEM)
printk (" mem %#5lx,", dev->mem_start);
if (info->flags & HAS_MISC_REG)
@@ -787,7 +816,7 @@ static void pcnet_release(u_long arg)
CardServices(ReleaseIO, link->handle, &link->io);
CardServices(ReleaseIRQ, link->handle, &link->irq);
- link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+ link->state &= ~DEV_CONFIG;
} /* pcnet_release */
@@ -813,9 +842,7 @@ static int pcnet_event(event_t event, int priority,
link->state &= ~DEV_PRESENT;
if (link->state & DEV_CONFIG) {
netif_device_detach(&info->dev);
- link->release.expires = jiffies + HZ/20;
- link->state |= DEV_RELEASE_PENDING;
- add_timer(&link->release);
+ mod_timer(&link->release, jiffies + HZ/20);
}
break;
case CS_EVENT_CARD_INSERTION:
@@ -829,7 +856,6 @@ static int pcnet_event(event_t event, int priority,
if (link->state & DEV_CONFIG) {
if (link->open)
netif_device_detach(&info->dev);
-
CardServices(ReleaseConfiguration, link->handle);
}
break;
@@ -889,7 +915,7 @@ static int pcnet_open(struct net_device *dev)
request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
/* Start by assuming the link is bad */
- info->state = 1;
+ info->link_status = 1;
info->watchdog.function = &ei_watchdog;
info->watchdog.data = (u_long)info;
info->watchdog.expires = jiffies + HZ;
@@ -910,12 +936,10 @@ static int pcnet_close(struct net_device *dev)
free_irq(dev->irq, dev);
link->open--;
+ netif_stop_queue(dev);
del_timer(&info->watchdog);
- if (link->state & DEV_STALE_CONFIG) {
- link->release.expires = jiffies + HZ/20;
- link->state |= DEV_RELEASE_PENDING;
- add_timer(&link->release);
- }
+ if (link->state & DEV_STALE_CONFIG)
+ mod_timer(&link->release, jiffies + HZ/20);
MOD_DEC_USE_COUNT;
@@ -952,7 +976,7 @@ static void pcnet_reset_8390(struct net_device *dev)
} /* pcnet_reset_8390 */
-/* ======================================================================= */
+/*====================================================================*/
static int set_config(struct net_device *dev, struct ifmap *map)
{
@@ -970,7 +994,7 @@ static int set_config(struct net_device *dev, struct ifmap *map)
return 0;
}
-/* ======================================================================= */
+/*====================================================================*/
static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
{
@@ -985,8 +1009,7 @@ static void ei_watchdog(u_long arg)
struct net_device *dev = &info->dev;
ioaddr_t nic_base = dev->base_addr;
- if (!netif_device_present(dev))
- goto reschedule;
+ 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 */
@@ -1004,14 +1027,14 @@ static void ei_watchdog(u_long arg)
return;
}
- if (info->flags & IS_DL10019A) {
- int state = inb(dev->base_addr+0x1c) & 0x01;
- if (state != info->state) {
+ if (info->flags & IS_DL10019) {
+ u_char link = inb(dev->base_addr+0x1c) & 0x01;
+ if (link != info->link_status) {
printk(KERN_INFO "%s: %s link beat\n", dev->name,
- (state) ? "lost" : "found");
- if (!state)
+ (link) ? "lost" : "found");
+ if (!link)
NS8390_init(dev, 1);
- info->state = state;
+ info->link_status = link;
}
}
@@ -1020,7 +1043,89 @@ reschedule:
add_timer(&info->watchdog);
}
-/* ======================================================================= */
+/*======================================================================
+
+ MII interface support for DL10019 based cards
+
+ There are two types of DL10019 based cards. Some have the MII IO
+ direction bit as 0x10, others as 0x20; setting both bits seems to
+ work on all cards.
+
+======================================================================*/
+
+#define MDIO_SHIFT_CLK 0x80
+#define MDIO_DATA_OUT 0x40
+#define MDIO_DIR_WRITE 0x30
+#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ 0x10
+#define MDIO_MASK 0x0f
+
+static void mdio_sync(ioaddr_t addr)
+{
+ int bits, mask = inb(addr) & MDIO_MASK;
+ for (bits = 0; bits < 32; bits++) {
+ outb(mask | MDIO_DATA_WRITE1, addr);
+ outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(ioaddr_t addr, int phy_id, int loc)
+{
+ u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+ int i, retval = 0, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 13; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb(mask, addr);
+ retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i, mask = inb(addr) & MDIO_MASK;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(mask | dat, addr);
+ outb(mask | dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb(mask, addr);
+ outb(mask | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ u16 *data = (u16 *)&rq->ifr_data;
+ ioaddr_t addr = dev->base_addr + 0x1c;
+ switch (cmd) {
+ case SIOCDEVPRIVATE:
+ data[0] = 0;
+ case SIOCDEVPRIVATE+1:
+ data[3] = mdio_read(addr, 0, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ mdio_write(addr, 0, data[1] & 0x1f, data[2]);
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
static void dma_get_8390_hdr(struct net_device *dev,
struct e8390_pkt_hdr *hdr,
@@ -1052,7 +1157,7 @@ static void dma_get_8390_hdr(struct net_device *dev,
ei_status.dmaing &= ~0x01;
}
-/* ======================================================================= */
+/*====================================================================*/
static void dma_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset)
@@ -1110,8 +1215,7 @@ static void dma_block_input(struct net_device *dev, int count,
/*====================================================================*/
static void dma_block_output(struct net_device *dev, int count,
- const unsigned char *buf,
- const int start_page)
+ const u_char *buf, const int start_page)
{
ioaddr_t nic_base = dev->base_addr;
pcnet_dev_t *info = (pcnet_dev_t *)dev;
@@ -1215,33 +1319,31 @@ static int setup_dma_config(dev_link_t *link, int start_pg,
/*====================================================================*/
-static void copyin(unsigned char *dest, unsigned char *src, int c)
+static void copyin(u_char *dest, u_char *src, int c)
{
- unsigned short *d = (unsigned short *) dest;
- unsigned short *s = (unsigned short *) src;
+ u_short *d = (u_short *)dest, *s = (u_short *)src;
int odd;
if (c <= 0)
return;
- odd = (c & 01); c >>= 1;
+ odd = (c & 1); c >>= 1;
if (c) {
do { *d++ = __raw_readw(s++); } while (--c);
}
/* get last byte by fetching a word and masking */
if (odd)
- *((unsigned char *)d) = readw(s) & 0xff;
+ *((u_char *)d) = readw(s) & 0xff;
}
-static void copyout(unsigned char *dest, const unsigned char *src, int c)
+static void copyout(u_char *dest, const u_char *src, int c)
{
- volatile unsigned short *d = (unsigned short *) dest;
- unsigned short *s = (unsigned short *) src;
+ u_short *d = (u_short *)dest, *s = (u_short *)src;
int odd;
if (c <= 0)
return;
- odd = (c & 01); c >>= 1;
+ odd = (c & 1); c >>= 1;
if (c) {
do { __raw_writew(*s++, d++); } while (--c);
@@ -1289,15 +1391,10 @@ static void shmem_block_input(struct net_device *dev, int count,
/*====================================================================*/
static void shmem_block_output(struct net_device *dev, int count,
- const unsigned char *buf,
- const int start_page)
+ const u_char *buf, const int start_page)
{
void *shmem = (void *)dev->mem_start + (start_page << 8);
shmem -= ei_status.tx_start_page << 8;
-
- if (ei_debug > 4)
- printk(KERN_DEBUG "[bo=%d @ %x]\n", count, start_page);
-
copyout(shmem, buf, count);
}