Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
[safe/jmp/linux-2.6] / drivers / net / smsc911x.c
index dceae8a..cc55974 100644 (file)
 #include <linux/netdevice.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
-#include <linux/slab.h>
 #include <linux/timer.h>
-#include <linux/version.h>
 #include <linux/bug.h>
 #include <linux/bitops.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/swab.h>
 #include <linux/phy.h>
 #include <linux/smsc911x.h>
+#include <linux/device.h>
 #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:
@@ -724,7 +736,7 @@ static void smsc911x_phy_adjust_link(struct net_device *dev)
                        SMSC_TRACE(HW, "configuring for carrier OK");
                        if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
                            (!pdata->using_extphy)) {
-                               /* Restore orginal GPIO configuration */
+                               /* Restore original GPIO configuration */
                                pdata->gpio_setting = pdata->gpio_orig_setting;
                                smsc911x_reg_write(pdata, GPIO_CFG,
                                        pdata->gpio_setting);
@@ -735,10 +747,10 @@ 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 */
+                                * original GPIO configuration */
                                pdata->gpio_orig_setting = pdata->gpio_setting;
 
                                pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
@@ -757,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",
@@ -803,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;
 }
 
@@ -895,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++;
                        }
@@ -924,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++;
        }
@@ -955,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 "
@@ -975,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;
@@ -1036,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 */
@@ -1121,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) |
@@ -1162,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);
        }
 
@@ -1176,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 */
@@ -1227,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);
 
@@ -1326,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);
@@ -1369,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;
@@ -1506,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)
 {
@@ -1653,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);
@@ -1661,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);
        }
 
@@ -1735,8 +1764,9 @@ 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
@@ -1894,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);
 
@@ -1912,7 +1942,6 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
        unsigned int intcfg = 0;
        int res_size, irq_flags;
        int retval;
-       DECLARE_MAC_BUF(mac);
 
        pr_info("%s: Driver version %s.\n", SMSC_CHIPNAME, SMSC_DRV_VERSION);
 
@@ -1933,7 +1962,7 @@ 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) {
@@ -2025,8 +2054,11 @@ 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 */
@@ -2039,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");
                }
@@ -2047,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;
 
@@ -2062,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,
        },
 };