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

Next file: linux-2.4.20/drivers/net/pcmcia/wavelan.h
Previous file: linux-2.4.20/drivers/net/pcmcia/ray_cs.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/net/pcmcia/smc91c92_cs.c linux-2.4.20/drivers/net/pcmcia/smc91c92_cs.c
@@ -8,7 +8,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
 
-    smc91c92_cs.c 1.113 2001/10/13 00:08:53
+    smc91c92_cs.c 1.2 2002/09/28 15:00:00
     
     This driver contains code written by Donald Becker
     (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
@@ -37,12 +37,15 @@
 #include <linux/crc32.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/ioport.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -88,6 +91,9 @@
 #define DEBUG(n, args...)
 #endif
 
+#define DRV_NAME	"smc91c92_cs"
+#define DRV_VERSION	"1.2"
+
 /*====================================================================*/
 
 /* Operational parameter that usually are not changed. */
@@ -109,6 +115,7 @@
 struct smc_private {
     dev_link_t			link;
     struct net_device		dev;
+    spinlock_t			lock;
     u_short			manfid;
     u_short			cardid;
     struct net_device_stats	stats;
@@ -122,7 +129,7 @@
     u_short			media_status;
     u_short			fast_poll;
     u_short			link_status;
-    int				phy_id;
+    struct mii_if_info		mii_if;
 };
 
 /* Special definitions for Megahertz multifunction cards */
@@ -292,9 +299,11 @@
 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);
+static void smc_mdio_sync(ioaddr_t addr);
+static int smc_mdio_read(struct net_device *dev, int phy_id, int loc);
+static void smc_mdio_write(struct net_device *dev, int phy_id, int loc, int value);
+static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int smc_link_ok(struct net_device *dev);
 
 /*======================================================================
 
@@ -346,7 +355,7 @@
     if (!smc) return NULL;
     memset(smc, 0, sizeof(struct smc_private));
     link = &smc->link; dev = &smc->dev;
-
+    spin_lock_init(&smc->lock);
     link->release.function = &smc91c92_release;
     link->release.data = (u_long)link;
     link->io.NumPorts1 = 16;
@@ -369,6 +378,7 @@
     dev->get_stats = &smc91c92_get_stats;
     dev->set_config = &s9k_config;
     dev->set_multicast_list = &set_rx_mode;
+    dev->do_ioctl = &smc_ioctl;
     ether_setup(dev);
     dev->open = &smc91c92_open;
     dev->stop = &smc91c92_close;
@@ -377,6 +387,12 @@
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
     dev->priv = link->priv = link->irq.Instance = smc;
+   
+    smc->mii_if.dev = dev;
+    smc->mii_if.mdio_read = smc_mdio_read;
+    smc->mii_if.mdio_write = smc_mdio_write;
+    smc->mii_if.phy_id_mask = 0x1f;
+    smc->mii_if.reg_num_mask = 0x1f;
     
     /* Register with Card Services */
     link->next = dev_list;
@@ -1044,10 +1060,10 @@
 	SMC_SELECT_BANK(3);
 
 	for (i = 0; i < 32; i++) {
-	    j = mdio_read(dev->base_addr + MGMT, i, 1);
+	    j = smc_mdio_read(dev, i, 1);
 	    if ((j != 0) && (j != 0xffff)) break;
 	}
-	smc->phy_id = (i < 32) ? i : -1;
+	smc->mii_if.phy_id = (i < 32) ? i : -1;
 	if (i < 32) {
 	    DEBUG(0, "  MII transceiver at index %d, status %x.\n", i, j);
 	} else {
@@ -1190,7 +1206,7 @@
 #define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
 #define MDIO_DATA_READ		0x02
 
-static void mdio_sync(ioaddr_t addr)
+static void smc_mdio_sync(ioaddr_t addr)
 {
     int bits;
     for (bits = 0; bits < 32; bits++) {
@@ -1199,12 +1215,13 @@
     }
 }
 
-static int mdio_read(ioaddr_t addr, int phy_id, int loc)
+static int smc_mdio_read(struct net_device *dev, int phy_id, int loc)
 {
+    ioaddr_t addr = dev->base_addr + MGMT;
     u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
     int i, retval = 0;
 
-    mdio_sync(addr);
+    smc_mdio_sync(addr);
     for (i = 13; i >= 0; i--) {
 	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
 	outb(dat, addr);
@@ -1218,12 +1235,13 @@
     return (retval>>1) & 0xffff;
 }
 
-static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value)
+static void smc_mdio_write(struct net_device *dev, int phy_id, int loc, int value)
 {
+    ioaddr_t addr = dev->base_addr + MGMT;
     u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
     int i;
 
-    mdio_sync(addr);
+    smc_mdio_sync(addr);
     for (i = 31; i >= 0; i--) {
 	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
 	outb(dat, addr);
@@ -1777,8 +1795,9 @@
 static void set_rx_mode(struct net_device *dev)
 {
     ioaddr_t ioaddr = dev->base_addr;
+    struct smc_private *smc = dev->priv;
     u_int multicast_table[ 2 ] = { 0, };
-    long flags;
+    unsigned long flags;
     u_short rx_cfg_setting;
     
     if (dev->flags & IFF_PROMISC) {
@@ -1795,16 +1814,15 @@
     }
     
     /* Load MC table and Rx setting into the chip without interrupts. */
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&smc->lock, flags);
     SMC_SELECT_BANK(3);
     outl(multicast_table[0], ioaddr + MULTICAST0);
     outl(multicast_table[1], ioaddr + MULTICAST4);
     SMC_SELECT_BANK(0);
     outw(rx_cfg_setting, ioaddr + RCR);
     SMC_SELECT_BANK(2);
-    restore_flags(flags);
-    
+    spin_unlock_irqrestore(&smc->lock, flags);
+ 
     return;
 }
 
@@ -1917,11 +1935,11 @@
 	SMC_SELECT_BANK(3);
 
 	/* Reset MII */
-	mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x8000);
+	smc_mdio_write(dev, smc->mii_if.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);
+	smc_mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
+	smc_mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
     }
 
     /* Enable interrupts. */
@@ -1942,7 +1960,6 @@
     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);
@@ -1974,20 +1991,20 @@
     }
 
     if (smc->cfg & CFG_MII_SELECT) {
-	if (smc->phy_id < 0)
+	if (smc->mii_if.phy_id < 0)
 	    goto reschedule;
 
 	SMC_SELECT_BANK(3);
-	link = mdio_read(mii_addr, smc->phy_id, 1);
+	link = smc_mdio_read(dev, smc->mii_if.phy_id, 1);
 	if (!link || (link == 0xffff)) {
   	    printk(KERN_INFO "%s: MII is missing!\n", dev->name);
-	    smc->phy_id = -1;
+	    smc->mii_if.phy_id = -1;
 	    goto reschedule;
 	}
 
 	link &= 0x0004;
 	if (link != smc->link_status) {
-	    u_short p = mdio_read(mii_addr, smc->phy_id, 5);
+	    u_short p = smc_mdio_read(dev, smc->mii_if.phy_id, 5);
 	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
 		(link) ? "found" : "lost");
 	    if (link) {
@@ -2043,6 +2060,191 @@
     SMC_SELECT_BANK(saved_bank);
 }
 
+static int smc_link_ok(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    struct smc_private *smc = dev->priv;
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	return mii_link_ok(&smc->mii_if);
+    } else {
+        SMC_SELECT_BANK(0);
+	return inw(ioaddr + EPH) & EPH_LINK_OK;
+    }
+}
+
+static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    ioaddr_t ioaddr = dev->base_addr;
+
+    ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI |
+	SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
+		
+    SMC_SELECT_BANK(1);
+    tmp = inw(ioaddr + CONFIG);
+    ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
+    ecmd->transceiver = XCVR_INTERNAL;
+    ecmd->speed = SPEED_10;
+    ecmd->phy_address = ioaddr + MGMT;
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+
+    return 0;
+}
+
+static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    ioaddr_t ioaddr = dev->base_addr;
+
+    if (ecmd->speed != SPEED_10)
+    	return -EINVAL;
+    if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+    	return -EINVAL;
+    if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI)
+	return -EINVAL;
+    if (ecmd->transceiver != XCVR_INTERNAL)
+    	return -EINVAL;
+
+    if (ecmd->port == PORT_AUI)
+	smc_set_xcvr(dev, 1);
+    else
+	smc_set_xcvr(dev, 0);
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    if (ecmd->duplex == DUPLEX_FULL)
+	tmp |= TCR_FDUPLX;
+    else
+	tmp &= ~TCR_FDUPLX;
+    outw(ioaddr + TCR, tmp);
+	
+    return 0;
+}
+
+static int smc_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+    u32 ethcmd;
+    struct smc_private *smc = dev->priv;
+
+    if (get_user(ethcmd, (u32 *)useraddr))
+	return -EFAULT;
+
+    switch (ethcmd) {
+
+    case ETHTOOL_GDRVINFO: {
+	struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+	strcpy(info.driver, DRV_NAME);
+	strcpy(info.version, DRV_VERSION);
+	if (copy_to_user(useraddr, &info, sizeof(info)))
+	    return -EFAULT;
+	return 0;
+    }
+
+    /* get settings */
+    case ETHTOOL_GSET: {
+	int ret;
+	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+	spin_lock_irq(&smc->lock);
+	if (smc->cfg & CFG_MII_SELECT)
+	    ret = mii_ethtool_gset(&smc->mii_if, &ecmd);
+	else
+	    ret = smc_netdev_get_ecmd(dev, &ecmd);
+	spin_unlock_irq(&smc->lock);
+	if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+	    return -EFAULT;
+	return ret;
+    }
+
+    /* set settings */
+    case ETHTOOL_SSET: {
+	int ret;
+	struct ethtool_cmd ecmd;
+	if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+	    return -EFAULT;
+	spin_lock_irq(&smc->lock);
+	if (smc->cfg & CFG_MII_SELECT)
+	    ret = mii_ethtool_sset(&smc->mii_if, &ecmd);    
+	else
+	    ret = smc_netdev_set_ecmd(dev, &ecmd);
+	spin_unlock_irq(&smc->lock);
+	return ret;
+    }
+
+    /* get link status */
+    case ETHTOOL_GLINK: {
+	struct ethtool_value edata = { ETHTOOL_GLINK };
+	spin_lock_irq(&smc->lock);
+	edata.data = smc_link_ok(dev);
+	spin_unlock_irq(&smc->lock);
+	if (copy_to_user(useraddr, &edata, sizeof(edata)))
+	    return -EFAULT;
+	return 0;
+    }
+
+#ifdef PCMCIA_DEBUG
+    /* get message-level */
+    case ETHTOOL_GMSGLVL: {
+	struct ethtool_value edata = { ETHTOOL_GMSGLVL };
+	edata.data = pc_debug;
+	if (copy_to_user(useraddr, &edata, sizeof(edata)))
+	    return -EFAULT;
+	return 0;
+    }
+    
+    /* set message-level */
+    case ETHTOOL_SMSGLVL: {
+	struct ethtool_value edata;
+	if (copy_from_user(&edata, useraddr, sizeof(edata)))
+	    return -EFAULT;
+	pc_debug = edata.data;
+	return 0;
+    }
+#endif
+    /* restart autonegotiation */
+    case ETHTOOL_NWAY_RST: {
+	if (smc->cfg & CFG_MII_SELECT)
+	    return mii_nway_restart(&smc->mii_if);
+	else
+	    return -EOPNOTSUPP;
+    }
+    
+    default:
+	break;
+    }
+
+    return -EOPNOTSUPP;
+}
+
+static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    struct smc_private *smc = dev->priv;
+    struct mii_ioctl_data *mii;
+    int rc = 0;
+
+    mii = (struct mii_ioctl_data *) &rq->ifr_data;
+    if (!netif_running(dev))
+    	return -EINVAL;
+
+    switch (cmd) {
+    case SIOCETHTOOL:
+	rc = smc_ethtool_ioctl(dev, (void *) rq->ifr_data);
+	break;
+    
+    default:
+	spin_lock_irq(&smc->lock);
+	rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
+	spin_unlock_irq(&smc->lock);
+	break;
+    }
+
+    return rc;
+}
+
+
 /*====================================================================*/
 
 static int __init init_smc91c92_cs(void)
@@ -2069,3 +2271,4 @@
 
 module_init(init_smc91c92_cs);
 module_exit(exit_smc91c92_cs);
+

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