in host/sdhci-tegra.c [1619:1778]
static int sdhci_tegra_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct sdhci_tegra_soc_data *soc_data;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_tegra *tegra_host;
struct clk *clk;
int rc;
match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
if (!match)
return -EINVAL;
soc_data = match->data;
host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
tegra_host = sdhci_pltfm_priv(pltfm_host);
tegra_host->ddr_signaling = false;
tegra_host->pad_calib_required = false;
tegra_host->pad_control_available = false;
tegra_host->soc_data = soc_data;
if (soc_data->nvquirks & NVQUIRK_HAS_ANDROID_GPT_SECTOR)
host->mmc->caps2 |= MMC_CAP2_ALT_GPT_TEGRA;
if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
if (rc == 0)
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_tegra_start_signal_voltage_switch;
}
/* Hook to periodically rerun pad calibration */
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
host->mmc_host_ops.request = tegra_sdhci_request;
host->mmc_host_ops.hs400_enhanced_strobe =
tegra_sdhci_hs400_enhanced_strobe;
if (!host->ops->platform_execute_tuning)
host->mmc_host_ops.execute_tuning =
tegra_sdhci_execute_hw_tuning;
rc = mmc_of_parse(host->mmc);
if (rc)
goto err_parse_dt;
if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
host->mmc->caps |= MMC_CAP_1_8V_DDR;
/* HW busy detection is supported, but R1B responses are required. */
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
tegra_sdhci_parse_dt(host);
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
GPIOD_OUT_HIGH);
if (IS_ERR(tegra_host->power_gpio)) {
rc = PTR_ERR(tegra_host->power_gpio);
goto err_power_req;
}
/*
* Tegra210 has a separate SDMMC_LEGACY_TM clock used for host
* timeout clock and SW can choose TMCLK or SDCLK for hardware
* data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT of
* the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL.
*
* USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses
* 12Mhz TMCLK which is advertised in host capability register.
* With TMCLK of 12Mhz provides maximum data timeout period that can
* be achieved is 11s better than using SDCLK for data timeout.
*
* So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's
* supporting separate TMCLK.
*/
if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) {
clk = devm_clk_get(&pdev->dev, "tmclk");
if (IS_ERR(clk)) {
rc = PTR_ERR(clk);
if (rc == -EPROBE_DEFER)
goto err_power_req;
dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc);
clk = NULL;
}
clk_set_rate(clk, 12000000);
rc = clk_prepare_enable(clk);
if (rc) {
dev_err(&pdev->dev,
"failed to enable tmclk: %d\n", rc);
goto err_power_req;
}
tegra_host->tmclk = clk;
}
clk = devm_clk_get(mmc_dev(host->mmc), NULL);
if (IS_ERR(clk)) {
rc = dev_err_probe(&pdev->dev, PTR_ERR(clk),
"failed to get clock\n");
goto err_clk_get;
}
pltfm_host->clk = clk;
tegra_host->rst = devm_reset_control_get_exclusive(&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 = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (rc)
goto err_rst_get;
pm_runtime_enable(&pdev->dev);
rc = pm_runtime_resume_and_get(&pdev->dev);
if (rc)
goto err_pm_get;
rc = reset_control_assert(tegra_host->rst);
if (rc)
goto err_rst_assert;
usleep_range(2000, 4000);
rc = reset_control_deassert(tegra_host->rst);
if (rc)
goto err_rst_assert;
usleep_range(2000, 4000);
rc = sdhci_tegra_add_host(host);
if (rc)
goto err_add_host;
return 0;
err_add_host:
reset_control_assert(tegra_host->rst);
err_rst_assert:
pm_runtime_put_sync_suspend(&pdev->dev);
err_pm_get:
pm_runtime_disable(&pdev->dev);
err_rst_get:
err_clk_get:
clk_disable_unprepare(tegra_host->tmclk);
err_power_req:
err_parse_dt:
sdhci_pltfm_free(pdev);
return rc;
}