SDIO三种触发枚举的方式

单独的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