SDIO WiFi 之设备初始化流程分析

关键字 :SDIOWIFINXP

一、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 卡设备初始化的整个过程。

 

 

二、参考文献

《Linux SD卡驱动开发(六) —— SD卡启动过程总体分析》

《Linux SD/MMC/SDIO驱动分析(下)》

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论

每天都是麻辣烫

每天都是麻辣烫

2021年6月28日
棒!好文