summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/fec_main.c
diff options
context:
space:
mode:
authorFugang Duan <b38611@freescale.com>2015-05-20 18:36:19 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-23 16:42:27 +0300
commit9bca93774800d97f859d0a7f25aff85aaf86b4a0 (patch)
tree82444511a547b368f21f7927ce69f42ea25b1e80 /drivers/net/ethernet/freescale/fec_main.c
parent9389c4b97032a4af895de12c6c96ea73d837930d (diff)
MLK-10939-01 net: fec: add stop mode support for dts register set
The current driver support stop mode by calling machine api. The patch add dts support to set gpr register for stop request. After magic pattern comming during system suspend status, system will be waked up, and irq handler will be running, there have enet register access. Since all clocks are disabled in suspend, and clocks are enabled after resume function. But irq handler run before resume function. For imx7d chip, access register need some clocks enabled, otherwise system hang. So the patch also disable wake up irq in the suspend, after resume back enable the irq, which can avoid system hang issue. Signed-off-by: Fugang Duan <B38611@freescale.com> (cherry pick and merge from commit: 8da4f80af0913781a4f9d50917c1dd66180e519d)
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c94
1 files changed, 78 insertions, 16 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index ea7a4b967df4..6f85e766ddc3 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -61,6 +61,8 @@
#include <linux/pinctrl/consumer.h>
#include <linux/prefetch.h>
#include <soc/imx/cpuidle.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <asm/cacheflush.h>
@@ -1087,11 +1089,30 @@ fec_restart(struct net_device *ndev)
}
+static int fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
+{
+ struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
+
+ if (fep->gpr.gpr) {
+ if (enabled)
+ regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr,
+ 1 << fep->gpr.req_bit,
+ 1 << fep->gpr.req_bit);
+ else
+ regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr,
+ 1 << fep->gpr.req_bit,
+ 0);
+ } else if (pdata && pdata->sleep_mode_enable) {
+ pdata->sleep_mode_enable(enabled);
+ }
+
+ return 0;
+}
+
static void
fec_stop(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
u32 val;
@@ -1121,8 +1142,7 @@ fec_stop(struct net_device *ndev)
val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);
- if (pdata && pdata->sleep_mode_enable)
- pdata->sleep_mode_enable(true);
+ fec_enet_stop_mode(fep, true);
}
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
@@ -2590,15 +2610,10 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
return -EINVAL;
device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
- if (device_may_wakeup(&ndev->dev)) {
+ if (device_may_wakeup(&ndev->dev))
fep->wol_flag |= FEC_WOL_FLAG_ENABLE;
- if (fep->irq[0] > 0)
- enable_irq_wake(fep->irq[0]);
- } else {
+ else
fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
- if (fep->irq[0] > 0)
- disable_irq_wake(fep->irq[0]);
- }
return 0;
}
@@ -3380,6 +3395,41 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
}
+static void fec_enet_of_parse_stop_mode(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct fec_enet_private *fep = netdev_priv(dev);
+ struct device_node *node;
+ phandle phandle;
+ u32 out_val[3];
+ int ret;
+
+ ret = of_property_read_u32_array(np, "stop-mode", out_val, 3);
+ if (ret) {
+ dev_dbg(&pdev->dev, "no stop-mode property\n");
+ return;
+ }
+
+ phandle = *out_val;
+ node = of_find_node_by_phandle(phandle);
+ if (!node) {
+ dev_dbg(&pdev->dev, "could not find gpr node by phandle\n");
+ return;
+ }
+
+ fep->gpr.gpr = syscon_node_to_regmap(node);
+ if (IS_ERR(fep->gpr.gpr)) {
+ dev_dbg(&pdev->dev, "could not find gpr regmap\n");
+ return;
+ }
+
+ of_node_put(node);
+
+ fep->gpr.req_gpr = out_val[1];
+ fep->gpr.req_bit = out_val[2];
+}
+
static int
fec_probe(struct platform_device *pdev)
{
@@ -3442,6 +3492,8 @@ fec_probe(struct platform_device *pdev)
!of_property_read_bool(np, "fsl,err006687-workaround-present"))
fep->quirks |= FEC_QUIRK_ERR006687;
+ fec_enet_of_parse_stop_mode(pdev);
+
if (of_get_property(np, "fsl,magic-packet", NULL))
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
@@ -3556,6 +3608,12 @@ fec_probe(struct platform_device *pdev)
fep->irq[i] = irq;
}
+ ret = of_property_read_u32(np, "fsl,wakeup_irq", &irq);
+ if (!ret && irq < FEC_IRQ_NUM)
+ fep->wake_irq = fep->irq[irq];
+ else
+ fep->wake_irq = fep->irq[0];
+
init_completion(&fep->mdio_done);
ret = fec_enet_mii_init(pdev);
if (ret)
@@ -3647,10 +3705,13 @@ static int __maybe_unused fec_suspend(struct device *dev)
netif_device_detach(ndev);
netif_tx_unlock_bh(ndev);
fec_stop(ndev);
- fec_enet_clk_enable(ndev, false);
- if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
+ if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
- pinctrl_pm_select_sleep_state(&fep->pdev->dev);
+ } else {
+ disable_irq(fep->wake_irq);
+ enable_irq_wake(fep->wake_irq);
+ }
+ fec_enet_clk_enable(ndev, false);
} else if (fep->mii_bus_share && !ndev->phydev) {
fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
@@ -3673,7 +3734,6 @@ static int __maybe_unused fec_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct fec_enet_private *fep = netdev_priv(ndev);
- struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
int ret;
int val;
@@ -3690,9 +3750,11 @@ static int __maybe_unused fec_resume(struct device *dev)
rtnl_unlock();
goto failed_clk;
}
+
if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
- if (pdata && pdata->sleep_mode_enable)
- pdata->sleep_mode_enable(false);
+ disable_irq_wake(fep->wake_irq);
+ fec_enet_stop_mode(fep, false);
+ enable_irq(fep->wake_irq);
val = readl(fep->hwp + FEC_ECNTRL);
val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);