Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST篇

本文详细探讨了Linux系统中SD卡驱动的HOST部分,包括mmc_host结构体的描述、初始化过程、设备注册和功能函数。重点分析了mmc_host_ops在SD卡控制器驱动中的作用,以及中断处理和数据传输的实现。内容涉及mmc_add_host函数、中断注册和响应,为理解SD卡驱动的底层工作机制提供了深入见解。

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

      回顾一下前面的知识,MMC 子系统范围三个部分:

HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。

CORE 部分: 这是整个MMC 的核心存,这部分完成了不同协议和规范的实现,并为HOST 层的驱动提供了接口函数。

CARD 部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD 卡如何实现为块设备的。

它们分布于下面的文件夹中 Linux/drivers/mmc中


     其中,card(区块层) 与core(核心层)是linux系统封装好了部分,我们不需要修改,host(主控制器层)中提供与各芯片构架相关的文件,这才是我们所要开发的部分

     核心层根据需要构造各种MMC/SD命令,这些命令怎么发送给MMC/SD卡呢?这通过主机控制器层来实现。这层是架构相关的,里面针对各款CPU提供一个文件,目前支持的CPU还很少。

      以本节即将移植的s3cmci.c为例,它首先进行一些低层设置,比如设置MMC/SD/SDIO控制器使用到的CPIO引脚使能控制器注册中断处理函数等,然后向上面的核心层增加一个主机(Host),这样核心层就能调用s3cmci.c提供的函数来识别、使用具体存储卡了。

      在向核心层增加主机之前,s3cmci.c 设置了一个mmc_host_ops结构体,它实现两个函数:

a -- 发起访问请求的request函数

b -- 进行一些属性设置(时钟频率、数据线位宽等)的set_ios函数

      下面列出识别存储卡、区块层发起操作请求两种情况下函数的主要调用关系:

1)识别存储卡


2)区块层发起操作请求


      以后上次对存储卡的操作都通过调用这两个函数来完成。下面对HOST层进行分析(Linux内核版本:Linux-3.14)。


一、struct mmc_host 结构体

        主要用来描述卡控制器位 结构体mmc_host定义于/include/linux/mmc/host.c,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,在本例芯片控制器的驱动程序s3cmci.c中,则为该类具体化了一个对象struct mmc_host *mmc,此mmc指针即指代着该ARM芯片SD卡控制器的一个具体化对象(可以看出虽然C是面向过程的语言,但还是用到了一些面向对象思想的)。

linux/driver/mmc/host/s3cmci.h下定义

struct s3cmci_host {
	struct platform_device	*pdev;
	struct s3c24xx_mci_pdata *pdata;
	struct mmc_host		*mmc;
	struct resource		*mem;
	struct clk		*clk;
	void __iomem		*base;
	int			irq;
	int			irq_cd;
	int			dma;

	unsigned long		clk_rate;
	unsigned long		clk_div;
	unsigned long		real_rate;
	u8			prescaler;

	int			is2440;
	unsigned		sdiimsk;
	unsigned		sdidata;
	int			dodma;
	int			dmatogo;

	bool			irq_disabled;
	bool			irq_enabled;
	bool			irq_state;
	int			sdio_irqen;

	struct mmc_request	*mrq;
	int			cmd_is_stop;

	spinlock_t		complete_lock;
	enum s3cmci_waitfor	complete_what;

	int			dma_complete;

	u32			pio_sgptr;
	u32			pio_bytes;
	u32			pio_count;
	u32			*pio_ptr;
#define XFER_NONE 0
#define XFER_READ 1
#define XFER_WRITE 2
	u32			pio_active;

	int			bus_width;

	char 			dbgmsg_cmd[301];
	char 			dbgmsg_dat[301];
	char			*status;

	unsigned int		ccnt, dcnt;
	struct tasklet_struct	pio_tasklet;

#ifdef CONFIG_DEBUG_FS
	struct dentry		*debug_root;
	struct dentry		*debug_state;
	struct dentry		*debug_regs;
#endif

#ifdef CONFIG_CPU_FREQ
	struct notifier_block	freq_transition;
#endif
};

   其中struct mmc_host(linux/include/linux/mmc/host.h)用于与core层的命令请求,数据 传输等信息,这里代码较长,展示部分

struct mmc_host 
{
	const struct mmc_host_ops *ops;     // SD卡主控制器的操作函数,即该控制器所具备的驱动能力
	const struct mmc_bus_ops *bus_ops; // SD总线驱动的操作函数,即SD总线所具备的驱动能力
	struct mmc_ios  ios;  // 配置时钟、总线、电源、片选、时序等
	struct mmc_card  *card;  // 连接到此主控制器的SD卡设备
	... ...
};

      本文中struct mmc_host_ops *ops定义

static struct mmc_host_ops s3cmci_ops = {
	.request	= s3cmci_request,
	.set_ios	= s3cmci_set_ios,
	.get_ro		= s3cmci_get_ro,
	.get_cd		= s3cmci_card_present,
	.enable_sdio_irq = s3cmci_enable_sdio_irq,
};


二、SD控制器之初始化(linux/driver/mmc/host)

        这一层讲述硬件与硬件之间将要发生的故事,也是最底层驱动的核心。通常所谓的驱动程序设计的任务将落实到这一层上,所以关注host故事的发展也将成为移植整个SD类设备驱动的核心。在host 目录中有各种平台下SD 卡主机驱动器的实例,这里我们选择s3c2440平台作为分析的重点。参看Kconfig和Makefile即可获得相应信息,这里对应的文件即是s3cmci.c。

1、设备的注册

      旧瓶装新酒,还是那个module_init,不一样的是其中的入口函数。在s3cmci.c中对应的是module_init(s3cmci_init);

module_platform_driver(s3cmci_driver);

 这里是不是很奇怪,不应该是module_init 与 module_exit 吗?module_platform_driver其实是一个宏定义,定义在include/linux/platform_device.h文件中:

/* module_platform_driver() - Helper macro for drivers that don't do
 * anything special in module init/exit.  This eliminates a lot of
 * boilerplate.  Each module may only use this macro once, and
 * calling it replaces module_init() and module_exit()
 */
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)

宏module_driver定义在include/linux/device.h文件中,其内容如下:

/**
 * module_driver() - Helper macro for drivers that don't do anything
 * special in module init/exit. This eliminates a lot of boilerplate.
 * Each module may only use this macro once, and calling it replaces
 * module_init() and module_exit().
 *
 * @__driver: driver name
 * @__register: register function for this driver type
 * @__unregister: unregister function for this driver type
 * @...: Additional arguments to be passed to __register and __unregister.
 *
 * Use this macro to construct bus specific macros for registering
 * drivers, and do not use it on its own.
 */
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
    可以看出,最终还是调用 module_init 与 module_exit。这里注册了一个平台驱动,这个前面分析的串口驱动等是一样的原理。这是新版内核中引入的一个虚拟的平台总线。对应的平台设备早在内核启动时通过platform_add_devices加入到了内核,相关的具体内容前面已经分析的挺多了,这里就不在详细说明。 该句调用的结果会导致s3cmci_driver中的probe方法得以调用,由此也就把我们引入了host的世界。


2、probe函数

     在驱动的接口函数中s3cmci_probe() 函数,用于分配mmc_host ,s3cmci_host结构体,并对结构体进行设置在SDI主机控制器操作接口函数mmc_host_ops中会调用s3cmci_host结构体,申请中断并设置中断服务函数,将结构体mmc_host添加到主机。

   SD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,

  • 初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下
CADENCE PCB仿真是一种电路板设计软件,其中使用了Allegro PCB SI PDN分析模块。该模块可用于通过仿真来确定目标阻抗并优化电路板的设计。 下面是设置目标阻抗的方法的图文教程: 第一步: 在CADENCE PCB软件中,打开要进行仿真的电路板设计文件。 第二步: 在工具栏中选择"PCB工具",然后选择"SI/PI Analysis Setup"。这将打开一个新的对话框。 第三步: 在对话框中,选择"PDN"选项卡,然后点击"Create Project"按钮。这将创建一个新的PDN项目。 第四步: 在PDN项目中,选择"Design Constraints"选项卡。在右侧的面板中,可以选择添加或编辑目标阻抗。 第五步: 点击"Add"按钮,然后在阻抗约束对话框中输入所需的目标阻抗数值。可以设置不同的目标阻抗值,例如示意图中的50欧姆和80欧姆。 第六步: 点击"OK"按钮保存设置。在PDN项目的"Design Constraints"选项卡中,可以看到已添加的目标阻抗。 第七步: 在PDN项目中,选择"PDN Explorer"选项卡。这将打开PDN资源树,显示电路板上的电源和地平面。 第八步: 在PDN资源树中选择要分析的功率/地区域。右键单击该区域,然后选择"创建电源分区"。 第九步: 在电源分区的属性面板中,选择在第五步中创建的目标阻抗。可以设置电源分区的电感和电容属性来满足目标阻抗要求。 第十步: 点击"Apply"按钮应用设置。PDN资源树中的电源分区将根据目标阻抗进行优化。 通过以上步骤,可以使用CADENCE PCB仿真和Allegro PCB SI PDN分析模块来设置目标阻抗并优化电路板的设计。这将确保电路板的信号完整性和稳定性,并减少干扰和噪声产生的影响。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值