diff options
Diffstat (limited to 'drivers/net/usb/smsc95xx.c')
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 488 |
1 files changed, 176 insertions, 312 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index bb4ccbda031a..ea0d5f04dc3a 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -18,10 +18,12 @@ #include <linux/usb/usbnet.h> #include <linux/slab.h> #include <linux/of_net.h> +#include <linux/mdio.h> +#include <linux/phy.h> #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" -#define SMSC_DRIVER_VERSION "1.0.6" +#define SMSC_DRIVER_VERSION "2.0.0" #define HS_USB_PKT_SIZE (512) #define FS_USB_PKT_SIZE (64) #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) @@ -49,10 +51,7 @@ #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) -#define CARRIER_CHECK_DELAY (2 * HZ) - struct smsc95xx_priv { - u32 chip_id; u32 mac_cr; u32 hash_hi; u32 hash_lo; @@ -60,10 +59,8 @@ struct smsc95xx_priv { spinlock_t mac_cr_lock; u8 features; u8 suspend_flags; - u8 mdix_ctrl; - bool link_ok; - struct delayed_work carrier_check; - struct usbnet *dev; + struct mii_bus *mdiobus; + struct phy_device *phydev; }; static bool turbo_mode = true; @@ -173,10 +170,14 @@ static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev, return -EIO; } -static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, +static u32 mii_address_cmd(int phy_id, int idx, u16 op) +{ + return (phy_id & 0x1f) << 11 | (idx & 0x1f) << 6 | op; +} + +static int __smsc95xx_mdio_read(struct usbnet *dev, int phy_id, int idx, int in_pm) { - struct usbnet *dev = netdev_priv(netdev); u32 val, addr; int ret; @@ -185,14 +186,12 @@ static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, /* confirm MII not busy */ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) { - netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); + netdev_warn(dev->net, "%s: MII is busy\n", __func__); goto done; } /* set the address, index & direction (read from PHY) */ - phy_id &= dev->mii.phy_id_mask; - idx &= dev->mii.reg_num_mask; - addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; + addr = mii_address_cmd(phy_id, idx, MII_READ_ | MII_BUSY_); ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); if (ret < 0) { netdev_warn(dev->net, "Error writing MII_ADDR\n"); @@ -218,10 +217,9 @@ done: return ret; } -static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, +static void __smsc95xx_mdio_write(struct usbnet *dev, int phy_id, int idx, int regval, int in_pm) { - struct usbnet *dev = netdev_priv(netdev); u32 val, addr; int ret; @@ -230,7 +228,7 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, /* confirm MII not busy */ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); if (ret < 0) { - netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); + netdev_warn(dev->net, "%s: MII is busy\n", __func__); goto done; } @@ -242,9 +240,7 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, } /* set the address, index & direction (write to PHY) */ - phy_id &= dev->mii.phy_id_mask; - idx &= dev->mii.reg_num_mask; - addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; + addr = mii_address_cmd(phy_id, idx, MII_WRITE_ | MII_BUSY_); ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); if (ret < 0) { netdev_warn(dev->net, "Error writing MII_ADDR\n"); @@ -261,27 +257,34 @@ done: mutex_unlock(&dev->phy_mutex); } -static int smsc95xx_mdio_read_nopm(struct net_device *netdev, int phy_id, - int idx) +static int smsc95xx_mdio_read_nopm(struct usbnet *dev, int idx) { - return __smsc95xx_mdio_read(netdev, phy_id, idx, 1); + struct smsc95xx_priv *pdata = dev->driver_priv; + + return __smsc95xx_mdio_read(dev, pdata->phydev->mdio.addr, idx, 1); } -static void smsc95xx_mdio_write_nopm(struct net_device *netdev, int phy_id, - int idx, int regval) +static void smsc95xx_mdio_write_nopm(struct usbnet *dev, int idx, int regval) { - __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 1); + struct smsc95xx_priv *pdata = dev->driver_priv; + + __smsc95xx_mdio_write(dev, pdata->phydev->mdio.addr, idx, regval, 1); } -static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +static int smsc95xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) { - return __smsc95xx_mdio_read(netdev, phy_id, idx, 0); + struct usbnet *dev = bus->priv; + + return __smsc95xx_mdio_read(dev, phy_id, idx, 0); } -static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, - int regval) +static int smsc95xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) { - __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0); + struct usbnet *dev = bus->priv; + + __smsc95xx_mdio_write(dev, phy_id, idx, regval, 0); + return 0; } static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) @@ -455,7 +458,7 @@ static unsigned int smsc95xx_hash(char addr[ETH_ALEN]) static void smsc95xx_set_multicast(struct net_device *netdev) { struct usbnet *dev = netdev_priv(netdev); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; unsigned long flags; int ret; @@ -511,22 +514,23 @@ static void smsc95xx_set_multicast(struct net_device *netdev) netdev_warn(dev->net, "failed to initiate async write to MAC_CR\n"); } -static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, - u16 lcladv, u16 rmtadv) +static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev) { u32 flow = 0, afc_cfg; + struct smsc95xx_priv *pdata = dev->driver_priv; + bool tx_pause, rx_pause; int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg); if (ret < 0) return ret; - if (duplex == DUPLEX_FULL) { - u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + if (pdata->phydev->duplex == DUPLEX_FULL) { + phy_get_pause(pdata->phydev, &tx_pause, &rx_pause); - if (cap & FLOW_CTRL_RX) + if (rx_pause) flow = 0xFFFF0002; - if (cap & FLOW_CTRL_TX) { + if (tx_pause) { afc_cfg |= 0xF; flow |= 0xFFFF0000; } else { @@ -534,8 +538,8 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, } netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", - cap & FLOW_CTRL_RX ? "enabled" : "disabled", - cap & FLOW_CTRL_TX ? "enabled" : "disabled"); + rx_pause ? "enabled" : "disabled", + tx_pause ? "enabled" : "disabled"); } else { netif_dbg(dev, link, dev->net, "half duplex\n"); afc_cfg |= 0xF; @@ -550,33 +554,16 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, static int smsc95xx_link_reset(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - struct mii_if_info *mii = &dev->mii; - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + struct smsc95xx_priv *pdata = dev->driver_priv; unsigned long flags; - u16 lcladv, rmtadv; int ret; - /* clear interrupt status */ - ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); - if (ret < 0) - return ret; - ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); if (ret < 0) return ret; - mii_check_media(mii, 1, 1); - mii_ethtool_gset(&dev->mii, &ecmd); - lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); - rmtadv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_LPA); - - netif_dbg(dev, link, dev->net, - "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", - ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); - spin_lock_irqsave(&pdata->mac_cr_lock, flags); - if (ecmd.duplex != DUPLEX_FULL) { + if (pdata->phydev->duplex != DUPLEX_FULL) { pdata->mac_cr &= ~MAC_CR_FDPX_; pdata->mac_cr |= MAC_CR_RCVOWN_; } else { @@ -589,7 +576,7 @@ static int smsc95xx_link_reset(struct usbnet *dev) if (ret < 0) return ret; - ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); + ret = smsc95xx_phy_update_flowcontrol(dev); if (ret < 0) netdev_warn(dev->net, "Error updating PHY flow control\n"); @@ -616,44 +603,6 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb) intdata); } -static void set_carrier(struct usbnet *dev, bool link) -{ - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - - if (pdata->link_ok == link) - return; - - pdata->link_ok = link; - - if (link) - usbnet_link_change(dev, 1, 0); - else - usbnet_link_change(dev, 0, 0); -} - -static void check_carrier(struct work_struct *work) -{ - struct smsc95xx_priv *pdata = container_of(work, struct smsc95xx_priv, - carrier_check.work); - struct usbnet *dev = pdata->dev; - int ret; - - if (pdata->suspend_flags != 0) - return; - - ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMSR); - if (ret < 0) { - netdev_warn(dev->net, "Failed to read MII_BMSR\n"); - return; - } - if (ret & BMSR_LSTATUS) - set_carrier(dev, 1); - else - set_carrier(dev, 0); - - schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); -} - /* Enable or disable Tx & Rx checksum offload engines */ static int smsc95xx_set_features(struct net_device *netdev, netdev_features_t features) @@ -747,7 +696,7 @@ static void smsc95xx_ethtool_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = netdev_priv(net); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; wolinfo->supported = SUPPORTED_WAKE; wolinfo->wolopts = pdata->wolopts; @@ -757,7 +706,7 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) { struct usbnet *dev = netdev_priv(net); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; int ret; if (wolinfo->wolopts & ~SUPPORTED_WAKE) @@ -772,108 +721,15 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net, return ret; } -static int get_mdix_status(struct net_device *net) +static u32 smsc95xx_get_link(struct net_device *net) { - struct usbnet *dev = netdev_priv(net); - u32 val; - int buf; - - buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, SPECIAL_CTRL_STS); - if (buf & SPECIAL_CTRL_STS_OVRRD_AMDIX_) { - if (buf & SPECIAL_CTRL_STS_AMDIX_ENABLE_) - return ETH_TP_MDI_AUTO; - else if (buf & SPECIAL_CTRL_STS_AMDIX_STATE_) - return ETH_TP_MDI_X; - } else { - buf = smsc95xx_read_reg(dev, STRAP_STATUS, &val); - if (val & STRAP_STATUS_AMDIX_EN_) - return ETH_TP_MDI_AUTO; - } - - return ETH_TP_MDI; -} - -static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl) -{ - struct usbnet *dev = netdev_priv(net); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - int buf; - - if ((pdata->chip_id == ID_REV_CHIP_ID_9500A_) || - (pdata->chip_id == ID_REV_CHIP_ID_9530_) || - (pdata->chip_id == ID_REV_CHIP_ID_89530_) || - (pdata->chip_id == ID_REV_CHIP_ID_9730_)) { - /* Extend Manual AutoMDIX timer for 9500A/9500Ai */ - buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, - PHY_EDPD_CONFIG); - buf |= PHY_EDPD_CONFIG_EXT_CROSSOVER_; - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, - PHY_EDPD_CONFIG, buf); - } - - if (mdix_ctrl == ETH_TP_MDI) { - buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS); - buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_; - buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ | - SPECIAL_CTRL_STS_AMDIX_STATE_); - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS, buf); - } else if (mdix_ctrl == ETH_TP_MDI_X) { - buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS); - buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_; - buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ | - SPECIAL_CTRL_STS_AMDIX_STATE_); - buf |= SPECIAL_CTRL_STS_AMDIX_STATE_; - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS, buf); - } else if (mdix_ctrl == ETH_TP_MDI_AUTO) { - buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS); - buf &= ~SPECIAL_CTRL_STS_OVRRD_AMDIX_; - buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ | - SPECIAL_CTRL_STS_AMDIX_STATE_); - buf |= SPECIAL_CTRL_STS_AMDIX_ENABLE_; - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, - SPECIAL_CTRL_STS, buf); - } - pdata->mdix_ctrl = mdix_ctrl; -} - -static int smsc95xx_get_link_ksettings(struct net_device *net, - struct ethtool_link_ksettings *cmd) -{ - struct usbnet *dev = netdev_priv(net); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - int retval; - - retval = usbnet_get_link_ksettings(net, cmd); - - cmd->base.eth_tp_mdix = pdata->mdix_ctrl; - cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; - - return retval; -} - -static int smsc95xx_set_link_ksettings(struct net_device *net, - const struct ethtool_link_ksettings *cmd) -{ - struct usbnet *dev = netdev_priv(net); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - int retval; - - if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl) - set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl); - - retval = usbnet_set_link_ksettings(net, cmd); - - return retval; + phy_read_status(net->phydev); + return net->phydev->link; } static const struct ethtool_ops smsc95xx_ethtool_ops = { - .get_link = usbnet_get_link, - .nway_reset = usbnet_nway_reset, + .get_link = smsc95xx_get_link, + .nway_reset = phy_ethtool_nway_reset, .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, @@ -884,30 +740,29 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_regs = smsc95xx_ethtool_getregs, .get_wol = smsc95xx_ethtool_get_wol, .set_wol = smsc95xx_ethtool_set_wol, - .get_link_ksettings = smsc95xx_get_link_ksettings, - .set_link_ksettings = smsc95xx_set_link_ksettings, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_ts_info = ethtool_op_get_ts_info, }; static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { - struct usbnet *dev = netdev_priv(netdev); - if (!netif_running(netdev)) return -EINVAL; - return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + return phy_mii_ioctl(netdev->phydev, rq, cmd); } static void smsc95xx_init_mac_address(struct usbnet *dev) { - const u8 *mac_addr; - /* maybe the boot loader passed the MAC address in devicetree */ - mac_addr = of_get_mac_address(dev->udev->dev.of_node); - if (!IS_ERR(mac_addr)) { - ether_addr_copy(dev->net->dev_addr, mac_addr); - return; + if (!eth_platform_get_mac_address(&dev->udev->dev, + dev->net->dev_addr)) { + if (is_valid_ether_addr(dev->net->dev_addr)) { + /* device tree values are valid so use them */ + netif_dbg(dev, ifup, dev->net, "MAC address read from the device tree\n"); + return; + } } /* try reading mac address from EEPROM */ @@ -942,7 +797,7 @@ static int smsc95xx_set_mac_address(struct usbnet *dev) /* starts the TX path */ static int smsc95xx_start_tx_path(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; unsigned long flags; int ret; @@ -962,7 +817,7 @@ static int smsc95xx_start_tx_path(struct usbnet *dev) /* Starts the Receive path */ static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; unsigned long flags; spin_lock_irqsave(&pdata->mac_cr_lock, flags); @@ -972,54 +827,9 @@ static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm); } -static int smsc95xx_phy_initialize(struct usbnet *dev) -{ - int bmcr, ret, timeout = 0; - - /* Initialize MII structure */ - dev->mii.dev = dev->net; - dev->mii.mdio_read = smsc95xx_mdio_read; - dev->mii.mdio_write = smsc95xx_mdio_write; - dev->mii.phy_id_mask = 0x1f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID; - - /* reset phy and wait for reset to complete */ - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); - - do { - msleep(10); - bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); - timeout++; - } while ((bmcr & BMCR_RESET) && (timeout < 100)); - - if (timeout >= 100) { - netdev_warn(dev->net, "timeout on PHY Reset"); - return -EIO; - } - - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | - ADVERTISE_PAUSE_ASYM); - - /* read to clear */ - ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); - if (ret < 0) { - netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n"); - return ret; - } - - smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, - PHY_INT_MASK_DEFAULT_); - mii_nway_restart(&dev->mii); - - netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); - return 0; -} - static int smsc95xx_reset(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; u32 read_buf, write_buf, burst_cap; int ret = 0, timeout; @@ -1198,12 +1008,6 @@ static int smsc95xx_reset(struct usbnet *dev) smsc95xx_set_multicast(dev->net); - ret = smsc95xx_phy_initialize(dev); - if (ret < 0) { - netdev_warn(dev->net, "Failed to init PHY\n"); - return ret; - } - ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); if (ret < 0) return ret; @@ -1247,7 +1051,8 @@ static const struct net_device_ops smsc95xx_netdev_ops = { static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) { - struct smsc95xx_priv *pdata = NULL; + struct smsc95xx_priv *pdata; + bool is_internal_phy; u32 val; int ret; @@ -1259,13 +1064,12 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) return ret; } - dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv), - GFP_KERNEL); - - pdata = (struct smsc95xx_priv *)(dev->data[0]); + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; + dev->driver_priv = pdata; + spin_lock_init(&pdata->mac_cr_lock); /* LAN95xx devices do not alter the computed checksum of 0 to 0xffff. @@ -1290,15 +1094,50 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) if (ret) goto free_pdata; + pdata->mdiobus = mdiobus_alloc(); + if (!pdata->mdiobus) { + ret = -ENOMEM; + goto free_pdata; + } + + ret = smsc95xx_read_reg(dev, HW_CFG, &val); + if (ret < 0) + goto free_mdio; + + is_internal_phy = !(val & HW_CFG_PSEL_); + if (is_internal_phy) + pdata->mdiobus->phy_mask = ~(1u << SMSC95XX_INTERNAL_PHY_ID); + + pdata->mdiobus->priv = dev; + pdata->mdiobus->read = smsc95xx_mdiobus_read; + pdata->mdiobus->write = smsc95xx_mdiobus_write; + pdata->mdiobus->name = "smsc95xx-mdiobus"; + pdata->mdiobus->parent = &dev->udev->dev; + + snprintf(pdata->mdiobus->id, ARRAY_SIZE(pdata->mdiobus->id), + "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); + + ret = mdiobus_register(pdata->mdiobus); + if (ret) { + netdev_err(dev->net, "Could not register MDIO bus\n"); + goto free_mdio; + } + + pdata->phydev = phy_find_first(pdata->mdiobus); + if (!pdata->phydev) { + netdev_err(dev->net, "no PHY found\n"); + ret = -ENODEV; + goto unregister_mdio; + } + + pdata->phydev->is_internal = is_internal_phy; + /* detect device revision as different features may be available */ ret = smsc95xx_read_reg(dev, ID_REV, &val); if (ret < 0) - goto free_pdata; + goto unregister_mdio; val >>= 16; - pdata->chip_id = val; - pdata->mdix_ctrl = get_mdix_status(dev->net); - if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_)) pdata->features = (FEATURE_8_WAKEUP_FILTERS | @@ -1314,12 +1153,13 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->min_mtu = ETH_MIN_MTU; dev->net->max_mtu = ETH_DATA_LEN; dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + return 0; - pdata->dev = dev; - INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier); - schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); +unregister_mdio: + mdiobus_unregister(pdata->mdiobus); - return 0; +free_mdio: + mdiobus_free(pdata->mdiobus); free_pdata: kfree(pdata); @@ -1328,15 +1168,47 @@ free_pdata: static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - - if (pdata) { - cancel_delayed_work_sync(&pdata->carrier_check); - netif_dbg(dev, ifdown, dev->net, "free pdata\n"); - kfree(pdata); - pdata = NULL; - dev->data[0] = 0; + struct smsc95xx_priv *pdata = dev->driver_priv; + + mdiobus_unregister(pdata->mdiobus); + mdiobus_free(pdata->mdiobus); + netif_dbg(dev, ifdown, dev->net, "free pdata\n"); + kfree(pdata); +} + +static void smsc95xx_handle_link_change(struct net_device *net) +{ + phy_print_status(net->phydev); +} + +static int smsc95xx_start_phy(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = dev->driver_priv; + struct net_device *net = dev->net; + int ret; + + ret = smsc95xx_reset(dev); + if (ret < 0) + return ret; + + ret = phy_connect_direct(net, pdata->phydev, + &smsc95xx_handle_link_change, + PHY_INTERFACE_MODE_MII); + if (ret) { + netdev_err(net, "can't attach PHY to %s\n", pdata->mdiobus->id); + return ret; } + + phy_attached_info(net->phydev); + phy_start(net->phydev); + return 0; +} + +static int smsc95xx_disconnect_phy(struct usbnet *dev) +{ + phy_stop(dev->net->phydev); + phy_disconnect(dev->net->phydev); + return 0; } static u32 smsc_crc(const u8 *buffer, size_t len, int filter) @@ -1347,39 +1219,37 @@ static u32 smsc_crc(const u8 *buffer, size_t len, int filter) static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) { - struct mii_if_info *mii = &dev->mii; int ret; netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); /* read to clear */ - ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); + ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_SRC); if (ret < 0) return ret; /* enable interrupt source */ - ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); + ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_MASK); if (ret < 0) return ret; ret |= mask; - smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); + smsc95xx_mdio_write_nopm(dev, PHY_INT_MASK, ret); return 0; } static int smsc95xx_link_ok_nopm(struct usbnet *dev) { - struct mii_if_info *mii = &dev->mii; int ret; /* first, a dummy read, needed to latch some MII phys */ - ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); + ret = smsc95xx_mdio_read_nopm(dev, MII_BMSR); if (ret < 0) return ret; - ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); + ret = smsc95xx_mdio_read_nopm(dev, MII_BMSR); if (ret < 0) return ret; @@ -1388,7 +1258,7 @@ static int smsc95xx_link_ok_nopm(struct usbnet *dev) static int smsc95xx_enter_suspend0(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; u32 val; int ret; @@ -1427,8 +1297,7 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev) static int smsc95xx_enter_suspend1(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - struct mii_if_info *mii = &dev->mii; + struct smsc95xx_priv *pdata = dev->driver_priv; u32 val; int ret; @@ -1436,17 +1305,17 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev) * compatibility with non-standard link partners */ if (pdata->features & FEATURE_PHY_NLP_CROSSOVER) - smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_EDPD_CONFIG, - PHY_EDPD_CONFIG_DEFAULT); + smsc95xx_mdio_write_nopm(dev, PHY_EDPD_CONFIG, + PHY_EDPD_CONFIG_DEFAULT); /* enable energy detect power-down mode */ - ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS); + ret = smsc95xx_mdio_read_nopm(dev, PHY_MODE_CTRL_STS); if (ret < 0) return ret; ret |= MODE_CTRL_STS_EDPWRDOWN_; - smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS, ret); + smsc95xx_mdio_write_nopm(dev, PHY_MODE_CTRL_STS, ret); /* enter SUSPEND1 mode */ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); @@ -1475,7 +1344,7 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev) static int smsc95xx_enter_suspend2(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; u32 val; int ret; @@ -1497,7 +1366,7 @@ static int smsc95xx_enter_suspend2(struct usbnet *dev) static int smsc95xx_enter_suspend3(struct usbnet *dev) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; u32 val; int ret; @@ -1536,7 +1405,7 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev) static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; int ret; if (!netif_running(dev->net)) { @@ -1584,7 +1453,7 @@ static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up) static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; u32 val, link_up; int ret; @@ -1594,8 +1463,6 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) return ret; } - cancel_delayed_work_sync(&pdata->carrier_check); - if (pdata->suspend_flags) { netdev_warn(dev->net, "error during last resume\n"); pdata->suspend_flags = 0; @@ -1839,10 +1706,6 @@ done: if (ret && PMSG_IS_AUTO(message)) usbnet_resume(intf); - if (ret) - schedule_delayed_work(&pdata->carrier_check, - CARRIER_CHECK_DELAY); - return ret; } @@ -1855,14 +1718,13 @@ static int smsc95xx_resume(struct usb_interface *intf) u32 val; BUG_ON(!dev); - pdata = (struct smsc95xx_priv *)(dev->data[0]); + pdata = dev->driver_priv; suspend_flags = pdata->suspend_flags; netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); /* do this first to ensure it's cleared even in error case */ pdata->suspend_flags = 0; - schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); if (suspend_flags & SUSPEND_ALLMODES) { /* clear wake-up sources */ @@ -1893,6 +1755,7 @@ static int smsc95xx_resume(struct usb_interface *intf) if (ret < 0) netdev_warn(dev->net, "usbnet_resume error\n"); + phy_init_hw(pdata->phydev); return ret; } @@ -2075,7 +1938,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, static int smsc95xx_manage_power(struct usbnet *dev, int on) { - struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct smsc95xx_priv *pdata = dev->driver_priv; dev->intf->needs_remote_wakeup = on; @@ -2098,7 +1961,8 @@ static const struct driver_info smsc95xx_info = { .bind = smsc95xx_bind, .unbind = smsc95xx_unbind, .link_reset = smsc95xx_link_reset, - .reset = smsc95xx_reset, + .reset = smsc95xx_start_phy, + .stop = smsc95xx_disconnect_phy, .rx_fixup = smsc95xx_rx_fixup, .tx_fixup = smsc95xx_tx_fixup, .status = smsc95xx_status, |