上文讲到
mmc_add_host
函数执行到最后会激活卡检测的延迟工作队列。延迟工作队列的执行函数为
mmc_rescan
,即卡检测函数
卡检测函数
mmc_rescan
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
......
mmc_claim_host(host);
......
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
mmc_claim_host
和mmc_release_host
是成对使用的,mmc_claim_host检测当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权。
最后的out部分代码,则是当需要轮训判断卡是否插入时,在本次判断完之后要将延迟工作队列重新激活。
mmc_rescan
最重要的函数是mmc_rescan_try_freq
,下面来分析一下代码,函数顾名思义,就是用不同的clock去尝试初始化与目标卡的连接。
mmc_rescan->mmc_rescan_try_freq
//传递的时钟频率,依次为400K,300K,200K,100K;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
<span class="token function">pr_debug</span><span class="token punctuation">(</span><span class="token string">"%s: %s: trying to init card at %u Hz\n"</span><span class="token punctuation">,</span>
<span class="token function">mmc_hostname</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token constant">__func__</span><span class="token punctuation">,</span> host<span class="token operator">-></span>f_init<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">mmc_power_up</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> host<span class="token operator">-></span>ocr_avail<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//host上电</span>
<span class="token comment">/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/</span>
<span class="token function">mmc_hw_reset_for_init</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//硬件复位host,可选择性实现</span>
<span class="token comment">/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
* Skip it if we already know that we do not support SDIO commands
*/</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-></span>caps2 <span class="token operator">&</span> MMC_CAP2_NO_SDIO<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//发送复位sdio设备的命令(CMD52)</span>
<span class="token function">sdio_reset</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//如果提前知道不是sdio卡,则跳过该步骤</span>
<span class="token function">mmc_go_idle</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//发送CMD0 复位SD卡。</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-></span>caps2 <span class="token operator">&</span> MMC_CAP2_NO_SD<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//如果提前知道不是SD卡,则跳过该步骤</span>
<span class="token function">mmc_send_if_cond</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> host<span class="token operator">-></span>ocr_avail<span class="token punctuation">)</span><span class="token punctuation">;</span>
/*为了支持sd version 2.0以上的sd卡,在初始化的过程中必须在发送ACMD41之前,先发送CMD8,CMD8一般是用于
检测SD卡是否能运行在host提供的电压范围内。大家可能发现,这个调用过程没有检查是否出错,其实一般CMD8是
用来辨别目标卡是否是高容量SD卡,如果是,CMD8 会有R7应答,R7应答中会有目标SD卡支持的电压范围以及CMD8
中发送过去的“check pattern(一般是0xAA)”,否则,目标卡不会应答,在Linux 内核代码中,判断是这样的,如
果应答,目标卡就是SD高容量卡,否则出现应答超时错误,就是标准SD卡!这里的调用,主要作用是为了在发送
ACMD41之前发送CMD8,这是version 2.0及以上的规定顺序,后面还会有发送CMD8的地方,那里才是真正检测目标
卡的类型的地方。 */
<span class="token comment">/* Order's important: probe SDIO, then SD, then MMC */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-></span>caps2 <span class="token operator">&</span> MMC_CAP2_NO_SDIO<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//如果提前知道不是SDIO卡,跳过该步骤</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">mmc_attach_sdio</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//先判断是否是sdio卡</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-></span>caps2 <span class="token operator">&</span> MMC_CAP2_NO_SD<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//如果提前知道不是SD卡,跳过该步骤</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">mmc_attach_sd</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//再判读是否是SD卡</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-></span>caps2 <span class="token operator">&</span> MMC_CAP2_NO_MMC<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//如果提前知道不是MMC卡,跳过该步骤</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">mmc_attach_mmc</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">//最后再判读是否是MMC卡</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token function">mmc_power_off</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//host掉电。</span>
<span class="token keyword">return</span> <span class="token operator">-</span>EIO<span class="token punctuation">;</span>
}
- 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
mmc_rescan_try_freq
函数代码解释在注释中基本都是写的比较明白了,中文注释是写的,英文注释是内核本身的代码。下面来依次看下mmc_attach_sd
,mmc_attach_mmc
以及mmc_attach_sdio
,函数。
SD卡初始化
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sd
int mmc_attach_sd(struct mmc_host *host) { int err; u32 ocr, rocr;
<span class="token function">WARN_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>host<span class="token operator">-></span>claimed<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_send_app_op_cond</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 发送CMD41,sd设备会响应 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> err<span class="token punctuation">;</span> <span class="token function">mmc_attach_bus</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token operator">&</span>mmc_sd_ops<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 将sd总线操作函数集分配给host */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ocr_avail_sd<span class="token punctuation">)</span> host<span class="token operator">-></span>ocr_avail <span class="token operator">=</span> host<span class="token operator">-></span>ocr_avail_sd<span class="token punctuation">;</span> <span class="token comment">/* 设置sd的ocr */</span> <span class="token comment">/* * We need to get OCR a different way for SPI. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_host_is_spi</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">mmc_go_idle</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_spi_read_ocr</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> err<span class="token punctuation">;</span> <span class="token punctuation">}</span> rocr <span class="token operator">=</span> <span class="token function">mmc_select_voltage</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 选择合适的电压值 */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> err <span class="token operator">=</span> <span class="token function">mmc_sd_init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> rocr<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 检测并初始化sd card */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> err<span class="token punctuation">;</span> <span class="token function">mmc_release_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_add_card</span><span class="token punctuation">(</span>host<span class="token operator">-></span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 注册sd card */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
}
- 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
检测到sd卡之后,比较重要的操作为mmc_sd_init_card
、mmc_add_card
。下面先来看下mmc_sd_init_card
函数。
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> mmc_sd_init_card
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; int err; u32 cid[4]; u32 rocr = 0;
<span class="token function">WARN_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>host<span class="token operator">-></span>claimed<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_sd_get_cid</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> ocr<span class="token punctuation">,</span> cid<span class="token punctuation">,</span> <span class="token operator">&</span>rocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 获取CID */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> err<span class="token punctuation">;</span> card <span class="token operator">=</span> <span class="token function">mmc_alloc_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token operator">&</span>sd_type<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 分配card内存,并初始化部分数据*/</span> card<span class="token operator">-></span>ocr <span class="token operator">=</span> ocr<span class="token punctuation">;</span> card<span class="token operator">-></span>type <span class="token operator">=</span> MMC_TYPE_SD<span class="token punctuation">;</span> <span class="token comment">/* 设置card type为sd卡 */</span> <span class="token function">memcpy</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">,</span> cid<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ops<span class="token operator">-></span>init_card<span class="token punctuation">)</span> host<span class="token operator">-></span>ops<span class="token operator">-></span><span class="token function">init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 如果控制器实现了init card回调,则执行 */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> host<span class="token operator">-></span>card <span class="token operator">=</span> card<span class="token punctuation">;</span> <span class="token comment">/* host->card指针指向刚初始化完的card数据结构 */</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 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
这个函数原函数很长,将与硬件操作相关的全部删掉,最后对我们有用的也就这几行了 mmc_alloc_card 申请了一个 struct mmc_card 结构,然后给 card->type 赋上 MMC_TYPE_SDI,最后将 card 又赋给了 host->card ,这和具体硬件还是挺像的,因为一个主控制器一般就插一个卡,有卡时 host->card 有值,没有卡时 host->card 自己就是 NULL 了。
再来看一下比较重要的mmc_alloc_card
函数:
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> mmc_sd_init_card->mmc_alloc_card
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type) { struct mmc_card *card;
card <span class="token operator">=</span> <span class="token function">kzalloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> mmc_card<span class="token punctuation">)</span><span class="token punctuation">,</span> GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>card<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>ENOMEM<span class="token punctuation">)</span><span class="token punctuation">;</span> card<span class="token operator">-></span>host <span class="token operator">=</span> host<span class="token punctuation">;</span> <span class="token function">device_initialize</span><span class="token punctuation">(</span><span class="token operator">&</span>card<span class="token operator">-></span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> card<span class="token operator">-></span>dev<span class="token punctuation">.</span>parent <span class="token operator">=</span> <span class="token function">mmc_classdev</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> card<span class="token operator">-></span>dev<span class="token punctuation">.</span>bus <span class="token operator">=</span> <span class="token operator">&</span>mmc_bus_type<span class="token punctuation">;</span> card<span class="token operator">-></span>dev<span class="token punctuation">.</span>release <span class="token operator">=</span> mmc_release_card<span class="token punctuation">;</span> card<span class="token operator">-></span>dev<span class="token punctuation">.</span>type <span class="token operator">=</span> type<span class="token punctuation">;</span> <span class="token keyword">return</span> card<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
代码比较简单。申请内存,随后初始化card->dev,并设置partent等参数,这里bus设置为mmc_bus_type
,type设置为sd_type
。
struct device_type sd_type = {
.groups = sd_std_groups,
};
- 1
- 2
- 3
mmc总线和i2c总线以及platform总线一样,就是我们比较熟悉的设备驱动模型这一套了。
再回到mmc_attach_sd
,看完mmc_sd_init_card
后继续看mmc_add_card
,这个函数再sdio、mmc卡初始化时也会调用,这里介绍完后面就不介绍了:
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sd-> mmc_add_card
int mmc_add_card(struct mmc_card *card) { int ret; const char *type; ...... dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); /* 设置设备名称 */
<span class="token keyword">switch</span> <span class="token punctuation">(</span>card<span class="token operator">-></span>type<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">case</span> MMC_TYPE_MMC<span class="token punctuation">:</span> type <span class="token operator">=</span> <span class="token string">"MMC"</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> MMC_TYPE_SD<span class="token punctuation">:</span> type <span class="token operator">=</span> <span class="token string">"SD"</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_card_blockaddr</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_card_ext_capacity</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">)</span> type <span class="token operator">=</span> <span class="token string">"SDXC"</span><span class="token punctuation">;</span> <span class="token keyword">else</span> type <span class="token operator">=</span> <span class="token string">"SDHC"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> MMC_TYPE_SDIO<span class="token punctuation">:</span> type <span class="token operator">=</span> <span class="token string">"SDIO"</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> MMC_TYPE_SD_COMBO<span class="token punctuation">:</span> type <span class="token operator">=</span> <span class="token string">"SD-combo"</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_card_blockaddr</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">)</span> type <span class="token operator">=</span> <span class="token string">"SDHC-combo"</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">default</span><span class="token punctuation">:</span> type <span class="token operator">=</span> <span class="token string">"?"</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_host_is_spi</span><span class="token punctuation">(</span>card<span class="token operator">-></span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">pr_info</span><span class="token punctuation">(</span><span class="token string">"%s: new %s%s%s card on SPI\n"</span><span class="token punctuation">,</span> <span class="token function">mmc_hostname</span><span class="token punctuation">(</span>card<span class="token operator">-></span>host<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">mmc_card_hs</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"high speed "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token function">mmc_card_ddr52</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"DDR "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">,</span> type<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token function">pr_info</span><span class="token punctuation">(</span><span class="token string">"%s: new %s%s%s%s%s%s card at address %04x\n"</span><span class="token punctuation">,</span> <span class="token function">mmc_hostname</span><span class="token punctuation">(</span>card<span class="token operator">-></span>host<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">mmc_card_uhs</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"ultra high speed "</span> <span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token function">mmc_card_hs</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"high speed "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">mmc_card_hs400</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"HS400 "</span> <span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token function">mmc_card_hs200</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"HS200 "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">mmc_card_hs400es</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"Enhanced strobe "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token function">mmc_card_ddr52</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"DDR "</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">,</span> uhs_bus_speed_mode<span class="token punctuation">,</span> type<span class="token punctuation">,</span> card<span class="token operator">-></span>rca<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> ret <span class="token operator">=</span> <span class="token function">device_add</span><span class="token punctuation">(</span><span class="token operator">&</span>card<span class="token operator">-></span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token keyword">return</span> ret<span class="token punctuation">;</span> <span class="token function">mmc_card_set_present</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 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
这个函数主要作用一是,打印card信息,上面一大段代码都是为了最终打印做准备的。
不同类型卡打印也不一样,我手上设备刚好有mmc、sd卡接口以及sdio wifi模组,下面看一下上电打印:
mmc4: new DDR MMC card at address 0001 /* mmc card */
mmc2: new high speed SDHC card at address 59b4 /* sd card */
mmc3: new high speed SDIO card at address 0001 /* sdio card(sdio wifi) */
- 1
- 2
- 3
添加设备到设备驱动模型中,line6设置了设备名称,最后调用device_add
。启动后可以通过sys文件系统查看。上文介绍了,bus设置为mmc_bus_type,则在/sys/bus/mmc/devices
下查看设备我手上设备上电后查看mmc bus下的设备,如下:
[root@linux:/root]# ls /sys/bus/mmc/devices/
mmc2:59b4 mmc3:0001 mmc4:0001
- 1
- 2
既然设备添加到了mmc bus总线上,那么必然要有驱动。首先来看下mmc 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,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在 device_add 里面,设备对应的总线会拿着你这个设备和挂在这个总线上的所有驱动程序去匹配( match ),此时会调用总线的 match 函数,如果匹配到了就会调用总线的 probe 函数或驱动的 probe 函数,那我们看一下这里的 mmc_bus_match
是如何进行匹配的:
mmc_bus_type ->mmc_bus_match
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
- 1
- 2
- 3
- 4
根据代码来看 match 永远都能成功,那就去执行 probe 吧:
mmc_bus_type ->mmc_bus_probe
static int mmc_bus_probe(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev);
<span class="token keyword">return</span> drv<span class="token operator">-></span><span class="token function">probe</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在这个函数里面又调用了一下 drv->probe() ,那这个drv是什么呢?上面有:struct mmc_driver *drv = to_mmc_driver(dev->driver);
match 函数总是返回 1 ,那看来只要是挂在这条总线上的 driver 都有可能跑到这里来了,事实的确也是这样的,不过好在挂在这条总线上的 driver 只有一个,它是这样定义的:
struct mmc_driver mmc_driver
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
.pm = &mmc_blk_pm_ops,
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.shutdown = mmc_blk_shutdown,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
看到这里时, mmc子系统的card/core/host 几个已经全部被扯进来了,边看 mmc_driver 中的几个函数,就能明白其中的联系了,那我们继续看:
mmc_driver->probe
static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; ...... md = mmc_blk_alloc(card); /* alloc_disk 和初始化队列 */ ...... if (mmc_add_disk(md)) /* 注册块设备 */ goto out;
<span class="token function">list_for_each_entry</span><span class="token punctuation">(</span>part_md<span class="token punctuation">,</span> <span class="token operator">&</span>md<span class="token operator">-></span>part<span class="token punctuation">,</span> part<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_add_disk</span><span class="token punctuation">(</span>part_md<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">goto</span> out<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
mmc_blk_alloc
中主要包含了alloc_disk 和初始化队列,最后调用了 add_disk ,到这里就全部都是块设备的注册操作了。最终,mmc bus上的driver驱动初始化后,注册块设备成功后,我手上设备的mmc卡打印如下:
[ 2.198146] mmcblk4: mmc4:0001 8WPD3R 7.28 GiB
[ 2.202910] mmcblk4boot0: mmc4:0001 8WPD3R partition 1 4.00 MiB
[ 2.216759] mmcblk4boot1: mmc4:0001 8WPD3R partition 2 4.00 MiB
[ 2.229342] mmcblk4: p1(uboot) p2(firmware) p3(config) p4(others)
- 1
- 2
- 3
- 4
在文件系统下通过设备节点查看,打印如下,这是我的mmc卡:
mmcblk4
mmcblk4boot0
mmcblk4boot1
mmcblk4p1
mmcblk4p2
mmcblk4p3
mmcblk4p4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
块设备驱动(mmc_driver)具体代码我这里就不展开说明了。
MMC卡初始化
mmc_rescan->mmc_rescan_try_freq->mmc_attach_mmc
int mmc_attach_mmc(struct mmc_host *host) { int err; u32 ocr, rocr;
<span class="token function">WARN_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>host<span class="token operator">-></span>claimed<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Set correct bus mode for MMC before attempting attach */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">mmc_host_is_spi</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">mmc_set_bus_mode</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> MMC_BUSMODE_OPENDRAIN<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 设置正确的总线模式在检测卡之前 */</span> err <span class="token operator">=</span> <span class="token function">mmc_send_op_cond</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 发送CMD1指令,SEND_OP_COND,mmc设备会相应 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> err<span class="token punctuation">;</span> <span class="token function">mmc_attach_bus</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token operator">&</span>mmc_ops<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 将mmc总线操作函数集分配给host */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ocr_avail_mmc<span class="token punctuation">)</span> host<span class="token operator">-></span>ocr_avail <span class="token operator">=</span> host<span class="token operator">-></span>ocr_avail_mmc<span class="token punctuation">;</span> <span class="token comment">/* 设置mmc的ocr */</span> <span class="token comment">/* * We need to get OCR a different way for SPI. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">mmc_host_is_spi</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> err <span class="token operator">=</span> <span class="token function">mmc_spi_read_ocr</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token operator">&</span>ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> err<span class="token punctuation">;</span> <span class="token punctuation">}</span> rocr <span class="token operator">=</span> <span class="token function">mmc_select_voltage</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 选择合适的电压值 */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> err <span class="token operator">=</span> <span class="token function">mmc_init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> rocr<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 识别和初始化mmc card */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> err<span class="token punctuation">;</span> <span class="token function">mmc_release_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_add_card</span><span class="token punctuation">(</span>host<span class="token operator">-></span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 注册mmc card */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> remove_card<span class="token punctuation">;</span> <span class="token function">mmc_claim_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
remove_card:
mmc_remove_card(host->card);
mmc_claim_host(host);
host->card = NULL;
err:
mmc_detach_bus(host);
<span class="token function">pr_err</span><span class="token punctuation">(</span><span class="token string">"%s: error %d whilst initialising MMC card\n"</span><span class="token punctuation">,</span>
<span class="token function">mmc_hostname</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> err<span class="token punctuation">;</span>
}
- 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
检测到mmc卡之后,比较重要的操作为mmc_init_card
、mmc_add_card
。mmc卡总的流程基本和sd卡一致,只是涉及到卡的命令等不一样,但是最终都是注册为块设备。mmc_add_card
在上文介绍SD卡初始化的时候已经讲过了。这里只要看一下mmc_init_card
就可以了:
mmc_rescan->mmc_rescan_try_freq->mmc_attach_mmc-> mmc_init_card
static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card;
card <span class="token operator">=</span> <span class="token function">mmc_alloc_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token operator">&</span>mmc_type<span class="token punctuation">)</span><span class="token punctuation">;</span> card<span class="token operator">-></span>ocr <span class="token operator">=</span> ocr<span class="token punctuation">;</span> card<span class="token operator">-></span>type <span class="token operator">=</span> MMC_TYPE_MMC<span class="token punctuation">;</span> card<span class="token operator">-></span>rca <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token function">memcpy</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">,</span> cid<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * Call the optional HC's init_card function to handle quirks. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ops<span class="token operator">-></span>init_card<span class="token punctuation">)</span> host<span class="token operator">-></span>ops<span class="token operator">-></span><span class="token function">init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>oldcard<span class="token punctuation">)</span> host<span class="token operator">-></span>card <span class="token operator">=</span> card<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
这个函数原函数y也很长,将与硬件操作相关的全部删掉,最后对我们有用的也就这几行了 mmc_alloc_card 申请了一个 struct mmc_card 结构,值得注意的是这里mmc_alloc_card传参type设置为mmc_type
。
static struct device_type mmc_type = {
.groups = mmc_std_groups,
};
- 1
- 2
- 3
然后给 card->type 赋上 MMC_TYPE_MMC,最后将 card 又赋给了 host->card ,可以看出,和mmc_sd_init_card
基本上如出一辙。其余的操作参考上文sd卡检测流程。
add card中都是注册mmc bus类型的device,并最终匹配mmc_driver,注册块设备;只不过涉及到卡特性,寄存器操作等有差异。
SDIO卡初始化
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio
int mmc_attach_sdio(struct mmc_host *host) { int err, i, funcs; u32 ocr, rocr; struct mmc_card *card;
<span class="token function">WARN_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>host<span class="token operator">-></span>claimed<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_send_io_op_cond</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 发送CMD5,sdio设备会响应 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> err<span class="token punctuation">;</span> <span class="token function">mmc_attach_bus</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token operator">&</span>mmc_sdio_ops<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 将sdio总线操作函数集分配给host */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ocr_avail_sdio<span class="token punctuation">)</span> host<span class="token operator">-></span>ocr_avail <span class="token operator">=</span> host<span class="token operator">-></span>ocr_avail_sdio<span class="token punctuation">;</span> <span class="token comment">/* 设置sdio的ocr */</span> rocr <span class="token operator">=</span> <span class="token function">mmc_select_voltage</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> ocr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 选择合适的电压值 */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> err <span class="token operator">=</span> <span class="token function">mmc_sdio_init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> rocr<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 检测并初始化sdio card */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> err<span class="token punctuation">;</span> card <span class="token operator">=</span> host<span class="token operator">-></span>card<span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> funcs <span class="token operator">=</span> <span class="token punctuation">(</span>ocr <span class="token operator">&</span> <span class="token number">0x70000000</span><span class="token punctuation">)</span> <span class="token operator">>></span> <span class="token number">28</span><span class="token punctuation">;</span> <span class="token comment">/* 卡支持的sdio function 数量在ocr中包含了 */</span> card<span class="token operator">-></span>sdio_funcs <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> funcs<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">,</span> card<span class="token operator">-></span>sdio_funcs<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> err <span class="token operator">=</span> <span class="token function">sdio_init_func</span><span class="token punctuation">(</span>host<span class="token operator">-></span>card<span class="token punctuation">,</span> i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 初始化所有sdio function ,但是不添加 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> remove<span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token comment">/* * First add the card to the driver model... */</span> <span class="token function">mmc_release_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">=</span> <span class="token function">mmc_add_card</span><span class="token punctuation">(</span>host<span class="token operator">-></span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 注册sdio card */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> remove_added<span class="token punctuation">;</span> <span class="token comment">/* * ...then the SDIO functions. */</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>i <span class="token operator"><</span> funcs<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> err <span class="token operator">=</span> <span class="token function">sdio_add_func</span><span class="token punctuation">(</span>host<span class="token operator">-></span>card<span class="token operator">-></span>sdio_func<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 注册所有sdio function */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">goto</span> remove_added<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
}
- 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
检测到sdio卡之后,比较重要的操作为mmc_sdio_init_card
、mmc_add_card
以及sdio_init_func
和sdio_add_func
。mmc_add_card
上文中已经介绍过了,这里就不展开了。
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> mmc_sdio_init_card
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard, int powered_resume) { struct mmc_card *card; ...... card = mmc_alloc_card(host, NULL); /* 分配card内存,并初始化部分数据 */ if (IS_ERR(card)) { err = PTR_ERR(card); goto err; }
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>rocr <span class="token operator">&</span> R4_MEMORY_PRESENT<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">mmc_sd_get_cid</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> ocr <span class="token operator">&</span> rocr<span class="token punctuation">,</span> card<span class="token operator">-></span>raw_cid<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> card<span class="token operator">-></span>type <span class="token operator">=</span> MMC_TYPE_SD_COMBO<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>oldcard <span class="token operator">&&</span> <span class="token punctuation">(</span>oldcard<span class="token operator">-></span>type <span class="token operator">!=</span> MMC_TYPE_SD_COMBO <span class="token operator">||</span> <span class="token function">memcmp</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">,</span> oldcard<span class="token operator">-></span>raw_cid<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>card<span class="token operator">-></span>raw_cid<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">mmc_remove_card</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token operator">-</span>ENOENT<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> card<span class="token operator">-></span>type <span class="token operator">=</span> MMC_TYPE_SDIO<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>oldcard <span class="token operator">&&</span> oldcard<span class="token operator">-></span>type <span class="token operator">!=</span> MMC_TYPE_SDIO<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">mmc_remove_card</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token operator">-</span>ENOENT<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>host<span class="token operator">-></span>ops<span class="token operator">-></span>init_card<span class="token punctuation">)</span> host<span class="token operator">-></span>ops<span class="token operator">-></span><span class="token function">init_card</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 如果控制器驱动实现了对应回调,则执行 */</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
finish:
if (!oldcard)
host->card = card;
return 0;
}
- 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
这个函数原函数很长,将与硬件操作相关的全部删掉,最后对我们有用的也就这几行了 mmc_alloc_card 申请了一个 struct mmc_card 结构,然后给 card->type 赋上 MMC_TYPE_SDIO ,最后将 card 又赋给了 host->card ,这和具体硬件还是挺像的,因为一个主控制器一般就插一个卡,有卡时 host->card 有值,没有卡时 host->card 自己就是 NULL 了。
再回到mmc_attach_sdio
,会调用mmc_add_card
到mmc bus下。最终在/sys/bus/mmc/devices/下生成节点。
最后看一下sdio_init_func
和sdio_add_func
,前面有介绍过struct sdio_func
时说过一张sdio卡可以支持很多Functions,一个Function对应软件上的struct sdio_func结构体,最后反应到驱动模型上就是一个device。
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> sdio_init_func
static int sdio_init_func(struct mmc_card *card, unsigned int fn) { int ret; struct sdio_func *func;
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">WARN_ON</span><span class="token punctuation">(</span>fn <span class="token operator">></span> SDIO_MAX_FUNCS<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span>EINVAL<span class="token punctuation">;</span> func <span class="token operator">=</span> <span class="token function">sdio_alloc_func</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 申请内存 */</span> func<span class="token operator">-></span>num <span class="token operator">=</span> fn<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>card<span class="token operator">-></span>quirks <span class="token operator">&</span> MMC_QUIRK_NONSTD_SDIO<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">/* 判断是否是标准sdio卡 */</span> ret <span class="token operator">=</span> <span class="token function">sdio_read_fbr</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 标准卡根据fn号,去读取func的vendor id、device id以及max_blksize*/</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token keyword">goto</span> fail<span class="token punctuation">;</span> ret <span class="token operator">=</span> <span class="token function">sdio_read_func_cis</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token keyword">goto</span> fail<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> func<span class="token operator">-></span>vendor <span class="token operator">=</span> func<span class="token operator">-></span>card<span class="token operator">-></span>cis<span class="token punctuation">.</span>vendor<span class="token punctuation">;</span> <span class="token comment">/* 非标准卡直接从card->cis中赋值vendor id、device id以及max_blksize */</span> func<span class="token operator">-></span>device <span class="token operator">=</span> func<span class="token operator">-></span>card<span class="token operator">-></span>cis<span class="token punctuation">.</span>device<span class="token punctuation">;</span> func<span class="token operator">-></span>max_blksize <span class="token operator">=</span> func<span class="token operator">-></span>card<span class="token operator">-></span>cis<span class="token punctuation">.</span>blksize<span class="token punctuation">;</span> <span class="token punctuation">}</span> card<span class="token operator">-></span>sdio_func<span class="token punctuation">[</span>fn <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> func<span class="token punctuation">;</span> <span class="token comment">/* 将初始化好的sdio_func指针赋值给func数组 */</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
fail:
/*
* It is okay to remove the function here even though we hold
* the host lock as we haven’t registered the device yet.
*/
sdio_remove_func(func);
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
sdio_alloc_func
类似于上文中介绍的mmc_alloc_card
,这里有必要展开说明一下,代码如下:
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> sdio_init_func->sdio_alloc_func
struct sdio_func *sdio_alloc_func(struct mmc_card *card) { struct sdio_func *func;
func <span class="token operator">=</span> <span class="token function">kzalloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> sdio_func<span class="token punctuation">)</span><span class="token punctuation">,</span> GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> func<span class="token operator">-></span>card <span class="token operator">=</span> card<span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token function">device_initialize</span><span class="token punctuation">(</span><span class="token operator">&</span>func<span class="token operator">-></span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> func<span class="token operator">-></span>dev<span class="token punctuation">.</span>parent <span class="token operator">=</span> <span class="token operator">&</span>card<span class="token operator">-></span>dev<span class="token punctuation">;</span> func<span class="token operator">-></span>dev<span class="token punctuation">.</span>bus <span class="token operator">=</span> <span class="token operator">&</span>sdio_bus_type<span class="token punctuation">;</span> func<span class="token operator">-></span>dev<span class="token punctuation">.</span>release <span class="token operator">=</span> sdio_release_func<span class="token punctuation">;</span> <span class="token keyword">return</span> func<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
代码与mmc_alloc_card
如出一辙。申请内存,随后初始化card->dev,并设置partent等参数,与mmc_alloc_card
不同的是,这里bus设置为sdio_bus_type。
看完sdio_init_func
,再来看一下sdio_add_func
:
mmc_rescan->mmc_rescan_try_freq->mmc_attach_sdio-> sdio_add_func
int sdio_add_func(struct sdio_func *func) { int ret;
<span class="token function">dev_set_name</span><span class="token punctuation">(</span><span class="token operator">&</span>func<span class="token operator">-></span>dev<span class="token punctuation">,</span> <span class="token string">"%s:%d"</span><span class="token punctuation">,</span> <span class="token function">mmc_card_id</span><span class="token punctuation">(</span>func<span class="token operator">-></span>card<span class="token punctuation">)</span><span class="token punctuation">,</span> func<span class="token operator">-></span>num<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 设置设备名称 */</span> <span class="token function">sdio_set_of_node</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">sdio_acpi_set_handle</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">device_enable_async_suspend</span><span class="token punctuation">(</span><span class="token operator">&</span>func<span class="token operator">-></span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> ret <span class="token operator">=</span> <span class="token function">device_add</span><span class="token punctuation">(</span><span class="token operator">&</span>func<span class="token operator">-></span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 添加到设备驱动模型 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token function">sdio_func_set_present</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> ret<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这个函数也和mmc_add_card
如出一辙,设置sdio func设备名称,并添加到设备驱动模型。启动后同样可以通过sys文件系统查看。上文介绍了,bus设置为sdio_bus_type,则在/sys/bus/sdio/devices
下查看设备我手上设备上电后查看sdio bus下的设备,这里是一个sdio wifi模组,如下:
[root@linux:/root]# ls /sys/bus/sdio/devices/
mmc3:0001:1 /* 最后的1代表func num */
- 1
- 2
跟上文的mmc_bus类似,设备添加到了sdio bus总线上,那么必然要有驱动。首先来看下sdio bus的数据结构:
sdio_bus_type
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,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在 device_add 里面,设备对应的总线会拿着你这个设备和挂在这个总线上的所有驱动程序去匹配( match ),此时会调用总线的 match 函数,如果匹配到了就会调用总线的 probe 函数或驱动的 probe 函数,那我们看一下这里的 sdio_bus_match
是如何进行匹配的:
sdio_bus_type ->sdio_bus_match
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);
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">sdio_match_device</span><span class="token punctuation">(</span>func<span class="token punctuation">,</span> sdrv<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
static const struct sdio_device_id sdio_match_device(struct sdio_func func,
struct sdio_driver sdrv)
{
const struct sdio_device_id ids;
ids <span class="token operator">=</span> sdrv<span class="token operator">-></span>id_table<span class="token punctuation">;</span> <span class="token comment">/* 驱动支持的id表 */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>ids<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">while</span> <span class="token punctuation">(</span>ids<span class="token operator">-></span>class <span class="token operator">||</span> ids<span class="token operator">-></span>vendor <span class="token operator">||</span> ids<span class="token operator">-></span>device<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">sdio_match_one</span><span class="token punctuation">(</span>func<span class="token punctuation">,</span> ids<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">/* driver中的id去匹配device id */</span>
<span class="token keyword">return</span> ids<span class="token punctuation">;</span> <span class="token comment">/* 匹配上返回id值(成功) */</span>
ids<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>
}
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;
}
- 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
简单的来说。是通过对比dirver支持的id table和device中从设备中读出来的id,match上就去执行probe吧。
sdio_bus_type ->sdio_bus_probe
static int sdio_bus_probe(struct device *dev) { struct sdio_driver *drv = to_sdio_driver(dev->driver); struct sdio_func *func = dev_to_sdio_func(dev); const struct sdio_device_id *id; int ret;
id <span class="token operator">=</span> <span class="token function">sdio_match_device</span><span class="token punctuation">(</span>func<span class="token punctuation">,</span> drv<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>id<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span>ENODEV<span class="token punctuation">;</span> ret <span class="token operator">=</span> <span class="token function">dev_pm_domain_attach</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> false<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator">==</span> <span class="token operator">-</span>EPROBE_DEFER<span class="token punctuation">)</span> <span class="token keyword">return</span> ret<span class="token punctuation">;</span> <span class="token comment">/* Unbound SDIO functions are always suspended. * During probe, the function is set active and the usage count * is incremented. If the driver supports runtime PM, * it should call pm_runtime_put_noidle() in its probe routine and * pm_runtime_get_noresume() in its remove routine. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>func<span class="token operator">-></span>card<span class="token operator">-></span>host<span class="token operator">-></span>caps <span class="token operator">&</span> MMC_CAP_POWER_OFF_CARD<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> ret <span class="token operator">=</span> <span class="token function">pm_runtime_get_sync</span><span class="token punctuation">(</span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">goto</span> disable_runtimepm<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* Set the default block size so the driver is sure it's something * sensible. */</span> <span class="token function">sdio_claim_host</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> ret <span class="token operator">=</span> <span class="token function">sdio_set_block_size</span><span class="token punctuation">(</span>func<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">sdio_release_host</span><span class="token punctuation">(</span>func<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token keyword">goto</span> disable_runtimepm<span class="token punctuation">;</span> ret <span class="token operator">=</span> drv<span class="token operator">-></span><span class="token function">probe</span><span class="token punctuation">(</span>func<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 调用驱动的probe函数 */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token keyword">goto</span> disable_runtimepm<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
disable_runtimepm:
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
dev_pm_domain_detach(dev, false);
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
中间那些代码不需要过多的关注,最终调用drv->probe(func, id);
这里举例一个sdio driver数据结构:
static struct sdio_driver sdio_driver = {
.name = "cw1200_wlan_sdio",
.id_table = cw1200_sdio_ids,
.probe = cw1200_sdio_probe,
.remove = cw1200_sdio_disconnect,
#ifdef CONFIG_PM
.drv = {
.pm = &cw1200_pm_ops,
}
#endif
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
进行到这一步,sdio driver的probe函数被调用,则进入sdio驱动的初始化流程了。我们熟知的rtl8189驱动,probe函数就在这时候被调用了。
wifi驱动,只编译过原厂给的驱动,没有仔细看过驱动,就不班门弄斧了~
热插拔
前面讲到控制器驱动的probe 函数做的事情,准备一个 mmc_host 结构,然后添加一个主控制器设备到内核,最后又调用了一下 mmc_rescan 来检测是不是有卡插入了。
如果上电的时候有卡插着还好,可以去操作卡了,那如果没有卡插入呢? mmc_rescan 不是白调用了一次吗?
可是卡插入时为什么 PC 还是能检测到呢?看来卡检测的动作不光是在 probe 的最后一步做了一次,其它地方也有做。
内核自带GPIO热插拔设备树配置
broken-cd 表示没有热插拔探测引脚,使用轮询检测
cd-gpios 使用gpio管脚作为热插拔探测引脚
non-removable 表示不能进行热插拔,设备一直连接(比如eMMC)
上面三个选项用于指定热插拔探测选项,如果三个选项都没有指定,则使用主机自带的热插拔引脚sdcd,有些控制器通过轮训查询热插拔。
cd-inverted 表示cd引脚是active high
总结
把流程大概过一下:
/* 通用部分 */
sunxi_mmc_probe(host/sumxi-mmc.c)->
mmc_alloc_host(core/core.c)->
mmc_rescan(core/core.c)->
mmc_rescan_try_freq(core/core.c)->
mmc_attach_sdio(core/sdio.c)
mmc_attach_sd(core/sd.c)
mmc_attach_mmc(core/mmc.c)
/* sd卡 */
mmc_attach_sd(core/sd.c)->
mmc_sd_init_card(core/sd.c)->
mmc_alloc_card(core/bus.c)
mmc_add_card(core/bus.c)->
device_add
mmc_bus_match(core/bus.c)->
mmc_bus_probe(core/bus.c)->
mmc_blk_probe(card/block.c)->
alloc_disk/add_disk
/* mmc卡 */
mmc_attach_mmc(core/mmc.c)->
mmc_init_card(core/mmc.c)->
mmc_alloc_card(core/bus.c)
mmc_add_card(core/bus.c)->
device_add
mmc_bus_match(core/bus.c)->
mmc_bus_probe(core/bus.c)->
mmc_blk_probe(card/block.c)->
alloc_disk/add_disk
/* sdio卡 */
mmc_attach_sdio(core/sdio.c)->
mmc_sdio_init_card(core/sdio.c)->
mmc_alloc_card(core/bus.c)
mmc_add_card(core/bus.c)->
sdio_init_func(core/sdio.c)->
sdio_add_func(core/sdio_bus.c)->
device_add
sdio_bus_match(core/sdio_bus.c)->
sdio_bus_probe(core/sdio_bus.c)->
sdio_driver->probe
- 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
SDIO/MMC/SD 卡的驱动流程就此分析完毕。