MLK-21411 can: flexcan: fix CAN can't suspend/resume when CAN is only capable of...
authorJoakim Zhang <qiangqing.zhang@nxp.com>
Mon, 8 Apr 2019 05:32:28 +0000 (13:32 +0800)
committerJoakim Zhang <qiangqing.zhang@nxp.com>
Thu, 18 Apr 2019 09:16:19 +0000 (17:16 +0800)
When dev->power.can_wakeup set to true, device_prepare() may runtime resume the device:
    dpm_suspend_start()
            dpm_prepare(state);
                    dev->driver->pm->prepare()
                            pm_genpd_prepare()
                                    if (resume_needed(dev, genpd))
                                        pm_runtime_resume(dev);

And imx8qm/qxp power domain driver don't implement the active_wakeup() callback, then
resume_needed() always return "true" when dev->power.can_wakeup is true.

Once CAN device is runtime active status, then CAN's clock's count is 1 during system
suspend. And CAN0/CAN1/CAN2 share the same module clock, so we can say all CAN's module
clock is enabled during system suspended.

    flexcan_runtime_resume()
            flexcan_clks_enable(priv);

i.MX8QM/QXP SCU code clock logic requires linux kernel clocks should be disabled during
suspend, otherwise SCU don't enable them after system resume back.

There are two ways to fix the issue:
1. CAN driver should check the runtime status to ensure all clocks are disabled  during
system suspend.
2. Don't set CAN wakeup capability during probe, move it into flexcan_open().

Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
drivers/net/can/flexcan.c

index 775e5bd..f59c207 100644 (file)
@@ -378,6 +378,7 @@ struct flexcan_priv {
 #ifdef CONFIG_ARCH_MXC_ARM64
        sc_ipc_t ipc_handle;
 #endif
+       bool wakeup;
 
        /* Selects the clock source to CAN Protocol Engine (PE), 1 by default*/
        u32 clk_src;
@@ -1503,6 +1504,8 @@ static int flexcan_open(struct net_device *dev)
        if (err)
                goto out_free_irq;
 
+       device_set_wakeup_capable(priv->dev, priv->wakeup);
+
        can_led_event(dev, CAN_LED_EVENT_OPEN);
 
        can_rx_offload_enable(&priv->offload);
@@ -1533,6 +1536,8 @@ static int flexcan_close(struct net_device *dev)
 
        close_candev(dev);
 
+       device_set_wakeup_capable(priv->dev, false);
+
        can_led_event(dev, CAN_LED_EVENT_STOP);
 
        pm_runtime_put(priv->dev);
@@ -1731,7 +1736,6 @@ static int flexcan_probe(struct platform_device *pdev)
        int err, irq;
        u32 clock_freq = 0;
        u32 clk_src = 1;
-       int wakeup = 1;
 
        reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
        if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER)
@@ -1887,22 +1891,21 @@ static int flexcan_probe(struct platform_device *pdev)
 
        devm_can_led_init(dev);
 
+       priv->wakeup = true;
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD) {
                err = imx8_sc_ipc_fetch(pdev);
                if (err) {
-                       wakeup = 0;
+                       priv->wakeup = false;
                        dev_dbg(&pdev->dev, "failed to fetch scu ipc\n");
                }
        } else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) {
                err = flexcan_of_parse_stop_mode(pdev);
                if (err) {
-                       wakeup = 0;
+                       priv->wakeup = false;
                        dev_dbg(&pdev->dev, "failed to parse stop-mode\n");
                }
        }
 
-       device_set_wakeup_capable(&pdev->dev, wakeup);
-
        pm_runtime_put(&pdev->dev);
 
        dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",