can: flexcan: fix possible deadlock and out-of-order reception after wakeup
authorSean Nyekjaer <sean@geanix.com>
Wed, 4 Dec 2019 11:36:06 +0000 (11:36 +0000)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 8 Dec 2019 10:52:02 +0000 (11:52 +0100)
When suspending, and there is still CAN traffic on the interfaces the
flexcan immediately wakes the platform again. As it should :-). But it
throws this error msg:

[ 3169.378661] PM: noirq suspend of devices failed

On the way down to suspend the interface that throws the error message
calls flexcan_suspend() but fails to call flexcan_noirq_suspend(). That
means flexcan_enter_stop_mode() is called, but on the way out of suspend
the driver only calls flexcan_resume() and skips flexcan_noirq_resume(),
thus it doesn't call flexcan_exit_stop_mode(). This leaves the flexcan
in stop mode, and with the current driver it can't recover from this
even with a soft reboot, it requires a hard reboot.

This patch fixes the deadlock when using self wakeup, by calling
flexcan_exit_stop_mode() from flexcan_resume() instead of
flexcan_noirq_resume().

This also fixes another issue: CAN frames are received out-of-order in
first IRQ handler run after wakeup.

The problem is that the wakeup latency from frame reception to the IRQ
handler (where the CAN frames are sorted by timestamp) is much bigger
than the time stamp counter wrap around time. This means it's
impossible to sort the CAN frames by timestamp.

The reason is that the controller exits stop mode during noirq resume,
which means it receives frames immediately, but interrupt handling is
still not possible.

So exit stop mode during resume stage instead of noirq resume fixes this
issue.

Fixes: de3578c198c6 ("can: flexcan: add self wakeup support")
Signed-off-by: Sean Nyekjaer <sean@geanix.com>
Tested-by: Sean Nyekjaer <sean@geanix.com>
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Cc: linux-stable <stable@vger.kernel.org> # >= v5.0
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/flexcan.c

index a929cdda9ab23af08838c08e27247cb408aa6102..b6f675a5e2d9d788b03fa7a6198865ed68acbb51 100644 (file)
@@ -1722,6 +1722,9 @@ static int __maybe_unused flexcan_resume(struct device *device)
                netif_start_queue(dev);
                if (device_may_wakeup(device)) {
                        disable_irq_wake(dev->irq);
+                       err = flexcan_exit_stop_mode(priv);
+                       if (err)
+                               return err;
                } else {
                        err = pm_runtime_force_resume(device);
                        if (err)
@@ -1767,14 +1770,9 @@ 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);
-       int err;
 
-       if (netif_running(dev) && device_may_wakeup(device)) {
+       if (netif_running(dev) && device_may_wakeup(device))
                flexcan_enable_wakeup_irq(priv, false);
-               err = flexcan_exit_stop_mode(priv);
-               if (err)
-                       return err;
-       }
 
        return 0;
 }