Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Marek Szyprowski and committed by
Ulf Hansson
ecf7c7c5 75067aba

+24 -9
+24 -9
drivers/mmc/host/dw_mmc-exynos.c
··· 175 175 176 176 return ret; 177 177 } 178 + #endif /* CONFIG_PM */ 179 + 180 + #ifdef CONFIG_PM_SLEEP 181 + /** 182 + * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code 183 + * 184 + * This ensures that device will be in runtime active state in 185 + * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume() 186 + */ 187 + static int dw_mci_exynos_suspend_noirq(struct device *dev) 188 + { 189 + pm_runtime_get_noresume(dev); 190 + return pm_runtime_force_suspend(dev); 191 + } 178 192 179 193 /** 180 194 * dw_mci_exynos_resume_noirq - Exynos-specific resume code ··· 200 186 * 201 187 * We run this code on all exynos variants because it doesn't hurt. 202 188 */ 203 - 204 189 static int dw_mci_exynos_resume_noirq(struct device *dev) 205 190 { 206 191 struct dw_mci *host = dev_get_drvdata(dev); 207 192 struct dw_mci_exynos_priv_data *priv = host->priv; 208 193 u32 clksel; 194 + int ret; 195 + 196 + ret = pm_runtime_force_resume(dev); 197 + if (ret) 198 + return ret; 209 199 210 200 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 211 201 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) ··· 225 207 mci_writel(host, CLKSEL, clksel); 226 208 } 227 209 210 + pm_runtime_put(dev); 211 + 228 212 return 0; 229 213 } 230 - #else 231 - #define dw_mci_exynos_resume_noirq NULL 232 - #endif /* CONFIG_PM */ 214 + #endif /* CONFIG_PM_SLEEP */ 233 215 234 216 static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) 235 217 { ··· 571 553 } 572 554 573 555 static const struct dev_pm_ops dw_mci_exynos_pmops = { 574 - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 575 - pm_runtime_force_resume) 556 + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, 557 + dw_mci_exynos_resume_noirq) 576 558 SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, 577 559 dw_mci_exynos_runtime_resume, 578 560 NULL) 579 - .resume_noirq = dw_mci_exynos_resume_noirq, 580 - .thaw_noirq = dw_mci_exynos_resume_noirq, 581 - .restore_noirq = dw_mci_exynos_resume_noirq, 582 561 }; 583 562 584 563 static struct platform_driver dw_mci_exynos_pltfm_driver = {