/* * Note that the register offsets used here are from omap_regs * base which is 0x100 for omap4 and later, and 0 for omap3 and * earlier.
*/ #define SDHCI_OMAP_SYSCONFIG 0x10
ret = sdhci_omap_set_pbias(omap_host, false, 0); if (ret) return ret;
if (!IS_ERR(mmc->supply.vqmmc)) { /* Pick the right voltage to allow 3.0V for 3.3V nominal PBIAS */
ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios); if (ret < 0) {
dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n"); return ret;
}
}
ret = sdhci_omap_set_pbias(omap_host, true, iov_pbias); if (ret) return ret;
thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal"); if (IS_ERR(thermal_dev)) {
dev_err(dev, "Unable to get thermal zone for tuning\n"); return PTR_ERR(thermal_dev);
}
ret = thermal_zone_get_temp(thermal_dev, &temperature); if (ret) return ret;
/* * OMAP5/DRA74X/DRA72x Errata i802: * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur * during the tuning procedure. So disable it during the * tuning procedure.
*/ if (host->ier & SDHCI_INT_DATA_CRC) {
host->ier &= ~SDHCI_INT_DATA_CRC;
dcrc_was_enabled = true;
}
omap_host->is_tuning = true;
/* * Stage 1: Search for a maximum pass window ignoring any * single point failures. If the tuning value ends up * near it, move away from it in stage 2 below
*/ while (phase_delay <= MAX_PHASE_DELAY) {
sdhci_omap_set_dll(omap_host, phase_delay);
cur_match = !mmc_send_tuning(mmc, opcode, NULL); if (cur_match) { if (prev_match) {
length++;
} elseif (single_point_failure) { /* ignore single point failure */
length++;
} else {
start_window = phase_delay;
length = 1;
}
} else {
single_point_failure = prev_match;
}
/* * Stage 2: Search for a single point failure near the chosen tuning * value in two steps. First in the +3 to +10 range and then in the * +2 to -10 range. If found, move away from it in the appropriate * direction by the appropriate amount depending on the temperature.
*/ for (i = 3; i <= 10; i++) {
sdhci_omap_set_dll(omap_host, phase_delay + i);
if (mmc_send_tuning(mmc, opcode, NULL)) { if (temperature < 10000)
phase_delay += i + 6; elseif (temperature < 20000)
phase_delay += i - 12; elseif (temperature < 70000)
phase_delay += i - 8; else
phase_delay += i - 6;
goto single_failure_found;
}
}
for (i = 2; i >= -10; i--) {
sdhci_omap_set_dll(omap_host, phase_delay + i);
if (mmc_send_tuning(mmc, opcode, NULL)) { if (temperature < 10000)
phase_delay += i + 12; elseif (temperature < 20000)
phase_delay += i + 8; elseif (temperature < 70000)
phase_delay += i + 8; elseif (temperature < 90000)
phase_delay += i + 10; else
phase_delay += i + 12;
goto single_failure_found;
}
}
single_failure_found:
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); if (!(reg & AC12_SCLK_SEL)) {
ret = -EIO; goto tuning_error;
}
/* * Delay is required for PSTATE to correctly reflect * DLEV/CLEV values after PADEN is set.
*/
usleep_range(50, 100);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE); if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
ret = true;
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
}
/* * MMCHS_HL_HWINFO has the MADMA_EN bit set if the controller instance * is connected to L3 interconnect and is bus master capable. Note that * the MMCHS_HL_HWINFO register is in the module registers before the * omap registers and sdhci registers. The offset can vary for omap * registers depending on the SoC. Do not use sdhci_omap_readl() here.
*/ staticbool sdhci_omap_has_adma(struct sdhci_omap_host *omap_host, int offset)
{ /* MMCHS_HL_HWINFO register is only available on omap4 and later */ if (offset < 0x200) returnfalse;
/* * Since we are not resetting data lines during tuning * operation, data error or data complete interrupts * might still arrive. Mark this request as a failure * but still wait for the data interrupt
*/ if (intmask & SDHCI_INT_TIMEOUT)
host->cmd->error = -ETIMEDOUT; else
host->cmd->error = -EILSEQ;
host->cmd = NULL;
/* * Sometimes command error interrupts and command complete * interrupt will arrive together. Clear all command related * interrupts here.
*/
sdhci_writel(host, intmask & CMD_MASK, SDHCI_INT_STATUS);
intmask &= ~CMD_MASK;
}
if (pbias != ~0U && vqmmc == ~0U)
dev_warn(dev, "vqmmc regulator missing for pbias\n"); elseif (caps == ~0U) return 0;
/* * Quirk handling to allow 3.0V vqmmc with a valid 3.3V PBIAS. This is * needed for 3.0V ldo9_reg on omap5 at least.
*/ if (pbias != ~0U && (pbias & SDHCI_CAN_VDD_330) &&
(vqmmc & SDHCI_CAN_VDD_300))
caps |= SDHCI_CAN_VDD_330;
/* voltage capabilities might be set by boot loader, clear it */
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
omap_host->pinctrl = devm_pinctrl_get(omap_host->dev); if (IS_ERR(omap_host->pinctrl)) {
dev_err(dev, "Cannot get pinctrl\n"); return PTR_ERR(omap_host->pinctrl);
}
state = pinctrl_lookup_state(omap_host->pinctrl, "default"); if (IS_ERR(state)) {
dev_err(dev, "no pinctrl state for default mode\n"); return PTR_ERR(state);
}
pinctrl_state[MMC_TIMING_LEGACY] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
MMC_CAP_UHS_SDR104); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
MMC_CAP_UHS_DDR50); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
MMC_CAP_UHS_SDR50); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
MMC_CAP_UHS_SDR25); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
MMC_CAP_UHS_SDR12); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
MMC_CAP_1_8V_DDR); if (!IS_ERR(state)) {
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
} else {
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v",
caps,
MMC_CAP_3_3V_DDR); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
}
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
MMC_CAP_SD_HIGHSPEED); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_SD_HS] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
MMC_CAP_MMC_HIGHSPEED); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_HS] = state;
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
MMC_CAP2_HS200_1_8V_SDR); if (!IS_ERR(state))
pinctrl_state[MMC_TIMING_MMC_HS200] = state;
mmc = host->mmc;
sdhci_get_of_property(pdev);
ret = mmc_of_parse(mmc); if (ret) return ret;
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_host_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)) return PTR_ERR(pltfm_host->clk);
ret = clk_set_rate(pltfm_host->clk, mmc->f_max); if (ret) return dev_err_probe(dev, ret, "failed to set clock to %d\n", mmc->f_max);
omap_host->pbias = devm_regulator_get_optional(dev, "pbias"); if (IS_ERR(omap_host->pbias)) {
ret = PTR_ERR(omap_host->pbias); if (ret != -ENODEV) return ret;
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 to clear any boot loader set voltage * capabilities before calling sdhci_setup_host(). 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;
}
/* * 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;
/* Enable SDIO card power off. */
mmc->caps |= MMC_CAP_POWER_OFF_CARD;
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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.