MLK-20166 can: flexcan: fix CAN can't wakeup system during suspend
authorJoakim Zhang <qiangqing.zhang@nxp.com>
Fri, 9 Nov 2018 04:58:48 +0000 (12:58 +0800)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
The system can be wakeuped only when system is totally suspend, when the
wakeup event comes during suspend will cause the system hang. For the
reason that CAN will not call flexcan_noirq_resume() callback if the wakeup
event comes before noirq suspend stage.

The way to fix the issue is that assure the system to call
flexcan_noirq_suspend() during suspend and then call
flexcan_noirq_resume() during resume.

Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Arulpandiyan Vadivel <arulpandiyan_vadivel@mentor.com>
drivers/net/can/flexcan.c

index a74fa50..8434856 100644 (file)
@@ -561,6 +561,26 @@ static void flexcan_clks_disable(const struct flexcan_priv *priv)
        clk_disable_unprepare(priv->clk_per);
 }
 
+static void flexcan_wake_mask_enable(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr |= FLEXCAN_MCR_WAK_MSK;
+       priv->write(reg_mcr, &regs->mcr);
+}
+
+static void flexcan_wake_mask_disable(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr &= ~FLEXCAN_MCR_WAK_MSK;
+       priv->write(reg_mcr, &regs->mcr);
+}
+
 static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
 {
        /* enable stop request */
@@ -1236,7 +1256,7 @@ static int flexcan_chip_start(struct net_device *dev)
                reg_mcr |= FLEXCAN_MCR_FEN;
 
        /* enable self wakeup */
-       reg_mcr |= FLEXCAN_MCR_WAK_MSK | FLEXCAN_MCR_SLF_WAK;
+       reg_mcr |= FLEXCAN_MCR_SLF_WAK;
 
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        priv->write(reg_mcr, &regs->mcr);
@@ -1872,9 +1892,10 @@ static int __maybe_unused flexcan_resume(struct device *device)
                netif_start_queue(dev);
 
                if (device_may_wakeup(device)) {
-                       disable_irq_wake(dev->irq);
-                       flexcan_exit_stop_mode(priv);
+                       flexcan_wake_mask_disable(priv);
                } else {
+                       pinctrl_pm_select_default_state(device);
+
                        err = pm_runtime_force_resume(device);
                        if (err)
                                return err;
@@ -1905,9 +1926,34 @@ static int __maybe_unused flexcan_runtime_resume(struct device *device)
        return 0;
 }
 
+static int __maybe_unused flexcan_noirq_suspend(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device))
+               flexcan_wake_mask_enable(priv);
+
+       return 0;
+}
+
+static int __maybe_unused flexcan_noirq_resume(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device)) {
+               disable_irq_wake(dev->irq);
+               flexcan_exit_stop_mode(priv);
+       }
+
+       return 0;
+}
+
 static const struct dev_pm_ops flexcan_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume)
        SET_RUNTIME_PM_OPS(flexcan_runtime_suspend, flexcan_runtime_resume, NULL)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume)
 };
 
 static struct platform_driver flexcan_driver = {