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
- Lines: 427
- Date:
Thu Nov 28 15:53:14 2002
- Orig file:
linux-2.4.19/drivers/net/pcmcia/smc91c92_cs.c
- Orig date:
Fri Aug 2 17:39:44 2002
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)