in host/sdhci-omap.c [1213:1399]
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
u32 offset;
struct device *dev = &pdev->dev;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_omap_host *omap_host;
struct mmc_host *mmc;
const struct of_device_id *match;
struct sdhci_omap_data *data;
const struct soc_device_attribute *soc;
struct resource *regs;
match = of_match_device(omap_sdhci_match, dev);
if (!match)
return -EINVAL;
data = (struct sdhci_omap_data *)match->data;
if (!data) {
dev_err(dev, "no sdhci omap data\n");
return -EINVAL;
}
offset = data->offset;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata,
sizeof(*omap_host));
if (IS_ERR(host)) {
dev_err(dev, "Failed sdhci_pltfm_init\n");
return PTR_ERR(host);
}
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
omap_host->host = host;
omap_host->base = host->ioaddr;
omap_host->dev = dev;
omap_host->power_mode = MMC_POWER_UNDEFINED;
omap_host->timing = MMC_TIMING_LEGACY;
omap_host->flags = data->flags;
omap_host->omap_offset = data->omap_offset;
omap_host->con = -EINVAL; /* Prevent invalid restore on first resume */
host->ioaddr += offset;
host->mapbase = regs->start + offset;
mmc = host->mmc;
sdhci_get_of_property(pdev);
ret = mmc_of_parse(mmc);
if (ret)
goto err_pltfm_free;
soc = soc_device_match(sdhci_omap_soc_devices);
if (soc) {
omap_host->version = "rev11";
if (!strcmp(dev_name(dev), "4809c000.mmc"))
mmc->f_max = 96000000;
if (!strcmp(dev_name(dev), "480b4000.mmc"))
mmc->f_max = 48000000;
if (!strcmp(dev_name(dev), "480ad000.mmc"))
mmc->f_max = 48000000;
}
if (!mmc_can_gpio_ro(mmc))
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
pltfm_host->clk = devm_clk_get(dev, "fck");
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
goto err_pltfm_free;
}
ret = clk_set_rate(pltfm_host->clk, mmc->f_max);
if (ret) {
dev_err(dev, "failed to set clock to %d\n", mmc->f_max);
goto err_pltfm_free;
}
omap_host->pbias = devm_regulator_get_optional(dev, "pbias");
if (IS_ERR(omap_host->pbias)) {
ret = PTR_ERR(omap_host->pbias);
if (ret != -ENODEV)
goto err_pltfm_free;
dev_dbg(dev, "unable to get pbias regulator %d\n", ret);
}
omap_host->pbias_enabled = false;
/*
* omap_device_pm_domain has callbacks to enable the main
* functional clock, interface clock and also configure the
* SYSCONFIG register of omap devices. The callback will be invoked
* as part of pm_runtime_get_sync.
*/
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(dev, "pm_runtime_get_sync failed\n");
goto err_rpm_disable;
}
ret = sdhci_omap_set_capabilities(host);
if (ret) {
dev_err(dev, "failed to set system capabilities\n");
goto err_rpm_put;
}
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
/*
* Switch to external DMA only if there is the "dmas" property and
* ADMA is not available on the controller instance.
*/
if (device_property_present(dev, "dmas") &&
!sdhci_omap_has_adma(omap_host, offset))
sdhci_switch_external_dma(host, true);
if (device_property_read_bool(dev, "ti,non-removable")) {
dev_warn_once(dev, "using old ti,non-removable property\n");
mmc->caps |= MMC_CAP_NONREMOVABLE;
}
/* R1B responses is required to properly manage HW busy detection. */
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
/* Allow card power off and runtime PM for eMMC/SD card devices */
mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM;
ret = sdhci_setup_host(host);
if (ret)
goto err_rpm_put;
ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
if (ret)
goto err_cleanup_host;
ret = __sdhci_add_host(host);
if (ret)
goto err_cleanup_host;
/*
* SDIO devices can use the dat1 pin as a wake-up interrupt. Some
* devices like wl1xxx, use an out-of-band GPIO interrupt instead.
*/
omap_host->wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
if (omap_host->wakeirq == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_cleanup_host;
}
if (omap_host->wakeirq > 0) {
device_init_wakeup(dev, true);
ret = dev_pm_set_dedicated_wake_irq(dev, omap_host->wakeirq);
if (ret) {
device_init_wakeup(dev, false);
goto err_cleanup_host;
}
host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
}
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
err_cleanup_host:
sdhci_cleanup_host(host);
err_rpm_put:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
err_rpm_disable:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
err_pltfm_free:
sdhci_pltfm_free(pdev);
return ret;
}