From: "Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>
To: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>,
Simon Horman <horms+renesas@verge.net.au>,
netdev@vger.kernel.org, linux-renesas-soc@vger.kernel.org
Cc: "Geert Uytterhoeven" <geert@linux-m68k.org>,
"Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>
Subject: [PATCHv2 1/5] sh_eth: add generic wake-on-lan support via magic packet
Date: Mon, 12 Dec 2016 17:09:27 +0100 [thread overview]
Message-ID: <20161212160931.6478-2-niklas.soderlund+renesas@ragnatech.se> (raw)
In-Reply-To: <20161212160931.6478-1-niklas.soderlund+renesas@ragnatech.se>
Add generic functionality to support Wake-on-Lan using MagicPacket which
are supported by at least a few versions of sh_eth. Only add
functionality for WoL, no specific sh_eth version are marked to support
WoL yet.
WoL is enabled in the suspend callback by setting MagicPacket detection
and disabling all interrupts expect MagicPacket. In the resume path the
driver needs to reset the hardware to rearm the WoL logic, this prevents
the driver from simply restoring the registers and to take advantage of
that sh_eth was not suspended to reduce resume time. To reset the
hardware the driver close and reopens the device just like it would do
in a normal suspend/resume scenario without WoL enabled, but it both
close and open the device in the resume callback since the device needs
to be open for WoL to work.
One quirk needed for WoL is that the module clock needs to be prevented
from being switched off by Runtime PM. To keep the clock alive the
suspend callback need to call clk_enable() directly to increase the
usage count of the clock. Then when Runtime PM decreases the clock usage
count it won't reach 0 and be switched off.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
drivers/net/ethernet/renesas/sh_eth.c | 112 +++++++++++++++++++++++++++++++---
drivers/net/ethernet/renesas/sh_eth.h | 3 +
2 files changed, 107 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 05b0dc5..87640b9 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -2199,6 +2199,33 @@ static int sh_eth_set_ringparam(struct net_device *ndev,
return 0;
}
+static void sh_eth_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ wol->supported = 0;
+ wol->wolopts = 0;
+
+ if (mdp->cd->magic && mdp->clk) {
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = mdp->wol_enabled ? WAKE_MAGIC : 0;
+ }
+}
+
+static int sh_eth_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ if (!mdp->cd->magic || !mdp->clk || wol->wolopts & ~WAKE_MAGIC)
+ return -EOPNOTSUPP;
+
+ mdp->wol_enabled = !!(wol->wolopts & WAKE_MAGIC);
+
+ device_set_wakeup_enable(&mdp->pdev->dev, mdp->wol_enabled);
+
+ return 0;
+}
+
static const struct ethtool_ops sh_eth_ethtool_ops = {
.get_regs_len = sh_eth_get_regs_len,
.get_regs = sh_eth_get_regs,
@@ -2213,6 +2240,8 @@ static const struct ethtool_ops sh_eth_ethtool_ops = {
.set_ringparam = sh_eth_set_ringparam,
.get_link_ksettings = sh_eth_get_link_ksettings,
.set_link_ksettings = sh_eth_set_link_ksettings,
+ .get_wol = sh_eth_get_wol,
+ .set_wol = sh_eth_set_wol,
};
/* network device open function */
@@ -3017,6 +3046,11 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
goto out_release;
}
+ /* Get clock, if not found that's OK but Wake-On-Lan is unavailable */
+ mdp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mdp->clk))
+ mdp->clk = NULL;
+
ndev->base_addr = res->start;
spin_lock_init(&mdp->lock);
@@ -3111,6 +3145,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
if (ret)
goto out_napi_del;
+ if (mdp->cd->magic && mdp->clk)
+ device_set_wakeup_capable(&pdev->dev, 1);
+
/* print device information */
netdev_info(ndev, "Base address at 0x%x, %pM, IRQ %d.\n",
(u32)ndev->base_addr, ndev->dev_addr, ndev->irq);
@@ -3150,15 +3187,67 @@ static int sh_eth_drv_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
+static int sh_eth_wol_setup(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ /* Only allow ECI interrupts */
+ synchronize_irq(ndev->irq);
+ napi_disable(&mdp->napi);
+ sh_eth_write(ndev, DMAC_M_ECI, EESIPR);
+
+ /* Enable MagicPacket */
+ sh_eth_modify(ndev, ECMR, 0, ECMR_PMDE);
+
+ /* Increased clock usage so device won't be suspended */
+ clk_enable(mdp->clk);
+
+ return enable_irq_wake(ndev->irq);
+}
+
+static int sh_eth_wol_restore(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+ int ret;
+
+ napi_enable(&mdp->napi);
+
+ /* Disable MagicPacket */
+ sh_eth_modify(ndev, ECMR, ECMR_PMDE, 0);
+
+ /* The device need to be reset to restore MagicPacket logic
+ * for next wakeup. If we close and open the device it will
+ * both be reset and all registers restored. This is what
+ * happens during suspend and resume without WoL enabled.
+ */
+ ret = sh_eth_close(ndev);
+ if (ret < 0)
+ return ret;
+ ret = sh_eth_open(ndev);
+ if (ret < 0)
+ return ret;
+
+ /* Restore clock usage count */
+ clk_disable(mdp->clk);
+
+ return disable_irq_wake(ndev->irq);
+}
+
static int sh_eth_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
+ struct sh_eth_private *mdp = netdev_priv(ndev);
int ret = 0;
- if (netif_running(ndev)) {
- netif_device_detach(ndev);
+ if (!netif_running(ndev))
+ return 0;
+
+ netif_device_detach(ndev);
+
+ if (mdp->wol_enabled)
+ ret = sh_eth_wol_setup(ndev);
+ else
ret = sh_eth_close(ndev);
- }
return ret;
}
@@ -3166,14 +3255,21 @@ static int sh_eth_suspend(struct device *dev)
static int sh_eth_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
+ struct sh_eth_private *mdp = netdev_priv(ndev);
int ret = 0;
- if (netif_running(ndev)) {
+ if (!netif_running(ndev))
+ return 0;
+
+ if (mdp->wol_enabled)
+ ret = sh_eth_wol_restore(ndev);
+ else
ret = sh_eth_open(ndev);
- if (ret < 0)
- return ret;
- netif_device_attach(ndev);
- }
+
+ if (ret < 0)
+ return ret;
+
+ netif_device_attach(ndev);
return ret;
}
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index d050f37..4ceed00 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -493,6 +493,7 @@ struct sh_eth_cpu_data {
unsigned shift_rd0:1; /* shift Rx descriptor word 0 right by 16 */
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
unsigned rtrate:1; /* EtherC has RTRATE register */
+ unsigned magic:1; /* EtherC has ECMR.PMDE and ECSR.MPD */
};
struct sh_eth_private {
@@ -501,6 +502,7 @@ struct sh_eth_private {
const u16 *reg_offset;
void __iomem *addr;
void __iomem *tsu_addr;
+ struct clk *clk;
u32 num_rx_ring;
u32 num_tx_ring;
dma_addr_t rx_desc_dma;
@@ -529,6 +531,7 @@ struct sh_eth_private {
unsigned no_ether_link:1;
unsigned ether_link_active_low:1;
unsigned is_opened:1;
+ unsigned wol_enabled:1;
};
static inline void sh_eth_soft_swap(char *src, int len)
--
2.10.2
next prev parent reply other threads:[~2016-12-12 16:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-12 16:09 [PATCHv2 0/5] sh_eth: add wake-on-lan support via magic packet Niklas Söderlund
2016-12-12 16:09 ` Niklas Söderlund [this message]
2016-12-13 13:03 ` [PATCHv2 1/5] sh_eth: add generic " Geert Uytterhoeven
2016-12-13 16:27 ` Niklas Söderlund
2016-12-17 21:50 ` Sergei Shtylyov
2016-12-19 16:41 ` Niklas Söderlund
2016-12-19 16:56 ` Sergei Shtylyov
2016-12-18 20:26 ` Sergei Shtylyov
2016-12-19 16:39 ` Niklas Söderlund
2016-12-19 17:11 ` Geert Uytterhoeven
2016-12-24 21:53 ` Sergei Shtylyov
2016-12-12 16:09 ` [PATCHv2 2/5] sh_eth: enable wake-on-lan for Gen2 devices Niklas Söderlund
2016-12-13 12:52 ` Geert Uytterhoeven
2016-12-14 13:37 ` Sergei Shtylyov
2016-12-17 21:52 ` Sergei Shtylyov
2016-12-12 16:09 ` [PATCHv2 3/5] sh_eth: enable wake-on-lan for r8a7740/armadillo Niklas Söderlund
2016-12-13 12:51 ` Geert Uytterhoeven
2016-12-12 16:09 ` [PATCHv2 4/5] sh_eth: enable wake-on-lan for sh7743 Niklas Söderlund
2016-12-13 10:59 ` Geert Uytterhoeven
2016-12-12 16:09 ` [PATCHv2 5/5] sh_eth: enable wake-on-lan for sh7763 Niklas Söderlund
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161212160931.6478-2-niklas.soderlund+renesas@ragnatech.se \
--to=niklas.soderlund+renesas@ragnatech.se \
--cc=geert@linux-m68k.org \
--cc=horms+renesas@verge.net.au \
--cc=linux-renesas-soc@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=sergei.shtylyov@cogentembedded.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).