patch-2.3.99-pre7 linux/drivers/net/pcmcia/pcnet_cs.c

Next file: linux/drivers/net/pcmcia/ray_cs.c
Previous file: linux/drivers/net/pcmcia/nmclan_cs.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre6/linux/drivers/net/pcmcia/pcnet_cs.c linux/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 @@
 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 @@
 		       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 @@
 /*====================================================================*/
 
 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 @@
     { /* 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 @@
     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 @@
 {
     pcnet_dev_t *info = link->priv;
     dev_link_t **linkp;
-    long flags;
 
     DEBUG(0, "pcnet_detach(0x%p)\n", link);
 
@@ -363,14 +361,7 @@
     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 @@
 
 /*======================================================================
 
-    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_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 @@
 	{E8390_RREAD+E8390_START, E8390_CMD},
     };
 
-    ioaddr = dev->base_addr;
-
     pcnet_reset_8390(dev);
     udelay(10000);
 
@@ -520,6 +488,58 @@
 
 /*======================================================================
 
+    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 @@
     } 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 @@
     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 @@
     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 @@
     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 @@
 	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 @@
 	if (link->state & DEV_CONFIG) {
 	    if (link->open)
 		netif_device_detach(&info->dev);
-
 	    CardServices(ReleaseConfiguration, link->handle);
 	}
 	break;
@@ -889,7 +915,7 @@
     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 @@
     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 @@
     
 } /* pcnet_reset_8390 */
 
-/* ======================================================================= */
+/*====================================================================*/
 
 static int set_config(struct net_device *dev, struct ifmap *map)
 {
@@ -970,7 +994,7 @@
     return 0;
 }
 
-/* ======================================================================= */
+/*====================================================================*/
 
 static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -985,8 +1009,7 @@
     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 @@
 	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 @@
     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 @@
     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_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 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_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);
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)