]> git.hungrycats.org Git - linux/commitdiff
Merge ethtool support and PPC fix into pcnet32 net driver,
authorJeff Garzik <jgarzik@rum.normnet.org>
Mon, 11 Feb 2002 20:29:52 +0000 (15:29 -0500)
committerJeff Garzik <jgarzik@rum.normnet.org>
Mon, 11 Feb 2002 20:29:52 +0000 (15:29 -0500)
from 2.4.x.
Also, remove deprecated SIOCDEVPRIVATE ioctl calls.

Via Dave Jones.

drivers/net/Makefile
drivers/net/pcnet32.c

index 8dea1e3295642d86e4947dc79a021b34a42e2343..0c0f48d75bcb2c333772166218451669cc7016aa 100644 (file)
@@ -65,7 +65,7 @@ obj-$(CONFIG_DGRS) += dgrs.o
 obj-$(CONFIG_RCPCI) += rcpci.o
 obj-$(CONFIG_VORTEX) += 3c59x.o
 obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
-obj-$(CONFIG_PCNET32) += pcnet32.o
+obj-$(CONFIG_PCNET32) += pcnet32.o mii.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
 obj-$(CONFIG_TLAN) += tlan.o
 obj-$(CONFIG_EPIC100) += epic100.o mii.o
index 8dd2af654ba15954eadd14ffbe48ca69d821186d..28ba296f2f4d1b32bb9825ac3d28cb301319898d 100644 (file)
  *
  *************************************************************************/
 
-static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n";
+#define DRV_NAME       "pcnet32"
+#define DRV_VERSION    "1.25kf"
+#define DRV_RELDATE    "17.11.2001"
+
+static const char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n";
 
 #include <linux/module.h>
 
@@ -36,10 +41,13 @@ static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/crc32.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -283,11 +291,11 @@ struct pcnet32_private {
     int         shared_irq:1,                  /* shared irq possible */
        ltint:1,
 #ifdef DO_DXSUFLO
-             dxsuflo:1,                                                    /* disable transmit stop on uflo */
+      dxsuflo:1,                           /* disable transmit stop on uflo */
 #endif
-       full_duplex:1,                          /* full duplex possible */
        mii:1;                                  /* mii port available */
     struct net_device *next;
+    struct mii_if_info mii_if;
 };
 
 static int  pcnet32_probe_vlbus(int cards_found);
@@ -302,9 +310,9 @@ static void pcnet32_interrupt(int, void *, struct pt_regs *);
 static int  pcnet32_close(struct net_device *);
 static struct net_device_stats *pcnet32_get_stats(struct net_device *);
 static void pcnet32_set_multicast_list(struct net_device *);
-#ifdef HAVE_PRIVATE_IOCTL
-static int  pcnet32_mii_ioctl(struct net_device *, struct ifreq *, int);
-#endif
+static int  pcnet32_ioctl(struct net_device *, struct ifreq *, int);
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val);
 
 enum pci_flags_bit {
     PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -647,6 +655,13 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
 #if defined(__i386__)
            printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
            memcpy(dev->dev_addr, promaddr, 6);
+#elif defined(__powerpc__)
+           if (!is_valid_ether_addr(dev->dev_addr)
+               && is_valid_ether_addr(promaddr)) {
+                   printk("\n" KERN_WARNING "%s: using PROM address:",
+                          dev->name);
+                   memcpy(dev->dev_addr, promaddr, 6);
+           }
 #endif
        }                   
     }
@@ -702,7 +717,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
-    lp->full_duplex = fdx;
+    lp->mii_if.full_duplex = fdx;
 #ifdef DO_DXSUFLO
     lp->dxsuflo = dxsuflo;
 #endif
@@ -712,6 +727,9 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
        lp->options = PCNET32_PORT_ASEL;
     else
        lp->options = options_mapping[options[card_idx]];
+    lp->mii_if.dev = dev;
+    lp->mii_if.mdio_read = mdio_read;
+    lp->mii_if.mdio_write = mdio_write;
     
     if (fdx && !(lp->options & PCNET32_PORT_ASEL) && full_duplex[card_idx])
        lp->options |= PCNET32_PORT_FD;
@@ -781,9 +799,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car
     dev->stop = &pcnet32_close;
     dev->get_stats = &pcnet32_get_stats;
     dev->set_multicast_list = &pcnet32_set_multicast_list;
-#ifdef HAVE_PRIVATE_IOCTL
-    dev->do_ioctl = &pcnet32_mii_ioctl;
-#endif
+    dev->do_ioctl = &pcnet32_ioctl;
     dev->tx_timeout = pcnet32_tx_timeout;
     dev->watchdog_timeo = (HZ >> 1);
 
@@ -834,7 +850,7 @@ pcnet32_open(struct net_device *dev)
     lp->a.write_bcr (ioaddr, 2, val);
     
     /* handle full duplex setting */
-    if (lp->full_duplex) {
+    if (lp->mii_if.full_duplex) {
        val = lp->a.read_bcr (ioaddr, 9) & ~3;
        if (lp->options & PCNET32_PORT_FD) {
            val |= 1;
@@ -1476,29 +1492,154 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
     pcnet32_restart(dev, 0x0042); /*  Resume normal operation */
 }
 
-#ifdef HAVE_PRIVATE_IOCTL
-static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
+{
+       struct pcnet32_private *lp = dev->priv;
+       unsigned long ioaddr = dev->base_addr;
+       u16 val_out;
+       int phyaddr;
+
+       if (!lp->mii)
+               return 0;
+               
+       phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+       lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+       val_out = lp->a.read_bcr(ioaddr, 34);
+       lp->a.write_bcr(ioaddr, 33, phyaddr);
+       
+       return val_out;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
+{
+       struct pcnet32_private *lp = dev->priv;
+       unsigned long ioaddr = dev->base_addr;
+       int phyaddr;
+
+       if (!lp->mii)
+               return;
+               
+       phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+       lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+       lp->a.write_bcr(ioaddr, 34, val);
+       lp->a.write_bcr(ioaddr, 33, phyaddr);
+}
+
+static int pcnet32_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+       struct pcnet32_private *lp = dev->priv;
+       u32 ethcmd;
+       int phyaddr = 0;
+       int phy_id = 0;
+       unsigned long ioaddr = dev->base_addr;
+
+       if (lp->mii) {
+               phyaddr = lp->a.read_bcr (ioaddr, 33);
+               phy_id = (phyaddr >> 5) & 0x1f;
+               lp->mii_if.phy_id = phy_id;
+       }
+
+       if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+               return -EFAULT;
+
+       switch (ethcmd) {
+       case ETHTOOL_GDRVINFO: {
+               struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+               strcpy (info.driver, DRV_NAME);
+               strcpy (info.version, DRV_VERSION);
+               if (lp->pci_dev)
+                       strcpy (info.bus_info, lp->pci_dev->slot_name);
+               else
+                       sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr);
+               if (copy_to_user (useraddr, &info, sizeof (info)))
+                       return -EFAULT;
+               return 0;
+       }
+
+       /* get settings */
+       case ETHTOOL_GSET: {
+               struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+               spin_lock_irq(&lp->lock);
+               mii_ethtool_gset(&lp->mii_if, &ecmd);
+               spin_unlock_irq(&lp->lock);
+               if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+                       return -EFAULT;
+               return 0;
+       }
+       /* set settings */
+       case ETHTOOL_SSET: {
+               int r;
+               struct ethtool_cmd ecmd;
+               if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+                       return -EFAULT;
+               spin_lock_irq(&lp->lock);
+               r = mii_ethtool_sset(&lp->mii_if, &ecmd);
+               spin_unlock_irq(&lp->lock);
+               return r;
+       }
+       /* restart autonegotiation */
+       case ETHTOOL_NWAY_RST: {
+               return mii_nway_restart(&lp->mii_if);
+       }
+       /* get link status */
+       case ETHTOOL_GLINK: {
+               struct ethtool_value edata = {ETHTOOL_GLINK};
+               edata.data = mii_link_ok(&lp->mii_if);
+               if (copy_to_user(useraddr, &edata, sizeof(edata)))
+                       return -EFAULT;
+               return 0;
+       }
+
+       /* get message-level */
+       case ETHTOOL_GMSGLVL: {
+               struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+               edata.data = pcnet32_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;
+               pcnet32_debug = edata.data;
+               return 0;
+       }
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
     unsigned long ioaddr = dev->base_addr;
     struct pcnet32_private *lp = dev->priv;     
-    u16 *data = (u16 *)&rq->ifr_data;
+    struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
     int phyaddr = lp->a.read_bcr (ioaddr, 33);
 
+    if (cmd == SIOCETHTOOL)
+       return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
     if (lp->mii) {
        switch(cmd) {
-       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
-           data[0] = (phyaddr >> 5) & 0x1f;
+       case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
+           data->phy_id = (phyaddr >> 5) & 0x1f;
            /* Fall Through */
-       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
-           lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-           data[3] = lp->a.read_bcr (ioaddr, 34);
+       case SIOCGMIIREG:               /* Read MII PHY register. */
+           lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+           data->val_out = lp->a.read_bcr (ioaddr, 34);
            lp->a.write_bcr (ioaddr, 33, phyaddr);
            return 0;
-       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+       case SIOCSMIIREG:               /* Write MII PHY register. */
            if (!capable(CAP_NET_ADMIN))
                return -EPERM;
-           lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-           lp->a.write_bcr (ioaddr, 34, data[2]);
+           lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+           lp->a.write_bcr (ioaddr, 34, data->val_in);
            lp->a.write_bcr (ioaddr, 33, phyaddr);
            return 0;
        default:
@@ -1507,13 +1648,12 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
     }
     return -EOPNOTSUPP;
 }
-#endif /* HAVE_PRIVATE_IOCTL */
                                            
 static struct pci_driver pcnet32_driver = {
-    name:  "pcnet32",
-    probe: pcnet32_probe_pci,
-    remove: NULL,
-    id_table: pcnet32_pci_tbl,
+       name:           DRV_NAME,
+       probe:          pcnet32_probe_pci,
+       remove:         NULL,
+       id_table:       pcnet32_pci_tbl,
 };
 
 MODULE_PARM(debug, "i");