Skip to content

Commit 20567be

Browse files
thierryredingstorulf
authored andcommitted
mmc: tegra: Support module reset
The device tree binding for the SDHCI controller found on Tegra SoCs specifies that a reset control can be provided by the device tree. No code was ever added to support the module reset, which can cause the driver to try and access registers from a module that's in reset. On most Tegra SoC generations doing so would cause a hang. Note that it's unlikely to see this happen because on most platforms these resets will have been deasserted by the bootloader. However the portability can be improved by making sure the driver deasserts the reset before accessing any registers. Since resets are synchronous on Tegra SoCs, the platform driver needs to implement a custom ->remove() callback now to make sure the clock is disabled after the reset is asserted. Acked-by: Adrian Hunter <[email protected]> Signed-off-by: Thierry Reding <[email protected]> Signed-off-by: Ulf Hansson <[email protected]>
1 parent a01fc2a commit 20567be

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

drivers/mmc/host/sdhci-tegra.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/io.h>
2222
#include <linux/of.h>
2323
#include <linux/of_device.h>
24+
#include <linux/reset.h>
2425
#include <linux/mmc/card.h>
2526
#include <linux/mmc/host.h>
2627
#include <linux/mmc/mmc.h>
@@ -65,6 +66,8 @@ struct sdhci_tegra {
6566
struct gpio_desc *power_gpio;
6667
bool ddr_signaling;
6768
bool pad_calib_required;
69+
70+
struct reset_control *rst;
6871
};
6972

7073
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@@ -489,13 +492,34 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
489492
clk_prepare_enable(clk);
490493
pltfm_host->clk = clk;
491494

495+
tegra_host->rst = devm_reset_control_get(&pdev->dev, "sdhci");
496+
if (IS_ERR(tegra_host->rst)) {
497+
rc = PTR_ERR(tegra_host->rst);
498+
dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
499+
goto err_rst_get;
500+
}
501+
502+
rc = reset_control_assert(tegra_host->rst);
503+
if (rc)
504+
goto err_rst_get;
505+
506+
usleep_range(2000, 4000);
507+
508+
rc = reset_control_deassert(tegra_host->rst);
509+
if (rc)
510+
goto err_rst_get;
511+
512+
usleep_range(2000, 4000);
513+
492514
rc = sdhci_add_host(host);
493515
if (rc)
494516
goto err_add_host;
495517

496518
return 0;
497519

498520
err_add_host:
521+
reset_control_assert(tegra_host->rst);
522+
err_rst_get:
499523
clk_disable_unprepare(pltfm_host->clk);
500524
err_clk_get:
501525
err_power_req:
@@ -504,14 +528,31 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
504528
return rc;
505529
}
506530

531+
static int sdhci_tegra_remove(struct platform_device *pdev)
532+
{
533+
struct sdhci_host *host = platform_get_drvdata(pdev);
534+
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
535+
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
536+
537+
sdhci_remove_host(host, 0);
538+
539+
reset_control_assert(tegra_host->rst);
540+
usleep_range(2000, 4000);
541+
clk_disable_unprepare(pltfm_host->clk);
542+
543+
sdhci_pltfm_free(pdev);
544+
545+
return 0;
546+
}
547+
507548
static struct platform_driver sdhci_tegra_driver = {
508549
.driver = {
509550
.name = "sdhci-tegra",
510551
.of_match_table = sdhci_tegra_dt_match,
511552
.pm = &sdhci_pltfm_pmops,
512553
},
513554
.probe = sdhci_tegra_probe,
514-
.remove = sdhci_pltfm_unregister,
555+
.remove = sdhci_tegra_remove,
515556
};
516557

517558
module_platform_driver(sdhci_tegra_driver);

0 commit comments

Comments
 (0)