in host/sdhci-msm.c [2486:2755]
static int sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_msm_host *msm_host;
struct clk *clk;
int ret;
u16 host_version, core_minor;
u32 core_version, config;
u8 core_major;
const struct sdhci_msm_offset *msm_offset;
const struct sdhci_msm_variant_info *var_info;
struct device_node *node = pdev->dev.of_node;
host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
if (IS_ERR(host))
return PTR_ERR(host);
host->sdma_boundary = 0;
pltfm_host = sdhci_priv(host);
msm_host = sdhci_pltfm_priv(pltfm_host);
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
ret = mmc_of_parse(host->mmc);
if (ret)
goto pltfm_free;
/*
* Based on the compatible string, load the required msm host info from
* the data associated with the version info.
*/
var_info = of_device_get_match_data(&pdev->dev);
msm_host->mci_removed = var_info->mci_removed;
msm_host->restore_dll_config = var_info->restore_dll_config;
msm_host->var_ops = var_info->var_ops;
msm_host->offset = var_info->offset;
msm_offset = msm_host->offset;
sdhci_get_of_property(pdev);
sdhci_msm_get_of_property(pdev, host);
msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
/* Setup SDCC bus voter clock. */
msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
if (!IS_ERR(msm_host->bus_clk)) {
/* Vote for max. clk rate for max. performance */
ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
if (ret)
goto pltfm_free;
ret = clk_prepare_enable(msm_host->bus_clk);
if (ret)
goto pltfm_free;
}
/* Setup main peripheral bus clock */
clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret);
goto bus_clk_disable;
}
msm_host->bulk_clks[1].clk = clk;
/* Setup SDC MMC clock */
clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
goto bus_clk_disable;
}
msm_host->bulk_clks[0].clk = clk;
/* Check for optional interconnect paths */
ret = dev_pm_opp_of_find_icc_paths(&pdev->dev, NULL);
if (ret)
goto bus_clk_disable;
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
if (ret)
goto bus_clk_disable;
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(&pdev->dev);
if (ret && ret != -ENODEV) {
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
goto bus_clk_disable;
}
/* Vote for maximum clock rate for maximum performance */
ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX);
if (ret)
dev_warn(&pdev->dev, "core clock boost failed\n");
clk = devm_clk_get(&pdev->dev, "cal");
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[2].clk = clk;
clk = devm_clk_get(&pdev->dev, "sleep");
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[3].clk = clk;
clk = sdhci_msm_ice_get_clk(&pdev->dev);
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[4].clk = clk;
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret)
goto bus_clk_disable;
/*
* xo clock is needed for FLL feature of cm_dll.
* In case if xo clock is not mentioned in DT, warn and proceed.
*/
msm_host->xo_clk = devm_clk_get(&pdev->dev, "xo");
if (IS_ERR(msm_host->xo_clk)) {
ret = PTR_ERR(msm_host->xo_clk);
dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret);
}
if (!msm_host->mci_removed) {
msm_host->core_mem = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(msm_host->core_mem)) {
ret = PTR_ERR(msm_host->core_mem);
goto clk_disable;
}
}
/* Reset the vendor spec register to power on reset state */
writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
host->ioaddr + msm_offset->core_vendor_spec);
if (!msm_host->mci_removed) {
/* Set HC_MODE_EN bit in HC_MODE register */
msm_host_writel(msm_host, HC_MODE_EN, host,
msm_offset->core_hc_mode);
config = msm_host_readl(msm_host, host,
msm_offset->core_hc_mode);
config |= FF_CLK_SW_RST_DIS;
msm_host_writel(msm_host, config, host,
msm_offset->core_hc_mode);
}
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
core_version = msm_host_readl(msm_host, host,
msm_offset->core_mci_version);
core_major = (core_version & CORE_VERSION_MAJOR_MASK) >>
CORE_VERSION_MAJOR_SHIFT;
core_minor = core_version & CORE_VERSION_MINOR_MASK;
dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n",
core_version, core_major, core_minor);
if (core_major == 1 && core_minor >= 0x42)
msm_host->use_14lpp_dll_reset = true;
/*
* SDCC 5 controller with major version 1, minor version 0x34 and later
* with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL.
*/
if (core_major == 1 && core_minor < 0x34)
msm_host->use_cdclp533 = true;
/*
* Support for some capabilities is not advertised by newer
* controller versions and must be explicitly enabled.
*/
if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
writel_relaxed(config, host->ioaddr +
msm_offset->core_vendor_spec_capabilities0);
}
if (core_major == 1 && core_minor >= 0x49)
msm_host->updated_ddr_cfg = true;
if (core_major == 1 && core_minor >= 0x71)
msm_host->uses_tassadar_dll = true;
ret = sdhci_msm_register_vreg(msm_host);
if (ret)
goto clk_disable;
/*
* Power on reset state may trigger power irq if previous status of
* PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq
* interrupt in GIC, any pending power irq interrupt should be
* acknowledged. Otherwise power irq interrupt handler would be
* fired prematurely.
*/
sdhci_msm_handle_pwr_irq(host, 0);
/*
* Ensure that above writes are propogated before interrupt enablement
* in GIC.
*/
mb();
/* Setup IRQ for handling power/voltage tasks with PMIC */
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (msm_host->pwr_irq < 0) {
ret = msm_host->pwr_irq;
goto clk_disable;
}
sdhci_msm_init_pwr_irq_wait(msm_host);
/* Enable pwr irq interrupts */
msm_host_writel(msm_host, INT_MASK, host,
msm_offset->core_pwrctl_mask);
ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
sdhci_msm_pwr_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), host);
if (ret) {
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret);
goto clk_disable;
}
msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
/* Set the timeout value to max possible */
host->max_timeout_count = 0xF;
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev,
MSM_MMC_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_msm_start_signal_voltage_switch;
host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning;
if (of_property_read_bool(node, "supports-cqe"))
ret = sdhci_msm_cqe_add_host(host, pdev);
else
ret = sdhci_add_host(host);
if (ret)
goto pm_runtime_disable;
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
pm_runtime_disable:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable:
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
bus_clk_disable:
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
pltfm_free:
sdhci_pltfm_free(pdev);
return ret;
}