一、SDIO 卡设备初始化
Kernel 启动后,执行 mmc_init 函数对 mmc 进行初始化,文件位于:drivers/mmc/core/core.c
static int __init mmc_init(void)
{
mmc_of_reserve_idx();
ret = mmc_register_bus();//注册一个 mmc 总线
ret = mmc_register_host_class();//注册了一个 mmc_host 类
ret = sdio_register_bus();//注册了一个 sdio_bus_type
}
完成 mmc 初始化后,会调用 mmc_rescan 函数开始扫描卡操作,文件位于:drivers/mmc/core/core.c
void mmc_rescan(struct work_struct *work)
{
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
...
for (i = 0; i < ARRAY_SIZE(freqs); i++)
{
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
}
...
}
调用到 mmc_rescan_try_freq 函数,会以 freqs 数组里面给的频率从高到低进行扫描,当某个频率扫描到之后就会退出,文件位于:drivers/mmc/core/core.c
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
...
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host); //首先发送复位命令(不过该命令只有 SDIO 类型的卡才能够识别)
mmc_go_idle(host); //发送 CMD0 使卡进入 IDLE 状态
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail); //发送 CMD8 获取该卡所支持的电压值(3.3v or 1.8v)
/* Order's important: probe SDIO, then SD, then MMC */
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host)) //识别 SDIO 卡
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;
mmc_power_off(host);
return -EIO;
}
执行函数 mmc_attach_sdio,开始识别 SDIO 卡,文件位于:drivers/mmc/core/sdio.c
这个函数大概来说做了如下的工作:
1、向卡发送 CMD5 命令,该命令有两个作用:
第一,通过判断卡是否有反馈信息来判断是否为 SDIO 设备(只有 SDIO 设备才对 CMD5 命令有反馈,其他卡是没有回馈的);
第二,如果是 SDIO 设备,就会给 host 反馈电压信息,就是说告诉 host,本卡所能支持的电压是多少多少。
2、host 根据 SDIO 卡反馈回来的电压要求,给其提供合适的电压。
3、初始化该 SDIO卡
4、注册 SDIO 的各个功能模块
5、注册 SDIO 卡
int mmc_attach_sdio(struct mmc_host *host)
{
...
err = mmc_send_io_op_cond(host, 0, &ocr); //发送 CMD5 读取 OCR 寄存器
mmc_attach_bus(host, &mmc_sdio_ops);
rocr = mmc_select_voltage(host, ocr); //根据读取到 OCR 寄存器配置 SDIO 卡电压
err = mmc_sdio_init_card(host, rocr, NULL, 0); //初始化 SDIO 卡
funcs = (ocr & 0x70000000) >> 28; //funcs 存储该 SDIO 卡所包含的 IO 功能块的个数
card->sdio_funcs = 0;
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
if (host->embedded_sdio_data.funcs) {
...
tmp = sdio_alloc_func(host->card);
...
} else {
err = sdio_init_func(host->card, i + 1); //初始化 SDIO 功能模块
}
...
err = mmc_add_card(host->card); //注册 SDIO 卡
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]); //注册 SDIO 功能模块
}
...
return err;
}
调用 mmc_sdio_init_card 函数,初始化 SDIO 卡,文件位于:drivers/mmc/core/sdio.c
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard, int powered_resume)
{
...
card = mmc_alloc_card(host, NULL);
//通过读取 R4 命令中的 bit27(也就是 Memory Present)来判断此卡是纯 IO 卡 ,还是同时包含存储功能。此时获取到卡的类型为 MMC_TYPE_SDIO
if ((rocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO;
} else {
card->type = MMC_TYPE_SDIO;
}
}
...
if (!powered_resume && !mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);//发送 CMD3 获取设备的从地址(relative addr),并且存放在变量 card->rca 中
}
...
if (!powered_resume && !mmc_host_is_spi(host)) {
err = mmc_select_card(card); //发送 CMD7,选中相应从地址的卡
}
if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
mmc_set_clock(host, card->cis.max_dtr); //设置卡工作的时钟频率
if (card->cccr.high_speed) {
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
}
}
...
err = sdio_read_common_cis(card); //发送 CMD52 命令
...
}
调用 sdio_init_func 函数初始化 SDIO 功能模块,文件位于:drivers/mmc/core/sdio.c
static int sdio_init_func(struct mmc_card *card, unsigned int fn)
{
...
func = sdio_alloc_func(card); //初始化 SDIO 功能结构体
func->num = fn; //从 1-7 给功能块编号
if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) {
ret = sdio_read_fbr(func); //读取 SDIO 卡中的 FBR 寄存器中关于该卡的功能类型的数据
ret = sdio_read_func_cis(func); //读取 SDIO 卡中的 CIS 寄存器的内容
} else {
func->vendor = func->card->cis.vendor;
func->device = func->card->cis.device;
func->max_blksize = func->card->cis.blksize;
}
card->sdio_func[fn - 1] = func;
sdio_remove_func(func);
return ret;
}
调用 sdio_add_func 函数注册 SDIO 功能模块,文件位于:drivers/mmc/core/sdio_bus.c
int sdio_add_func(struct sdio_func *func)
{
int ret;
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
sdio_set_of_node(func);
sdio_acpi_set_handle(func);
device_enable_async_suspend(&func->dev);
ret = device_add(&func->dev);
if (ret == 0)
sdio_func_set_present(func);
return ret;
}
调用 mmc_add_card 函数注册 SDIO 卡,文件位于:drivers/mmc/core/bus.c
注册成功后会在设备目录 /sys/bus/mmc/devices/ 底下创建一个类似 mmc2:0001 名称的卡设备
int mmc_add_card(struct mmc_card *card)
{
...
static const char *const uhs_speeds[] = {
[UHS_SDR12_BUS_SPEED] = "SDR12 ",
[UHS_SDR25_BUS_SPEED] = "SDR25 ",
[UHS_SDR50_BUS_SPEED] = "SDR50 ",
[UHS_SDR104_BUS_SPEED] = "SDR104 ",
[UHS_DDR50_BUS_SPEED] = "DDR50 ",
};
dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
switch (card->type) {
case MMC_TYPE_MMC:
type = "MMC";
break;
case MMC_TYPE_SD:
type = "SD";
if (mmc_card_blockaddr(card)) {
if (mmc_card_ext_capacity(card))
type = "SDXC";
else
type = "SDHC";
}
break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
case MMC_TYPE_SD_COMBO:
type = "SD-combo";
if (mmc_card_blockaddr(card))
type = "SDHC-combo";
break;
default:
type = "?";
break;
}
if (mmc_card_uhs(card) &&
(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
if (mmc_host_is_spi(card->host)) {
pr_info("%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_hs(card) ? "high speed " : "",
mmc_card_ddr52(card) ? "DDR " : "",
type);
} else {
//卡正确识别后,内核会打印出这部分信息
pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_hs(card) ? "high speed " : ""),
mmc_card_hs400(card) ? "HS400 " :
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_hs400es(card) ? "Enhanced strobe " : "",
mmc_card_ddr52(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);
}
#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
#endif
mmc_init_context_info(card->host);
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
device_enable_async_suspend(&card->dev);
ret = device_add(&card->dev);//将 card 注册进 linux 设备模型
mmc_card_set_present(card);
return 0;
}
到这里就完成整个 SDIO 卡设备初始化的整个过程。
评论
每天都是麻辣烫
2021年6月28日