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

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

diff -u --recursive --new-file v2.4.14/linux/drivers/net/pcmcia/smc91c92_cs.c linux/drivers/net/pcmcia/smc91c92_cs.c
@@ -8,7 +8,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
 
-    smc91c92_cs.c 1.106 2001/02/07 00:19:58
+    smc91c92_cs.c 1.113 2001/10/13 00:08:53
     
     This driver contains code written by Donald Becker
     (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
@@ -56,19 +56,14 @@
 
 /*====================================================================*/
 
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-static const char *version =
-"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@scyld.com.\n";
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
-
 static char *if_names[] = { "auto", "10baseT", "10base2"};
 
-/* Parameters that can be set with 'insmod' */
+/* Module parameters */
+
+MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
 
 /*
   Transceiver/media type.
@@ -76,16 +71,23 @@
    1 = 10baseT (and autoselect if #define AUTOSELECT),
    2 = AUI/10base2,
 */
-static int if_port;
+INT_MODULE_PARM(if_port, 0);
 
 /* Bit map of interrupts to choose from. */
-static u_int irq_mask = 0xdeb8;
+INT_MODULE_PARM(irq_mask, 0xdeb8);
 static int irq_list[4] = { -1 };
-
-MODULE_PARM(if_port, "i");
-MODULE_PARM(irq_mask, "i");
 MODULE_PARM(irq_list, "1-4i");
-MODULE_LICENSE("GPL");
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+static const char *version =
+"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@scyld.com.\n";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
 
 /* Operational parameter that usually are not changed. */
 
@@ -118,7 +120,8 @@
     int				watchdog, tx_err;
     u_short			media_status;
     u_short			fast_poll;
-    u_long			last_rx;
+    u_short			link_status;
+    int				phy_id;
 };
 
 /* Special definitions for Megahertz multifunction cards */
@@ -246,6 +249,7 @@
 #define	MULTICAST2	2
 #define	MULTICAST4	4
 #define	MULTICAST6	6
+#define MGMT    	8 
 #define REVISION	0x0a
 
 /* Transmit status bits. */
@@ -287,6 +291,9 @@
 static void smc_set_xcvr(struct net_device *dev, int if_port);
 static void smc_reset(struct net_device *dev);
 static void media_check(u_long arg);
+static void mdio_sync(ioaddr_t addr);
+static int mdio_read(ioaddr_t addr, int phy_id, int loc);
+static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value);
 
 /*======================================================================
 
@@ -908,7 +915,8 @@
     cisparse_t parse;
     u_short buf[32];
     char *name;
-    int i, rev;
+    int i, j, rev;
+    ioaddr_t ioaddr;
 
     DEBUG(0, "smc91c92_config(0x%p)\n", link);
     
@@ -1006,9 +1014,10 @@
 	   dev->irq);
     for (i = 0; i < 6; i++)
 	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    ioaddr = dev->base_addr;
     if (rev > 0) {
 	u_long mir, mcr;
-	ioaddr_t ioaddr = dev->base_addr;
 	SMC_SELECT_BANK(0);
 	mir = inw(ioaddr + MEMINFO) & 0xff;
 	if (mir == 0xff) mir++;
@@ -1030,6 +1039,23 @@
 	       "MII" : if_names[dev->if_port]);
     }
     
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	for (i = 0; i < 32; i++) {
+	    j = mdio_read(dev->base_addr + MGMT, i, 1);
+	    if ((j != 0) && (j != 0xffff)) break;
+	}
+	smc->phy_id = (i < 32) ? i : -1;
+	if (i < 32) {
+	    DEBUG(0, "  MII transceiver at index %d, status %x.\n", i, j);
+	} else {
+    	    printk(KERN_NOTICE "  No MII transceivers found!\n");
+	}
+
+	SMC_SELECT_BANK(0);
+    }
+
     return;
     
 config_undo:
@@ -1088,7 +1114,8 @@
     dev_link_t *link = args->client_data;
     struct smc_private *smc = link->priv;
     struct net_device *dev = &smc->dev;
-    
+    int i;   
+ 
     DEBUG(1, "smc91c92_event(0x%06x)\n", event);
 
     switch (event) {
@@ -1130,6 +1157,16 @@
 		set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
 		set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
 	    }
+	    if (((smc->manfid == MANFID_OSITECH) &&
+	 	(smc->cardid == PRODID_OSITECH_SEVEN)) ||
+		((smc->manfid == MANFID_PSION) &&
+	 	(smc->cardid == PRODID_PSION_NET100))) {
+		/* Download the Seven of Diamonds firmware */
+		for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+	    	    outb(__Xilinx7OD[i], link->io.BasePort1+2);
+	   	    udelay(50);
+		}
+	    }
 	    if (link->open) {
 		smc_reset(dev);
 		netif_device_attach(dev);
@@ -1141,6 +1178,63 @@
 } /* smc91c92_event */
 
 /*======================================================================
+
+    MII interface support for SMC91cXX based cards
+======================================================================*/
+
+#define MDIO_SHIFT_CLK		0x04
+#define MDIO_DATA_OUT		0x01
+#define MDIO_DIR_WRITE		0x08
+#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ		0x02
+
+static void mdio_sync(ioaddr_t addr)
+{
+    int bits;
+    for (bits = 0; bits < 32; bits++) {
+	outb(MDIO_DATA_WRITE1, addr);
+	outb(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;
+
+    mdio_sync(addr);
+    for (i = 13; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb(0, addr);
+	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+	outb(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;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb(0, addr);
+	outb(MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*======================================================================
   
     The driver core code, most of which should be common with a
     non-PCMCIA implementation.
@@ -1501,7 +1595,6 @@
 	if (status & IM_RCV_INT) {
 	    /* Got a packet(s). */
 	    smc_rx(dev);
-	    smc->last_rx = jiffies;
 	}
 	if (status & IM_TX_INT) {
 	    smc_tx_err(dev);
@@ -1844,6 +1937,17 @@
 	 TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);
     set_rx_mode(dev);
 
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	/* Reset MII */
+	mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x8000);
+
+	/* Restart MII autonegotiation */
+	mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x0000);
+	mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x1200);
+    }
+
     /* Enable interrupts. */
     SMC_SELECT_BANK(2);
     outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
@@ -1862,18 +1966,20 @@
     struct net_device *dev = &smc->dev;
     ioaddr_t ioaddr = dev->base_addr;
     u_short i, media, saved_bank;
+    ioaddr_t mii_addr = dev->base_addr + MGMT;
+    u_short link;
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
 
     if (!netif_device_present(dev))
 	goto reschedule;
 
-    saved_bank = inw(ioaddr + BANK_SELECT);
     SMC_SELECT_BANK(2);
     i = inw(ioaddr + INTERRUPT);
     SMC_SELECT_BANK(0);
     media = inw(ioaddr + EPH) & EPH_LINK_OK;
     SMC_SELECT_BANK(1);
     media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
-    SMC_SELECT_BANK(saved_bank);
     
     /* Check for pending interrupt with watchdog flag set: with
        this, we can limp along even if the interrupt is blocked */
@@ -1887,14 +1993,42 @@
 	smc->fast_poll--;
 	smc->media.expires = jiffies + 1;
 	add_timer(&smc->media);
+	SMC_SELECT_BANK(saved_bank);
 	return;
     }
 
+    if (smc->cfg & CFG_MII_SELECT) {
+	if (smc->phy_id < 0)
+	    goto reschedule;
+
+	SMC_SELECT_BANK(3);
+	link = mdio_read(mii_addr, smc->phy_id, 1);
+	if (!link || (link == 0xffff)) {
+  	    printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+	    smc->phy_id = -1;
+	    goto reschedule;
+	}
+
+	link &= 0x0004;
+	if (link != smc->link_status) {
+	    u_short p = mdio_read(mii_addr, smc->phy_id, 5);
+	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
+		(link) ? "found" : "lost");
+	    if (link) {
+	        printk(KERN_INFO "%s: autonegotiation complete: "
+	   	    "%sbaseT-%cD selected\n", dev->name,
+		    ((p & 0x0180) ? "100" : "10"),
+		    (((p & 0x0100) || ((p & 0x1c0) == 0x40)) ? 'F' : 'H'));
+	    }
+	    smc->link_status = link;
+	}
+    }
+
     if (smc->cfg & CFG_MII_SELECT)
 	goto reschedule;
 
     /* Ignore collisions unless we've had no rx's recently */
-    if (jiffies - smc->last_rx > HZ) {
+    if (jiffies - dev->last_rx > HZ) {
 	if (smc->tx_err || (smc->media_status & EPH_16COL))
 	    media |= EPH_16COL;
     }
@@ -1930,6 +2064,7 @@
 reschedule:
     smc->media.expires = jiffies + HZ;
     add_timer(&smc->media);
+    SMC_SELECT_BANK(saved_bank);
 }
 
 /*====================================================================*/

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