diff options
Diffstat (limited to 'drivers/net/smsc911x.c')
-rw-r--r-- | drivers/net/smsc911x.c | 152 |
1 files changed, 85 insertions, 67 deletions
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 9a78daec2fe..dceae8a6580 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -368,48 +368,53 @@ out: return reg; } -/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors. - * If something goes wrong, returns -ENODEV to revert back to internal phy. - * Performed at initialisation only, so interrupts are enabled */ -static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata) +/* Switch to external phy. Assumes tx and rx are stopped. */ +static void smsc911x_phy_enable_external(struct smsc911x_data *pdata) { unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); - /* External phy is requested, supported, and detected */ - if (hwcfg & HW_CFG_EXT_PHY_DET_) { + /* Disable phy clocks to the MAC */ + hwcfg &= (~HW_CFG_PHY_CLK_SEL_); + hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); + udelay(10); /* Enough time for clocks to stop */ - /* Switch to external phy. Assuming tx and rx are stopped - * because smsc911x_phy_initialise is called before - * smsc911x_rx_initialise and tx_initialise. */ + /* Switch to external phy */ + hwcfg |= HW_CFG_EXT_PHY_EN_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); - /* Disable phy clocks to the MAC */ - hwcfg &= (~HW_CFG_PHY_CLK_SEL_); - hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - udelay(10); /* Enough time for clocks to stop */ + /* Enable phy clocks to the MAC */ + hwcfg &= (~HW_CFG_PHY_CLK_SEL_); + hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); + udelay(10); /* Enough time for clocks to restart */ - /* Switch to external phy */ - hwcfg |= HW_CFG_EXT_PHY_EN_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - - /* Enable phy clocks to the MAC */ - hwcfg &= (~HW_CFG_PHY_CLK_SEL_); - hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - udelay(10); /* Enough time for clocks to restart */ + hwcfg |= HW_CFG_SMI_SEL_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); +} - hwcfg |= HW_CFG_SMI_SEL_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); +/* Autodetects and enables external phy if present on supported chips. + * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY + * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */ +static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata) +{ + unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); - SMSC_TRACE(HW, "Successfully switched to external PHY"); + if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) { + SMSC_TRACE(HW, "Forcing internal PHY"); + pdata->using_extphy = 0; + } else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) { + SMSC_TRACE(HW, "Forcing external PHY"); + smsc911x_phy_enable_external(pdata); + pdata->using_extphy = 1; + } else if (hwcfg & HW_CFG_EXT_PHY_DET_) { + SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET set, using external PHY"); + smsc911x_phy_enable_external(pdata); pdata->using_extphy = 1; } else { - SMSC_WARNING(HW, "No external PHY detected, " - "Using internal PHY instead."); - /* Use internal phy */ - return -ENODEV; + SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET clear, using internal PHY"); + pdata->using_extphy = 0; } - return 0; } /* Fetches a tx status out of the status fifo */ @@ -769,7 +774,7 @@ static int smsc911x_mii_probe(struct net_device *dev) return -ENODEV; } - phydev = phy_connect(dev, phydev->dev.bus_id, + phydev = phy_connect(dev, dev_name(&phydev->dev), &smsc911x_phy_adjust_link, 0, pdata->config.phy_interface); if (IS_ERR(phydev)) { @@ -778,7 +783,8 @@ static int smsc911x_mii_probe(struct net_device *dev) } pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq); + dev->name, phydev->drv->name, + dev_name(&phydev->dev), phydev->irq); /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | @@ -824,22 +830,18 @@ static int __devinit smsc911x_mii_init(struct platform_device *pdev, pdata->mii_bus->parent = &pdev->dev; - pdata->using_extphy = 0; - switch (pdata->idrev & 0xFFFF0000) { case 0x01170000: case 0x01150000: case 0x117A0000: case 0x115A0000: /* External PHY supported, try to autodetect */ - if (smsc911x_phy_initialise_external(pdata) < 0) { - SMSC_TRACE(HW, "No external PHY detected, " - "using internal PHY"); - } + smsc911x_phy_initialise_external(pdata); break; default: SMSC_TRACE(HW, "External PHY is not supported, " "using internal PHY"); + pdata->using_extphy = 0; break; } @@ -984,7 +986,7 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) /* We processed all packets available. Tell NAPI it can * stop polling then re-enable rx interrupts */ smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_); - netif_rx_complete(napi); + napi_complete(napi); temp = smsc911x_reg_read(pdata, INT_EN); temp |= INT_EN_RSFL_EN_; smsc911x_reg_write(pdata, INT_EN, temp); @@ -1246,7 +1248,7 @@ static int smsc911x_open(struct net_device *dev) napi_enable(&pdata->napi); temp = smsc911x_reg_read(pdata, INT_EN); - temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_); + temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_RXSTOP_INT_EN_); smsc911x_reg_write(pdata, INT_EN, temp); spin_lock_irq(&pdata->mac_lock); @@ -1418,11 +1420,6 @@ static void smsc911x_set_multicast_list(struct net_device *dev) /* Request the hardware to stop, then perform the * update when we get an RX_STOP interrupt */ - smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_); - temp = smsc911x_reg_read(pdata, INT_EN); - temp |= INT_EN_RXSTOP_INT_EN_; - smsc911x_reg_write(pdata, INT_EN, temp); - temp = smsc911x_mac_read(pdata, MAC_CR); temp &= ~(MAC_CR_RXEN_); smsc911x_mac_write(pdata, MAC_CR, temp); @@ -1461,11 +1458,9 @@ static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id) /* Called when there is a multicast update scheduled and * it is now safe to complete the update */ SMSC_TRACE(INTR, "RX Stop interrupt"); - temp = smsc911x_reg_read(pdata, INT_EN); - temp &= (~INT_EN_RXSTOP_INT_EN_); - smsc911x_reg_write(pdata, INT_EN, temp); smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_); - smsc911x_rx_multicast_update_workaround(pdata); + if (pdata->multicast_update_pending) + smsc911x_rx_multicast_update_workaround(pdata); serviced = IRQ_HANDLED; } @@ -1485,16 +1480,16 @@ static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id) } if (likely(intsts & inten & INT_STS_RSFL_)) { - if (likely(netif_rx_schedule_prep(&pdata->napi))) { + if (likely(napi_schedule_prep(&pdata->napi))) { /* Disable Rx interrupts */ temp = smsc911x_reg_read(pdata, INT_EN); temp &= (~INT_EN_RSFL_EN_); smsc911x_reg_write(pdata, INT_EN, temp); /* Schedule a NAPI poll */ - __netif_rx_schedule(&pdata->napi); + __napi_schedule(&pdata->napi); } else { SMSC_WARNING(RX_ERR, - "netif_rx_schedule_prep failed"); + "napi_schedule_prep failed"); } serviced = IRQ_HANDLED; } @@ -1545,7 +1540,7 @@ static void smsc911x_ethtool_getdrvinfo(struct net_device *dev, { strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver)); strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev->dev.parent->bus_id, + strlcpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } @@ -1747,6 +1742,21 @@ static const struct net_device_ops smsc911x_netdev_ops = { #endif }; +/* copies the current mac address from hardware to dev->dev_addr */ +static void __devinit smsc911x_read_mac_address(struct net_device *dev) +{ + struct smsc911x_data *pdata = netdev_priv(dev); + u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH); + u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL); + + dev->dev_addr[0] = (u8)(mac_low32); + dev->dev_addr[1] = (u8)(mac_low32 >> 8); + dev->dev_addr[2] = (u8)(mac_low32 >> 16); + dev->dev_addr[3] = (u8)(mac_low32 >> 24); + dev->dev_addr[4] = (u8)(mac_high16); + dev->dev_addr[5] = (u8)(mac_high16 >> 8); +} + /* Initializing private device structures, only called from probe */ static int __devinit smsc911x_init(struct net_device *dev) { @@ -1834,6 +1844,12 @@ static int __devinit smsc911x_init(struct net_device *dev) SMSC_WARNING(PROBE, "This driver is not intended for this chip revision"); + /* workaround for platforms without an eeprom, where the mac address + * is stored elsewhere and set by the bootloader. This saves the + * mac address before resetting the device */ + if (pdata->config.flags & SMSC911X_SAVE_MAC_ADDRESS) + smsc911x_read_mac_address(dev); + /* Reset the LAN911x */ if (smsc911x_soft_reset(pdata)) return -ENODEV; @@ -1892,9 +1908,9 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) struct net_device *dev; struct smsc911x_data *pdata; struct smsc911x_platform_config *config = pdev->dev.platform_data; - struct resource *res; + struct resource *res, *irq_res; unsigned int intcfg = 0; - int res_size; + int res_size, irq_flags; int retval; DECLARE_MAC_BUF(mac); @@ -1919,6 +1935,14 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) } res_size = res->end - res->start; + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq_res) { + pr_warning("%s: Could not allocate irq resource.\n", + SMSC_CHIPNAME); + retval = -ENODEV; + goto out_0; + } + if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) { retval = -EBUSY; goto out_0; @@ -1935,7 +1959,8 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) pdata = netdev_priv(dev); - dev->irq = platform_get_irq(pdev, 0); + dev->irq = irq_res->start; + irq_flags = irq_res->flags & IRQF_TRIGGER_MASK; pdata->ioaddr = ioremap_nocache(res->start, res_size); /* copy config parameters across to pdata */ @@ -1968,8 +1993,8 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) smsc911x_reg_write(pdata, INT_EN, 0); smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF); - retval = request_irq(dev->irq, smsc911x_irqhandler, IRQF_DISABLED, - dev->name, dev); + retval = request_irq(dev->irq, smsc911x_irqhandler, + irq_flags | IRQF_SHARED, dev->name, dev); if (retval) { SMSC_WARNING(PROBE, "Unable to claim requested irq: %d", dev->irq); @@ -2005,14 +2030,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) } else { /* Try reading mac address from device. if EEPROM is present * it will already have been set */ - u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH); - u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL); - dev->dev_addr[0] = (u8)(mac_low32); - dev->dev_addr[1] = (u8)(mac_low32 >> 8); - dev->dev_addr[2] = (u8)(mac_low32 >> 16); - dev->dev_addr[3] = (u8)(mac_low32 >> 24); - dev->dev_addr[4] = (u8)(mac_high16); - dev->dev_addr[5] = (u8)(mac_high16 >> 8); + smsc911x_read_mac_address(dev); if (is_valid_ether_addr(dev->dev_addr)) { /* eeprom values are valid so use them */ |