summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2018-06-12 12:55:23 +0200
committerUlf Hansson <ulf.hansson@linaro.org>2018-07-16 11:21:45 +0200
commitecf7c7c5855aca18ddde534d7bfff111ce0cb26f (patch)
tree4a0472609e775f15ac53f4a9fd871d699822c5b2 /drivers/mmc/host
parent75067aba7afbeca2101f50f78ac1b7c036778c44 (diff)
downloadlinux-ecf7c7c5855aca18ddde534d7bfff111ce0cb26f.tar.gz
linux-ecf7c7c5855aca18ddde534d7bfff111ce0cb26f.tar.bz2
linux-ecf7c7c5855aca18ddde534d7bfff111ce0cb26f.zip
mmc: dw_mmc-exynos: fix potential external abort in resume_noirq()
dw_mci_exynos_resume_noirq() performs DWMMC register access without ensuring that respective clocks are enabled. This might cause external abort on some systems (observed on Exynos5433 based boards). Fix this by forcing a PM runtime active state before register access. Using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS allows also to cleanup conditional code a bit. Suggested-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c33
1 files changed, 24 insertions, 9 deletions
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index a84aa3f1ae85..ab47b018716a 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
return ret;
}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code
+ *
+ * This ensures that device will be in runtime active state in
+ * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume()
+ */
+static int dw_mci_exynos_suspend_noirq(struct device *dev)
+{
+ pm_runtime_get_noresume(dev);
+ return pm_runtime_force_suspend(dev);
+}
/**
* dw_mci_exynos_resume_noirq - Exynos-specific resume code
@@ -186,12 +200,16 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
*
* We run this code on all exynos variants because it doesn't hurt.
*/
-
static int dw_mci_exynos_resume_noirq(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel;
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
@@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
mci_writel(host, CLKSEL, clksel);
}
+ pm_runtime_put(dev);
+
return 0;
}
-#else
-#define dw_mci_exynos_resume_noirq NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
{
@@ -553,14 +571,11 @@ static int dw_mci_exynos_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_exynos_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
+ dw_mci_exynos_resume_noirq)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_exynos_runtime_resume,
NULL)
- .resume_noirq = dw_mci_exynos_resume_noirq,
- .thaw_noirq = dw_mci_exynos_resume_noirq,
- .restore_noirq = dw_mci_exynos_resume_noirq,
};
static struct platform_driver dw_mci_exynos_pltfm_driver = {