|
17 | 17 | #include <linux/module.h>
|
18 | 18 | #include <linux/of.h>
|
19 | 19 | #include <linux/platform_device.h>
|
| 20 | +#include <linux/pm_domain.h> |
20 | 21 | #include <linux/pm_runtime.h>
|
21 | 22 | #include <linux/reset.h>
|
22 | 23 | #include <linux/sizes.h>
|
@@ -745,6 +746,29 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv
|
745 | 746 | }
|
746 | 747 | }
|
747 | 748 |
|
| 749 | +static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) |
| 750 | +{ |
| 751 | + struct device *dev = mmc_dev(host->mmc); |
| 752 | + int ret; |
| 753 | + |
| 754 | + /* |
| 755 | + * This works around the design of the RK3576's power domains, which |
| 756 | + * makes the PD_NVM power domain, which the sdhci controller on the |
| 757 | + * RK3576 is in, never come back the same way once it's run-time |
| 758 | + * suspended once. This can happen during early kernel boot if no driver |
| 759 | + * is using either PD_NVM or its child power domain PD_SDGMAC for a |
| 760 | + * short moment, leading to it being turned off to save power. By |
| 761 | + * keeping it on, sdhci suspending won't lead to PD_NVM becoming a |
| 762 | + * candidate for getting turned off. |
| 763 | + */ |
| 764 | + ret = dev_pm_genpd_rpm_always_on(dev, true); |
| 765 | + if (ret && ret != -EOPNOTSUPP) |
| 766 | + dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n", |
| 767 | + ERR_PTR(ret)); |
| 768 | + |
| 769 | + dwcmshc_rk35xx_postinit(host, dwc_priv); |
| 770 | +} |
| 771 | + |
748 | 772 | static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode)
|
749 | 773 | {
|
750 | 774 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
@@ -1176,6 +1200,18 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
1176 | 1200 | .postinit = dwcmshc_rk35xx_postinit,
|
1177 | 1201 | };
|
1178 | 1202 |
|
| 1203 | +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = { |
| 1204 | + .pdata = { |
| 1205 | + .ops = &sdhci_dwcmshc_rk35xx_ops, |
| 1206 | + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | |
| 1207 | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, |
| 1208 | + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | |
| 1209 | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, |
| 1210 | + }, |
| 1211 | + .init = dwcmshc_rk35xx_init, |
| 1212 | + .postinit = dwcmshc_rk3576_postinit, |
| 1213 | +}; |
| 1214 | + |
1179 | 1215 | static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
|
1180 | 1216 | .pdata = {
|
1181 | 1217 | .ops = &sdhci_dwcmshc_th1520_ops,
|
@@ -1274,6 +1310,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
1274 | 1310 | .compatible = "rockchip,rk3588-dwcmshc",
|
1275 | 1311 | .data = &sdhci_dwcmshc_rk35xx_pdata,
|
1276 | 1312 | },
|
| 1313 | + { |
| 1314 | + .compatible = "rockchip,rk3576-dwcmshc", |
| 1315 | + .data = &sdhci_dwcmshc_rk3576_pdata, |
| 1316 | + }, |
1277 | 1317 | {
|
1278 | 1318 | .compatible = "rockchip,rk3568-dwcmshc",
|
1279 | 1319 | .data = &sdhci_dwcmshc_rk35xx_pdata,
|
|
0 commit comments