From: Dave Klotz <[email protected]>

All three SDHCI controllers of the K1 share the SDH AXI reset, which
is the "axi" entry of every node's resets. When a probe fails, the
error path calls reset_release_bulk(), which asserts each reset.
Resets are not reference counted, so this holds the AXI bus of the
controllers that already probed in reset. mmc_probe() probes the
controllers by sequence number and then again in uclass order, so a
failing controller asserts the reset again after the others have
probed, whichever one it is, and the next access to another
controller hangs the hart.

The clocks are reference counted, so releasing them only drops this
controller's reference. Keep that, and leave the resets deasserted.
The SpacemiT reset driver has no rfree, so nothing leaks.

Fixes: 18e9745c9327 ("mmc: k1: add sdhci platform driver")
Signed-off-by: Dave Klotz <[email protected]>
Signed-off-by: Tanmay Kathpalia <[email protected]>
---
Changes in v3:
- Keep clk_release_bulk(); only drop reset_release_bulk()
- Merge the err_reset label into err_clk
- Reworded the commit message
---
 drivers/mmc/spacemit_sdhci.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/spacemit_sdhci.c b/drivers/mmc/spacemit_sdhci.c
index d3748847e48..fdedb9a301a 100644
--- a/drivers/mmc/spacemit_sdhci.c
+++ b/drivers/mmc/spacemit_sdhci.c
@@ -586,19 +586,19 @@ static int spacemit_sdhci_probe(struct udevice *dev)
        ret = reset_deassert_bulk(&plat->resets);
        if (ret) {
                log_err("Failed to reset: %d\n", ret);
-               goto err_reset;
+               goto err_clk;
        }
 
        ret = clk_get_by_index(dev, 1, &clk);
        if (ret) {
                log_err("Can't get io clk: %d\n", ret);
-               goto err_reset;
+               goto err_clk;
        }
 
        ret = clk_set_rate(&clk, plat->cfg.f_max);
        if (ret) {
                log_err("Failed to set io clk: %d\n", ret);
-               goto err_reset;
+               goto err_clk;
        }
 
        /* Set quirks */
@@ -613,17 +613,20 @@ static int spacemit_sdhci_probe(struct udevice *dev)
        ret = sdhci_setup_cfg(&plat->cfg, host, plat->cfg.f_max,
                              SDHC_MIN_CLOCK);
        if (ret)
-               goto err_reset;
+               goto err_clk;
 
        ret = sdhci_probe(dev);
        if (ret)
-               goto err_reset;
+               goto err_clk;
 
        spacemit_sdhci_phy_init(dev, host);
        return 0;
 
-err_reset:
-       reset_release_bulk(&plat->resets);
+       /*
+        * Leave the resets deasserted: the "axi" reset is shared by all
+        * three SDHCI controllers and is not reference counted, so
+        * asserting it would hold the bus of the others in reset.
+        */
 err_clk:
        clk_release_bulk(&plat->clks);
        return ret;
-- 
2.43.7

Reply via email to