基于RK3399的Linux驱动开发 -- EMMC驱动框架

本文深入剖析基于RK3399平台的Linux EMMC驱动框架,涵盖host层的dw_mci框架和dw_mci-rockchip驱动,以及core层的接口设计。详细阐述了mmc总线接口的注册、card层块设备驱动的实现,特别是mmc设备的初始化、扫描和操作流程。

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

一、概述

本文章主要通过源代码分析EMMC的驱动框架,解释如何注册一个host驱动、并通过card层的块设备驱动接口和core层相关接口访问host驱动接口,进而访问emmc器件。

二、host层

1、dw_mci框架

dw_mci_drv_data

这个是dw_mci框架的私有数据,主要包含一些回调接口,通过接口访问具体芯片mmc控制器的一些参数。

struct dw_mci_drv_data {
   
   
	unsigned long	*caps;
	int		(*init)(struct dw_mci *host);
	int		(*setup_clock)(struct dw_mci *host);
	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);
	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
	int		(*parse_dt)(struct dw_mci *host);
	int		(*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
	int		(*prepare_hs400_tuning)(struct dw_mci *host,
						struct mmc_ios *ios);
	int		(*switch_voltage)(struct mmc_host *mmc,
					  struct mmc_ios *ios);
};

dw_mci_dma_ops

struct dw_mci_dma_ops {
   
   
	int (*init)(struct dw_mci *host);
	int (*start)(struct dw_mci *host, unsigned int sg_len);
	void (*complete)(void *host);
	void (*stop)(struct dw_mci *host);
	void (*cleanup)(struct dw_mci *host);
	void (*exit)(struct dw_mci *host);
};

dw_mci

struct dw_mci {
   
   
	int			use_dma;
	const struct dw_mci_dma_ops	*dma_ops;
	struct device		*dev;
	const struct dw_mci_drv_data	*drv_data;
	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
};

dw_mci_pltfm_register

该接口由具体mmc控制器驱动调用,注册为一个dw_mci兼容框架的驱动,这一类驱动访问的寄存器,时钟参数都一样的,彼此兼容。

int dw_mci_pltfm_register(struct platform_device *pdev,
			  const struct dw_mci_drv_data *drv_data)
{
   
   
	struct dw_mci *host;

	host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
	/* 填充host实例 */
	return dw_mci_probe(host);
}

dw_mci_probe

int dw_mci_probe(struct dw_mci *host)
{
   
   
	if (!host->pdata) {
   
   
		/* 从具体平台dts解析出mmc接口的节点参数 */
		host->pdata = dw_mci_parse_dt(host); 
	}
	
	/* 获取时钟 */
	host->biu_clk = devm_clk_get(host->dev, "biu");
	host->ciu_clk = devm_clk_get(host->dev, "ciu");
	
	/* 调用具体mmc控制器的驱动回调 */
	if (drv_data && drv_data->init) {
   
   
	}
	if (drv_data && drv_data->setup_clock) {
   
   
	}

	host->dma_ops = host->pdata->dma_ops;
	dw_mci_init_dma(host);
	
	for (i = 0; i < host->num_slots; i++) {
   
   
		/* 一个mmc控制器就是一个slot,例如rk3399有两个mmc控制器,一个是sdio0,一个是sdmmc */
		ret = dw_mci_init_slot(host, i);
	}
}

dw_mci_init_slot

/* 这个就是mmc控制器的回调接口 */
static const struct mmc_host_ops dw_mci_ops = {
   
   
	.request		= dw_mci_request,
	.pre_req		= dw_mci_pre_req,
	.post_req		= dw_mci_post_req,
	.set_ios		= dw_mci_set_ios,
	.set_sdio_status	= dw_mci_set_sdio_status,
	.get_ro			= dw_mci_get_ro,
	.get_cd			= dw_mci_get_cd,
	.enable_sdio_irq	= dw_mci_enable_sdio_irq,
	.execute_tuning		= dw_mci_execute_tuning,
	.card_busy		= dw_mci_card_busy,
	.start_signal_voltage_switch = dw_mci_switch_voltage,
	.init_card		= dw_mci_init_card,
	.prepare_hs400_tuning	= dw_mci_prepare_hs400_tuning,
};

static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
{
   
   
	struct mmc_host *mmc;
	
	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); 
	/* 绑定回调,以便core层调用,例如request、card_busy等 */
	mmc->ops = &dw_mci_ops;
	ret = mmc_add_host(mmc);
}

dw_mci_init_dma

static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
   
   
	.init = dw_mci_idmac_init,
	.start = dw_mci_idmac_start_dma,
	.stop = dw_mci_idmac_stop_dma,
	.complete = dw_mci_dmac_complete_dma,
	.cleanup = dw_mci_dma_cleanup,
};

static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
   
   
	.init = dw_mci_edmac_init,
	.exit = dw_mci_edmac_exit,
	.start = dw_mci_edmac_start_dma,
	.stop = dw_mci_edmac_stop_dma,
	.complete = dw_mci_dmac_complete_dma,
	.cleanup = dw_mci_dma_cleanup,
};

static void dw_mci_init_dma(struct dw_mci *host)
{
   
   
	if (host->use_dma == TRANS_MODE_IDMAC) {
   
   
		host->dma_ops = &dw_mci_idmac_ops;
	} else {
   
   
		host->dma_ops = &dw_mci_edmac_ops;
	}
}

2、dw_mci-rockchip驱动

dw_mci_rockchip_probe

/* 这里实现了dm_mci框架的回调接口 */
static const struct dw_mci_drv_data rk3288_drv_data = {
   
   
	.caps			= dw_mci_rk3288_dwmmc_caps,
	.prepare_command        = dw_mci_rockchip_prepare_command,
	.set_ios		= dw_mci_rk3288_set_ios,
	.execute_tuning		= dw_mci_rk3288_execute_tuning,
	.parse_dt		= dw_mci_rk3288_parse_dt
### Linux 系统下 eMMC 存储设备的识别方法 在 Linux 系统中,eMMC 设备的识别主要依赖于内核中的 MMC(MultiMediaCard)子系统以及设备树(Device Tree)。以下是关于 eMMC 设备识别的关键点: #### 1. **设备树的作用** 在 RK3288 平台上的 Linux 内核中,eMMC 的配置信息通过设备树文件 `arch/arm/boot/dts/rk3288.dtsi` 提供。此文件定义了 eMMC 设备作为 MMC 设备节点的一部分,并描述了其分区结构、启动分区、分区大小和名称等细节[^1]。 #### 2. **CID 注册表的应用** 当 Linux 内核初始化 eMMC 控制器时,会读取 eMMC 设备的 CID(Card Identification Number),这是一个唯一标识符,包含制造商信息、OEM 序列号、设备名称和其他元数据。这些信息帮助内核确认连接的是哪个具体的 eMMC 芯片并加载相应的驱动程序[^3]。 #### 3. **MMC 子系统的角色** Linux 中的 MMC 子系统负责管理 SD 卡和 eMMC 类型的存储介质。尽管 SD 卡最初设计为独立可移动存储解决方案,但它后来扩展到支持固定嵌入式存储方案如 eMMC。因此,在实现上,Linux 使用相同的框架来处理这两种类型的设备[^4]。 #### 4. **具体流程概述** - **硬件探测阶段**: 当板级支持包 (BSP) 初始化完成后,RK3288 SoC 上的 eMMC 控制器会被激活。 - **固件交互阶段**: 接下来,Linux 内核利用设备树提供的参数与物理层通信,获取有关 eMMC 特性的详细资料,比如容量和支持的功能集。 - **注册块设备**: 最终,经过上述过程验证后的 eMMC 将作为一个标准块设备呈现给操作系统层面,通常命名为 `/dev/mmcblk0`, 后续任何对该路径的操作都会映射到底层实际的数据交换操作上去。 ```bash ls /sys/class/block/ ``` 运行以上命令可以查看当前系统中存在的所有块设备列表;如果存在名为 mmcblk* 的条目,则表明至少有一个有效的 eMMC 或者 SD/MMC 卡已被成功检测出来。 --- ### 示例代码展示如何枚举现有mmc控制器及其关联状态 下面给出一段简单的脚本用来列举系统内的所有可用mmc主机连同它们的状态报告: ```python import os def list_mmc_devices(): base_path = '/sys/bus/mmc/drivers/' drivers = next(os.walk(base_path))[1] for driver in drivers: devices_dir = f"{base_path}{driver}/" try: device_dirs = next(os.walk(devices_dir))[1] for dev in device_dirs: print(f"Found {dev} under {driver}") with open(f'{devices_dir}/{dev}/power/runtime_status', 'r') as file: status = file.read().strip() print(f"\tPower Status: {status}") except StopIteration: continue if __name__ == "__main__": list_mmc_devices() ``` 执行这段 Python 脚本能够动态扫描整个系统架构下的 mmc 总线布局状况,从而辅助开发者快速定位目标资源位置。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值