#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/bug.h>
#include <linux/bitops.h>
#include <linux/swab.h>
#include <linux/phy.h>
#include <linux/smsc911x.h>
+#include <linux/device.h>
#include "smsc911x.h"
#define SMSC_CHIPNAME "smsc911x"
* 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;
{
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);
- if (IS_ERR(phydev)) {
+ ret = phy_connect_direct(dev, phydev,
+ &smsc911x_phy_adjust_link, 0,
+ pdata->config.phy_interface);
+
+ 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",
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;
}
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;
/* Update counters */
dev->stats.rx_packets++;
dev->stats.rx_bytes += (pktlength - 4);
- dev->last_rx = jiffies;
}
/* Return total received packets */
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);
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;
.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 = smsc911x_set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
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);
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) {
if (is_valid_ether_addr(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 */
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,
},
};