The MMC core uses mmc->clk_disable to request that the card clock is
stopped, but the PL180 driver leaves SDI_CLKCR_CLKEN unchanged. This can
leave inactive hosts driving their card clocks.

Honor clk_disable when programming the clock control register and enable
the controller power-save clock gating.

Hardware testing on Ux500 showed that leaving SDI_CLKCR_PWRSAV clear
causes external SD card accesses to hang when both the SD and eMMC hosts
have been initialized. Linux does not set this bit, but its runtime PM
and clock handling differ from U-Boot, where both hosts stay initialized.

Acked-by: Peng Fan <[email protected]>
Signed-off-by: Linus Walleij <[email protected]>
---
 drivers/mmc/arm_pl180_mmci.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c
index af21b814641e..528612359789 100644
--- a/drivers/mmc/arm_pl180_mmci.c
+++ b/drivers/mmc/arm_pl180_mmci.c
@@ -294,6 +294,11 @@ static int  host_set_ios(struct mmc *dev)
        u32 sdi_clkcr;
 
        sdi_clkcr = readl(&host->base->clock);
+       sdi_clkcr |= SDI_CLKCR_PWRSAV;
+       if (dev->clk_disable)
+               sdi_clkcr &= ~SDI_CLKCR_CLKEN;
+       else
+               sdi_clkcr |= SDI_CLKCR_CLKEN;
 
        /* Ramp up the clock rate */
        if (dev->clock) {
@@ -400,7 +405,7 @@ static int arm_pl180_mmc_probe(struct udevice *dev)
 
        host->pwr_init = INIT_PWR;
        host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN |
-                           SDI_CLKCR_HWFC_EN;
+                           SDI_CLKCR_PWRSAV | SDI_CLKCR_HWFC_EN;
        host->clock_in = clk_get_rate(&clk);
 
        cfg->name = dev->name;
@@ -418,7 +423,7 @@ static int arm_pl180_mmc_probe(struct udevice *dev)
        case UX500V2_MMCI_ID:
                host->pwr_init = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
                host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V2 | SDI_CLKCR_CLKEN |
-                                   SDI_CLKCR_HWFC_EN;
+                                   SDI_CLKCR_PWRSAV | SDI_CLKCR_HWFC_EN;
                cfg->voltages = VOLTAGE_WINDOW_MMC;
                cfg->f_min = host->clock_in / (2 + SDI_CLKCR_CLKDIV_INIT_V2);
                host->version2 = true;

-- 
2.55.0

Reply via email to