[PATCH] 2.4.25 pcnet32.c bus master arbitration failure fix
From: Don Fry <hidden>
Date: 2004-02-19 21:54:41
The driver did not properly serialize accesses to chip registers, resulting in reading/writing the wrong register. This patch eliminates this problem and gets rid of the cause of the symptom of 'bus master arbitration failure'. It was easier to use generic_mii_ioctl than modify the current pcnet32_ioctl routine with the necessary locks. This has been re-tested on an IA32 system.
--- linux-2.4.25/drivers/net/rmm.pcnet32.c Thu Feb 19 13:34:29 2004
+++ linux-2.4.25/drivers/net/pcnet32.c Thu Feb 19 13:48:02 2004@@ -713,6 +713,8 @@ lp->name = chipname; lp->shared_irq = shared; lp->mii_if.full_duplex = fdx; + lp->mii_if.phy_id_mask = 0x1f; + lp->mii_if.reg_num_mask = 0x1f; lp->dxsuflo = dxsuflo; lp->ltint = ltint; lp->mii = mii;
@@ -784,6 +786,9 @@ } } + /* Set the mii phy_id so that we can query the link state */ + if (lp->mii) + lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f; /* The PCNET32-specific entries in the device structure. */ dev->open = &pcnet32_open;
@@ -1604,12 +1609,18 @@ } /* restart autonegotiation */ case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&lp->mii_if); + int r; + spin_lock_irq(&lp->lock); + r = mii_nway_restart(&lp->mii_if); + spin_unlock_irq(&lp->lock); + return r; } /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; + spin_lock_irq(&lp->lock); edata.data = mii_link_ok(&lp->mii_if); + spin_unlock_irq(&lp->lock); if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0;
@@ -1640,36 +1651,24 @@ 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; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; - int phyaddr = lp->a.read_bcr (ioaddr, 33); + int rc; + unsigned long flags; if (cmd == SIOCETHTOOL) return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data); + /* SIOC[GS]MIIxxx ioctls */ if (lp->mii) { - switch(cmd) { - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = (phyaddr >> 5) & 0x1f; - /* Fall Through */ - 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 SIOCSMIIREG: /* Write MII PHY register. */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - 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: - return -EOPNOTSUPP; - } + spin_lock_irqsave(&lp->lock, flags); + rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL); + spin_unlock_irqrestore(&lp->lock, flags); + } else { + rc = -EOPNOTSUPP; } - return -EOPNOTSUPP; + + return rc; } static struct pci_driver pcnet32_driver = {
--
Don Fry
brazilnut@us.ibm.com