SDIO MSHC 电压切换

背景

我们的sdio访问sd card过去一直跑在低频上,HS50M。前段时间给eMMc添加了HS200模式,eMMc的总线模式定义是这样的:
在这里插入图片描述
可以看到1.8V的IO 电压可以支持所有模式,我们过去的芯片,由硬件部门放到evb上,其IO 电压要不就是定死的,要不就是用跳帽来选择电压是3V3或者1V8,不支持电压切换,这对eMMc来说还尚可接受,因为我们可以将它定到1V8上,反正eMMc在这个电压上可以切换到各个速度,emmc的hs200也就在这个条件下跑起来了。但是SD Card就没那么幸运了,SD Card的标准是:
在这里插入图片描述
我们过去的板子都IO电压都是定死了3.3v,最高跑到HS50这个模式,所以如果要切换到SDR12以及后面的模式,还是需要一个能够动态切换电压的方法,一般来说SDIO刚初始化完毕会用1线模式去读取卡id,使用400k,300K,100K这种兼容模式去做协商,这些模式需要工作的3.3V,随后才能调到高速。

控制器支持

DWC mobile storage 控制器支持SD3.0 UHS-1,能够在SD模式下做电压切换,可以应用到SDHC,SDXC卡上。
注意,UHS-1仅支持4bit模式

详细切换方法:

以下部分摘自databook手册:
在这里插入图片描述
翻译:
只有在SD mode下才可以执行电压切换,无论在SD mode还是SPI mode,首先要发送CMD0来选择bus mode,card必须在SD mode下才能应用1.8v信号模式,在此期间card不能在不带电源周期时被切换到SPI模式或者3.3V信号

如果嵌入式系统的System BIOS已经知道它连接了SD 3.0卡,那么驱动程序会编程DWC_mobile_storage来启动ACMD41。
软件从ACMD41的响应得知卡是否支持电压切换到1.8V。

  • 如果ACMD41响应的32位为1’b1,则卡支持电压切换,下一个命令cmd11调用电压切换顺序。CMD11启动后,软件必须在CSR空间中用适当的卡号对VOLT REG寄存器进行编程。
  • 如果ACMD41的32位响应为1’ 0 -卡不支持电压切换,CMD11不应该启动。

如果卡和主机控制器接受电压切换,则它们支持UHS-1数据传输模式。电压切换到1.8V后,SDR12为默认速度。由于UHS-1只能在4位模式下使用,因此软件必须启动ACMD6并将卡数据宽度更改为4位模式;ACMD6是在任何UHS-1速度驱动。如果主机想要选择DDR模式的数据传输,那么软件必须在CSR空间中用相应的卡号对DDR_REG寄存器进行编程。要从SDR或DDR模式中进行选择,应在CLKDIV寄存器中编程适当的值。

获取更多的VOLT_REG和DDR_REG寄存器,参考USH_REG:
在这里插入图片描述
这个寄存器的两个域就是VOLT_REG和DDR_REG
VOLT_REG通过一个外部的电压调节器,决定了反馈到buffers的电压
0 - buffers使用3.3v vdd供电
1- buffers使用1.8v vdd供电
这些bit功能作为主机控制器的输出,反馈外部电压调节器,电压调节器必须切换一个特定的卡的buffers的电压到要么3.3v要么1.8v,取决于写入该寄存器的值。
VOLT_REG[0]应该为card 0写入1来使它工作在1.8V

下图为ACMD41的命令参数和命令响应

ACMD41命令参数

在这里插入图片描述

ACMD41命令响应

在这里插入图片描述
如果参数中S18R为1,且响应中S18A为1,此时为3.3V状态,主机便可以发送CMD11进行电压切换,其他情况下不能执行CMD11

Linux内核驱动对电压切换的支持

Linux内核驱动在mmc子系统的core层,实现了通用的,对uhs卡的电压配置接口。

mmc_set_uhs_voltage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
{
struct mmc_command cmd = {};
int err = 0;
u32 clock;

/*
* 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;
}
/*
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);

if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
/*
* Voltages may not have been switched, but we've already
* sent CMD11, so a power cycle is required anyway
*/
err = -EAGAIN;
goto power_cycle;
}

/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
mmc_delay(10);
host->ios.clock = clock;
mmc_set_ios(host);

/* 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;
}

解析:

判断主机设备驱动是否实现的start_signal_voltage_switch函数,主要用于判断是否支持切换电压。没有实现就直接返回EPERM错误。
就DWC控制器而言,其ops实现为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios,
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
.hw_reset = dw_mci_hw_reset,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
.ack_sdio_irq = dw_mci_ack_sdio_irq,
.execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
.init_card = dw_mci_init_card,
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
};

dw_mci_switch_voltage实现为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
u32 uhs;
u32 v18 = SDMMC_UHS_18V << slot->id;
int ret;

if (drv_data && drv_data->switch_voltage)
return drv_data->switch_voltage(mmc, ios);

/*
* Program the voltage. Note that some instances of dw_mmc may use
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
* does no harm but you need to set the regulator directly. Try both.
*/
uhs = mci_readl(host, UHS_REG);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
uhs &= ~v18;
else
uhs |= v18;

if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);

if (ret) {
dev_dbg(&mmc->class_dev,
"Regulator set error %d - %s V\n",
ret, uhs & v18 ? "1.8" : "3.3");
return ret;
}
}
mci_writel(host, UHS_REG, uhs);

return 0;
}

对dw_mci_drv_data *drv_data 该驱动数据结构体的实现,我们暂时比较poor,看rk的:

1
2
3
4
5
6
7
8
9
10
11
12
static const struct dw_mci_drv_data rk2928_drv_data = {
.init = dw_mci_rockchip_init,
};

static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps,
.num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3288_parse_dt,
.init = dw_mci_rockchip_init,
};

rk也没有实现这个接口,所以假设这里不会走:

1
2
if (drv_data && drv_data->switch_voltage)
return drv_data->switch_voltage(mmc, ios);

下面是这么一段注释:

1
2
3
4
5
/*
* Program the voltage. Note that some instances of dw_mmc may use
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
* does no harm but you need to set the regulator directly. Try both.
*/

说明后面的代码是配置电压,要注意一些dw_mmc的控制器实现也许使用UHS_REG来实现,其他的一些实例例如exynos,UHS_REG不生效,你需要直接设置regulator。下面的代码是二者都尝试。

先读取UHS_REG寄存器到uhs临时变量,判断本函数传入的ios结构的signal_voltage是否为3.3V电压模式,这个结构一般包含mmc控制器当前的配置状态

1
2
3
4
5
6
7
8
#define SDMMC_UHS_18V			BIT(0)
u32 v18 = SDMMC_UHS_18V << slot->id;

uhs = mci_readl(host, UHS_REG);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
uhs &= ~v18;
else
uhs |= v18;

上面的代码就是在设置对应控制器卡槽下的UHS_REG对应的bit为,如果要求设置电压为3.3,则清除对应为,反之,置位。

下面的代码就是先判断是否实现vqmmc regulator,设置该regulator到对应电压状态

1
2
3
4
5
6
7
8
9
10
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);

if (ret) {
dev_dbg(&mmc->class_dev,
"Regulator set error %d - %s V\n",
ret, uhs & v18 ? "1.8" : "3.3");
return ret;
}
}

函数实现为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* mmc_regulator_set_vqmmc - Set VQMMC as per the ios
*
* For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
* That will match the behavior of old boards where VQMMC and VMMC were supplied
* by the same supply. The Bus Operating conditions for 3.3V signaling in the
* SD card spec also define VQMMC in terms of VMMC.
* If this is not possible we'll try the full 2.7-3.6V of the spec.
*
* For 1.2V and 1.8V signaling we'll try to get as close as possible to the
* requested voltage. This is definitely a good idea for UHS where there's a
* separate regulator on the card that's trying to make 1.8V and it's best if
* we match.
*
* This function is expected to be used by a controller's
* start_signal_voltage_switch() function.
*/
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct device *dev = mmc_dev(mmc);
int ret, volt, min_uV, max_uV;

/* If no vqmmc supply then we can't change the voltage */
if (IS_ERR(mmc->supply.vqmmc))
return -EINVAL;

switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_120:
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
1100000, 1200000, 1300000);
case MMC_SIGNAL_VOLTAGE_180:
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
1700000, 1800000, 1950000);
case MMC_SIGNAL_VOLTAGE_330:
ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
if (ret < 0)
return ret;

dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
__func__, volt, max_uV);

min_uV = max(volt - 300000, 2700000);
max_uV = min(max_uV + 200000, 3600000);

/*
* Due to a limitation in the current implementation of
* regulator_set_voltage_triplet() which is taking the lowest
* voltage possible if below the target, search for a suitable
* voltage in two steps and try to stay close to vmmc
* with a 0.3V tolerance at first.
*/
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
min_uV, volt, max_uV))
return 0;

return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
2700000, volt, 3600000);
default:
return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
int min_uV, int target_uV,
int max_uV)
{
/*
* Check if supported first to avoid errors since we may try several
* signal levels during power up and don't want to show errors.
*/
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
return -EINVAL;

return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
max_uV);
}

电压调节器,在dts中注册,我们看一下rk的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
&sdmmc {
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
card-detect-delay = <200>;
disable-wp;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
vmmc-supply = <&vcc_sdmmc>;
vqmmc-supply = <&vccio_sd>;
status = "okay";
};

vcc_sdmmc: sdmmc-regulator {
compatible = "regulator-fixed";
regulator-name = "sdmmc-supply";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio7 RK_PB3 GPIO_ACTIVE_LOW>;
startup-delay-us = <100000>;
vin-supply = <&vcc_io>;
};

vccio_sd: REG5 {
regulator-name = "VCCIO_SD";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};

其probe函数中应该有对regulator的解析:
需要注意这个mmc_host结构下的:

1
2
3
4
5
6
7
8
9
10
struct mmc_host {
...
struct mmc_supply supply;
...
}

struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
struct regulator *vqmmc; /* Optional Vccq supply */
};

vmmc代表卡的电压,一般是3.3V,可以进行gpio开关控制

vqmmc代表的是信号线bus上的上拉电压,以及控制器的sd卡bank电压,可以切换为1.8和3.3V

dw mmc对vqmmc的解析和注册:

1
2
3
kernel/drivers/mmc/host/dw_mmc.c
dw_mci_init_slot
ret = mmc_regulator_get_supply(mmc);

mmc_regulator_get_supply函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int mmc_regulator_get_supply(struct mmc_host *mmc)
{
struct device *dev = mmc_dev(mmc);
int ret;

mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");

if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_dbg(dev, "No vmmc regulator found\n");
} else {
ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
if (ret > 0)
mmc->ocr_avail = ret;
else
dev_warn(dev, "Failed getting OCR mask: %d\n", ret);
}

if (IS_ERR(mmc->supply.vqmmc)) {
if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_dbg(dev, "No vqmmc regulator found\n");
}

return 0;
}
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);

mmc_set_uhs_voltage 后半部

前面介绍了mmc_set_uhs_voltage 函数内对主机控制器驱动start_signal_voltage_switch回调是否实现的判断,如果没有实现,就直接返回,说明不支持切换。
实现了主机控制器的voltage switch才能执行下面的流程:

1
2
3
4
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);

这里是在构造CMD11的命令参数

1
mmc_wait_for_cmd(host, &cmd, 0);

发送命令,等待完成

1
2
3
4
5
6
7
8
9
10
/*
* 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;
}
/*

card应当立即驱动cmd和dat[0:3]到低电平,在cmd11响应之后,这里等待一秒做确保

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);

if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
/*
* Voltages may not have been switched, but we've already
* sent CMD11, so a power cycle is required anyway
*/
err = -EAGAIN;
goto power_cycle;
}

在执行电压切换过程中,clock必须被gated 5ms,这具体得看sd spec。
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)执行1.8v的电压切换,它内部就是调用mmc_set_uhs_voltage,失败的话需要mmc_power_cycle。