diff options
author | Stefan Agner <stefan@agner.ch> | 2015-05-18 15:27:18 +0200 |
---|---|---|
committer | Stefan Agner <stefan@agner.ch> | 2016-02-10 18:42:27 -0800 |
commit | 2a2ac993a772107d21a2c70fecd2032327366043 (patch) | |
tree | df77a8e1c6f5b7f5171a62ffe640e27705c69fc0 /drivers/mmc/host/sdhci-pltfm.c | |
parent | 71148de4f7e42bbe3d4a76de00365f541c6bd485 (diff) |
mmc: sdhci: fix abort due to missing runtime PM
When using i.MX ESDHC driver, while entering suspend while the device
is in runtime PM, the sdhci_(suspend|resume)_host function are called
with disabled clocks. Since this functions access the SDHC host
registers, this leads to an external abort on Vybrid SoC:
[ 37.772967] Unhandled fault: imprecise external abort (0x1c06) at 0x76f5f000
[ 37.780304] Internal error: : 1c06 [#1] ARM
[ 37.784670] Modules linked in:
[ 37.787908] CPU: 0 PID: 428 Comm: sh Not tainted 3.18.0-rc5-00119-geefd097-dirty #1540
[ 37.796142] task: 8e246c00 ti: 8ca6c000 task.ti: 8ca6c000
[ 37.801785] PC is at esdhc_writel_le+0x40/0xec
[ 37.806431] LR is at sdhci_set_card_detection+0xe0/0xe4
[ 37.811877] pc : [<803f0584>] lr : [<803eaaa0>] psr: 400f0013
[ 37.811877] sp : 8ca6dd28 ip : 00000001 fp : 8ca6dd3c
[ 37.823766] r10: 807a233c r9 : 00000000 r8 : 8e8b7210
[ 37.829194] r7 : 802d8a08 r6 : 8082e928 r5 : 00000000 r4 : 00000002
[ 37.835974] r3 : 8ea34e90 r2 : 00000038 r1 : 00000000 r0 : 8ea32ac0
...
Clocks need to be enabled to access the registers. Fix the issue by
add runtime PM enabled pltfm implementation of suspend/resume which
take care of clocks by using the runtime PM API properly.
Diffstat (limited to 'drivers/mmc/host/sdhci-pltfm.c')
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 87fb5ea8ebe7..714ffb5a6929 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -31,6 +31,7 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #ifdef CONFIG_PPC #include <asm/machdep.h> #endif @@ -252,6 +253,41 @@ const struct dev_pm_ops sdhci_pltfm_pmops = { .resume = sdhci_pltfm_resume, }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); + +int sdhci_pltfm_rpm_suspend(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + ret = sdhci_suspend_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + return pm_runtime_force_suspend(dev); +} +EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_suspend); + +int sdhci_pltfm_rpm_resume(struct device *dev) +{ + int ret; + struct sdhci_host *host = dev_get_drvdata(dev); + + ret = pm_runtime_force_resume(dev); + + if (ret) + return ret; + + pm_runtime_get_sync(dev); + ret = sdhci_resume_host(host); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} +EXPORT_SYMBOL_GPL(sdhci_pltfm_rpm_resume); #endif /* CONFIG_PM */ static int __init sdhci_pltfm_drv_init(void) |