Commit ecf7c7c5 authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Ulf Hansson

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: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 75067aba
...@@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) ...@@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
return ret; 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 * dw_mci_exynos_resume_noirq - Exynos-specific resume code
...@@ -186,12 +200,16 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) ...@@ -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. * We run this code on all exynos variants because it doesn't hurt.
*/ */
static int dw_mci_exynos_resume_noirq(struct device *dev) static int dw_mci_exynos_resume_noirq(struct device *dev)
{ {
struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci *host = dev_get_drvdata(dev);
struct dw_mci_exynos_priv_data *priv = host->priv; struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel; u32 clksel;
int ret;
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
...@@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) ...@@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
mci_writel(host, CLKSEL, clksel); mci_writel(host, CLKSEL, clksel);
} }
pm_runtime_put(dev);
return 0; return 0;
} }
#else #endif /* CONFIG_PM_SLEEP */
#define dw_mci_exynos_resume_noirq NULL
#endif /* CONFIG_PM */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) 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) ...@@ -553,14 +571,11 @@ static int dw_mci_exynos_remove(struct platform_device *pdev)
} }
static const struct dev_pm_ops dw_mci_exynos_pmops = { static const struct dev_pm_ops dw_mci_exynos_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
pm_runtime_force_resume) dw_mci_exynos_resume_noirq)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_exynos_runtime_resume, dw_mci_exynos_runtime_resume,
NULL) 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 = { static struct platform_driver dw_mci_exynos_pltfm_driver = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment