Linux MMC子系统6

Linux MMC子系统6(基于Linux6.6)---MMC card添加流程介绍

 


 对iic设备、spi设备、rtc设备等非热插拔设备而言,一般在板级文件或者设备树中定义外设信息,完成外设的注册;但针对mmc card而言,其属于热插拔设备,不需要在板级文件或设备树中进行外设的注册,而由mmc子系统通过mmc card的rescan机制,实现mmc card的自动检测及注册机制。

一、概述

MMC 卡的添加流程涉及硬件检测、驱动加载、设备注册、卡初始化以及最终通过用户空间访问设备。以下是概述的流程:

1. 硬件检测

当一个新的 MMC 卡(如 SD 卡、eMMC 等)被插入到系统中时,系统需要能够检测到这一事件。检测的方式通常包括:

  • 硬件事件驱动:例如,通过 GPIO 引脚或总线事件(如 SDIO 或 SPI 总线上的信号变化)来触发插卡的事件。
  • 轮询机制:在没有硬件事件支持的情况下,MMC 主机控制器的驱动可能会定期轮询卡插入状态。

2. 主机控制器驱动检测卡的插入

当 MMC 卡插入时,系统的主机控制器驱动会通过以下方式来探测和识别卡:

  • mmc_host 的探测:Linux 内核中的 MMC 子系统通过 mmc_host 来管理 MMC 主机控制器。主机控制器负责与卡之间的通信。

    • 主机控制器会发送识别命令(如 CMD0,CMD8 等)来初始化和识别卡的类型(如 SD 卡、eMMC 卡等)。
    • 内核通过 mmc 子系统与主机控制器交互,获取卡的基本信息(如卡的容量、类型等)。

3. MMC 卡的初始化

卡被检测到后,MMC 子系统会开始卡的初始化过程。这一过程通常包括:

  • 发送命令识别卡:内核向卡发送一系列的命令(如 CMD1, CMD2 等),以确认卡的类型(如 SD、eMMC 等)并读取卡的相关信息。

  • 分配 mmc_card 结构体:为每个卡创建一个 mmc_card 结构体,保存卡的相关信息(如容量、特性等)。

  • 初始化卡的硬件资源:包括时钟、总线宽度、电压等设置。

  • 块设备初始化:对于支持块设备的 MMC 卡(如 SD 卡、eMMC 卡等),内核会将其注册为块设备,创建 block_device 结构体,并通过 register_blkdevregister_disk 将卡注册为块设备。

4. 驱动与设备绑定

当卡初始化成功后,内核会将 MMC 卡与相应的驱动进行绑定。

  • 驱动探测:内核通过 MMC 子系统的驱动探测机制,根据 mmc_driverprobe 函数来为卡加载相应的驱动。

  • 驱动注册:例如,SD 卡可能会使用 sdhci 驱动,eMMC 卡可能会使用 mmc 驱动。驱动负责管理卡的读取和写入操作、卡的状态检测等功能。

5. 设备节点创建

在卡初始化完成并与驱动绑定后,内核会在 /dev 目录下为该卡创建设备节点,通常是以 /dev/mmcblkX 的形式,其中 X 是数字索引。例如,第一张卡会被命名为 /dev/mmcblk0,第二张卡会是 /dev/mmcblk1

  • 设备节点的创建使得用户空间可以通过文件系统访问 MMC 卡。例如,用户可以使用 mount 命令挂载该设备、进行读写操作等。

6. 用户空间访问

一旦 MMC 卡被正确注册并创建了设备节点,用户空间的应用程序可以像访问普通块设备一样访问该设备。常见的操作包括:

  • 挂载文件系统:如 mount /dev/mmcblk0 /mnt
  • 使用 fdiskparted 等工具对卡进行分区。
  • 读写文件系统:如通过 cpdd 等工具对卡进行数据读写操作。

二、Mmc card rescan机制

    mmc card是依附于mmc controller的,因此mmc card rescan机制主要是基于mmc host进行设计的;该rescan机制主要 是通过工作队列实现的,如下图为该机制的实现流程,主要说明如下:

  1. Mmc host子系统提供了延迟队列机制,在执行mmc_alloc_host、mmc_add_host后,则完成了mmc card rescan延迟工作队列及其处理接口的创建等操作;
  2. 若要触发mmc card rescan(即调度工作队列),则调用mmc_detect_change接口,即可触发mmc card rescan(即完成mmc_host->detect队列的调度);
  3. 延迟队列的处理函数为mmc_rescan,该函数实现mmc card的添加与移除操作。

2.1、Mmc card rescan的几种方式

延迟工作队列的创建及处理函数的注册操作由mmc host子模块完成(mmc_alloc_host/mmc_add),无需驱动开发者关心;而延迟工作队列的调度则需要具体的mmc host驱动进行实现。而mmc card rescan的方式有如下几种:

  1. Mmc card是不可移除的(如emmc),则在mmc host初始化时设置mmc host为nonremovable(仅在mmc_add_host时,调用mmc_detect_change完成一次mmc rescan,此后不再执行mmc rescan操作);
  2. Mmc host支持mmc card detect功能(通过提供mmc detect中断,进行mmc card detect),此种情况在mmc card detect中断对应的中断处理接口中,调用mmc_detect_change接口,对延迟工作队列进行调度,从而调用接口mmc_rescan,完成一次mmc card的rescan;
  3. Mmc host不支持mmc card detect功能,针对此情形,可以设置mmc host为poll模式。针对此种模式,在mmc_add_host执行一次mmc rescan时,在mmc rescan的最后会执行延迟1s调度该延迟工作队列,从而完成每秒执行一次mmc rescan操作。

三、mmc card探测及移除

        Mmc card的探测及移除由mmc_rescan完成(而mmc_rescan接口则由上述一中的mmc_detect_change调度),下面我们分析mmc_rescan,来说明mmc card的探测及移除

3.1、mmc_rescan接口分析

drivers/mmc/core/core.c

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);
}

mmc_rescan接口实现mmc card rescan的操作,该接口的处理流程图如下:

  1. 若mmc host不支持mmc card的移除操作,则做相应的判断(非第一次执行,则直接退出);
  2. 针对已注册的mmc card,则执行一次detect(host->bus_ops->detect),移除已经离线的mmc card(这会触发mmc_card的release操作,即调用device_unregister将该mmc_card从mmc_bus上移除,这会触发mmc_card的release操作,即调用device_unregister将该mmc_card从mmc_bus上移除,而这即触发mmc_driver与mmc_card的解绑流程(详见上一篇分析文章),从而调用mmc_driver->remove接口,即将已注册的mmc block device进行注销。);
  3. 若mmc host支持get_cd接口,则调用其get_cd接口针对cd引脚值,判断mmc card是否在线;
  4. 针对每一个频率(400khz、300khz、200khz、100khz(若host->f_min大于400khz,则以host->f_min进行探测)),调用mmc_rescan_try_freq进行mmc card rescan操作。

3.2、mmc_rescan_try_freq接口

 drivers/mmc/core/core.c

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
	host->f_init = freq;

	pr_debug("%s: %s: trying to init card at %u Hz\n",
		mmc_hostname(host), __func__, host->f_init);

	mmc_power_up(host, host->ocr_avail);

	/*
	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
	 * do a hardware reset if possible.
	 */
	mmc_hw_reset_for_init(host);

	/*
	 * sdio_reset sends CMD52 to reset card.  Since we do not know
	 * if the card is being re-initialized, just send it.  CMD52
	 * should be ignored by SD/eMMC cards.
	 * Skip it if we already know that we do not support SDIO commands
	 */
	if (!(host->caps2 & MMC_CAP2_NO_SDIO))
		sdio_reset(host);

	mmc_go_idle(host);

	if (!(host->caps2 & MMC_CAP2_NO_SD)) {
		if (mmc_send_if_cond_pcie(host, host->ocr_avail))
			goto out;
		if (mmc_card_sd_express(host))
			return 0;
	}
#ifdef CONFIG_SCM_CM
	if (host->ops->encrypt_config)
		host->ops->encrypt_config(host, 0);
#endif

	/* Order's important: probe SDIO, then SD, then MMC */
	if (!(host->caps2 & MMC_CAP2_NO_SDIO))
		if (!mmc_attach_sdio(host))
			return 0;

	if (!(host->caps2 & MMC_CAP2_NO_SD))
		if (!mmc_attach_sd(host))
			return 0;

	if (!(host->caps2 & MMC_CAP2_NO_MMC))
		if (!mmc_attach_mmc(host))
			return 0;

#ifdef CONFIG_SCM_CM
	if (host->ops->encrypt_config)
		host->ops->encrypt_config(host, 1);
#endif
out:
	mmc_power_off(host);
	return -EIO;
}

该接口的处理流程如上图所示,该接口实现的功能如下:

  1. 设置mmc host的总线带宽、总线模式(pushpull),并设置mmc host(从powerup->poweron);
  2. 若mmc host支持hw_reset,执行hw_reset;
  3. 向mmc card 发送MMC_GO_IDLE_STATE命令(让mmc卡进入idle状态,该命令为broadcast commnad without response);
  4. 执行mmc 设备的探测:
    1. 针对sdio 设备,则调用mmc_attach_sdio执行sdio设备的探测;
    2. 针对sd设备,则调用mmc_attach_sd执行sd设备的探测;
    3. 针对mmc设备,则调用mmc_attach_mmc执行mmc设备的探测

mmc_attach_sdio、mmc_attach_sd、mmc_attach_mmc则为具体设备的探测及绑定流程。下面我们以sd card为主,进行sd card探测及绑定流程说明:

3.3、mmc_attach_sd接口分析

 drivers/mmc/core/sd.c

/*
 * Starting point for SD card init.
 */
int mmc_attach_sd(struct mmc_host *host)
{
	int err;
	u32 ocr, rocr;

	WARN_ON(!host->claimed);

	err = mmc_send_app_op_cond(host, 0, &ocr);
	if (err)
		return err;

	mmc_attach_bus(host, &mmc_sd_ops);
	if (host->ocr_avail_sd)
		host->ocr_avail = host->ocr_avail_sd;

	/*
	 * We need to get OCR a different way for SPI.
	 */
	if (mmc_host_is_spi(host)) {
		mmc_go_idle(host);

		err = mmc_spi_read_ocr(host, 0, &ocr);
		if (err)
			goto err;
	}

	/*
	 * Some SD cards claims an out of spec VDD voltage range. Let's treat
	 * these bits as being in-valid and especially also bit7.
	 */
	ocr &= ~0x7FFF;

	rocr = mmc_select_voltage(host, ocr);

	/*
	 * Can we support the voltage(s) of the card(s)?
	 */
	if (!rocr) {
		err = -EINVAL;
		goto err;
	}

	/*
	 * Detect and init the card.
	 */
	err = mmc_sd_init_card(host, rocr, NULL);
	if (err)
		goto err;

	mmc_release_host(host);
	err = mmc_add_card(host->card);
	if (err)
		goto remove_card;

	mmc_claim_host(host);
#ifdef CONFIG_SCM_CM
	if (host->ops->encrypt_config)
		host->ops->encrypt_config(host, 1);
#endif
	return 0;

remove_card:
	mmc_remove_card(host->card);
	host->card = NULL;
	mmc_claim_host(host);
err:
	mmc_detach_bus(host);

	pr_err("%s: error %d whilst initialising SD card\n",
		mmc_hostname(host), err);

	return err;
}

mmc_attach_sd接口的主要处理流程如下图所示,其实现的主要功能如下:

  1. 发送send_op_cond(该指令为broadcast command with response);若没有响应,则说明card不在线,程序返回;
  2. 若card回复,则说明card在线,则调用mmc_sd_attach_bus_ops,设置mmc_host->bus_ops为mmc_sd_ops_unsafe/mmc_sd_ops;
  3. 调用mmc_sd_init_card,获取mmc card的csd、cid,并创建mmc_card,并对mmc card进行初始化(如是否只读等信息);
  4. 调用mmc_add_card,将该mmc_card注册至mmc_bus中,该接口会调用device_register将mmc_card注册至mmc_bus上,而这即触发mmc_driver与mmc_card的绑定流程(详见上一篇分析文章),从而调用mmc_driver->probe接口,即执行mmc block device的注册操作。

 

四、总结

在 Linux 系统中,MMC 卡的添加流程主要包括硬件识别、驱动加载、设备节点创建和文件系统挂载等步骤。以下是MMC卡添加的总体流程,从插入卡到成功使用的每个步骤:

1. 硬件插入

当你将一个 MMC 卡插入到设备(如 SD 卡读卡器或嵌入式系统)时,设备会物理连接到主机控制器(例如 SDHCI 或其他 MMC 控制器)。此时,内核检测到硬件的插入并开始硬件初始化过程。

2. 内核设备检测与驱动加载

内核通过检测设备总线来识别 MMC 卡。具体过程如下:

设备检测

  • 内核通过 SDHCI (Secure Digital Host Controller Interface) 驱动、MMC 驱动或其他与 MMC 相关的驱动来识别设备。内核会扫描相关的总线(如 SDIOSATA 或其他总线)来检查设备。

  • dmesg 日志中,系统会打印出相关信息,表明设备被检测到。例如,类似以下输出:

  • [ 123.456789] mmc0: new high speed SDHC card at address 0007
    [ 123.457000] mmcblk0: mmc0:0007 SD32G 29.8 GiB
    

驱动加载

  • 如果内核已经正确配置了 MMC 驱动(例如 mmc_block, sdhci, mmc_core),驱动会被加载并开始初始化卡。驱动程序负责与硬件交互。
  • 如果需要,内核会自动加载相关的模块(例如 mmc_blocksdhci)。

3. 设备节点创建

一旦硬件被正确识别,内核会为该设备创建一个相应的块设备节点。通常,该节点位于 /dev 目录下,格式为 /dev/mmcblkX(X 是设备编号),例如:

/dev/mmcblk0

如果 MMC 卡支持分区,还会创建分区节点,例如:

/dev/mmcblk0p1  /dev/mmcblk0p2

4. 分区表识别

如果 MMC 卡上已经存在分区表(例如 GPT 或 MBR 分区表),内核会识别并读取该分区表。此时,可以通过以下命令查看分区信息:

fdisk -l /dev/mmcblk0

如果没有分区表,可以手动创建分区,或者使用工具如 partedfdisk 来操作。

5. 文件系统检测

如果分区上有文件系统(如 ext4、vfat、ntfs 等),内核会自动识别并加载该文件系统。否则,你需要手动格式化分区为合适的文件系统。格式化命令如下:

mkfs.ext4 /dev/mmcblk0p1

6. 挂载文件系统

在文件系统创建后,可以将文件系统挂载到系统的某个目录,以便访问。使用 mount 命令来挂载 MMC 卡:

mount /dev/mmcblk0p1 /mnt

如果一切顺利,MMC 卡中的数据就可以通过 /mnt 目录访问。

7. 自动挂载(可选)

在许多 Linux 系统中,udev 或其他设备管理系统会自动处理挂载过程。例如,桌面环境(如 GNOME 或 KDE)会自动挂载插入的 MMC 卡并在文件管理器中显示它。

你也可以配置 /etc/fstab 文件,实现 MMC 卡的自动挂载:

/dev/mmcblk0p1  /mnt  ext4  defaults  0  2

8. 设备使用

此时,MMC 卡已经成功插入、识别、分区、格式化、挂载,可以开始使用。

9. 设备卸载与移除

使用完毕后,可以通过 umount 命令卸载设备:

umount /mnt

然后可以安全地移除 MMC 卡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值