X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fsmsc911x.c;h=89f35f94f720603429b8c6bf698ce08d2a174aa1;hb=df01093bb08916f434ebedde4610805d4105d05f;hp=ae4c74b51827ab2252dfc328a34b17d144b0d965;hpb=05bee4737774881e027bfd9a8b5c40a7d68f6325;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index ae4c74b..89f35f9 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -41,15 +41,15 @@ #include #include #include -#include #include -#include #include #include #include #include +#include #include #include +#include #include "smsc911x.h" #define SMSC_CHIPNAME "smsc911x" @@ -176,6 +176,12 @@ static inline void smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf, unsigned int wordcount) { + if (pdata->config.flags & SMSC911X_SWAP_FIFO) { + while (wordcount--) + smsc911x_reg_write(pdata, TX_DATA_FIFO, swab32(*buf++)); + return; + } + if (pdata->config.flags & SMSC911X_USE_32BIT) { writesl(pdata->ioaddr + TX_DATA_FIFO, buf, wordcount); return; @@ -195,6 +201,12 @@ static inline void smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf, unsigned int wordcount) { + if (pdata->config.flags & SMSC911X_SWAP_FIFO) { + while (wordcount--) + *buf++ = swab32(smsc911x_reg_read(pdata, RX_DATA_FIFO)); + return; + } + if (pdata->config.flags & SMSC911X_USE_32BIT) { readsl(pdata->ioaddr + RX_DATA_FIFO, buf, wordcount); return; @@ -318,7 +330,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx) goto out; } - SMSC_WARNING(HW, "Timed out waiting for MII write to finish"); + SMSC_WARNING(HW, "Timed out waiting for MII read to finish"); reg = -EIO; out: @@ -368,48 +380,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 */ @@ -730,8 +747,8 @@ static void smsc911x_phy_adjust_link(struct net_device *dev) * usage is 10/100 indicator */ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG); - if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) - && (!pdata->using_extphy)) { + if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) && + (!pdata->using_extphy)) { /* Force 10/100 LED off, after saving * orginal GPIO configuration */ pdata->gpio_orig_setting = pdata->gpio_setting; @@ -752,29 +769,25 @@ static int smsc911x_mii_probe(struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); struct phy_device *phydev = NULL; - int phy_addr; + int ret; /* find the first phy */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (pdata->mii_bus->phy_map[phy_addr]) { - phydev = pdata->mii_bus->phy_map[phy_addr]; - SMSC_TRACE(PROBE, "PHY %d: addr %d, phy_id 0x%08X", - phy_addr, phydev->addr, phydev->phy_id); - break; - } - } - + phydev = phy_find_first(pdata->mii_bus); if (!phydev) { pr_err("%s: no PHY found\n", dev->name); return -ENODEV; } - phydev = phy_connect(dev, dev_name(&phydev->dev), - &smsc911x_phy_adjust_link, 0, pdata->config.phy_interface); + SMSC_TRACE(PROBE, "PHY %d: addr %d, phy_id 0x%08X", + phy_addr, phydev->addr, phydev->phy_id); + + ret = phy_connect_direct(dev, phydev, + &smsc911x_phy_adjust_link, 0, + pdata->config.phy_interface); - if (IS_ERR(phydev)) { + if (ret) { pr_err("%s: Could not attach to PHY\n", dev->name); - return PTR_ERR(phydev); + return ret; } pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", @@ -798,7 +811,7 @@ static int smsc911x_mii_probe(struct net_device *dev) SMSC_TRACE(HW, "Passed Loop Back Test"); #endif /* USE_PHY_WORK_AROUND */ - SMSC_TRACE(HW, "phy initialised succesfully"); + SMSC_TRACE(HW, "phy initialised successfully"); return 0; } @@ -825,22 +838,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; } @@ -894,22 +903,22 @@ static void smsc911x_tx_update_txcounters(struct net_device *dev) SMSC_WARNING(HW, "Packet tag reserved bit is high"); } else { - if (unlikely(tx_stat & 0x00008000)) { + if (unlikely(tx_stat & TX_STS_ES_)) { dev->stats.tx_errors++; } else { dev->stats.tx_packets++; dev->stats.tx_bytes += (tx_stat >> 16); } - if (unlikely(tx_stat & 0x00000100)) { + if (unlikely(tx_stat & TX_STS_EXCESS_COL_)) { dev->stats.collisions += 16; dev->stats.tx_aborted_errors += 1; } else { dev->stats.collisions += ((tx_stat >> 3) & 0xF); } - if (unlikely(tx_stat & 0x00000800)) + if (unlikely(tx_stat & TX_STS_LOST_CARRIER_)) dev->stats.tx_carrier_errors += 1; - if (unlikely(tx_stat & 0x00000200)) { + if (unlikely(tx_stat & TX_STS_LATE_COL_)) { dev->stats.collisions++; dev->stats.tx_aborted_errors++; } @@ -923,19 +932,17 @@ smsc911x_rx_counterrors(struct net_device *dev, unsigned int rxstat) { int crc_err = 0; - if (unlikely(rxstat & 0x00008000)) { + if (unlikely(rxstat & RX_STS_ES_)) { dev->stats.rx_errors++; - if (unlikely(rxstat & 0x00000002)) { + if (unlikely(rxstat & RX_STS_CRC_ERR_)) { dev->stats.rx_crc_errors++; crc_err = 1; } } if (likely(!crc_err)) { - if (unlikely((rxstat & 0x00001020) == 0x00001020)) { - /* Frame type indicates length, - * and length error is set */ + if (unlikely((rxstat & RX_STS_FRAME_TYPE_) && + (rxstat & RX_STS_LENGTH_ERR_))) dev->stats.rx_length_errors++; - } if (rxstat & RX_STS_MCAST_) dev->stats.multicast++; } @@ -954,7 +961,7 @@ smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes) do { udelay(1); val = smsc911x_reg_read(pdata, RX_DP_CTRL); - } while (--timeout && (val & RX_DP_CTRL_RX_FFWD_)); + } while ((val & RX_DP_CTRL_RX_FFWD_) && --timeout); if (unlikely(timeout == 0)) SMSC_WARNING(HW, "Timed out waiting for " @@ -974,7 +981,7 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) struct net_device *dev = pdata->dev; int npackets = 0; - while (likely(netif_running(dev)) && (npackets < budget)) { + while (npackets < budget) { unsigned int pktlength; unsigned int pktwords; struct sk_buff *skb; @@ -1035,7 +1042,6 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) /* Update counters */ dev->stats.rx_packets++; dev->stats.rx_bytes += (pktlength - 4); - dev->last_rx = jiffies; } /* Return total received packets */ @@ -1120,7 +1126,7 @@ static int smsc911x_soft_reset(struct smsc911x_data *pdata) /* Sets the device MAC address to dev_addr, called with mac_lock held */ static void -smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6]) +smsc911x_set_hw_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6]) { u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4]; u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) | @@ -1161,8 +1167,8 @@ static int smsc911x_open(struct net_device *dev) /* Make sure EEPROM has finished loading before setting GPIO_CFG */ timeout = 50; - while ((timeout--) && - (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) { + while ((smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) && + --timeout) { udelay(10); } @@ -1175,7 +1181,7 @@ static int smsc911x_open(struct net_device *dev) /* The soft reset above cleared the device's MAC address, * restore it from local copy (set in probe) */ spin_lock_irq(&pdata->mac_lock); - smsc911x_set_mac_address(pdata, dev->dev_addr); + smsc911x_set_hw_mac_address(pdata, dev->dev_addr); spin_unlock_irq(&pdata->mac_lock); /* Initialise irqs, but leave all sources disabled */ @@ -1226,6 +1232,10 @@ static int smsc911x_open(struct net_device *dev) dev_info(&dev->dev, "SMSC911x/921x identified at %#08lx, IRQ: %d\n", (unsigned long)pdata->ioaddr, dev->irq); + /* Reset the last known duplex and carrier */ + pdata->last_duplex = -1; + pdata->last_carrier = -1; + /* Bring the PHY up */ phy_start(pdata->phy_dev); @@ -1325,7 +1335,6 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz); freespace -= (skb->len + 32); dev_kfree_skb(skb); - dev->trans_start = jiffies; if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30)) smsc911x_tx_update_txcounters(dev); @@ -1368,33 +1377,24 @@ static void smsc911x_set_multicast_list(struct net_device *dev) pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_); pdata->hashhi = 0; pdata->hashlo = 0; - } else if (dev->mc_count > 0) { + } else if (!netdev_mc_empty(dev)) { /* Enabling specific multicast addresses */ unsigned int hash_high = 0; unsigned int hash_low = 0; - unsigned int count = 0; - struct dev_mc_list *mc_list = dev->mc_list; + struct netdev_hw_addr *ha; pdata->set_bits_mask = MAC_CR_HPFILT_; pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_); - while (mc_list) { - count++; - if ((mc_list->dmi_addrlen) == ETH_ALEN) { - unsigned int bitnum = - smsc911x_hash(mc_list->dmi_addr); - unsigned int mask = 0x01 << (bitnum & 0x1F); - if (bitnum & 0x20) - hash_high |= mask; - else - hash_low |= mask; - } else { - SMSC_WARNING(DRV, "dmi_addrlen != 6"); - } - mc_list = mc_list->next; + netdev_for_each_mc_addr(ha, dev) { + unsigned int bitnum = smsc911x_hash(ha->addr); + unsigned int mask = 0x01 << (bitnum & 0x1F); + + if (bitnum & 0x20) + hash_high |= mask; + else + hash_low |= mask; } - if (count != (unsigned int)dev->mc_count) - SMSC_WARNING(DRV, "mc_count != dev->mc_count"); pdata->hashhi = hash_high; pdata->hashlo = hash_low; @@ -1505,6 +1505,31 @@ static void smsc911x_poll_controller(struct net_device *dev) } #endif /* CONFIG_NET_POLL_CONTROLLER */ +static int smsc911x_set_mac_address(struct net_device *dev, void *p) +{ + struct smsc911x_data *pdata = netdev_priv(dev); + struct sockaddr *addr = p; + + /* On older hardware revisions we cannot change the mac address + * registers while receiving data. Newer devices can safely change + * this at any time. */ + if (pdata->generation <= 1 && netif_running(dev)) + return -EBUSY; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + + spin_lock_irq(&pdata->mac_lock); + smsc911x_set_hw_mac_address(pdata, dev->dev_addr); + spin_unlock_irq(&pdata->mac_lock); + + dev_info(&dev->dev, "MAC Address: %pM\n", dev->dev_addr); + + return 0; +} + /* Standard ioctls for mii-tool */ static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -1618,7 +1643,7 @@ static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op) do { msleep(1); e2cmd = smsc911x_reg_read(pdata, E2P_CMD); - } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--)); + } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { SMSC_TRACE(DRV, "TIMED OUT"); @@ -1652,6 +1677,7 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata, u8 address, u8 data) { u32 op = E2P_CMD_EPC_CMD_ERASE_ | address; + u32 temp; int ret; SMSC_TRACE(DRV, "address 0x%x, data 0x%x", address, data); @@ -1660,6 +1686,10 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata, if (!ret) { op = E2P_CMD_EPC_CMD_WRITE_ | address; smsc911x_reg_write(pdata, E2P_DATA, (u32)data); + + /* Workaround for hardware read-after-write restriction */ + temp = smsc911x_reg_read(pdata, BYTE_TEST); + ret = smsc911x_eeprom_send_cmd(pdata, op); } @@ -1734,13 +1764,29 @@ static const struct net_device_ops smsc911x_netdev_ops = { .ndo_get_stats = smsc911x_get_stats, .ndo_set_multicast_list = smsc911x_set_multicast_list, .ndo_do_ioctl = smsc911x_do_ioctl, + .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = smsc911x_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = smsc911x_poll_controller, #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) { @@ -1828,6 +1874,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; @@ -1872,7 +1924,7 @@ static int __devexit smsc911x_drv_remove(struct platform_device *pdev) if (!res) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start); + release_mem_region(res->start, resource_size(res)); iounmap(pdata->ioaddr); @@ -1886,11 +1938,10 @@ 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); pr_info("%s: Driver version %s.\n", SMSC_CHIPNAME, SMSC_DRV_VERSION); @@ -1911,7 +1962,15 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) retval = -ENODEV; goto out_0; } - res_size = res->end - res->start; + res_size = resource_size(res); + + 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; @@ -1929,7 +1988,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 */ @@ -1962,8 +2022,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); @@ -1994,19 +2054,15 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) /* Check if mac address has been specified when bringing interface up */ if (is_valid_ether_addr(dev->dev_addr)) { - smsc911x_set_mac_address(pdata, dev->dev_addr); + smsc911x_set_hw_mac_address(pdata, dev->dev_addr); SMSC_TRACE(PROBE, "MAC Address is specified by configuration"); + } else if (is_valid_ether_addr(pdata->config.mac)) { + memcpy(dev->dev_addr, pdata->config.mac, 6); + SMSC_TRACE(PROBE, "MAC Address specified by platform data"); } 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 */ @@ -2015,7 +2071,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) } else { /* eeprom values are invalid, generate random MAC */ random_ether_addr(dev->dev_addr); - smsc911x_set_mac_address(pdata, dev->dev_addr); + smsc911x_set_hw_mac_address(pdata, dev->dev_addr); SMSC_TRACE(PROBE, "MAC Address is set to random_ether_addr"); } @@ -2023,8 +2079,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) spin_unlock_irq(&pdata->mac_lock); - dev_info(&dev->dev, "MAC Address: %s\n", - print_mac(mac, dev->dev_addr)); + dev_info(&dev->dev, "MAC Address: %pM\n", dev->dev_addr); return 0; @@ -2038,16 +2093,70 @@ out_unmap_io_3: out_free_netdev_2: free_netdev(dev); out_release_io_1: - release_mem_region(res->start, res->end - res->start); + release_mem_region(res->start, resource_size(res)); out_0: return retval; } +#ifdef CONFIG_PM +/* This implementation assumes the devices remains powered on its VDDVARIO + * pins during suspend. */ + +/* TODO: implement freeze/thaw callbacks for hibernation.*/ + +static int smsc911x_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct smsc911x_data *pdata = netdev_priv(ndev); + + /* enable wake on LAN, energy detection and the external PME + * signal. */ + smsc911x_reg_write(pdata, PMT_CTRL, + PMT_CTRL_PM_MODE_D1_ | PMT_CTRL_WOL_EN_ | + PMT_CTRL_ED_EN_ | PMT_CTRL_PME_EN_); + + return 0; +} + +static int smsc911x_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct smsc911x_data *pdata = netdev_priv(ndev); + unsigned int to = 100; + + /* Note 3.11 from the datasheet: + * "When the LAN9220 is in a power saving state, a write of any + * data to the BYTE_TEST register will wake-up the device." + */ + smsc911x_reg_write(pdata, BYTE_TEST, 0); + + /* poll the READY bit in PMT_CTRL. Any other access to the device is + * forbidden while this bit isn't set. Try for 100ms and return -EIO + * if it failed. */ + while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to) + udelay(1000); + + return (to == 0) ? -EIO : 0; +} + +static const struct dev_pm_ops smsc911x_pm_ops = { + .suspend = smsc911x_suspend, + .resume = smsc911x_resume, +}; + +#define SMSC911X_PM_OPS (&smsc911x_pm_ops) + +#else +#define SMSC911X_PM_OPS NULL +#endif + static struct platform_driver smsc911x_driver = { .probe = smsc911x_drv_probe, - .remove = smsc911x_drv_remove, + .remove = __devexit_p(smsc911x_drv_remove), .driver = { - .name = SMSC_CHIPNAME, + .name = SMSC_CHIPNAME, + .owner = THIS_MODULE, + .pm = SMSC911X_PM_OPS, }, };