diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 76659c1c525a..4402eedb3d1a 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -168,7 +168,7 @@ config USB_NET_AX8817X tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters" depends on USB_USBNET select CRC32 - select PHYLIB + select PHYLINK select AX88796B_PHY imply NET_SELFTESTS default y diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 21c1ca275cc4..74162190bccc 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -27,6 +27,7 @@ #include #include #include +#include #define DRIVER_VERSION "22-Dec-2011" #define DRIVER_NAME "asix" @@ -185,6 +186,8 @@ struct asix_common_private { struct mii_bus *mdio; struct phy_device *phydev; struct phy_device *phydev_int; + struct phylink *phylink; + struct phylink_config phylink_config; u16 phy_addr; bool embd_phy; u8 chipcode; diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 5b5eb630c4b7..caa1bed1fe34 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -343,7 +343,7 @@ static int ax88772_reset(struct usbnet *dev) if (ret < 0) goto out; - phy_start(priv->phydev); + phylink_start(priv->phylink); return 0; @@ -590,8 +590,11 @@ static void ax88772_suspend(struct usbnet *dev) struct asix_common_private *priv = dev->driver_priv; u16 medium; - if (netif_running(dev->net)) - phy_stop(priv->phydev); + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_suspend(priv->phylink, false); + rtnl_unlock(); + } /* Stop MAC operation */ medium = asix_read_medium_status(dev, 1); @@ -622,8 +625,11 @@ static void ax88772_resume(struct usbnet *dev) if (!priv->reset(dev, 1)) break; - if (netif_running(dev->net)) - phy_start(priv->phydev); + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_resume(priv->phylink); + rtnl_unlock(); + } } static int asix_resume(struct usb_interface *intf) @@ -667,8 +673,7 @@ static int ax88772_init_phy(struct usbnet *dev) return -ENODEV; } - ret = phy_connect_direct(dev->net, priv->phydev, &asix_adjust_link, - PHY_INTERFACE_MODE_INTERNAL); + ret = phylink_connect_phy(priv->phylink, priv->phydev); if (ret) { netdev_err(dev->net, "Could not connect PHY\n"); return ret; @@ -688,6 +693,9 @@ static int ax88772_init_phy(struct usbnet *dev) */ priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR); if (!priv->phydev_int) { + rtnl_lock(); + phylink_disconnect_phy(priv->phylink); + rtnl_unlock(); netdev_err(dev->net, "Could not find internal PHY\n"); return -ENODEV; } @@ -698,6 +706,89 @@ static int ax88772_init_phy(struct usbnet *dev) return 0; } +static void ax88772_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + /* Nothing to do */ +} + +static void ax88772_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); + + asix_write_medium_mode(dev, 0, 0); + usbnet_link_change(dev, false, false); +} + +static void ax88772_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); + u16 m = AX_MEDIUM_AC | AX_MEDIUM_RE; + + m |= duplex ? AX_MEDIUM_FD : 0; + + switch (speed) { + case SPEED_100: + m |= AX_MEDIUM_PS; + break; + case SPEED_10: + break; + default: + return; + } + + if (tx_pause) + m |= AX_MEDIUM_TFC; + + if (rx_pause) + m |= AX_MEDIUM_RFC; + + asix_write_medium_mode(dev, m, 0); + usbnet_link_change(dev, true, false); +} + +static const struct phylink_mac_ops ax88772_phylink_mac_ops = { + .validate = phylink_generic_validate, + .mac_config = ax88772_mac_config, + .mac_link_down = ax88772_mac_link_down, + .mac_link_up = ax88772_mac_link_up, +}; + +static int ax88772_phylink_setup(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + phy_interface_t phy_if_mode; + struct phylink *phylink; + + priv->phylink_config.dev = &dev->net->dev; + priv->phylink_config.type = PHYLINK_NETDEV; + priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | + MAC_10 | MAC_100; + + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RMII, + priv->phylink_config.supported_interfaces); + + if (priv->embd_phy) + phy_if_mode = PHY_INTERFACE_MODE_INTERNAL; + else + phy_if_mode = PHY_INTERFACE_MODE_RMII; + + phylink = phylink_create(&priv->phylink_config, dev->net->dev.fwnode, + phy_if_mode, &ax88772_phylink_mac_ops); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + + priv->phylink = phylink; + return 0; +} + static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) { struct asix_common_private *priv; @@ -788,14 +879,22 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) if (ret) return ret; - return ax88772_init_phy(dev); + ret = ax88772_phylink_setup(dev); + if (ret) + return ret; + + ret = ax88772_init_phy(dev); + if (ret) + phylink_destroy(priv->phylink); + + return ret; } static int ax88772_stop(struct usbnet *dev) { struct asix_common_private *priv = dev->driver_priv; - phy_stop(priv->phydev); + phylink_stop(priv->phylink); return 0; } @@ -804,7 +903,10 @@ static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) { struct asix_common_private *priv = dev->driver_priv; - phy_disconnect(priv->phydev); + rtnl_lock(); + phylink_disconnect_phy(priv->phylink); + rtnl_unlock(); + phylink_destroy(priv->phylink); asix_rx_fixup_common_free(dev->driver_priv); }