diff options
author | Fugang Duan <b38611@freescale.com> | 2015-05-20 18:36:19 +0800 |
---|---|---|
committer | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-23 16:42:27 +0300 |
commit | 9bca93774800d97f859d0a7f25aff85aaf86b4a0 (patch) | |
tree | 82444511a547b368f21f7927ce69f42ea25b1e80 /drivers/net/ethernet/freescale/fec_main.c | |
parent | 9389c4b97032a4af895de12c6c96ea73d837930d (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.c | 94 |
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); |