From 0ba78b4a4989de294dd0f17e243f1f91f07d97b3 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 29 Nov 2018 17:06:31 +0900 Subject: net: ethernet: ave: Add suspend/resume support This patch introduces suspend and resume functions to ave driver. Signed-off-by: Kunihiko Hayashi Signed-off-by: David S. Miller --- drivers/net/ethernet/socionext/sni_ave.c | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index 9e7391faa1dc..0933c13cc82a 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -1734,6 +1734,49 @@ static int ave_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int ave_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct ave_private *priv = netdev_priv(ndev); + int ret = 0; + + if (netif_running(ndev)) { + ret = ave_stop(ndev); + netif_device_detach(ndev); + } + + return ret; +} + +static int ave_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct ave_private *priv = netdev_priv(ndev); + int ret = 0; + + ave_global_reset(ndev); + + if (ndev->phydev) { + ret = phy_resume(ndev->phydev); + if (ret) + return ret; + } + + if (netif_running(ndev)) { + ret = ave_open(ndev); + netif_device_attach(ndev); + } + + return ret; +} + +static SIMPLE_DEV_PM_OPS(ave_pm_ops, ave_suspend, ave_resume); +#define AVE_PM_OPS (&ave_pm_ops) +#else +#define AVE_PM_OPS NULL +#endif + static int ave_pro4_get_pinmode(struct ave_private *priv, phy_interface_t phy_mode, u32 arg) { @@ -1908,6 +1951,7 @@ static struct platform_driver ave_driver = { .remove = ave_remove, .driver = { .name = "ave", + .pm = AVE_PM_OPS, .of_match_table = of_ave_match, }, }; -- cgit v1.2.3-59-g8ed1b From 7200f2e3c9e267d29e2bfa075794339032e0b98e Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 29 Nov 2018 17:06:32 +0900 Subject: net: ethernet: ave: Set initial wol state to disabled If wol state of phy hardware is enabled after reset, phy_ethtool_get_wol() returns that wol.wolopts is true. However, since net_device.wol_enabled is zero and this doesn't apply wol state until calling ethtool_set_wol(), so mdio_bus_phy_may_suspend() returns true, that is, it's in a state where phy can suspend even though wol state is enabled. In this inconsistency, phy_suspend() returns -EBUSY, and at last, suspend sequence fails with the following message: dpm_run_callback(): mdio_bus_phy_suspend+0x0/0x58 returns -16 PM: Device 65000000.ethernet-ffffffff:01 failed to suspend: error -16 PM: Some devices failed to suspend, or early wake event detected In order to fix the above issue, this patch forces to set initial wol state to disabled as default. Signed-off-by: Kunihiko Hayashi Signed-off-by: David S. Miller --- drivers/net/ethernet/socionext/sni_ave.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index 0933c13cc82a..96b80309e36d 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -1208,9 +1208,13 @@ static int ave_init(struct net_device *ndev) priv->phydev = phydev; - phy_ethtool_get_wol(phydev, &wol); + ave_ethtool_get_wol(ndev, &wol); device_set_wakeup_capable(&ndev->dev, !!wol.supported); + /* set wol initial state disabled */ + wol.wolopts = 0; + ave_ethtool_set_wol(ndev, &wol); + if (!phy_interface_is_rgmii(phydev)) phy_set_max_speed(phydev, SPEED_100); -- cgit v1.2.3-59-g8ed1b From 8d1283b1d6afe9c67ad8d8053f6202ef6fcb038a Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 29 Nov 2018 17:06:33 +0900 Subject: net: ethernet: ave: Preserve wol state in suspend/resume sequence Since the wol state forces to be initialized after reset, the state should be preserved in suspend/resume sequence. Signed-off-by: Kunihiko Hayashi Signed-off-by: David S. Miller --- drivers/net/ethernet/socionext/sni_ave.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index 96b80309e36d..1f9ef68d91ee 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -261,6 +261,7 @@ struct ave_private { struct regmap *regmap; unsigned int pinmode_mask; unsigned int pinmode_val; + u32 wolopts; /* stats */ struct ave_stats stats_rx; @@ -1741,6 +1742,7 @@ static int ave_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int ave_suspend(struct device *dev) { + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct net_device *ndev = dev_get_drvdata(dev); struct ave_private *priv = netdev_priv(ndev); int ret = 0; @@ -1750,17 +1752,25 @@ static int ave_suspend(struct device *dev) netif_device_detach(ndev); } + ave_ethtool_get_wol(ndev, &wol); + priv->wolopts = wol.wolopts; + return ret; } static int ave_resume(struct device *dev) { + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct net_device *ndev = dev_get_drvdata(dev); struct ave_private *priv = netdev_priv(ndev); int ret = 0; ave_global_reset(ndev); + ave_ethtool_get_wol(ndev, &wol); + wol.wolopts = priv->wolopts; + ave_ethtool_set_wol(ndev, &wol); + if (ndev->phydev) { ret = phy_resume(ndev->phydev); if (ret) -- cgit v1.2.3-59-g8ed1b