MLK-11285-01 net: fec: handle WAIT mode issue for imx6qdl
authorShawn Guo <shawn.guo@freescale.com>
Mon, 15 Sep 2014 03:20:58 +0000 (11:20 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:46:48 +0000 (14:46 -0500)
This is a combination of commits 919d46e37e04 (ENGR00265935 net: fec:
add pm_qos to avoid cpu enter to wait mode) and 8a12c90c9974
(ENGR00313685-14 net: fec: check workaround for FEC_QUIRK_BUG_WAITMODE)
from imx_3.10.y branch.  It's added for imx_3.14.y branch to work around
imx6qdl issue ERR006687 (ENET: Only the ENET wake-up interrupt request
can wake the system from Wait mode).

Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
(cherry-pick and merge from commit: 4f406fae257cc7945a0e3a425213440bb12ba345)

drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c

index 5ea740b..a6a977b 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/clocksource.h>
 #include <linux/net_tstamp.h>
+#include <linux/pm_qos.h>
 #include <linux/ptp_clock_kernel.h>
 #include <linux/timecounter.h>
 
@@ -446,6 +447,12 @@ struct bufdesc_ex {
 #define FEC_QUIRK_HAS_COALESCE         (1 << 13)
 /* Interrupt doesn't wake CPU from deep idle */
 #define FEC_QUIRK_ERR006687            (1 << 14)
+/*
+ * i.MX6Q/DL ENET cannot wake up system in wait mode because ENET tx & rx
+ * interrupt signal don't connect to GPC. So use pm qos to avoid cpu enter
+ * to wait mode.
+ */
+#define FEC_QUIRK_BUG_WAITMODE         (1 << 15)
 
 struct bufdesc_prop {
        int qid;
@@ -553,6 +560,7 @@ struct fec_enet_private {
        int hwts_tx_en;
        struct delayed_work time_keep;
        struct regulator *reg_phy;
+       struct pm_qos_request pm_qos_req;
 
        unsigned int tx_align;
        unsigned int rx_align;
index 9170918..c0dc643 100644 (file)
@@ -102,7 +102,7 @@ static struct platform_device_id fec_devtype[] = {
                .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
                                FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
                                FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
-                               FEC_QUIRK_HAS_RACC,
+                               FEC_QUIRK_HAS_RACC | FEC_QUIRK_BUG_WAITMODE,
        }, {
                .name = "mvf600-fec",
                .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
@@ -2828,10 +2828,29 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
        return 0;
 }
 
+static inline bool fec_enet_irq_workaround(struct fec_enet_private *fep)
+{
+       struct device_node *np = fep->pdev->dev.of_node;
+       struct device_node *intr_node;
+
+       intr_node = of_parse_phandle(np, "interrupts-extended", 0);
+       if (intr_node && !strcmp(intr_node->name, "gpio")) {
+               /*
+                * If the interrupt controller is a GPIO node, it must have
+                * applied the workaround for WAIT mode bug.
+                */
+               return true;
+       }
+
+       return false;
+}
+
 static int
 fec_enet_open(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
        int ret;
 
        ret = pm_runtime_get_sync(&fep->pdev->dev);
@@ -2866,6 +2885,16 @@ fec_enet_open(struct net_device *ndev)
        phy_start(ndev->phydev);
        netif_tx_start_all_queues(ndev);
 
+       if ((id_entry->driver_data & FEC_QUIRK_BUG_WAITMODE) &&
+           !fec_enet_irq_workaround(fep))
+               pm_qos_add_request(&fep->pm_qos_req,
+                                  PM_QOS_CPU_DMA_LATENCY,
+                                  0);
+       else
+               pm_qos_add_request(&fep->pm_qos_req,
+                                  PM_QOS_CPU_DMA_LATENCY,
+                                  PM_QOS_DEFAULT_VALUE);
+
        device_set_wakeup_enable(&ndev->dev, fep->wol_flag &
                                 FEC_WOL_FLAG_ENABLE);