单独的cd-gpio
如果有单独的gpio来触发探测就会在mmc_gpiod_request_cd_irq注册中断处理函数,来探测一次,下述会通过sdhci_probe_config_dt来解析设备树里是否有cd-gpios
static int sdhci_probe(struct platform_device *pdev)
{
ssdhci_alloc_hosttruct sdhci_host *host;
struct resource *iomem;
struct spear_sdhci *sdhci;
struct device *dev;
int ret;
dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
host = sdhci_alloc_host(dev, sizeof(*sdhci));
if (IS_ERR(host)) {
ret = PTR_ERR(host);
dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
goto err;
}
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
if (IS_ERR(host->ioaddr)) {
ret = PTR_ERR(host->ioaddr);
dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
goto err_host;
}
host->hw_name = "sdhci";
host->ops = &sdhci_pltfm_ops;
host->irq = platform_get_irq(pdev, 0);
host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
sdhci = sdhci_priv(host);
/* clk enable */
sdhci->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n");
goto err_host;
}
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
goto err_host;
}
ret = clk_set_rate(sdhci->clk, 50000000);
if (ret)
dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
clk_get_rate(sdhci->clk));
sdhci_probe_config_dt(pdev->dev.of_node, sdhci);
/*
* It is optional to use GPIOs for sdhci card detection. If
* sdhci->card_int_gpio < 0, then use original sdhci lines otherwise
* GPIO lines. We use the built-in GPIO support for this.
*/
if (sdhci->card_int_gpio >= 0) {
ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);
if (ret < 0) {
dev_dbg(&pdev->dev,
"failed to request card-detect gpio%d\n",
sdhci->card_int_gpio);
goto disable_clk;
}
}
ret = sdhci_add_host(host);
if (ret) {
dev_dbg(&pdev->dev, "error adding host\n");
goto disable_clk;
}
platform_set_drvdata(pdev, host);
return 0;
disable_clk:
clk_disable_unprepare(sdhci->clk);
err_host:
sdhci_free_host(host);
err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret;
}
probe函数调用sdhci_probe_config_dt从设备树获取中断脚,并赋给 host->card_int_gpio = cd_gpio; 没有的话值为-1
static void sdhci_probe_config_dt(struct device_node *np,
struct spear_sdhci *host)
{
int cd_gpio;
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
if (!gpio_is_valid(cd_gpio))
cd_gpio = -1;
host->card_int_gpio = cd_gpio;
}
上述probe函数中会检测card_int_gpio ,大于等于0,有些cpu,gpio号可能为0,gpio控制器的base从0 开始的话,而不是从1024开始,就会为0
if (sdhci->card_int_gpio >= 0) {
ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);
if (ret < 0) {
dev_dbg(&pdev->dev,
"failed to request card-detect gpio%d\n",
sdhci->card_int_gpio);
goto disable_clk;
}
}
mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);将cd脚设置成输入
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
unsigned int debounce)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
int ret;
ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
ctx->cd_label);
if (ret < 0)
/*
* don't bother freeing memory. It might still get used by other
* slot functions, in any case it will be freed, when the device
* is destroyed.
*/
return ret;
if (debounce) {
ret = gpio_set_debounce(gpio, debounce);
if (ret < 0)
return ret;
}
ctx->override_cd_active_level = true;
ctx->cd_gpio = gpio_to_desc(gpio);
return 0;
}
probe函数调用,sdhci_add_host调用mmc_start_host,通过mmc_gpiod_request_cd_irq将中断脚注册中断,并注册中断处理函数
oid mmc_gpiod_request_cd_irq(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
int irq = -EINVAL;
int ret;
if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
return;
/*
* Do not use IRQ if the platform prefers to poll, e.g., because that
* IRQ number is already used by another unit and cannot be shared.
*/
if (ctx->cd_irq >= 0)
irq = ctx->cd_irq;
else if (!(host->caps & MMC_CAP_NEEDS_POLL))
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;
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
POLL检测
dts设有broken-cd属性,就会走mmc_schedule_delayed_work来每秒调用mmc_rescan
sdhci_alloc_host
mmc_alloc_host
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
mmc_rescan_try_freq
mmc_attach_sdio/sd/mmc
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
sdhci_add_host
mmc_add_host
mmc_start_host
mmc_gpiod_request_cd_irq
mmc_gpio_cd_irqt
mmc_detect_change
_mmc_detect_change
_mmc_detect_change
mmc_schedule_delayed_work(&host->detect, delay);
mmc_rescan调用mmc_rescan_try_freq来使用freqs里面的频率表来枚举,一般是400k,300k,200k,100k
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
if (host->rescan_disable)
return;
/* If there is a non-removable card registered, only scan once */
if (!mmc_card_is_removable(host) && host->rescan_entered)
return;
host->rescan_entered = 1;
if (host->trigger_card_event && host->ops->card_event) {
mmc_claim_host(host);
host->ops->card_event(host);
mmc_release_host(host);
host->trigger_card_event = false;
}
/* Verify a registered card to be functional, else remove it. */
if (host->bus_ops)
host->bus_ops->detect(host);
host->detect_change = 0;
/* if there still is a card present, stop here */
if (host->bus_ops != NULL)
goto out;
mmc_claim_host(host);
if (mmc_card_is_removable(host) && host->ops->get_cd &&
host->ops->get_cd(host) == 0) {
mmc_power_off(host);
mmc_release_host(host);
goto out;
}
/* If an SD express card is present, then leave it as is. */
if (mmc_card_sd_express(host)) {
mmc_release_host(host);
goto out;
}
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
unsigned int freq = freqs[i];
if (freq > host->f_max) {
if (i + 1 < ARRAY_SIZE(freqs))
continue;
freq = host->f_max;
}
if (!mmc_rescan_try_freq(host, max(freq, host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
/* A non-removable card should have been detected by now. */
if (!mmc_card_is_removable(host) && !host->bus_ops)
pr_info("%s: Failed to initialize a non-removable card",
mmc_hostname(host));
/*
* Ignore the command timeout errors observed during
* the card init as those are excepted.
*/
host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
mmc_release_host(host);
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
sdhci的intmask
cd脚跟sdio的数据脚等都是属于sdhci控制器控制,intmask是SDHCI_INT_CARD_INSERT或者SDHCI_INT_CARD_REMOVE就触发一次探测
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
{
struct sdhci_host *host = dev_id;
struct mmc_command *cmd;
unsigned long flags;
u32 isr;
while (!sdhci_request_done(host))
;
spin_lock_irqsave(&host->lock, flags);
isr = host->thread_isr;
host->thread_isr = 0;
cmd = host->deferred_cmd;
if (cmd && !sdhci_send_command_retry(host, cmd, flags))
sdhci_finish_mrq(host, cmd->mrq);
spin_unlock_irqrestore(&host->lock, flags);
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
struct mmc_host *mmc = host->mmc;
mmc->ops->card_event(mmc);
mmc_detect_change(mmc, msecs_to_jiffies(200));
}
return IRQ_HANDLED;
}
MMC驱动pr_debug开启方法
CONFIG_DYNAMIC_DEBUG为第一优先级,开了直接通过debugfs打开对应文件日志
DEBUG为第二优先级,CONFIG_DYNAMIC_DEBUG没开的话,在对应文件,或文件的Makefile中加DEBUG宏,可让pr_debug替换成printk
/include/linux/printk.h
#if defined(CONFIG_DYNAMIC_DEBUG) || \
(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#include <linux/dynamic_debug.h>
/**
* pr_debug - Print a debug-level message conditionally
* @fmt: format string
* @...: arguments for the format string
*
* This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is
* set. Otherwise, if DEBUG is defined, it's equivalent to a printk with
* KERN_DEBUG loglevel. If DEBUG is not defined it does nothing.
*
* It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses
* pr_fmt() internally).
*/
#define pr_debug(fmt, ...) \
dynamic_pr_debug(fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
drivers/mmc/Makefile
subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
shell
echo 8 4 1 7 > /proc/sys/kernel/printk
