patch-2.4.15 linux/drivers/net/pcmcia/pcnet_cs.c

Next file: linux/drivers/net/pcmcia/smc91c92_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.4.14/linux/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c
@@ -11,7 +11,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
 
-    pcnet_cs.c 1.132 2001/02/09 03:13:29
+    pcnet_cs.c 1.144 2001/11/07 04:06:56
     
     The network driver code is based on Donald Becker's NE2000 code:
 
@@ -20,7 +20,7 @@
     Director, National Security Agency.  This software may be used and
     distributed according to the terms of the GNU General Public License,
     incorporated herein by reference.
-    Donald Becker may be reached at becker@scyld.com.
+    Donald Becker may be reached at becker@scyld.com
 
     Based also on Keith Moore's changes to Don Becker's code, for IBM
     CCAE support.  Drivers merged back together, and shared-memory
@@ -39,6 +39,7 @@
 #include <linux/delay.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/byteorder.h>
 
 #include <linux/netdevice.h>
 #include <../drivers/net/8390.h>
@@ -72,14 +73,18 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"pcnet_cs.c 1.132 2001/02/09 03:13:29 (David Hinds)";
+"pcnet_cs.c 1.144 2001/11/07 04:06:56 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
 
 /*====================================================================*/
 
-/* Parameters that can be set with 'insmod' */
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
 
 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
 
@@ -94,6 +99,7 @@
 INT_MODULE_PARM(delay_output,	0);	/* pause after xmit? */
 INT_MODULE_PARM(delay_time,	4);	/* in usec */
 INT_MODULE_PARM(use_shmem,	-1);	/* use shared memory? */
+INT_MODULE_PARM(full_duplex,	0);	/* full duplex? */
 
 /* Ugh!  Let the user hardwire the hardware address for queer cards */
 static int hw_addr[6] = { 0, /* ... */ };
@@ -101,13 +107,14 @@
 
 /*====================================================================*/
 
+static void mii_phy_probe(struct net_device *dev);
 static void pcnet_config(dev_link_t *link);
 static void pcnet_release(u_long arg);
 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 int ei_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);
@@ -140,6 +147,12 @@
 #define HAS_MII		0x40
 #define USE_SHMEM	0x80	/* autodetected */
 
+#define AM79C9XX_HOME_PHY	0x00006B90  /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY	0x00006B70  /* 10baseT PHY */
+#define MII_PHYID_REV_MASK	0xfffffff0
+#define MII_PHYID_REG1		0x02
+#define MII_PHYID_REG2		0x03
+
 static hw_info_t hw_info[] = {
     { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, 
     { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
@@ -216,7 +229,10 @@
     caddr_t		base;
     struct timer_list	watchdog;
     int			stale, fast_poll;
+    u_char		phy_id;
+    u_char		eth_phy, pna_phy;
     u_short		link_status;
+    u_long		mii_reset;
 } pcnet_dev_t;
 
 /*======================================================================
@@ -518,8 +534,8 @@
 	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");
+    printk(KERN_NOTICE "pcnet_cs: this is an AX88190 card!\n");
+    printk(KERN_NOTICE "pcnet_cs: use axnet_cs instead.\n");
     return NULL;
 }
 
@@ -749,10 +765,13 @@
     link->state &= ~DEV_CONFIG_PENDING;
 
     if (info->flags & (IS_DL10019|IS_DL10022)) {
-	dev->do_ioctl = &do_ioctl;
+	u_char id = inb(dev->base_addr + 0x1a);
+	dev->do_ioctl = &ei_ioctl;
+	mii_phy_probe(dev);
 	printk(KERN_INFO "%s: NE2000 (DL100%d rev %02x): ",
-	       dev->name, ((info->flags & IS_DL10022) ? 22 : 19),
-	       inb(dev->base_addr + 0x1a));
+	       dev->name, ((info->flags & IS_DL10022) ? 22 : 19), id);
+	if (info->pna_phy)
+	    printk("PNA, ");
     } else
 	printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name);
     printk("io %#3lx, irq %d,", dev->base_addr, dev->irq);
@@ -954,15 +973,44 @@
 	outb_p(tmp, nic_base + PCNET_MISC);
     }
     if (info->flags & IS_DL10022) {
-	mdio_reset(nic_base + DLINK_GPIO, 0);
-	/* Restart MII autonegotiation */
-	mdio_write(nic_base + DLINK_GPIO, 0, 0, 0x0000);
-	mdio_write(nic_base + DLINK_GPIO, 0, 0, 0x1200);
+	if (info->flags & HAS_MII) {
+	    mdio_reset(nic_base + DLINK_GPIO, info->eth_phy);
+	    /* Restart MII autonegotiation */
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+	    info->mii_reset = jiffies;
+	} else {
+	    outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+	}
     }
 }
 
 /*====================================================================*/
 
+static void mii_phy_probe(struct net_device *dev)
+{
+    pcnet_dev_t *info = (pcnet_dev_t *)dev;	
+    ioaddr_t mii_addr = dev->base_addr + DLINK_GPIO;
+    int i;
+    u_int tmp, phyid;
+
+    for (i = 31; i >= 0; i--) {
+	tmp = mdio_read(mii_addr, i, 1);
+	if ((tmp == 0) || (tmp == 0xffff))
+	    continue;
+	tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+	phyid = tmp << 16;
+	phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+	phyid &= MII_PHYID_REV_MASK;
+	DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid);
+	if (phyid == AM79C9XX_HOME_PHY) {
+	    info->pna_phy = i;
+	} else if (phyid != AM79C9XX_ETH_PHY) {
+	    info->eth_phy = i;
+	}
+    }
+}
+
 static int pcnet_open(struct net_device *dev)
 {
     pcnet_dev_t *info = (pcnet_dev_t *)dev;
@@ -979,6 +1027,7 @@
     set_misc_reg(dev);
     request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
 
+    info->phy_id = info->eth_phy;
     info->link_status = 0x00;
     info->watchdog.function = &ei_watchdog;
     info->watchdog.data = (u_long)info;
@@ -1074,6 +1123,7 @@
     pcnet_dev_t *info = (pcnet_dev_t *)(arg);
     struct net_device *dev = &info->dev;
     ioaddr_t nic_base = dev->base_addr;
+    ioaddr_t mii_addr = nic_base + DLINK_GPIO;
     u_short link;
 
     if (!netif_device_present(dev)) goto reschedule;
@@ -1097,35 +1147,57 @@
     if (!(info->flags & HAS_MII))
 	goto reschedule;
 
-    link = mdio_read(dev->base_addr + DLINK_GPIO, 0, 1);
+    mdio_read(mii_addr, info->phy_id, 1);
+    link = mdio_read(mii_addr, info->phy_id, 1);
     if (!link || (link == 0xffff)) {
-	printk(KERN_INFO "%s: MII is missing!\n", dev->name);
-	info->flags &= ~HAS_MII;
+	if (info->eth_phy) {
+	    info->phy_id = info->eth_phy = 0;
+	} else {
+	    printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+	    info->flags &= ~HAS_MII;
+	}
 	goto reschedule;
     }
 
     link &= 0x0004;
     if (link != info->link_status) {
-	u_short p = mdio_read(dev->base_addr + DLINK_GPIO, 0, 5);
+	u_short p = mdio_read(mii_addr, info->phy_id, 5);
 	printk(KERN_INFO "%s: %s link beat\n", dev->name,
 	       (link) ? "found" : "lost");
 	if (link && (info->flags & IS_DL10022)) {
 	    /* Disable collision detection on full duplex links */
-	    outb((p & 0x0140) ? 4 : 0, dev->base_addr + DLINK_DIAG);
+	    outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
 	}
 	if (link) {
-	    if (p)
-		printk(KERN_INFO "%s: autonegotiation complete: "
-		       "%sbaseT-%cD selected\n", dev->name,
-		       ((p & 0x0180) ? "100" : "10"),
-		       ((p & 0x0140) ? 'F' : 'H'));
-	    else
-		printk(KERN_INFO "%s: link partner did not autonegotiate\n",
-		       dev->name);
+	    if (info->phy_id == info->eth_phy) {
+		if (p)
+		    printk(KERN_INFO "%s: autonegotiation complete: "
+			   "%sbaseT-%cD selected\n", dev->name,
+			   ((p & 0x0180) ? "100" : "10"),
+			   ((p & 0x0140) ? 'F' : 'H'));
+		else
+		    printk(KERN_INFO "%s: link partner did not "
+			   "autonegotiate\n", dev->name);
+	    }
 	    NS8390_init(dev, 1);
 	}
 	info->link_status = link;
     }
+    if (info->pna_phy && (jiffies - info->mii_reset > 6*HZ)) {
+	link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+	if (((info->phy_id == info->pna_phy) && link) ||
+	    ((info->phy_id != info->pna_phy) && !link)) {
+	    /* isolate this MII and try flipping to the other one */
+	    mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+	    info->phy_id ^= info->pna_phy ^ info->eth_phy;
+	    printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name,
+		   (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+	    mdio_write(mii_addr, info->phy_id, 0,
+		       (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+	    info->link_status = 0;
+	    info->mii_reset = jiffies;
+	}
+    }
 
 reschedule:
     info->watchdog.expires = jiffies + HZ;
@@ -1134,20 +1206,21 @@
 
 /*====================================================================*/
 
-static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
+    pcnet_dev_t *info = (pcnet_dev_t *)dev;
     u16 *data = (u16 *)&rq->ifr_data;
-    ioaddr_t addr = dev->base_addr + DLINK_GPIO;
+    ioaddr_t mii_addr = dev->base_addr + DLINK_GPIO;
     switch (cmd) {
     case SIOCDEVPRIVATE:
-	data[0] = 0;
+	data[0] = info->phy_id;
     case SIOCDEVPRIVATE+1:
-	data[3] = mdio_read(addr, data[0], data[1] & 0x1f);
+	data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f);
 	return 0;
     case SIOCDEVPRIVATE+2:
 	if (!capable(CAP_NET_ADMIN))
 	    return -EPERM;
-	mdio_write(addr, data[0], data[1] & 0x1f, data[2]);
+	mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]);
 	return 0;
     }
     return -EOPNOTSUPP;
@@ -1522,4 +1595,3 @@
 
 module_init(init_pcnet_cs);
 module_exit(exit_pcnet_cs);
-MODULE_LICENSE("GPL");

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