diff options
Diffstat (limited to 'drivers/net/ethernet/smsc/smsc911x.c')
| -rw-r--r-- | drivers/net/ethernet/smsc/smsc911x.c | 156 |
1 files changed, 87 insertions, 69 deletions
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 0b6a39b003a4..3ebd0664c697 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** * * Copyright (C) 2004-2008 SMSC * Copyright (C) 2005-2008 ARM * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * *************************************************************************** * Rewritten, heavily based on smsc911x simple driver by SMSC. * Partly uses io macros from smc91x.c by Nicolas Pitre @@ -26,7 +14,6 @@ * LAN9210, LAN9211 * LAN9220, LAN9221 * LAN89218,LAN9250 - * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -56,8 +43,6 @@ #include <linux/smsc911x.h> #include <linux/device.h> #include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/of_net.h> #include <linux/acpi.h> #include <linux/pm_runtime.h> @@ -70,6 +55,7 @@ #define SMSC_MDIONAME "smsc911x-mdio" #define SMSC_DRV_VERSION "2008-10-21" +MODULE_DESCRIPTION("SMSC LAN911x/LAN921x Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(SMSC_DRV_VERSION); MODULE_ALIAS("platform:smsc911x"); @@ -565,11 +551,12 @@ static void smsc911x_mac_write(struct smsc911x_data *pdata, /* Get a phy register */ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx) { - struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv; + struct smsc911x_data *pdata = bus->priv; unsigned long flags; unsigned int addr; int i, reg; + pm_runtime_get_sync(bus->parent); spin_lock_irqsave(&pdata->mac_lock, flags); /* Confirm MII not busy */ @@ -595,6 +582,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx) out: spin_unlock_irqrestore(&pdata->mac_lock, flags); + pm_runtime_put(bus->parent); return reg; } @@ -602,11 +590,12 @@ out: static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx, u16 val) { - struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv; + struct smsc911x_data *pdata = bus->priv; unsigned long flags; unsigned int addr; int i, reg; + pm_runtime_get_sync(bus->parent); spin_lock_irqsave(&pdata->mac_lock, flags); /* Confirm MII not busy */ @@ -636,6 +625,7 @@ static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx, out: spin_unlock_irqrestore(&pdata->mac_lock, flags); + pm_runtime_put(bus->parent); return reg; } @@ -1025,7 +1015,7 @@ static void smsc911x_phy_adjust_link(struct net_device *dev) static int smsc911x_mii_probe(struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); - struct phy_device *phydev = NULL; + struct phy_device *phydev; int ret; /* find the first phy */ @@ -1048,10 +1038,10 @@ static int smsc911x_mii_probe(struct net_device *dev) phy_attached_info(phydev); + phy_set_max_speed(phydev, SPEED_100); + /* mask with MAC supported features */ - phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause); - phydev->advertising = phydev->supported; + phy_support_asym_pause(phydev); pdata->last_duplex = -1; pdata->last_carrier = -1; @@ -1073,6 +1063,7 @@ static int smsc911x_mii_init(struct platform_device *pdev, struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); + struct phy_device *phydev; int err = -ENXIO; pdata->mii_bus = mdiobus_alloc(); @@ -1115,6 +1106,10 @@ static int smsc911x_mii_init(struct platform_device *pdev, goto err_out_free_bus_2; } + phydev = phy_find_first(pdata->mii_bus); + if (phydev) + phydev->mac_managed_pm = true; + return 0; err_out_free_bus_2: @@ -1209,9 +1204,8 @@ smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktwords) SMSC_WARN(pdata, hw, "Timed out waiting for " "RX FFWD to finish, RX_DP_CTRL: 0x%08X", val); } else { - unsigned int temp; while (pktwords--) - temp = smsc911x_reg_read(pdata, RX_DATA_FIFO); + smsc911x_reg_read(pdata, RX_DATA_FIFO); } } @@ -1513,7 +1507,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_hw_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6]) +smsc911x_set_hw_mac_address(struct smsc911x_data *pdata, const 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) | @@ -1603,6 +1597,8 @@ static int smsc911x_open(struct net_device *dev) int retval; int irq_flags; + pm_runtime_get_sync(dev->dev.parent); + /* find and start the given phy */ if (!dev->phydev) { retval = smsc911x_mii_probe(dev); @@ -1747,8 +1743,8 @@ irq_stop_out: free_irq(dev->irq, dev); mii_free_out: phy_disconnect(dev->phydev); - dev->phydev = NULL; out: + pm_runtime_put(dev->dev.parent); return retval; } @@ -1777,16 +1773,17 @@ static int smsc911x_stop(struct net_device *dev) if (dev->phydev) { phy_stop(dev->phydev); phy_disconnect(dev->phydev); - dev->phydev = NULL; } netif_carrier_off(dev); + pm_runtime_put(dev->dev.parent); SMSC_TRACE(pdata, ifdown, "Interface stopped"); return 0; } /* Entry point for transmitting a packet */ -static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t +smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); unsigned int freespace; @@ -1944,7 +1941,7 @@ static int smsc911x_set_mac_address(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + eth_hw_addr_set(dev, addr->sa_data); spin_lock_irq(&pdata->mac_lock); smsc911x_set_hw_mac_address(pdata, dev->dev_addr); @@ -1955,21 +1952,12 @@ static int smsc911x_set_mac_address(struct net_device *dev, void *p) return 0; } -/* Standard ioctls for mii-tool */ -static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - if (!netif_running(dev) || !dev->phydev) - return -EINVAL; - - return phy_mii_ioctl(dev->phydev, ifr, cmd); -} - static void smsc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver)); - strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver)); + strscpy(info->version, SMSC_DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } @@ -2076,7 +2064,6 @@ 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(pdata, drv, "address 0x%x, data 0x%x", address, data); @@ -2087,7 +2074,7 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata, smsc911x_reg_write(pdata, E2P_DATA, (u32)data); /* Workaround for hardware read-after-write restriction */ - temp = smsc911x_reg_read(pdata, BYTE_TEST); + smsc911x_reg_read(pdata, BYTE_TEST); ret = smsc911x_eeprom_send_cmd(pdata, op); } @@ -2163,7 +2150,7 @@ static const struct net_device_ops smsc911x_netdev_ops = { .ndo_start_xmit = smsc911x_hard_start_xmit, .ndo_get_stats = smsc911x_get_stats, .ndo_set_rx_mode = smsc911x_set_multicast_list, - .ndo_do_ioctl = smsc911x_do_ioctl, + .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = smsc911x_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2175,15 +2162,27 @@ static const struct net_device_ops smsc911x_netdev_ops = { static void 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); + u32 mac_high16, mac_low32; + u8 addr[ETH_ALEN]; + + mac_high16 = smsc911x_mac_read(pdata, ADDRH); + 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); + /* The first mac_read in some setups can incorrectly read 0. Re-read it + * to get the full MAC if this is observed. + */ + if (mac_high16 == 0) { + SMSC_TRACE(pdata, probe, "Re-read MAC ADDRH\n"); + mac_high16 = smsc911x_mac_read(pdata, ADDRH); + } + + addr[0] = (u8)(mac_low32); + addr[1] = (u8)(mac_low32 >> 8); + addr[2] = (u8)(mac_low32 >> 16); + addr[3] = (u8)(mac_low32 >> 24); + addr[4] = (u8)(mac_high16); + addr[5] = (u8)(mac_high16 >> 8); + eth_hw_addr_set(dev, addr); } /* Initializing private device structures, only called from probe */ @@ -2317,14 +2316,15 @@ static int smsc911x_init(struct net_device *dev) return -ENODEV; dev->flags |= IFF_MULTICAST; - netif_napi_add(dev, &pdata->napi, smsc911x_poll, SMSC_NAPI_WEIGHT); + netif_napi_add_weight(dev, &pdata->napi, smsc911x_poll, + SMSC_NAPI_WEIGHT); dev->netdev_ops = &smsc911x_netdev_ops; dev->ethtool_ops = &smsc911x_ethtool_ops; return 0; } -static int smsc911x_drv_remove(struct platform_device *pdev) +static void smsc911x_drv_remove(struct platform_device *pdev) { struct net_device *dev; struct smsc911x_data *pdata; @@ -2335,14 +2335,14 @@ static int smsc911x_drv_remove(struct platform_device *pdev) pdata = netdev_priv(dev); BUG_ON(!pdata); BUG_ON(!pdata->ioaddr); - WARN_ON(dev->phydev); SMSC_TRACE(pdata, ifdown, "Stopping driver"); + unregister_netdev(dev); + mdiobus_unregister(pdata->mii_bus); mdiobus_free(pdata->mii_bus); - unregister_netdev(dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smsc911x-memory"); if (!res) @@ -2357,13 +2357,10 @@ static int smsc911x_drv_remove(struct platform_device *pdev) free_netdev(dev); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } -/* standard register acces */ +/* standard register access */ static const struct smsc911x_ops standard_smsc911x_ops = { .reg_read = __smsc911x_reg_read, .reg_write = __smsc911x_reg_write, @@ -2391,7 +2388,7 @@ static int smsc911x_probe_config(struct smsc911x_platform_config *config, phy_interface = PHY_INTERFACE_MODE_NA; config->phy_interface = phy_interface; - device_get_mac_address(dev, config->mac, ETH_ALEN); + device_get_mac_address(dev, config->mac); err = device_property_read_u32(dev, "reg-io-width", &width); if (err == -ENXIO) @@ -2445,7 +2442,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev) if (irq == -EPROBE_DEFER) { retval = -EPROBE_DEFER; goto out_0; - } else if (irq <= 0) { + } else if (irq < 0) { pr_warn("Could not allocate irq resource\n"); retval = -ENODEV; goto out_0; @@ -2466,7 +2463,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev) pdata = netdev_priv(dev); dev->irq = irq; - pdata->ioaddr = ioremap_nocache(res->start, res_size); + pdata->ioaddr = ioremap(res->start, res_size); if (!pdata->ioaddr) { retval = -ENOMEM; goto out_ioremap_fail; @@ -2514,20 +2511,20 @@ static int smsc911x_drv_probe(struct platform_device *pdev) retval = smsc911x_init(dev); if (retval < 0) - goto out_disable_resources; + goto out_init_fail; netif_carrier_off(dev); retval = smsc911x_mii_init(pdev, dev); if (retval) { SMSC_WARN(pdata, probe, "Error %i initialising mii", retval); - goto out_disable_resources; + goto out_init_fail; } retval = register_netdev(dev); if (retval) { SMSC_WARN(pdata, probe, "Error %i registering device", retval); - goto out_disable_resources; + goto out_init_fail; } else { SMSC_TRACE(pdata, probe, "Network interface: \"%s\"", dev->name); @@ -2541,7 +2538,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev) SMSC_TRACE(pdata, probe, "MAC Address is specified by configuration"); } else if (is_valid_ether_addr(pdata->config.mac)) { - memcpy(dev->dev_addr, pdata->config.mac, ETH_ALEN); + eth_hw_addr_set(dev, pdata->config.mac); SMSC_TRACE(pdata, probe, "MAC Address specified by platform data"); } else { @@ -2563,14 +2560,16 @@ static int smsc911x_drv_probe(struct platform_device *pdev) } spin_unlock_irq(&pdata->mac_lock); + pm_runtime_put(&pdev->dev); netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr); return 0; -out_disable_resources: +out_init_fail: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); +out_disable_resources: (void)smsc911x_disable_resources(pdev); out_enable_resources_fail: smsc911x_free_resources(pdev); @@ -2595,6 +2594,13 @@ static int smsc911x_suspend(struct device *dev) struct net_device *ndev = dev_get_drvdata(dev); struct smsc911x_data *pdata = netdev_priv(ndev); + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + if (!device_may_wakeup(dev)) + phy_stop(ndev->phydev); + } + /* enable wake on LAN, energy detection and the external PME * signal. */ smsc911x_reg_write(pdata, PMT_CTRL, @@ -2628,7 +2634,17 @@ static int smsc911x_resume(struct device *dev) while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to) udelay(1000); - return (to == 0) ? -EIO : 0; + if (to == 0) + return -EIO; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + if (!device_may_wakeup(dev)) + phy_start(ndev->phydev); + } + + return 0; } static const struct dev_pm_ops smsc911x_pm_ops = { @@ -2650,11 +2666,13 @@ static const struct of_device_id smsc911x_dt_ids[] = { MODULE_DEVICE_TABLE(of, smsc911x_dt_ids); #endif +#ifdef CONFIG_ACPI static const struct acpi_device_id smsc911x_acpi_match[] = { { "ARMH9118", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, smsc911x_acpi_match); +#endif static struct platform_driver smsc911x_driver = { .probe = smsc911x_drv_probe, |
