Skip to content

Commit

Permalink
mmc: tegra: Support module reset
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
thierryreding authored and storulf committed Apr 24, 2017
1 parent a01fc2a commit 20567be
Showing 1 changed file with 42 additions and 1 deletion.
43 changes: 42 additions & 1 deletion drivers/mmc/host/sdhci-tegra.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
Expand Down Expand Up @@ -65,6 +66,8 @@ struct sdhci_tegra {
struct gpio_desc *power_gpio;
bool ddr_signaling;
bool pad_calib_required;

struct reset_control *rst;
};

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

tegra_host->rst = devm_reset_control_get(&pdev->dev, "sdhci");
if (IS_ERR(tegra_host->rst)) {
rc = PTR_ERR(tegra_host->rst);
dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
goto err_rst_get;
}

rc = reset_control_assert(tegra_host->rst);
if (rc)
goto err_rst_get;

usleep_range(2000, 4000);

rc = reset_control_deassert(tegra_host->rst);
if (rc)
goto err_rst_get;

usleep_range(2000, 4000);

rc = sdhci_add_host(host);
if (rc)
goto err_add_host;

return 0;

err_add_host:
reset_control_assert(tegra_host->rst);
err_rst_get:
clk_disable_unprepare(pltfm_host->clk);
err_clk_get:
err_power_req:
Expand All @@ -504,14 +528,31 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
return rc;
}

static int sdhci_tegra_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);

sdhci_remove_host(host, 0);

reset_control_assert(tegra_host->rst);
usleep_range(2000, 4000);
clk_disable_unprepare(pltfm_host->clk);

sdhci_pltfm_free(pdev);

return 0;
}

static struct platform_driver sdhci_tegra_driver = {
.driver = {
.name = "sdhci-tegra",
.of_match_table = sdhci_tegra_dt_match,
.pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_tegra_probe,
.remove = sdhci_pltfm_unregister,
.remove = sdhci_tegra_remove,
};

module_platform_driver(sdhci_tegra_driver);
Expand Down

0 comments on commit 20567be

Please sign in to comment.