mmc驱动中的mmc_host结构体中rescan_disable变量的作用及使用

本文详细解析了MMC卡探测机制中的关键变量rescan_disable的作用及其在不同函数中的设置过程。通过对mmci_probe函数的深入分析,揭示了在主机初始化过程中如何通过设置rescan_disable来控制卡的探测状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、rescan_disable的作用

该变量在mmc_host中是这样定义的:

int			rescan_disable;	/* disable card detection */
注释的意思:取消探测卡的操作。

作用:当为0,表示开启卡的探测开关;为非0表示关闭卡的探测。


二、被调用的地方

rescan_disable=0rescan_disable=1
mmc_start_host(core.c)mmc_stop_host(core.c)
mmc_pm_notify的准备挂载休眠分支mmc_pm_notify的挂载休眠分支
 mmc_alloc_host(host.c)


三、重点需要说明的先后调用关系

在host初始化完成之前,由于此时host没有做好探测卡的准备,所以在初始化完成前将rescan_disable的值设置为1,下面结合代码来说明。

还是以mmci的host来说明,在mmci.c中有一个mmci的probe函数,如下:

static int mmci_probe(struct amba_device *dev,
	const struct amba_id *id)
{
	struct mmci_platform_data *plat = dev->dev.platform_data;
	struct device_node *np = dev->dev.of_node;
	struct variant_data *variant = id->data;
	struct mmci_host *host;
	struct mmc_host *mmc;
	int ret;

	/* Must have platform data or Device Tree. */
	if (!plat && !np) {
		dev_err(&dev->dev, "No plat data or DT found\n");
		return -EINVAL;
	}

	if (!plat) {
		plat = devm_kzalloc(&dev->dev, sizeof(*plat), GFP_KERNEL);
		if (!plat)
			return -ENOMEM;
	}

	if (np)
		mmci_dt_populate_generic_pdata(np, plat);

	ret = amba_request_regions(dev, DRIVER_NAME);
	if (ret)
		goto out;

	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
	if (!mmc) {
		ret = -ENOMEM;
		goto rel_regions;
	}

	host = mmc_priv(mmc);
	host->mmc = mmc;

	host->gpio_wp = -ENOSYS;
	host->gpio_cd = -ENOSYS;
	host->gpio_cd_irq = -1;

	host->hw_designer = amba_manf(dev);
	host->hw_revision = amba_rev(dev);
	dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
	dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision);

	host->clk = devm_clk_get(&dev->dev, NULL);
	if (IS_ERR(host->clk)) {
		ret = PTR_ERR(host->clk);
		goto host_free;
	}

	ret = clk_prepare_enable(host->clk);
	if (ret)
		goto host_free;

	host->plat = plat;
	host->variant = variant;
	host->mclk = clk_get_rate(host->clk);
	/*
	 * According to the spec, mclk is max 100 MHz,
	 * so we try to adjust the clock down to this,
	 * (if possible).
	 */
	if (host->mclk > 100000000) {
		ret = clk_set_rate(host->clk, 100000000);
		if (ret < 0)
			goto clk_disable;
		host->mclk = clk_get_rate(host->clk);
		dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
			host->mclk);
	}
	host->phybase = dev->res.start;
	host->base = ioremap(dev->res.start, resource_size(&dev->res));
	if (!host->base) {
		ret = -ENOMEM;
		goto clk_disable;
	}

	if (variant->busy_detect) {
		mmci_ops.card_busy = mmci_card_busy;
		mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
	}

	mmc->ops = &mmci_ops;
	/*
	 * The ARM and ST versions of the block have slightly different
	 * clock divider equations which means that the minimum divider
	 * differs too.
	 */
	if (variant->st_clkdiv)
		mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
	else
		mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
	/*
	 * If the platform data supplies a maximum operating
	 * frequency, this takes precedence. Else, we fall back
	 * to using the module parameter, which has a (low)
	 * default value in case it is not specified. Either
	 * value must not exceed the clock rate into the block,
	 * of course.
	 */
	if (plat->f_max)
		mmc->f_max = min(host->mclk, plat->f_max);
	else
		mmc->f_max = min(host->mclk, fmax);
	dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);

	/* Get regulators and the supported OCR mask */
	mmc_regulator_get_supply(mmc);
	if (!mmc->ocr_avail)
		mmc->ocr_avail = plat->ocr_mask;
	else if (plat->ocr_mask)
		dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");

	mmc->caps = plat->capabilities;
	mmc->caps2 = plat->capabilities2;

	/* We support these PM capabilities. */
	mmc->pm_caps = MMC_PM_KEEP_POWER;

	/*
	 * We can do SGIO
	 */
	mmc->max_segs = NR_SG;

	/*
	 * Since only a certain number of bits are valid in the data length
	 * register, we must ensure that we don't exceed 2^num-1 bytes in a
	 * single request.
	 */
	mmc->max_req_size = (1 << variant->datalength_bits) - 1;

	/*
	 * Set the maximum segment size.  Since we aren't doing DMA
	 * (yet) we are only limited by the data length register.
	 */
	mmc->max_seg_size = mmc->max_req_size;

	/*
	 * Block size can be up to 2048 bytes, but must be a power of two.
	 */
	mmc->max_blk_size = 1 << 11;

	/*
	 * Limit the number of blocks transferred so that we don't overflow
	 * the maximum request size.
	 */
	mmc->max_blk_count = mmc->max_req_size >> 11;

	spin_lock_init(&host->lock);

	writel(0, host->base + MMCIMASK0);
	writel(0, host->base + MMCIMASK1);
	writel(0xfff, host->base + MMCICLEAR);

	if (plat->gpio_cd == -EPROBE_DEFER) {
		ret = -EPROBE_DEFER;
		goto err_gpio_cd;
	}
	if (gpio_is_valid(plat->gpio_cd)) {
		ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
		if (ret == 0)
			ret = gpio_direction_input(plat->gpio_cd);
		if (ret == 0)
			host->gpio_cd = plat->gpio_cd;
		else if (ret != -ENOSYS)
			goto err_gpio_cd;

		/*
		 * A gpio pin that will detect cards when inserted and removed
		 * will most likely want to trigger on the edges if it is
		 * 0 when ejected and 1 when inserted (or mutatis mutandis
		 * for the inverted case) so we request triggers on both
		 * edges.
		 */
		ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd),
				mmci_cd_irq,
				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
				DRIVER_NAME " (cd)", host);
		if (ret >= 0)
			host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd);
	}
	if (plat->gpio_wp == -EPROBE_DEFER) {
		ret = -EPROBE_DEFER;
		goto err_gpio_wp;
	}
	if (gpio_is_valid(plat->gpio_wp)) {
		ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)");
		if (ret == 0)
			ret = gpio_direction_input(plat->gpio_wp);
		if (ret == 0)
			host->gpio_wp = plat->gpio_wp;
		else if (ret != -ENOSYS)
			goto err_gpio_wp;
	}

	if ((host->plat->status || host->gpio_cd != -ENOSYS)
	    && host->gpio_cd_irq < 0)
		mmc->caps |= MMC_CAP_NEEDS_POLL;

	ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
	if (ret)
		goto unmap;

	if (!dev->irq[1])
		host->singleirq = true;
	else {
		ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED,
				  DRIVER_NAME " (pio)", host);
		if (ret)
			goto irq0_free;
	}

	writel(MCI_IRQENABLE, host->base + MMCIMASK0);

	amba_set_drvdata(dev, mmc);

	dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n",
		 mmc_hostname(mmc), amba_part(dev), amba_manf(dev),
		 amba_rev(dev), (unsigned long long)dev->res.start,
		 dev->irq[0], dev->irq[1]);

	mmci_dma_setup(host);

	pm_runtime_set_autosuspend_delay(&dev->dev, 50);
	pm_runtime_use_autosuspend(&dev->dev);
	pm_runtime_put(&dev->dev);

	mmc_add_host(mmc);

	return 0;

 irq0_free:
	free_irq(dev->irq[0], host);
 unmap:
	if (host->gpio_wp != -ENOSYS)
		gpio_free(host->gpio_wp);
 err_gpio_wp:
	if (host->gpio_cd_irq >= 0)
		free_irq(host->gpio_cd_irq, host);
	if (host->gpio_cd != -ENOSYS)
		gpio_free(host->gpio_cd);
 err_gpio_cd:
	iounmap(host->base);
 clk_disable:
	clk_disable_unprepare(host->clk);
 host_free:
	mmc_free_host(mmc);
 rel_regions:
	amba_release_regions(dev);
 out:
	return ret;
}

第30行,有一个mmc_alloc_host的函数,通过第二节可以知道,这个函数将rescan_disable标记设置为了1;

第231行,有一个mmc_add_host的函数,其实现如下:

int mmc_add_host(struct mmc_host *host)
{
	int err;

	WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
		!host->ops->enable_sdio_irq);

	err = device_add(&host->class_dev);
	if (err)
		return err;

	led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

#ifdef CONFIG_DEBUG_FS
	mmc_add_host_debugfs(host);
#endif
	mmc_host_clk_sysfs_init(host);

	mmc_start_host(host);
	register_pm_notifier(&host->pm_notify);

	return 0;
}
由此可以知道,它调用了mmc_start_host函数, 通过第二节可以知道,这个函数将rescan_disable设置为了0,开启了卡的侦测。

总结:在mmci_probe中先关闭卡的侦测,初始化一个host,当host初始化完毕,则开启侦测,此时如果有卡设备接到总线上,就会调用中断函数mmci_cd_irq去完成卡的上盘操作。

### mmc_ioc_cmd 结构体成员变量功能解释 在 Linux 内核中,`mmc_ioc_cmd` 结构体用于封装向多媒体卡 (MMC) 发送的命令及其参数。此结构体允许用户空间应用程序通过 ioctl 调用来执行特定的操作。 #### 成员变量及功能说明: - **opcode**: 存储要发送给 MMC 设备的具体命令编号[^2]。 - **arg**: 作为命令的参数传递给设备,其含义取决于具体的 `opcode`。这通常是一个32位整数值,具体意义由命令本身决定。 - **flags**: 表示命令标志,这些标志控制着如何处理这条指令以及它的响应方式。例如,某些标志可能指示这是一个读取还是写入操作;是否期望返回数据等[^5]。 - **blksz**: 定义每次传输的数据块大小(单位为字节)。对于多块传输来说非常重要,它决定了每一块应该有多大[^4]。 - **blocks**: 指明当进行批量传送时涉及多少个这样的数据块。如果只是单次请求,则这个值通常是1。 - **data**: 这是一个指向缓冲区指针的指针,其中包含了待传送给设备的数据或者是接收来自设备的数据的地方。根据上下文的不同,它可以是指向输入或输出缓冲区的地址。 - **cdone**: 用户态提供的完成信号量(semaphore),用于同步机制,在命令完成后会被唤醒通知应用层。 ```c struct mmc_ioc_cmd { __u32 opcode; __u32 flags; __u32 arg; __u32 blksz; __u32 blocks; void __user *data; struct semaphore cdone; }; ``` 上述代码展示了 `mmc_ioc_cmd` 的基本定义,注意这里使用了 `__u32` 类型来表示无符号32位整数,并且 `void __user *data` 是一个特殊的指针类型,表明这是指向用户空间内存区域的指针。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值