diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-of-dwcmshc.c')
-rw-r--r-- | drivers/mmc/host/sdhci-of-dwcmshc.c | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 09b9ab15e499..ee6b1096f709 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/sizes.h> @@ -745,6 +746,29 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv } } +static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + struct device *dev = mmc_dev(host->mmc); + int ret; + + /* + * This works around the design of the RK3576's power domains, which + * makes the PD_NVM power domain, which the sdhci controller on the + * RK3576 is in, never come back the same way once it's run-time + * suspended once. This can happen during early kernel boot if no driver + * is using either PD_NVM or its child power domain PD_SDGMAC for a + * short moment, leading to it being turned off to save power. By + * keeping it on, sdhci suspending won't lead to PD_NVM becoming a + * candidate for getting turned off. + */ + ret = dev_pm_genpd_rpm_always_on(dev, true); + if (ret && ret != -EOPNOTSUPP) + dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n", + ERR_PTR(ret)); + + dwcmshc_rk35xx_postinit(host, dwc_priv); +} + static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1176,6 +1200,18 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { .postinit = dwcmshc_rk35xx_postinit, }; +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk3576_postinit, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = { .pdata = { .ops = &sdhci_dwcmshc_th1520_ops, @@ -1275,6 +1311,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .data = &sdhci_dwcmshc_rk35xx_pdata, }, { + .compatible = "rockchip,rk3576-dwcmshc", + .data = &sdhci_dwcmshc_rk3576_pdata, + }, + { .compatible = "rockchip,rk3568-dwcmshc", .data = &sdhci_dwcmshc_rk35xx_pdata, }, @@ -1347,14 +1387,13 @@ static int dwcmshc_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get(dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - err = PTR_ERR(pltfm_host->clk); - dev_err(dev, "failed to get core clk: %d\n", err); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(dev, PTR_ERR(pltfm_host->clk), + "failed to get core clk\n"); + err = clk_prepare_enable(pltfm_host->clk); if (err) - goto free_pltfm; + return err; priv->bus_clk = devm_clk_get(dev, "bus"); if (!IS_ERR(priv->bus_clk)) @@ -1427,8 +1466,6 @@ err_clk: clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); -free_pltfm: - sdhci_pltfm_free(pdev); return err; } @@ -1460,7 +1497,6 @@ static void dwcmshc_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM_SLEEP |