MLK-10939-01 net: fec: add stop mode support for dts register set
authorFugang Duan <b38611@freescale.com>
Wed, 20 May 2015 10:36:19 +0000 (18:36 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:46:49 +0000 (14:46 -0500)
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)

Documentation/devicetree/bindings/net/fsl-fec.txt
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c

index a1e3693..775ec4e 100644 (file)
@@ -30,6 +30,9 @@ Optional properties:
 - fsl,err006687-workaround-present: If present indicates that the system has
   the hardware workaround for ERR006687 applied and does not need a software
   workaround.
+- fsl,wakeup_irq : The property define the wakeup irq index in enet irq source.
+- stop-mode : If present, indicates soc need to set gpr bit to request stop
+  mode.
 
 Optional subnodes:
 - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
index 4f3f5e9..c9a16ca 100644 (file)
@@ -471,6 +471,12 @@ struct bufdesc_prop {
        unsigned char dsize_log2;
 };
 
+struct fec_enet_stop_mode {
+       struct regmap *gpr;
+       u8 req_gpr;
+       u8 req_bit;
+};
+
 struct fec_enet_priv_tx_q {
        struct bufdesc_prop bd;
        unsigned char *tx_bounce[TX_RING_SIZE];
@@ -545,6 +551,7 @@ struct fec_enet_private {
        bool    bufdesc_ex;
        int     pause_flag;
        int     wol_flag;
+       int     wake_irq;
        u32     quirks;
 
        struct  napi_struct napi;
@@ -589,6 +596,8 @@ struct fec_enet_private {
        unsigned int next_counter;
 
        u64 ethtool_stats[0];
+
+       struct fec_enet_stop_mode gpr;
 };
 
 void fec_ptp_init(struct platform_device *pdev);
index 335d801..48fd750 100644 (file)
@@ -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);
 
@@ -2586,15 +2606,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;
 }
@@ -3371,6 +3386,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)
 {
@@ -3433,6 +3483,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;
 
@@ -3544,6 +3596,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)
@@ -3631,10 +3689,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);
@@ -3657,7 +3718,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;
 
@@ -3674,9 +3734,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);