From 8220d7d4e869d39d2b594e7becdab1b6f478f64e Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Fri, 9 Nov 2018 12:58:48 +0800 Subject: [PATCH] MLK-20166 can: flexcan: fix CAN can't wakeup system during suspend 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 --- drivers/net/can/flexcan.c | 40 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index c29611cee0f3..2b1ea96736d7 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -547,6 +547,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 = flexcan_read(®s->mcr); + reg_mcr |= FLEXCAN_MCR_WAK_MSK; + flexcan_write(reg_mcr, ®s->mcr); +} + +static void flexcan_wake_mask_disable(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + u32 reg_mcr; + + reg_mcr = flexcan_read(®s->mcr); + reg_mcr &= ~FLEXCAN_MCR_WAK_MSK; + flexcan_write(reg_mcr, ®s->mcr); +} + #ifdef CONFIG_ARCH_MXC_ARM64 static void imx8_ipg_stop_enable(struct flexcan_priv *priv, bool enabled) { @@ -1229,7 +1249,7 @@ static int flexcan_chip_start(struct net_device *dev) } /* 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); flexcan_write(reg_mcr, ®s->mcr); @@ -1899,7 +1919,9 @@ static int __maybe_unused flexcan_resume(struct device *device) netif_device_attach(dev); netif_start_queue(dev); - if (!device_may_wakeup(device)) { + if (device_may_wakeup(device)) { + flexcan_wake_mask_disable(priv); + } else { pinctrl_pm_select_default_state(device); err = pm_runtime_force_resume(device); @@ -1932,6 +1954,18 @@ 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); @@ -1948,7 +1982,7 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) 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(NULL, flexcan_noirq_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume) }; static struct platform_driver flexcan_driver = {