1.速度切换框图
2.电压切换时序
(1) 主机发送 CMD11 来启动电压切换序列。
(2) 卡返回 R1 响应。
(3) 卡返回响应后,立即驱动 CMD 和 DAT[3:0]信号为低电平。
(4) 主机停止 SDCLK,卡应在主机停止 SDCLK 后开始切换电压。停止 SDCLK 的时间没有说明。主机可以检查任一 CMD 或 DAT[3:0]信号来确认切换序列是否开始,检查哪个信号由主机功能决定。如果主机没有检测到低电平信号,则应中端切换序列,并执行电源周期。
(5) SD 1.8V 输出电压调节器(voltage regulator)应在 5ms 内稳定下来。主机保持 SDCLK
为低至少 5ms。这说明 5ms 对卡是最大时间而对主机是最小时间。
(6) 在(4)之后 5ms 且主机电压稳定后,主机开始在产生 1.8V 的 SDCLK。卡可以检查 SDCLK是否为 1.8V。
(7) 卡在检测到 SDCLK 后,则驱动 CMD 信号为 1.8V 高电平并维持至少一个时钟周期,然后停止驱动(三态)。CMD 在 SDCLK 上升沿被触发(SDR 时序)。
(8) 卡可以通过主机的 pull-up register 检测主机是否将 CMD 驱动到 1.8V。(9) 如果 1.8V 切换成功,则卡驱动 DAT[3:0]到 1.8V 高电平并维持至少一个时钟周期,然后停止驱动(三态)。DAT[3:0]在 SDCLK 上升沿触发(SDR 时序)。DAT[3:0]应该在 SDCLK 产生后1ms 内变为高电平,而主机在 SDCLK 开始后 1ms 检查DAT[3:0]是否变为高电平,也就是说 1ms对卡是最大时间而对主机是最小时间。
2.1示例代码
if(S18A){
host = mmc->priv;
//驱动 CMD 和 DAT[3:0]信号为1.8V
signal_vol(0XD1);
//主机发送 CMD11 来启动电压切换序列。
err = send_cmd11(mmc, S18R);
if(err){
return err;
}
mdelay(1);
//驱动 CMD 和 DAT[3:0]信号为低电平
signal_vol(0XC0);
//主机停止 SDCLK,卡应在主机停止 SDCLK 后开始切换电压
//dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
//dwmci_writel(host, DWMCI_CLKENA, 0);
printf("after cmd 11 mmc->clock = %d \r\n", mmc->clock);
clock = mmc->clock;
mmc->clock = 0;
mmc_set_ios(mmc);
//主机保持 SDCLK 为低至少 5ms。
mdelay(10);
//主机开始在产生 1.8V 的 SDCLK。卡可以检查 SDCLK 是否为 1.8V。
signal_vol(0XD1);
mmc->clock = clock;
mmc_set_ios(mmc);
//biwin_clock_on(mmc->priv);
mdelay(1);
start = get_timer(0);
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
if (get_timer(start) > timeout) {
debug("%s: Timeout on data busy\n", __func__);
printf("%s: Timeout on data busy\n", __func__);
return -ETIMEDOUT;
}
}
}
3.驱动强度,电流以及速度模式的关系
3.1示例代码:
int mmc_sd_init_uhs_card(struct mmc *mmc, u8 * sd_status)
{
int err;
struct mmc_cmd cmd;
struct mmc_data data;
mmc_select_mode(mmc, SPEED_MODE);
switch(SPEED_MODE){
case UHS_SDR104:
mmc_select_mode(mmc, UHS_SDR104);
err = sd_select_current_type(mmc, CURRENT_LIMIT_800);
if(err){
printf("cmd6 select current fail!\r\n");
return err;
}
send_cmd12(mmc);
err = sd_select_speed_type(mmc, SDR104);
if(err){
printf("cmd6 select speed fail!\r\n");
return err;
}
break;
···
}
int mmc_tuning(struct mmc *mmc){
int ret,i,cycle;
int start_time, end_time,time;
u8 start_smpl, smpl, candiates = 0;
struct dwmci_host * host = mmc->priv;
s8 found = -1;
mmc->ddr_mode = 0;
set_pms(3, 200, 1);
set_clksel(mmc, CLK400_default);
mdelay(100);
mmc_set_clock(mmc, 200000000);
mdelay(100);
start_smpl = dw_mci_exynos_get_clksmpl(host);
do {
smpl = dw_mci_exynos_move_next_clksmpl(host,mmc);
ret = mmc_send_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK);
if (!ret)
candiates |= (1 << smpl);
mdelay(50);
} while (start_smpl != smpl);
found = dw_mci_exynos_get_best_clksmpl(candiates);
if (found >= 0) {
dw_mci_exynos_set_clksmpl(host, found);
} else {
ret = -EIO;
}
printf("Success!\n");
return 0;
}