Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+24 −9
Original line number Original line Diff line number Diff line
@@ -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)
 *
 *
 * 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)
			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)
}
}


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 = {