赞
踩
Linux MMC子系统分析(三)——Card检测方式分析
&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卡实现为块设备。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。