[PATCH V2 1/5] mmc: fsl_esdhc_imx: Enable AHB/IPG clk with clk bulk API

From: Peng Fan peng.fan@nxp.com
With partition reset supported for i.MX8QM/QXP/95 and etc, when linux mmc runtime suspended, the mmc clks are gated off. While at same time system controller reset Cortex-A cores because of various reasons( WDOG timeout and etc), with SPL run again, only enable PER clk is not enough, also need to enable AHB/IPG clk, here use clk bulk API to enable all the clocks.
Signed-off-by: Peng Fan peng.fan@nxp.com ---
V2: Update commit log and use clk bulk API per Fabio.
drivers/mmc/fsl_esdhc_imx.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 03de7dcd505..0ced796ff49 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -148,6 +148,7 @@ struct fsl_esdhc_priv { struct fsl_esdhc *esdhc_regs; unsigned int sdhc_clk; struct clk per_clk; + struct clk_bulk clk_bulk; unsigned int clock; unsigned int mode; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -1521,14 +1522,21 @@ static int fsl_esdhc_probe(struct udevice *dev)
#if CONFIG_IS_ENABLED(CLK) /* Assigned clock already set clock */ - ret = clk_get_by_name(dev, "per", &priv->per_clk); + ret = clk_get_bulk(dev, &priv->clk_bulk); if (ret) { - printf("Failed to get per_clk\n"); + dev_err(dev, "Failed to get clks: %d\n", ret); return ret; } - ret = clk_enable(&priv->per_clk); + + ret = clk_enable_bulk(&priv->clk_bulk); if (ret) { - printf("Failed to enable per_clk\n"); + dev_err(dev, "Failed to enable clks: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "per", &priv->per_clk); + if (ret) { + printf("Failed to get per_clk\n"); return ret; }

From: Ye Li ye.li@nxp.com
According to SD and MMC spec, 74 clocks must be sent to device after power stable. This is need in reinit ops for DM MMC or init ops for non-DM MMC after power cycle.
So set the INTIA to send 80 clocks in esdhc_init_common and move its calling from probe to reinit.
However, on 8MQ EVK and 8QXP MEK with some brands of SD cards, sending 80 clocks may not work well.
The root cause is related with power up time. According to spec, after power stable, host shall supply at least 74 SD clocks to the SD card with the maximum of 1ms. However, the power ram up time is related with the characteristic of SD card. At the moment of sending 74 SD clocks, the power probably not ram up to the operating level on the problematic cards. Then cause the cards not ready.
This patch changes to send SD clock with 1ms duration to replace 80 SD clocks (0.2ms at 400Khz clock). This way meets the spec requirement as well, and adds the margin for power ram up time to be compatible with the problematic SD cards. This is also aligned with implementation which has FORCE clock always on.
Reviewed-and-tested-by: Haibo Chen haibo.chen@nxp.com Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com ---
V2: None
drivers/mmc/fsl_esdhc_imx.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 0ced796ff49..e61c5d544ea 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1035,6 +1035,11 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) /* Set timout to the maximum value */ esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16);
+ /* max 1ms delay with clock on for initialization */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + udelay(1000); + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + return 0; }
@@ -1569,7 +1574,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
upriv->mmc = mmc;
- return esdhc_init_common(priv, mmc); + return 0; }
static int fsl_esdhc_get_cd(struct udevice *dev) @@ -1621,6 +1626,14 @@ static int fsl_esdhc_wait_dat0(struct udevice *dev, int state, return esdhc_wait_dat0_common(priv, state, timeout_us); }
+static int fsl_esdhc_reinit(struct udevice *dev) +{ + struct fsl_esdhc_plat *plat = dev_get_plat(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_init_common(priv, &plat->mmc); +} + static const struct dm_mmc_ops fsl_esdhc_ops = { .get_cd = fsl_esdhc_get_cd, .send_cmd = fsl_esdhc_send_cmd, @@ -1632,6 +1645,7 @@ static const struct dm_mmc_ops fsl_esdhc_ops = { .set_enhanced_strobe = fsl_esdhc_set_enhanced_strobe, #endif .wait_dat0 = fsl_esdhc_wait_dat0, + .reinit = fsl_esdhc_reinit, };
static struct esdhc_soc_data usdhc_imx7d_data = {

From: Ye Li ye.li@nxp.com
The plat->cfg is wrongly memset to 0, so the host_caps value configured in fsl_esdhc_initialize is reset. Remove the unnecessary memset since plat is allocated via calloc.
Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com ---
V2: None
drivers/mmc/fsl_esdhc_imx.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index e61c5d544ea..a3defe952b2 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1194,8 +1194,6 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
esdhc_write32(®s->irqstaten, SDHCI_IRQ_EN_BITS); cfg = &plat->cfg; - if (!CONFIG_IS_ENABLED(DM_MMC)) - memset(cfg, '\0', sizeof(*cfg));
caps = esdhc_read32(®s->hostcapblt);

From: Ye Li ye.li@nxp.com
The memory of priv and plat are leaked if max_bus_width is wrong.
Signed-off-by: Ye Li ye.li@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com ---
V2: None
drivers/mmc/fsl_esdhc_imx.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index a3defe952b2..debfc78b7f0 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1327,6 +1327,8 @@ int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg) break; default: printf("invalid max bus width %u\n", cfg->max_bus_width); + free(plat); + free(priv); return -EINVAL; }

From: Peng Fan peng.fan@nxp.com
When supporting partition reset for SoC such as i.MX95 , the Linux Kernel may have configured the tuning, while after force reset by wdog or else, uboot CMD0 will never pass unless config RSTT to reset tuning logic.
Since RSTA and RSTT are independent, so need both to be reseted in the controller.
Acked-by: Haibo Chen haibo.chen@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com ---
V2: None
drivers/mmc/fsl_esdhc_imx.c | 8 ++++---- include/fsl_esdhc_imx.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index debfc78b7f0..fb410104c1f 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -987,11 +987,11 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) ulong start;
/* Reset the entire host controller */ - esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA | SYSCTL_RSTT);
/* Wait until the controller is available */ start = get_timer(0); - while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + while ((esdhc_read32(®s->sysctl) & (SYSCTL_RSTA | SYSCTL_RSTT))) { if (get_timer(start) > 1000) return -ETIMEDOUT; } @@ -1095,11 +1095,11 @@ static int esdhc_reset(struct fsl_esdhc *regs) ulong start;
/* reset the controller */ - esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA | SYSCTL_RSTT);
/* hardware clears the bit when it is done */ start = get_timer(0); - while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + while ((esdhc_read32(®s->sysctl) & (SYSCTL_RSTA | SYSCTL_RSTT))) { if (get_timer(start) > 100) { printf("MMC/SD: Reset never completed.\n"); return -ETIMEDOUT; diff --git a/include/fsl_esdhc_imx.h b/include/fsl_esdhc_imx.h index b8efd2a1664..8612b56609e 100644 --- a/include/fsl_esdhc_imx.h +++ b/include/fsl_esdhc_imx.h @@ -31,6 +31,7 @@ #define SYSCTL_RSTA 0x01000000 #define SYSCTL_RSTC 0x02000000 #define SYSCTL_RSTD 0x04000000 +#define SYSCTL_RSTT 0x10000000
#define VENDORSPEC_CKEN 0x00004000 #define VENDORSPEC_PEREN 0x00002000

On Tue, Oct 1, 2024 at 9:04 AM Peng Fan (OSS) peng.fan@oss.nxp.com wrote:
#if CONFIG_IS_ENABLED(CLK) /* Assigned clock already set clock */
ret = clk_get_by_name(dev, "per", &priv->per_clk);
ret = clk_get_bulk(dev, &priv->clk_bulk); if (ret) {
printf("Failed to get per_clk\n");
dev_err(dev, "Failed to get clks: %d\n", ret); return ret; }
ret = clk_enable(&priv->per_clk);
ret = clk_enable_bulk(&priv->clk_bulk); if (ret) {
printf("Failed to enable per_clk\n");
dev_err(dev, "Failed to enable clks: %d\n", ret);
return ret;
}
ret = clk_get_by_name(dev, "per", &priv->per_clk);
Now that you use clk_get_bulk(), why must the 'per' clock be retrieved separately?
I expect that clk_get_bulk() gets all the clocks.

Subject: Re: [PATCH V2 1/5] mmc: fsl_esdhc_imx: Enable AHB/IPG clk with clk bulk API
On Tue, Oct 1, 2024 at 9:04 AM Peng Fan (OSS) peng.fan@oss.nxp.com wrote:
#if CONFIG_IS_ENABLED(CLK) /* Assigned clock already set clock */
ret = clk_get_by_name(dev, "per", &priv->per_clk);
ret = clk_get_bulk(dev, &priv->clk_bulk); if (ret) {
printf("Failed to get per_clk\n");
dev_err(dev, "Failed to get clks: %d\n", ret); return ret; }
ret = clk_enable(&priv->per_clk);
ret = clk_enable_bulk(&priv->clk_bulk); if (ret) {
printf("Failed to enable per_clk\n");
dev_err(dev, "Failed to enable clks: %d\n", ret);
return ret;
}
ret = clk_get_by_name(dev, "per", &priv->per_clk);
Now that you use clk_get_bulk(), why must the 'per' clock be retrieved separately?
I expect that clk_get_bulk() gets all the clocks.
Yes, but we need the rate of per clock, clk_get_bulk does not know which one is per clock.
Thanks, Peng.

On Tue, Oct 1, 2024 at 9:04 AM Peng Fan (OSS) peng.fan@oss.nxp.com wrote:
From: Peng Fan peng.fan@nxp.com
With partition reset supported for i.MX8QM/QXP/95 and etc, when linux mmc runtime suspended, the mmc clks are gated off. While at same time system controller reset Cortex-A cores because of various reasons( WDOG timeout and etc), with SPL run again, only enable PER clk is not enough, also need to enable AHB/IPG clk, here use clk bulk API to enable all the clocks.
Signed-off-by: Peng Fan peng.fan@nxp.com
V2: Update commit log and use clk bulk API per Fabio.
Applied all to u-boot-imx/next, thanks.
participants (3)
-
Fabio Estevam
-
Peng Fan
-
Peng Fan (OSS)