Linux MMC子系统分析(三)——Card检测方式分析
前言
前面对host驱动内容进行简单的学习分析,现在开始对SD卡的添加检测进行简单的学习和分析。Linux内核中SD卡的检测方式
1、轮询方式 ,通过在设备树中指定 broken-cd ,便可使用轮询方式来检测SD卡的插拔检测 适用于没有中断引脚的情况下。这种方式不做过多说明。 2、中断检测方式,通过在设备树中指定cd-gpios,即可使用中断方式来检测。&sdhci_1 {
pinctrl-names = "default";
pinctrl-0 = <&sd0_clk>, <&sd0_cmd>,
<&sd0_bus1>, <&sd0_bus4>;
bus-width = <4>;
cd-gpios = <&gpf 1 0>; //当在设备树中指定了该引脚,相应的内核就会自行的注册想相应的中断处理函数
cd-inverted;
status = "okay";
};
中断处理函数的位置位于core/slot-gpio.c,默认提供的中断函数:
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{
/* Schedule a card detection after a debounce timeout */
struct mmc_host *host = dev_id;
host->trigger_card_event = true;
mmc_detect_change(host, msecs_to_jiffies(200)); //最终会调用到工 mmc_rescan,
return IRQ_HANDLED;
}
当然,如果你需要在中断函数中引入其他功能,你也可以手动指定中断函数,该文件中同样提供了设置中断函数的接口:
void mmc_gpio_set_cd_isr(struct mmc_host *host,
irqreturn_t (*isr)(int irq, void *dev_id))
那么该中断函数是在什么位置被注册的?
之前的分析,在mmc_start_host函数中调用了 mmc_gpiod_request_cd_irq,可以看一下该函数中的内容
void mmc_gpiod_request_cd_irq(struct mmc_host *host)
{
..........
irq = gpiod_to_irq(ctx->cd_gpio);
.................
if (irq >= 0) {
if (!ctx->cd_gpio_isr) //如果没有设置自定义的中断处理函数,就使用默认的
ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
ret = devm_request_threaded_irq(host->parent, irq,
NULL, ctx->cd_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
ctx->cd_label, host); //注册中断函数
if (ret < 0)
irq = ret;
}
host->slot.cd_irq = irq;
if (irq < 0)
host->caps |= MMC_CAP_NEEDS_POLL;
}
关于中断检测SD卡的方式大概就是这么多,更加细节的内容还需要细读内核源码。
下面来分析 mmc_rescan函数 该函数位于 core/core.c中
该函数是探测SD卡的重要函数
void mmc_rescan(struct work_struct *work)
{
..........
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) //依次使用 400K 300 200 100KHz的时钟对SD卡或eMMC进行初始化操作
break;
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
...........
}
mmc_rescan_try_freq函数
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
mc_power_up(host, host->ocr_avail); //打开供电 这里会涉及到电平切换的问题 SD卡在初始化时,需要在3.3V的条件下进行,初始化完成后高速卡是在1.8v条件下工作
mmc_hw_reset_for_init(host); //硬件复位,需要在设备树中使能,并在驱动中实现相应函数
sdio_reset(host);
mmc_go_idle(host); // cmd0
mmc_send_if_cond(host, host->ocr_avail);
/* 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;
}
void mmc_power_up(struct mmc_host *host, u32 ocr)
{
if (host->ios.power_mode == MMC_POWER_ON)
return;
mmc_pwrseq_pre_power_on(host);
host->ios.vdd = fls(ocr) - 1;
host->ios.power_mode = MMC_POWER_UP;
/* Set initial state and call mmc_set_ios */
mmc_set_initial_state(host);
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ //尝试将电平设置为3.3V,因为根据协议说明,在初始化时需要在3.3v条件下进行
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0) //会进行如下调用host->ops->start_signal_voltage_switch(host, &host->ios);
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0)
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0)
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
/*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmc_delay(10);
mmc_pwrseq_post_power_on(host);
host->ios.clock = host->f_init;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
/*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
mmc_delay(10); //上电完成后,需要等待74个clk
}
int mmc_attach_sd(struct mmc_host *host)
{
......
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, rocr, NULL); //根据SD协议来初始化设备
err = mmc_add_card(host->card); //将初始化完成的设备,添加到内核中
}
mmc_add_card(host->card);执行后,会mmc_bus_probe函数 函数位于core/bus.c中
在该函数中会直接调用mmc_driver中的probe函数,将SD卡实现为块设备。