本文以kernel-4.4,以MTK平台分析host端初始化的扫卡流程
mtk平台host驱动代码在drivers/mmc/host/mtk-sdio.c,sd card,mmc和sdio card的相关核心代码都存在drivers/mmc/目录中,本文只关心host是如何扫描到card的,下面分析host扫卡流程。
1,host驱动初始化
平台驱动代码中注册platform_driver,匹配到device调用到probe函数int msdc_drv_probe(struct platform_device *pdev),下面代码省略了很多host的初始化的细节,只截取了我们关心的部分:
static int msdc_drv_probe(struct platform_device *pdev)
{
...
在mmc core中分配一个mmc_host结构体
mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
...
ret = mmc_add_host(mmc);
}
如上所示代码中主要调用两个接口来向mmc core注册host驱动
- 调用 mmc_alloc_host用于分配一个struct mmc_host 结构体,在该函数中初始化了一个延时工作队列INIT_DELAYED_WORK(&host->detect, mmc_rescan);后续的扫卡工作主要是靠mmc_rescan来完成,这点在本文之后会分析。
- mmc_add_host,主要的工作是初始化host,并对sdio总线上进行一次detect用于扫描总线的card
下面对mmc_add_host进行分析
int mmc_add_host(struct mmc_host *host)
{
int err;
err = device_add(&host->class_dev);
if (err)
return err;
mmc_start_host(host);
return 0;
}
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false); detect SDIO总线上是否存在card
}
void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
bool cd_irq)
{
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
}
从中可以看出最终调用到的是在mmc_alloc_host中初始化的延迟工作队列host->detect,
所以mmc_rescan函数会被调用
从中我们总结出HOST在初始化阶段的detect 流程:
platform_driver probe接口msdc_drv_probe
>mmc_alloc_host
>> 初始化延迟工作队列host->detect,设置工作队列work func为mmc_rescan
>mmc_start_host
>>mmc_schedule_delayed_work(&host->detect, delay); 最终会调用到host->detect延迟工作队列work func mmc_rescan
2,mmc_rescan接口分析
下面是具体的扫卡过程
void mmc_rescan(struct work_struct *work)
{
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
//unsigned freqs[] = { 400000, 300000, 200000, 100000 };扫卡频率
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
}
scan card设置的频率是在400k到host支持的最低频率,逐个频率之间进行扫描
比如说400k频率扫卡失败了,接着进行300k的频率进行扫描,直到host支持的最低频率,或者100k为止
int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
这个power_up接口主要做两件事:
1)根据DTS中mmc-pwrseq中的配置对card进行上电操作
2)对sdio host controller进行上电,设置3.3或者1.8v信号电压
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);
在power up上电之后通过command对card进行复位设置初始状态
发送CMD52 REG6 ,对card进行reset
sdio_reset(host);
发送CMD0 设置card为idle状态
mmc_go_idle(host);
发送CMD8 询问card是否支持host的电压域,如果不是memory card那么不回复
mmc_send_if_cond(host, host->ocr_avail);
//依次进行匹配是否是SDIO,SD card,MMC设备
/* Order's important: probe SDIO, then SD, then MMC */
if (!mmc_attach_sdio(host))
return 0;
if (!mmc_attach_sd(host))
return 0;
if (!mmc_attach_mmc(host))
return 0;
mmc_power_off(host);
return -EIO;
}
我们只分析SDIO card,所有接下来分析mmc_attach_sdio(host)
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr, rocr;
struct mmc_card *card;
获取sdio card的电压域
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;
选择host和card都支持的电压rocr
rocr = mmc_select_voltage(host, ocr);
检测并初始化card
err = mmc_sdio_init_card(host, rocr, NULL, 0);
if (err)
goto err;
card = host->card;
/*
* The number of functions on the card is encoded inside
* the ocr.
*/
funcs = (ocr & 0x70000000) >> 28; 从OCR中获取card中的function number
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;
}
//dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
//调用device_add(&card->dev)
err = mmc_add_card(host->card);
if (err)
goto remove_added;
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
//调用dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);//设置name
//调用device_add(&func->dev);
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
return err;
}
这个函数比较长,拆分为几部分分析:
- mmc_send_io_op_cond通过CMD5,获取card支持的电压域,根据host所支持的电压域,得到host和card都支持的电压域rocr,并将rocr传入mmc_sdio_init_card。有关CMD的说明可以参考:二,sdio总线之cmd
- mmc_sdio_init_card,这个函数用于检测,并初始化card,从CCCR寄存器获取card支持的speed模式,bus width。从CIS Area中获取CIS 信息,比如vendor id,device id等。这个函数接下来会分析。
- 根据在CMD5 response中获取到的function number初始化每个function,,获取每个function的CIS信息,并为每个function分配一个device,设置bus type为sdio_bus_type,拥有匹配sdio_driver。
- mmc_add_card调用device_add(&card->dev),将card->dev添加到device框架中,如果成功扫到了card,会有这么一句打印:
mmc1: new ultra high speed SDR104 SDIO card at address 0001(从log中可以看出card支持UHS mode,支持速率为SDR104,RCA为1)
- sdio_add_func()将所有的function device调用device_add(&func->dev);添加到device架构中。
接着分析mmc_sdio_init_card函数
int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard, int powered_resume)
{
如果host支持uhs模式,则switch信号电压为1.8v
if (mmc_host_uhs(host))
ocr |= R4_18V_PRESENT;
设置card支持的电压为ocr,这个电压是mmc_sdio_init_card传入的rocr
if (!powered_resume) {
err = mmc_send_io_op_cond(host, ocr, &rocr);
分配一个mmc_card结构体
card = mmc_alloc_card(host, NULL);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
如果card存在memory部分,那么获取card的cid,并设置card类型是SD_COMBO 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 && (oldcard->type != MMC_TYPE_SD_COMBO ||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
mmc_remove_card(card);
return -ENOENT;
}
} else {
如果没有memory部分,那么card类型为MMC_TYPE_SDIO card
card->type = MMC_TYPE_SDIO;
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
mmc_remove_card(card);
return -ENOENT;
}
}
如果card支持UHS-I(rocr 中S18A=1),并且和host也支持UHS-I,那么进行信号电压电平切换为1.8v
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
发送CMD11,然后进行电压切换操作
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
ocr_card);
if (err == -EAGAIN) {
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
mmc_remove_card(card);
retries--;
goto try_again;
} else if (err) {
ocr &= ~R4_18V_PRESENT;
}
err = 0;
} else {
ocr &= ~R4_18V_PRESENT;
}
/*
* For native busses: set card RCA and quit open drain mode.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
发送CMD3,获取RCA
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;
}
/*
* Read CSD, before selecting the card
*/
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
如果包含memory部分,则获取CSD
err = mmc_sd_get_csd(host, card);
if (err)
return err;
mmc_decode_cid(card);
}
/*
* Select card, as all following commands rely on that.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
发送CMD7 select card
err = mmc_select_card(card);
if (err)
goto remove;
}
获取CCCR信息,下面会分析
err = sdio_read_cccr(card, ocr);
if (err)
goto remove;
获取Common CIS信息,下面会分析
err = sdio_read_common_cis(card);
if (err)
goto remove;
card->ocr = ocr_card;
mmc_fixup_device(card, NULL);
/*
* 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 */
设置card speed,wide
if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode)
err = mmc_sdio_init_uhs_card(card);
}
CCCR(Card Common Control Registers)寄存器读取
int sdio_read_cccr(struct mmc_card *card, u32 ocr)
{
int ret;
int cccr_vsn;
int uhs = ocr & R4_18V_PRESENT;
unsigned char data;
unsigned char speed;
读取CCCR寄存器0x00,获取CCCR version(0-3bit)和sdio version(4-7bit)
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
if (ret)
goto out;
CCCR version (1.0 ,1.1, 2.0, 3.0)
cccr_vsn = data & 0x0f;
if (cccr_vsn > SDIO_CCCR_REV_3_00) {
pr_err("%s: unrecognised CCCR structure version %d\n",
mmc_hostname(card->host), cccr_vsn);
return -EINVAL;
}
SDIO version(1.0 ,1.1, 1.2, 2.0, 3.0)
card->cccr.sdio_vsn = (data & 0xf0) >> 4;
获取card capability(地址0x08)
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
if (ret)
goto out;
是否支持多块传输,该标志表示card是否支持CMD53进行多块传输,如果该位为1则支持
if (data & SDIO_CCCR_CAP_SMB)
card->cccr.multi_block = 1;
如果该位为1,则代表该卡为低速卡(Low-Speed Card),如果为0,则是全速卡(High Speed card)
if (data & SDIO_CCCR_CAP_LSC)
card->cccr.low_speed = 1;
该标志代表Low -Speed Card是否支持4 bit模式,当LSC=1,并且支持4bit模式,该标志则为1
if (data & SDIO_CCCR_CAP_4BLS)
card->cccr.wide_bus = 1;
当CCCR version大于1.1
if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
寄存器0x12
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
if (ret)
goto out;
//bit 0,These bits tell the host if the card supports Master Power Control.
//SMPC=0: The total card power is up to 720mW (3.6Vx200mA),
//even if all functions are active (IOEx=1). EMPC, SPS and EPS shall be zero.
//SMPC=1: The total card power may exceed 720mW (3.6Vx200mA).
//Controls of EMPC, SPS and EPS are available.
if (data & SDIO_POWER_SMPC)
card->cccr.high_power = 1;
}
如果cccr_version 大于等于2.0,读取speed寄存器0x13
if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
//读取Bus Speed Select寄存器获取速度
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
if (ret)
goto out;
card->scr.sda_spec3 = 0;
card->sw_caps.sd3_bus_mode = 0;
card->sw_caps.sd3_drv_type = 0;
if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) {
card->scr.sda_spec3 = 1;
读取UHS-I Support寄存器(0x14)
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_UHS, 0, &data);
host 是否支持uhs模式
if (mmc_host_uhs(card->host)) {
if (data & SDIO_UHS_DDR50)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_DDR50;//card支持 UHS_DDR50
if (data & SDIO_UHS_SDR50)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_SDR50;//card支持 UHS_SDR50
if (data & SDIO_UHS_SDR104)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_SDR104;//card支持UHS_SDR104
}
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
if (ret)
goto out;
if (data & SDIO_DRIVE_SDTA)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A;
if (data & SDIO_DRIVE_SDTC)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
if (data & SDIO_DRIVE_SDTD)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_INTERRUPT_EXT, 0, &data);
if (ret) {
goto out;
} else {
//card是否支持同步中断
if (data & SDIO_INTERRUPT_EXT_SAI) {
card->cccr.sai = 1;
data |= SDIO_INTERRUPT_EXT_EAI;//使能同步中断
ret = mmc_io_rw_direct(card, 1, 0,
SDIO_CCCR_INTERRUPT_EXT,
data, NULL);
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_CCCR_INTERRUPT_EXT,
0, &data);
if (ret)
goto out;
else if (data & SDIO_INTERRUPT_EXT_EAI)
card->cccr.eai = 1;
}
}
}
/* if no uhs mode ensure we check for high speed */
if (!card->sw_caps.sd3_bus_mode) {
如果card 不支持uhs模式,则根据speed(寄存器0x13)判断是否支持high speed
if (speed & SDIO_SPEED_SHS) {
card->cccr.high_speed = 1;
card->sw_caps.hs_max_dtr = 50000000; //host最大时钟频率为50Mhz
} else {
card->cccr.high_speed = 0;
card->sw_caps.hs_max_dtr = 25000000;//host最大时钟频率为25Mhz
}
}
}
return ret;
}
Card Speed分类:
是否支持UHS,OCR中S18A=1,则card支持,否则不支持,UHShost模式信号电压需要切换到1.8v,非UHS模式的信号电压为3.3V
1)1.8V信号电压UHS模式:
信号电压1.8V时,UHS模式的选择是通过Bus Speed Select寄存器(0x13),BSS[2:0]进行选择,如果SHS=0,那么选择无效
BSS[2:0] Bus Speed 1.8V Max Clock Frequency
000b: SDR12 25MHz
001b: SDR25 50MHz
010b: SDR50 100MHz
011b: SDR104 208MHz
100b: DDR50 50MHz
101b-111b: Reserved
2)3.3V信号电压模式
只有当SHS=1时才能选择High Speed
BSS[2:0] Bus Speed 3.3V Max. Clock Frequency
xx0b: Default Speed 25MHz
xx1b: High Speed 50MHz
//获取common cis信息
CCCR的地址区间为0x00 - 0xff
FBR地址区间为0xn00 - 0xnff (n从1到7)
int sdio_read_common_cis(struct mmc_card *card)
{
//获取common cis信息,获取的是common CIS 所以func设置为NULL
return sdio_read_cis(card, NULL);
}
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
{
int ret;
struct sdio_func_tuple *this, **prev;
unsigned i, ptr = 0;
/*
* Note that this works for the common CIS (function number 0) as
* well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
* have the same offset.
*/
for (i = 0; i < 3; i++) {
unsigned char x, fn;
if (func)
fn = func->num;
else
fn = 0;
这个for循环主要做这么几个事情:
1)在SDIO_FBR_CIS 寄存器中获取CIS信息所在的地址,如果是Common CIS则SDIO_FBR_BASE(fn)=0
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
if (ret)
return ret;
2)将得到的地址拼成一位3字节的地址,这个地址实际上是CIS Area中的地址
ptr |= x << (i * 8);
}
if (func)
prev = &func->tuples;
else
prev = &card->tuples;
BUG_ON(*prev);
接下里的do循环中做这几件事情来解析CIS中的tuple数据:
1)获取tuple的code码
2)获取tuple的size大小tpl_link
3)根据tpl_link大小读取这么多个字节的data
4)根据tuple的code码,解析data
do {
unsigned char tpl_code, tpl_link;
读取ptr地址中的数据,tuple数据的第一个字节代表tuple的code码
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
if (ret)
break;
/* 0xff means we're done */ 如果code码为0xff则退出
if (tpl_code == 0xff)
break;
/* null entries have no link field or data */
if (tpl_code == 0x00)
continue;
code码接下来是tuple的数据的大小
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
if (ret)
break;
/* a size of 0xff also means we're done */
if (tpl_link == 0xff)
break;
分配一个tuple结构体内存,其中tpl_link大小用来存储data
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
if (!this)
return -ENOMEM;
读取tpl_link个字节大小到data[i]
for (i = 0; i < tpl_link; i++) {
ret = mmc_io_rw_direct(card, 0, 0,
ptr + i, 0, &this->data[i]);
if (ret)
break;
}
if (ret) {
kfree(this);
break;
}
根据tuple code,对data数据进行解析
/* Try to parse the CIS tuple */
ret = cis_tpl_parse(card, func, "CIS",
cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
tpl_code, this->data, tpl_link);
if (ret == -EILSEQ || ret == -ENOENT) {
/*
* The tuple is unknown or known but not parsed.
* Queue the tuple for the function driver.
*/
this->next = NULL;
this->code = tpl_code;
this->size = tpl_link;
*prev = this;
prev = &this->next;
if (ret == -ENOENT) {
/* warn about unknown tuples */
pr_warn_ratelimited("%s: queuing unknown"
" CIS tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host),
tpl_code, tpl_link);
}
/* keep on analyzing tuples */
ret = 0;
} else {
/*
* We don't need the tuple anymore if it was
* successfully parsed by the SDIO core or if it is
* not going to be queued for a driver.
*/
kfree(this);
}
ptr += tpl_link;
} while (!ret);
/*
* Link in all unknown tuples found in the common CIS so that
* drivers don't have to go digging in two places.
*/
if (func)
*prev = card->tuples;
return ret;
}
分析一下这个函数的实现cis_tpl_parse
static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
const char *tpl_descr,
const struct cis_tpl *tpl, int tpl_count,
unsigned char code,
const unsigned char *buf, unsigned size)
{
int i, ret;
/* look for a matching code in the table */
for (i = 0; i < tpl_count; i++, tpl++) {
if (tpl->code == code) /根据code知道对应的tuple解析函数
break;
}
if (i < tpl_count) {
if (size >= tpl->min_size) {
if (tpl->parse)
ret = tpl->parse(card, func, buf, size);调用tuple解析函数
else
ret = -EILSEQ; /* known tuple, not parsed */
} else {
/* invalid tuple */
ret = -EINVAL;
}
if (ret && ret != -EILSEQ && ret != -ENOENT) {
pr_err("%s: bad %s tuple 0x%02x (%u bytes)\n",
mmc_hostname(card->host), tpl_descr, code, size);
}
} else {
/* unknown tuple */
ret = -ENOENT;
}
return ret;
}
我们在cis_tpl_parse中传进去的tpl参数实际上是cis_tpl_list,
结构体第一个成员是tuple code,第二个成员是tuple数据data的长度,第三个成员是解析
static const struct cis_tpl cis_tpl_list[] = {
{ 0x15, 3, cistpl_vers_1 },
{ 0x20, 4, cistpl_manfid },
{ 0x21, 2, /* cistpl_funcid */ },
{ 0x22, 0, cistpl_funce },
};
tuple code 0x20对应的tuple信息实际上是vendor id,devie id,解析函数如下
int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned int vendor, device;
/* TPLMID_MANF */
vendor = buf[0] | (buf[1] << 8);
/* TPLMID_CARD */
device = buf[2] | (buf[3] << 8);
if (func) {
func->vendor = vendor;
func->device = device;
} else {
card->cis.vendor = vendor;
card->cis.device = device;
}
return 0;
}
4,sdio_driver匹配原则
为了弄清sdio bus 的match原则,我们先分析在什么地方设置dev的bus type
在mmc_sdio_init_card函数中,sdio_alloc_func为function分配一个sdio_func结构体,还初始化了function的device
struct sdio_func *sdio_alloc_func(struct mmc_card *card)
{
struct sdio_func *func;
func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
if (!func)
return ERR_PTR(-ENOMEM);
/*
* allocate buffer separately to make sure it's properly aligned for
* DMA usage (incl. 64 bit DMA)
*/
func->tmpbuf = kmalloc(4, GFP_KERNEL);
if (!func->tmpbuf) {
kfree(func);
return ERR_PTR(-ENOMEM);
}
func->card = card;
//初始化func的dev
device_initialize(&func->dev);
func->dev.parent = &card->dev;
func->dev.bus = &sdio_bus_type; 设置func->dev的bus type
func->dev.release = sdio_release_func;
return func;
}
值得注意的是sdio_bus_type的match函数
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_groups = sdio_dev_groups,
.match = sdio_bus_match,
.uevent = sdio_bus_uevent,
.probe = sdio_bus_probe,
.remove = sdio_bus_remove,
.pm = &sdio_bus_pm_ops,
};
static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct sdio_driver *sdrv = to_sdio_driver(drv);
if (sdio_match_device(func, sdrv))
return 1;
return 0;
}
static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
struct sdio_driver *sdrv)
{
const struct sdio_device_id *ids;
ids = sdrv->id_table;
if (ids) {
while (ids->class || ids->vendor || ids->device) {
if (sdio_match_one(func, ids))
return ids;
ids++;
}
}
return NULL;
}
static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
const struct sdio_device_id *id)
{
if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
return NULL;
if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
return NULL;
if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
return NULL;
return id;
}
可以看出匹配规则为通过driver id和func比较vendor和device来确定是否匹配device和driver
从上面可以看出sdio_driver通过driver id匹配的device实际上是function的device
4,sdio device之间的关系图
card device的bus type和func device的bus type是有区别的
1)card device的bus类型为mmc_bus_type
static struct bus_type mmc_bus_type = {
.name = “mmc”,
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
2)func device的bus类型为sdio_bus_type
5,sdio 扫卡小结
1)scan card设置的频率是在400k到host支持的最低频率,逐个频率之间进行扫描,直到扫描到card为止
2)获取card支持的电压ocr域,是否支持UHS,以及card支持的function num
3)card支持uhs,则对电压电平进行切换
4)读取card的CCCR和CIS寄存器信息,其中CCCR信息包括了sdio version,bus wide以及card支持的速率,CIS信息包括vendor,device id等
5)调用sdio_alloc_func 为每一func分配一个sdio_func结构体,并通过device_initialize初始化func->dev
6)调用sdio_add_func 通过device_add添加每个fun的dev,用于匹配sdio_driver