sdio的切换I/O电压的详细流程
不管是中断,还是poll,触发检测首先执行mmc_rescan这个work
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };void mmc_rescan(struct work_struct *work)
{ ......for (i = 0; i < ARRAY_SIZE(freqs); i++) {if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {extend_wakelock = true;break;}if (freqs[i] <= host->f_min)break;}......
}
之后按照sdio(CMD5),sd,mmc的顺序扫描,一旦扫到立即返回
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{host->f_init = freq;pr_debug("%s: %s: trying to init card at %u Hz\n",mmc_hostname(host), __func__, host->f_init);mmc_power_up(host, host->ocr_avail);/** Some eMMCs (with VCCQ always on) may not be reset after power up, so* do a hardware reset if possible.*/mmc_hw_reset_for_init(host);/** sdio_reset sends CMD52 to reset card. Since we do not know* if the card is being re-initialized, just send it. CMD52* should be ignored by SD/eMMC cards.* Skip it if we already know that we do not support SDIO commands*/if (!(host->caps2 & MMC_CAP2_NO_SDIO))sdio_reset(host);mmc_go_idle(host);if (!(host->caps2 & MMC_CAP2_NO_SD)) {if (mmc_send_if_cond_pcie(host, host->ocr_avail))goto out;if (mmc_card_sd_express(host))return 0;}/* Order's important: probe SDIO, then SD, then MMC */if (!(host->caps2 & MMC_CAP2_NO_SDIO))if (!mmc_attach_sdio(host))return 0;if (!(host->caps2 & MMC_CAP2_NO_SD))if (!mmc_attach_sd(host))return 0;if (!(host->caps2 & MMC_CAP2_NO_MMC))if (!mmc_attach_mmc(host))return 0;out:mmc_power_off(host);return -EIO;
}
先看上面的mmc_power_up里面的
void mmc_power_up(struct mmc_host *host, u32 ocr)
{if (host->ios.power_mode == MMC_POWER_ON)return;mmc_pwrseq_pre_power_on(host);host->ios.vdd = fls(ocr) - 1;host->ios.power_mode = MMC_POWER_UP;/* Set initial state and call mmc_set_ios */mmc_set_initial_state(host);mmc_set_initial_signal_voltage(host);/** This delay should be sufficient to allow the power supply* to reach the minimum voltage.*/mmc_delay(host->ios.power_delay_ms);mmc_pwrseq_post_power_on(host);host->ios.clock = host->f_init;host->ios.power_mode = MMC_POWER_ON;mmc_set_ios(host);/** This delay must be at least 74 clock sizes, or 1 ms, or the* time required to reach a stable voltage.*/mmc_delay(host->ios.power_delay_ms);
}
mmc_set_initial_signal_voltage直接默认上电给3.3V;打印Initial signal voltage of 3.3v;也就是IO信号的电压默认给了3.3V,让vqmmc控制引脚拉高,让ldo1输出3.3V的电平给上拉
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{int err = 0;int old_signal_voltage = host->ios.signal_voltage;host->ios.signal_voltage = signal_voltage;if (host->ops->start_signal_voltage_switch)err = host->ops->start_signal_voltage_switch(host, &host->ios);if (err)host->ios.signal_voltage = old_signal_voltage;return err;}void mmc_set_initial_signal_voltage(struct mmc_host *host)
{/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330))dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
}
host->ops->start_signal_voltage_switch;sdhci_host的mmc_host_ops赋值为:sdhci_ops;mmc_host的ops成员赋值为:sdhci_host的mmc_host_ops
struct sdhci_host *sdhci_alloc_host(struct device *dev,size_t priv_size)
{struct mmc_host *mmc;struct sdhci_host *host;WARN_ON(dev == NULL);mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);if (!mmc)return ERR_PTR(-ENOMEM);host = mmc_priv(mmc);host->mmc = mmc;host->mmc_host_ops = sdhci_ops;mmc->ops = &host->mmc_host_ops;
成员start_signal_voltage_switch 做了赋值
host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch;
sdio需要切换到1V8的IO话:mmc_regulator_set_vqmmc 让vqmmc控制引脚拉高,让ldo1输出3.3V的电平给上拉
static int sdhci_am654_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{struct sdhci_host *host = mmc_priv(mmc);struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);int ret;if ((sdhci_am654->quirks & SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA) &&ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {if (!IS_ERR(mmc->supply.vqmmc)) {ret = mmc_regulator_set_vqmmc(mmc, ios);if (ret < 0) {pr_err("%s: Switching to 1.8V signalling voltage failed,\n",mmc_hostname(mmc));return -EIO;}}return 0;}return sdhci_start_signal_voltage_switch(mmc, ios);
}
最终也会调用sdhci_start_signal_voltage_switch,来通过vqmmc控制电平切换;低于sdhci3版本直接返回了,用外部vqmmc去切;不通过IP切
vmmc-supply:直接为SD/MMC卡提供核心电压(通常为3.3V或1.8V),确保卡内存储单元和逻辑电路正常工作
vqmmc-supply:为控制器与卡之间的信号线(如CMD、DAT0-DAT3)提供上拉电压,并控制总线电平切换(如1.8V/3.3V),确保信号传输的稳定性和兼容性
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,struct mmc_ios *ios)
{struct sdhci_host *host = mmc_priv(mmc);u16 ctrl;int ret;/** Signal Voltage Switching is only applicable for Host Controllers* v3.00 and above.*/if (host->version < SDHCI_SPEC_300)return 0;ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);switch (ios->signal_voltage) {case MMC_SIGNAL_VOLTAGE_330:if (!(host->flags & SDHCI_SIGNALING_330))return -EINVAL;/* Set 1.8V Signal Enable in the Host Control2 register to 0 */ctrl &= ~SDHCI_CTRL_VDD_180;sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);if (!IS_ERR(mmc->supply.vqmmc)) {ret = mmc_regulator_set_vqmmc(mmc, ios);if (ret < 0) {pr_warn("%s: Switching to 3.3V signalling voltage failed\n",mmc_hostname(mmc));return -EIO;}}/* Wait for 5ms */usleep_range(5000, 5500);/* 3.3V regulator output should be stable within 5 ms */ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);if (!(ctrl & SDHCI_CTRL_VDD_180))return 0;pr_warn("%s: 3.3V regulator output did not become stable\n",mmc_hostname(mmc));return -EAGAIN;case MMC_SIGNAL_VOLTAGE_180:if (!(host->flags & SDHCI_SIGNALING_180))return -EINVAL;if (!IS_ERR(mmc->supply.vqmmc)) {ret = mmc_regulator_set_vqmmc(mmc, ios);if (ret < 0) {pr_warn("%s: Switching to 1.8V signalling voltage failed\n",mmc_hostname(mmc));return -EIO;}}/** Enable 1.8V Signal Enable in the Host Control2* register*/ctrl |= SDHCI_CTRL_VDD_180;sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);/* Some controller need to do more when switching */if (host->ops->voltage_switch)host->ops->voltage_switch(host);/* 1.8V regulator output should be stable within 5 ms */ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);if (ctrl & SDHCI_CTRL_VDD_180)return 0;pr_warn("%s: 1.8V regulator output did not become stable\n",mmc_hostname(mmc));return -EAGAIN;case MMC_SIGNAL_VOLTAGE_120:if (!(host->flags & SDHCI_SIGNALING_120))return -EINVAL;if (!IS_ERR(mmc->supply.vqmmc)) {ret = mmc_regulator_set_vqmmc(mmc, ios);if (ret < 0) {pr_warn("%s: Switching to 1.2V signalling voltage failed\n",mmc_hostname(mmc));return -EIO;}}return 0;default:/* No signal voltage switch required */return 0;}
mmc_set_ios直接打印pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u timing %u\n"
static inline void mmc_set_ios(struct mmc_host *host)
{struct mmc_ios *ios = &host->ios;pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u ""width %u timing %u\n",mmc_hostname(host), ios->clock, ios->bus_mode,ios->power_mode, ios->chip_select, ios->vdd,1 << ios->bus_width, ios->timing);host->ops->set_ios(host, ios);
}
对于sdhci主控sdhci_ops的成员set_ios:sdhci_set_ios
static const struct mmc_host_ops sdhci_ops = {.request = sdhci_request,.post_req = sdhci_post_req,.pre_req = sdhci_pre_req,.set_ios = sdhci_set_ios,.get_cd = sdhci_get_cd,.get_ro = sdhci_get_ro,.card_hw_reset = sdhci_hw_reset,.enable_sdio_irq = sdhci_enable_sdio_irq,.ack_sdio_irq = sdhci_ack_sdio_irq,.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,.execute_tuning = sdhci_execute_tuning,.card_event = sdhci_card_event,.card_busy = sdhci_card_busy,
};
也即如下,来设置供电,时钟,总线宽度,时序等;这里面的host变成了sdhci_host,
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{struct sdhci_host *host = mmc_priv(mmc);bool reinit_uhs = host->reinit_uhs;bool turning_on_clk = false;u8 ctrl;host->reinit_uhs = false;if (ios->power_mode == MMC_POWER_UNDEFINED)return;if (host->flags & SDHCI_DEVICE_DEAD) {if (!IS_ERR(mmc->supply.vmmc) &&ios->power_mode == MMC_POWER_OFF)mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);return;}/** Reset the chip on each power off.* Should clear out any weird states.*/if (ios->power_mode == MMC_POWER_OFF) {sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);sdhci_reinit(host);}if (host->version >= SDHCI_SPEC_300 &&(ios->power_mode == MMC_POWER_UP) &&!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))sdhci_enable_preset_value(host, false);if (!ios->clock || ios->clock != host->clock) {turning_on_clk = ios->clock && !host->clock;host->ops->set_clock(host, ios->clock);host->clock = ios->clock;if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&host->clock) {host->timeout_clk = mmc->actual_clock ?mmc->actual_clock / 1000 :host->clock / 1000;mmc->max_busy_timeout =host->ops->get_max_timeout_count ?host->ops->get_max_timeout_count(host) :1 << 27;mmc->max_busy_timeout /= host->timeout_clk;}}if (host->ops->set_power)host->ops->set_power(host, ios->power_mode, ios->vdd);elsesdhci_set_power(host, ios->power_mode, ios->vdd);if (host->ops->platform_send_init_74_clocks)host->ops->platform_send_init_74_clocks(host, ios->power_mode);host->ops->set_bus_width(host, ios->bus_width);/** Special case to avoid multiple clock changes during voltage* switching.*/if (!reinit_uhs &&turning_on_clk &&host->timing == ios->timing &&host->version >= SDHCI_SPEC_300 &&!sdhci_presetable_values_change(host, ios))return;ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) {if (ios->timing == MMC_TIMING_SD_HS ||ios->timing == MMC_TIMING_MMC_HS ||ios->timing == MMC_TIMING_MMC_HS400 ||ios->timing == MMC_TIMING_MMC_HS200 ||ios->timing == MMC_TIMING_MMC_DDR52 ||ios->timing == MMC_TIMING_UHS_SDR50 ||ios->timing == MMC_TIMING_UHS_SDR104 ||ios->timing == MMC_TIMING_UHS_DDR50 ||ios->timing == MMC_TIMING_UHS_SDR25)ctrl |= SDHCI_CTRL_HISPD;elsectrl &= ~SDHCI_CTRL_HISPD;}if (host->version >= SDHCI_SPEC_300) {u16 clk, ctrl_2;/** According to SDHCI Spec v3.00, if the Preset Value* Enable in the Host Control 2 register is set, we* need to reset SD Clock Enable before changing High* Speed Enable to avoid generating clock glitches.*/clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);if (clk & SDHCI_CLOCK_CARD_EN) {clk &= ~SDHCI_CLOCK_CARD_EN;sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);}sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);if (!host->preset_enabled) {/** We only need to set Driver Strength if the* preset value enable is not set.*/ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B)ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D)ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D;else {pr_warn("%s: invalid driver type, default to driver type B\n",mmc_hostname(mmc));ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;}sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);host->drv_type = ios->drv_type;}host->ops->set_uhs_signaling(host, ios->timing);host->timing = ios->timing;if (sdhci_preset_needed(host, ios->timing)) {u16 preset;sdhci_enable_preset_value(host, true);preset = sdhci_get_preset_value(host);ios->drv_type = FIELD_GET(SDHCI_PRESET_DRV_MASK,preset);host->drv_type = ios->drv_type;}/* Re-enable SD Clock */host->ops->set_clock(host, host->clock);} elsesdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
上述的sdhci_host的ops成员---也即struct sdhci_ops ;
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,const struct sdhci_pltfm_data *pdata,size_t priv_size)
{struct sdhci_host *host;void __iomem *ioaddr;int irq;ioaddr = devm_platform_ioremap_resource(pdev, 0);if (IS_ERR(ioaddr))return ERR_CAST(ioaddr);irq = platform_get_irq(pdev, 0);if (irq < 0)return ERR_PTR(irq);host = sdhci_alloc_host(&pdev->dev,sizeof(struct sdhci_pltfm_host) + priv_size);if (IS_ERR(host)) {dev_err(&pdev->dev, "%s failed %pe\n", __func__, host);return ERR_CAST(host);}host->ioaddr = ioaddr;host->irq = irq;host->hw_name = dev_name(&pdev->dev);if (pdata && pdata->ops)host->ops = pdata->ops;elsehost->ops = &sdhci_pltfm_ops;if (pdata) {host->quirks = pdata->quirks;host->quirks2 = pdata->quirks2;}platform_set_drvdata(pdev, host);return host;
}
通过soc的控制器驱动调用sdhci_pltfm_init传入sdhci_ops的实例
static const struct sdhci_ops sdhci_am654_ops = {.platform_execute_tuning = sdhci_am654_platform_execute_tuning,.get_max_clock = sdhci_pltfm_clk_get_max_clock,.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,.set_uhs_signaling = sdhci_set_uhs_signaling,.set_bus_width = sdhci_set_bus_width,.set_power = sdhci_set_power_and_bus_voltage,.set_clock = sdhci_am654_set_clock,.write_b = sdhci_am654_write_b,.irq = sdhci_am654_cqhci_irq,.reset = sdhci_and_cqhci_reset,
};
那对对应的set_power就为sdhci_set_power_and_bus_voltage
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,unsigned char mode,unsigned short vdd)
{if (!IS_ERR(host->mmc->supply.vmmc)) {struct mmc_host *mmc = host->mmc;mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);}sdhci_set_power_noreg(host, mode, vdd);
}
支持vmmc属性的话,直接通过vmmc属性所指的regulator来设置电压;
int mmc_regulator_set_ocr(struct mmc_host *mmc,struct regulator *supply,unsigned short vdd_bit)
{int result = 0;int min_uV, max_uV;if (IS_ERR(supply))return 0;if (vdd_bit) {mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);result = regulator_set_voltage(supply, min_uV, max_uV);if (result == 0 && !mmc->regulator_enabled) {result = regulator_enable(supply);if (!result)mmc->regulator_enabled = true;}} else if (mmc->regulator_enabled) {result = regulator_disable(supply);if (result == 0)mmc->regulator_enabled = false;}if (result)dev_err(mmc_dev(mmc),"could not set regulator OCR (%d)\n", result);return result;
}
然后最后通过sdhci_set_power_noreg来同步相关寄存器(启用或禁用设备的电源供应,如卡检测信号、写保护信号等)
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,unsigned short vdd)
{u8 pwr = 0;if (mode != MMC_POWER_OFF) {switch (1 << vdd) {case MMC_VDD_165_195:/** Without a regulator, SDHCI does not support 2.0v* so we only get here if the driver deliberately* added the 2.0v range to ocr_avail. Map it to 1.8v* for the purpose of turning on the power.*/case MMC_VDD_20_21:pwr = SDHCI_POWER_180;break;case MMC_VDD_29_30:case MMC_VDD_30_31:pwr = SDHCI_POWER_300;break;case MMC_VDD_32_33:case MMC_VDD_33_34:/** 3.4 ~ 3.6V are valid only for those platforms where it's* known that the voltage range is supported by hardware.*/case MMC_VDD_34_35:case MMC_VDD_35_36:pwr = SDHCI_POWER_330;break;default:WARN(1, "%s: Invalid vdd %#x\n",mmc_hostname(host->mmc), vdd);break;}}if (host->pwr == pwr)return;host->pwr = pwr;if (pwr == 0) {sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)sdhci_runtime_pm_bus_off(host);} else {/** Spec says that we should clear the power reg before setting* a new value. Some controllers don't seem to like this though.*/if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);/** At least the Marvell CaFe chip gets confused if we set the* voltage and set turn on power at the same time, so set the* voltage first.*/if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);pwr |= SDHCI_POWER_ON;sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)sdhci_runtime_pm_bus_on(host);/** Some controllers need an extra 10ms delay of 10ms before* they can apply clock after applying power*/if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)mdelay(10);}
}
对应到上面提到过的sdhci_am654_write_b
static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg)
{unsigned char timing = host->mmc->ios.timing;u8 pwr;int ret;if (reg == SDHCI_HOST_CONTROL) {switch (timing) {/** According to the data manual, HISPD bit* should not be set in these speed modes.*/case MMC_TIMING_SD_HS:case MMC_TIMING_MMC_HS:val &= ~SDHCI_CTRL_HISPD;}}writeb(val, host->ioaddr + reg);if (reg == SDHCI_POWER_CONTROL && (val & SDHCI_POWER_ON)) {/** Power on will not happen until the card detect debounce* timer expires. Wait at least 1.5 seconds for the power on* bit to be set*/ret = read_poll_timeout(sdhci_am654_write_power_on, pwr,pwr & SDHCI_POWER_ON, 0,MAX_POWER_ON_TIMEOUT, false, host, val,reg);if (ret)dev_info(mmc_dev(host->mmc), "Power on failed\n");}
}
mmc_rescan_try_freq再mmc_power_up之后,调用sdio_reset
if (!(host->caps2 & MMC_CAP2_NO_SDIO))sdio_reset(host);
发送一次cmd52复位sdio
int sdio_reset(struct mmc_host *host)
{int ret;u8 abort;/* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort);if (ret)abort = 0x08;elseabort |= 0x08;return mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
}
实际没做响应也不影响
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,unsigned addr, u8 in, u8 *out)
{struct mmc_command cmd = {};int err;if (fn > 7)return -EINVAL;/* sanity check */if (addr & ~0x1FFFF)return -EINVAL;cmd.opcode = SD_IO_RW_DIRECT;cmd.arg = write ? 0x80000000 : 0x00000000;cmd.arg |= fn << 28;cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;cmd.arg |= addr << 9;cmd.arg |= in;cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;err = mmc_wait_for_cmd(host, &cmd, 0);if (err)return err;if (mmc_host_is_spi(host)) {/* host driver already reported errors */} else {if (cmd.resp[0] & R5_ERROR)return -EIO;if (cmd.resp[0] & R5_FUNCTION_NUMBER)return -EINVAL;if (cmd.resp[0] & R5_OUT_OF_RANGE)return -ERANGE;}if (out) {if (mmc_host_is_spi(host))*out = (cmd.resp[0] >> 8) & 0xFF;else*out = cmd.resp[0] & 0xFF;}return 0;
}
mmc_rescan_try_freq里再sdio_reset之后,调用mmc_go_idle 来发送一次cmd0 且opcode=0;实际没响应,虽然返回错误,但不影响
int mmc_go_idle(struct mmc_host *host)
{int err;struct mmc_command cmd = {};/** Non-SPI hosts need to prevent chipselect going active during* GO_IDLE; that would put chips into SPI mode. Remind them of* that in case of hardware that won't pull up DAT3/nCS otherwise.** SPI hosts ignore ios.chip_select; it's managed according to* rules that must accommodate non-MMC slaves which this layer* won't even know about.*/if (!mmc_host_is_spi(host)) {mmc_set_chip_select(host, MMC_CS_HIGH);mmc_delay(1);}cmd.opcode = MMC_GO_IDLE_STATE;cmd.arg = 0;cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;err = mmc_wait_for_cmd(host, &cmd, 0);mmc_delay(1);if (!mmc_host_is_spi(host)) {mmc_set_chip_select(host, MMC_CS_DONTCARE);mmc_delay(1);}host->use_spi_crc = 0;return err;
}
mmc_rescan_try_freq在mmc_go_idle 之后,调用mmc_attach_sdio来探测卡了
int mmc_attach_sdio(struct mmc_host *host)
{int err, i, funcs;u32 ocr, rocr;struct mmc_card *card;WARN_ON(!host->claimed);err = mmc_send_io_op_cond(host, 0, &ocr);if (err)return err;mmc_attach_bus(host, &mmc_sdio_ops);if (host->ocr_avail_sdio)host->ocr_avail = host->ocr_avail_sdio;rocr = mmc_select_voltage(host, ocr);/** Can we support the voltage(s) of the card(s)?*/if (!rocr) {err = -EINVAL;goto err;}/** Detect and init the card.*/err = mmc_sdio_init_card(host, rocr, NULL);if (err)goto err;card = host->card;/** Enable runtime PM only if supported by host+card+board*/if (host->caps & MMC_CAP_POWER_OFF_CARD) {/** Do not allow runtime suspend until after SDIO function* devices are added.*/pm_runtime_get_noresume(&card->dev);/** Let runtime PM core know our card is active*/err = pm_runtime_set_active(&card->dev);if (err)goto remove;/** Enable runtime PM for this card*/pm_runtime_enable(&card->dev);}/** The number of functions on the card is encoded inside* the ocr.*/funcs = (ocr & 0x70000000) >> 28;card->sdio_funcs = 0;/** Initialize (but don't add) all present functions.*/for (i = 0; i < funcs; i++, card->sdio_funcs++) {err = sdio_init_func(host->card, i + 1);if (err)goto remove;/** Enable Runtime PM for this func (if supported)*/if (host->caps & MMC_CAP_POWER_OFF_CARD)pm_runtime_enable(&card->sdio_func[i]->dev);}/** First add the card to the driver model...*/mmc_release_host(host);err = mmc_add_card(host->card);if (err)goto remove_added;/** ...then the SDIO functions.*/for (i = 0;i < funcs;i++) {err = sdio_add_func(host->card->sdio_func[i]);if (err)goto remove_added;}if (host->caps & MMC_CAP_POWER_OFF_CARD)pm_runtime_put(&card->dev);mmc_claim_host(host);return 0;remove:mmc_release_host(host);
remove_added:/** The devices are being deleted so it is not necessary to disable* runtime PM. Similarly we also don't pm_runtime_put() the SDIO card* because it needs to be active to remove any function devices that* were probed, and after that it gets deleted.*/mmc_sdio_remove(host);mmc_claim_host(host);
err:mmc_detach_bus(host);pr_err("%s: error %d whilst initialising SDIO card\n",mmc_hostname(host), err);return err;
}
然后mmc_attach_sdio调用mmc_send_io_op_cond(host, 0, &ocr);来发送一次cmd5 ,opcode为cmd5;arg为0 ,来读取ocr
7,19882,43397942,-;mmc1: starting CMD5 arg 00000000 flags 000002e1
7,19883,43398236,-;mmc1: sdhci: IRQ status 0x00000001
7,19884,43398247,-;mmc1: req done (CMD5): 0: 10ff8000 00000000 00000000 00000000
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{struct mmc_command cmd = {};int i, err = 0;cmd.opcode = SD_IO_SEND_OP_COND;cmd.arg = ocr;cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;for (i = 100; i; i--) {err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);if (err)break;/* if we're just probing, do a single pass */if (ocr == 0)break;/* otherwise wait until reset completes */if (mmc_host_is_spi(host)) {/** Both R1_SPI_IDLE and MMC_CARD_BUSY indicate* an initialized card under SPI, but some cards* (Marvell's) only behave when looking at this* one.*/if (cmd.resp[1] & MMC_CARD_BUSY)break;} else {if (cmd.resp[0] & MMC_CARD_BUSY)break;}err = -ETIMEDOUT;mmc_delay(10);}if (rocr)*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];return err;
}
mmc_attach_sdio在mmc_send_io_op_cond之后调用mmc_attach_bus来赋值操作函数
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{host->bus_ops = ops;
}
mmc_attach_sdio在mmc_attach_bus之后,调用mmc_select_voltage(host, ocr);来设置电压;如果打印no support for card's volts 就是获取的ocr跟主机的ocr不匹配
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{int bit;/** Sanity check the voltages that the card claims to* support.*/if (ocr & 0x7F) {dev_warn(mmc_dev(host),"card claims to support voltages below defined range\n");ocr &= ~0x7F;}ocr &= host->ocr_avail;if (!ocr) {dev_warn(mmc_dev(host), "no support for card's volts\n");return 0;}if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {bit = ffs(ocr) - 1;ocr &= 3 << bit;mmc_power_cycle(host, ocr);} else {bit = fls(ocr) - 1;/** The bit variable represents the highest voltage bit set in* the OCR register.* To keep a range of 2 values (e.g. 3.2V/3.3V and 3.3V/3.4V),* we must shift the mask '3' with (bit - 1).*/ocr &= 3 << (bit - 1);if (bit != host->ios.vdd)dev_warn(mmc_dev(host), "exceeding card's volts\n");}return ocr;
}
在返回ocr之后,就通过mmc_sdio_init_card(host, rocr, NULL);探测和初始化卡;这里买呢通过mmc_send_io_op_cond(host, ocr, &rocr);发送第二次cmd5,且arg=ocr
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)
{struct mmc_card *card;int err;int retries = 10;u32 rocr = 0;u32 ocr_card = ocr;WARN_ON(!host->claimed);/* to query card if 1.8V signalling is supported */if (mmc_host_uhs(host))ocr |= R4_18V_PRESENT;try_again:if (!retries) {pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));ocr &= ~R4_18V_PRESENT;}/** Inform the card of the voltage*/err = mmc_send_io_op_cond(host, ocr, &rocr);if (err)return err;/** For SPI, enable CRC as appropriate.*/if (mmc_host_is_spi(host)) {err = mmc_spi_set_crc(host, use_spi_crc);if (err)return err;}/** Allocate card structure.*/card = mmc_alloc_card(host, &sdio_type);if (IS_ERR(card))return PTR_ERR(card);if ((rocr & R4_MEMORY_PRESENT) &&mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {card->type = MMC_TYPE_SD_COMBO;if (oldcard && (!mmc_card_sd_combo(oldcard) ||memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {err = -ENOENT;goto mismatch;}} else {card->type = MMC_TYPE_SDIO;if (oldcard && !mmc_card_sdio(oldcard)) {err = -ENOENT;goto mismatch;}}/** Call the optional HC's init_card function to handle quirks.*/if (host->ops->init_card)host->ops->init_card(host, card);mmc_fixup_device(card, sdio_card_init_methods);card->ocr = ocr_card;/** If the host and card support UHS-I mode request the card* to switch to 1.8V signaling level. No 1.8v signalling if* UHS mode is not enabled to maintain compatibility and some* systems that claim 1.8v signalling in fact do not support* it. Per SDIO spec v3, section 3.1.2, if the voltage is already* 1.8v, the card sets S18A to 0 in the R4 response. So it will* fails to check rocr & R4_18V_PRESENT, but we still need to* try to init uhs card. sdio_read_cccr will take over this task* to make sure which speed mode should work.*/if (rocr & ocr & R4_18V_PRESENT) {err = mmc_set_uhs_voltage(host, ocr_card);if (err == -EAGAIN) {mmc_sdio_pre_init(host, ocr_card, card);retries--;goto try_again;} else if (err) {ocr &= ~R4_18V_PRESENT;}}/** For native busses: set card RCA and quit open drain mode.*/if (!mmc_host_is_spi(host)) {err = mmc_send_relative_addr(host, &card->rca);if (err)goto remove;/** Update oldcard with the new RCA received from the SDIO* device -- we're doing this so that it's updated in the* "card" struct when oldcard overwrites that later.*/if (oldcard)oldcard->rca = card->rca;}/** Read CSD, before selecting the card*/if (!oldcard && mmc_card_sd_combo(card)) {err = mmc_sd_get_csd(card, false);if (err)goto remove;mmc_decode_cid(card);}/** Select card, as all following commands rely on that.*/if (!mmc_host_is_spi(host)) {err = mmc_select_card(card);if (err)goto remove;}if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {/** This is non-standard SDIO device, meaning it doesn't* have any CIA (Common I/O area) registers present.* It's host's responsibility to fill cccr and cis* structures in init_card().*/mmc_set_clock(host, card->cis.max_dtr);if (card->cccr.high_speed) {mmc_set_timing(card->host, MMC_TIMING_SD_HS);}if (oldcard)mmc_remove_card(card);elsehost->card = card;return 0;}/** Read the common registers. Note that we should try to* validate whether UHS would work or not.*/err = sdio_read_cccr(card, ocr);if (err) {mmc_sdio_pre_init(host, ocr_card, card);if (ocr & R4_18V_PRESENT) {/* Retry init sequence, but without R4_18V_PRESENT. */retries = 0;goto try_again;}return err;}/** Read the common CIS tuples.*/err = sdio_read_common_cis(card);if (err)goto remove;if (oldcard) {if (card->cis.vendor == oldcard->cis.vendor &&card->cis.device == oldcard->cis.device) {mmc_remove_card(card);card = oldcard;} else {err = -ENOENT;goto mismatch;}}mmc_fixup_device(card, sdio_fixup_methods);if (mmc_card_sd_combo(card)) {err = mmc_sd_setup_card(host, card, oldcard != NULL);/* handle as SDIO-only card if memory init failed */if (err) {mmc_go_idle(host);if (mmc_host_is_spi(host))/* should not fail, as it worked previously */mmc_spi_set_crc(host, use_spi_crc);card->type = MMC_TYPE_SDIO;} elsecard->dev.type = &sd_type;}/** If needed, disconnect card detection pull-up resistor.*/err = sdio_disable_cd(card);if (err)goto remove;/* Initialization sequence for UHS-I cards *//* Only if card supports 1.8v and UHS signaling */if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {err = mmc_sdio_init_uhs_card(card);if (err)goto remove;} else {/** Switch to high-speed (if supported).*/err = sdio_enable_hs(card);if (err > 0)mmc_set_timing(card->host, MMC_TIMING_SD_HS);else if (err)goto remove;/** Change to the card's maximum speed.*/mmc_set_clock(host, mmc_sdio_get_max_clock(card));/** Switch to wider bus (if supported).*/err = sdio_enable_4bit_bus(card);if (err)goto remove;}if (host->caps2 & MMC_CAP2_AVOID_3_3V &&host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {pr_err("%s: Host failed to negotiate down from 3.3V\n",mmc_hostname(host));err = -EINVAL;goto remove;}host->card = card;return 0;mismatch:pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
remove:if (oldcard != card)mmc_remove_card(card);return err;
}
日志如下
7,19885,43398266,-;mmc1: starting CMD5 arg 01300000 flags 000002e1
7,19886,43398559,-;mmc1: sdhci: IRQ status 0x00000001
7,19887,43398570,-;mmc1: req done (CMD5): 0: 91ff8000 00000000 00000000 00000000
C位为1,表示ready状态
返回的R4(91ff8000 )的S18A置为1了,需要切换电平
mmc_set_uhs_voltage来实现切换电压
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
{struct mmc_command cmd = {};int err = 0;/** If we cannot switch voltages, return failure so the caller* can continue without UHS mode*/if (!host->ops->start_signal_voltage_switch)return -EPERM;if (!host->ops->card_busy)pr_warn("%s: cannot verify signal voltage switch\n",mmc_hostname(host));cmd.opcode = SD_SWITCH_VOLTAGE;cmd.arg = 0;cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;err = mmc_wait_for_cmd(host, &cmd, 0);if (err)goto power_cycle;if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))return -EIO;/** The card should drive cmd and dat[0:3] low immediately* after the response of cmd11, but wait 1 ms to be sure*/mmc_delay(1);if (host->ops->card_busy && !host->ops->card_busy(host)) {err = -EAGAIN;goto power_cycle;}if (mmc_host_set_uhs_voltage(host)) {/** Voltages may not have been switched, but we've already* sent CMD11, so a power cycle is required anyway*/err = -EAGAIN;goto power_cycle;}/* Wait for at least 1 ms according to spec */mmc_delay(1);/** Failure to switch is indicated by the card holding* dat[0:3] low*/if (host->ops->card_busy && host->ops->card_busy(host))err = -EAGAIN;power_cycle:if (err) {pr_debug("%s: Signal voltage switch failed, ""power cycling card\n", mmc_hostname(host));mmc_power_cycle(host, ocr);}return err;
}