The suspend and resume functions are called via the usb_driver structure, for which the driver has only a single instance. Add wrapper functions for the different implementations for the AX88179 and AX179A architecutres in ax88179_lib which calls the 2 implementations in ax88179_178a and ax88179a_devices, respectively. Signed-off-by: Birger Koblitz --- drivers/net/usb/ax88179_178a.c | 9 ++-- drivers/net/usb/ax88179_lib.c | 19 ++++++++ drivers/net/usb/ax88179_lib.h | 4 ++ drivers/net/usb/ax88179a_devices.c | 95 +++++++++++++++++++++++++++++++++++++- 4 files changed, 123 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 6bd9704046009d7aad5ee6ddddc18faab5b030dc..8c22c2517cc08249c4e89625f142a76d4edc7d6a 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -684,6 +684,9 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->driver_priv = ax179_data; + ax179_data->resume = ax88179_resume; + ax179_data->suspend = ax88179_suspend; + dev->net->netdev_ops = &ax88179_netdev_ops; dev->net->ethtool_ops = &ax88179_ethtool_ops; dev->net->needed_headroom = 8; @@ -1340,9 +1343,9 @@ static struct usb_driver ax88179_178a_driver = { .name = "ax88179_178a", .id_table = products, .probe = usbnet_probe, - .suspend = ax88179_suspend, - .resume = ax88179_resume, - .reset_resume = ax88179_resume, + .suspend = ax88179_suspend_wrapper, + .resume = ax88179_resume_wrapper, + .reset_resume = ax88179_resume_wrapper, .disconnect = ax88179_disconnect, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c index e3f01a9654f8399158aa67b688ee76dd93997939..d40b1d3238c86f9e4fb1e5996c239ab417544e7e 100644 --- a/drivers/net/usb/ax88179_lib.c +++ b/drivers/net/usb/ax88179_lib.c @@ -457,3 +457,22 @@ int ax88179_set_mac_addr(struct net_device *net, void *p) return 0; } +int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *priv; + + priv = dev->driver_priv; + + return priv->suspend(intf, message); +} + +int ax88179_resume_wrapper(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *priv; + + priv = dev->driver_priv; + return priv->resume(intf); +} + diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h index 32989a6ac79c08919d67c2a04b2a5e2e02170cd0..d0150d1db12e5dc7812cd21515792fb5399a3a18 100644 --- a/drivers/net/usb/ax88179_lib.h +++ b/drivers/net/usb/ax88179_lib.h @@ -315,6 +315,8 @@ struct ax88179_data { struct phy_device *phydev; struct phylink *phylink; struct phylink_config phylink_config; + int (*resume)(struct usb_interface *intf); + int (*suspend)(struct usb_interface *intf, pm_message_t message); }; struct ax88179_int_data { @@ -345,6 +347,8 @@ int ax88179_set_features(struct net_device *net, netdev_features_t features); void ax88179_get_mac_addr(struct usbnet *dev); int ax88179_change_mtu(struct net_device *net, int new_mtu); int ax88179_set_mac_addr(struct net_device *net, void *p); +int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message); +int ax88179_resume_wrapper(struct usb_interface *intf); extern const struct driver_info ax88179a_info; extern const struct driver_info ax88772d_info; diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c index 9667cdbda9fa5e8da5eddc610f902b7c9bb66cfc..24ff28acc9366385c2e7c0f7e412635786995568 100644 --- a/drivers/net/usb/ax88179a_devices.c +++ b/drivers/net/usb/ax88179a_devices.c @@ -5,6 +5,8 @@ #include #include "ax88179_lib.h" +static int ax88179a_reset(struct usbnet *dev); + #define AX88279_EEPROM_LEN 0x4000 #define AX88179A_EEPROM_LEN (32 * 20) @@ -107,6 +109,53 @@ static int ax88179_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum, return ax179a_write_mmd(dev, devnum, regnum, val); } +static int ax88179a_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *priv; + u16 tmp16; + u8 tmp8; + + priv = dev->driver_priv; + ax88179_set_pm_mode(dev, true); + + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_suspend(priv->phylink, !!priv->wolopts); + rtnl_unlock(); + } + + /* Enable WoL */ + if (priv->wolopts) { + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8); + if (priv->wolopts & WAKE_PHY) + tmp8 |= AX_MONITOR_MODE_RWLC; + if (priv->wolopts & WAKE_MAGIC) + tmp8 |= AX_MONITOR_MODE_RWMP; + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8); + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); + tmp16 |= AX_MEDIUM_RECEIVE_EN; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16); + + if (priv->chip_version == AX_VERSION_AX88279) + ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, + EPHY_LOW_POWER_EN | S5_WOL_EN + | S5_WOL_LOW_POWER | 0x8000, 0, NULL); + else + ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 0, + EPHY_LOW_POWER_EN, 0, NULL); + + } else if (priv->chip_version == AX_VERSION_AX88279) { + ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, 0x8000, 0, NULL); + } + + usbnet_suspend(intf, message); + ax88179_set_pm_mode(dev, false); + return 0; +} + static int ax88179a_auto_detach(struct usbnet *dev) { u16 tmp16; @@ -116,6 +165,43 @@ static int ax88179a_auto_detach(struct usbnet *dev) return 0; } +static int ax88179a_resume(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct ax88179_data *ax179_data; + u8 reg8; + + ax179_data = dev->driver_priv; + ax88179_set_pm_mode(dev, true); + + ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8); + if (!(reg8 & AX_PHY_POWER)) { + reg8 = AX_PHY_POWER; + ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8); + msleep(250); + } + ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL); + + /* Now, that AX_FW_MODE_179A is enabled, the PHY needs a power-cycle. + * PHY-power is re-enabled in ax88179_reset() + */ + reg8 = 0; + ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8); + msleep(250); + + if (netif_running(dev->net)) { + rtnl_lock(); + phylink_resume(ax179_data->phylink); + rtnl_unlock(); + } + + ax88179a_reset(dev); + + ax88179_set_pm_mode(dev, false); + + return usbnet_resume(intf); +} + static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts) { struct ax88179_data *ax179_data = dev->driver_priv; @@ -695,6 +781,9 @@ static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf) ax179_data->eeprom_wen = 0; } + ax179_data->resume = ax88179a_resume; + ax179_data->suspend = ax88179a_suspend; + dev->net->netdev_ops = &ax88179a_netdev_ops; dev->net->ethtool_ops = &ax88179a_ethtool_ops; dev->net->needed_headroom = 8; @@ -1071,7 +1160,11 @@ static int ax88179a_reset(struct usbnet *dev) ax179_data->eee_enabled = 0; ax179_data->eee_active = 0; - phylink_start(ax179_data->phylink); + /* ax88179a_reset() may also be called from resume context, phylink + * is already started, then. + */ + if (!ax179_data->in_pm) + phylink_start(ax179_data->phylink); usbnet_link_change(dev, 0, 0); -- 2.47.3