结合RTC驱动浅析I2C子系统技术研究

本文详细解析了Linux内核中的I2C子系统架构,包括I2C核心、总线驱动和设备驱动的组成部分。介绍了i2c_adapter、i2c_client及i2c_driver等关键结构体,以及设备树配置、适配器驱动、i2c_client读写操作的实现。此外,还探讨了RTC设备的注册、功能操作集和代码修改,展示了如何通过RTC驱动设置和调整时间。

1. I2C子系统总体架构

I2C子系统可以让驱动开发者可以在内核中方便添加自己的I2C设备驱动程序,让内核统一管理I2C设备,从而可以更容易的在linux驱动自己的I2C接口硬件

1.1 I2C子系统的组成部分

i2c子系统主要由3部分组成I2C核心,I2C总线驱动,I2C设备驱动

i2c子系统总体架构图如下图所示

在这里插入图片描述

1.I2C核心

I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册、注销方法,I2C通信方法(”algorithm”)上层的,与具体硬件无关的代码以及探测设备 检测设备地址的上层代码等。

2.I2c总线驱动

(1)I2C总线驱动是对I2C硬件体系结构中适配端的实现,适配器可由CPU控制,甚至可以直接集成到CPU内部。

(2)I2C总线驱动主要包含I2C适配器数据结构i2c_adapter ,I2C适配器的Algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数

(3)经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位,停止位,读写周期,以及以从设备方式被读写,产生ACK等。

3.I2C设备驱动

(1)I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据

(2)I2C设备驱动主要包含数据结构i2c_driver和i2c_clien,我们需要根据具体设备实现其中的成员函数。

1.2 I2C子系统相关的结构体

1.i2c_adapter结构体(I2C适配器)

struct i2c_adapter是用来描述一个I2C适配器,在SoC中的指的就是内部外设I2C控制器,当向I2C核心层注册一个I2C适配器时就需要提供这样的一个结构体变量。

struct i2c_adapter {
   
   
	struct module *owner;		// 所有者
	unsigned int class;		  // 该适配器支持的从设备的类型
	const struct i2c_algorithm *algo; // 该适配器与从设备的通信算法
	void *algo_data;

	/* data fields that are valid for all devices	*/
	struct rt_mutex bus_lock;

	int timeout;			// 超时时间
	int retries;
	struct device dev;		// 该适配器设备对应的device

	int nr;				// 适配器的编号
	char name[48];		// 适配器的名字
	struct completion dev_released;
	
	struct mutex userspace_clients_lock;
	struct list_head userspace_clients; //// 用来挂接与适配器匹配成功的从设备i2c_client的一个链表头

	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
};

2.i2c_algorithm结构体(I2C算法)

struct i2c_algorithm结构体代表的是适配器的通信算法,在构建i2c_adapter结构体变量的时候会去填充这个元素。

struct i2c_algorithm {
   
   

	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

3.i2c_client结构体

i2c_client结构体用来描述一个i2c次设备 一个i2c次设备用对应一个i2c_client结构体来描述其信息

struct i2c_client {
   
       //描述一个i2c次设备
	unsigned short flags;		//  描述i2c次设备特性的标志位
	unsigned short addr;		//  i2c 次设备的地址
	
	char name[I2C_NAME_SIZE];			////  i2c次设备的名字
	struct i2c_adapter *adapter;	//  指向与次设备匹配成功的适配器
	struct device dev;		//  该次设备对应的device
	int irq;			//  次设备的中断引脚
	struct list_head detected;  //  作为一个链表节点挂接到与他匹配成功的i2c_driver 相应的链表头上
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

4.device_driver结构体

device_driver结构体用来描述一个i2c设备驱动,一个i2c设备驱动可以对应许多i2c_client从设备

struct i2c_driver {
   
      // 代表一个i2c设备驱动
	unsigned int class;  //// i2c设备驱动所支持的i2c设备的类型  

	int (*attach_adapter)(struct i2c_adapter *) __deprecated;// 用来匹配适配器的函数 adapter
	int (*probe)(struct i2c_client *, const struct i2c_device_id *); // 设备驱动层的probe函数
	int (*remove)(struct i2c_client *);							 // 设备驱动层卸载函数

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);

	void (*alert)(struct i2c_client *, unsigned int data);

	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;    //  该i2c设备驱动所对应的device_driver
	const struct i2c_device_id *id_table;   //  设备驱动层用来匹配设备的id_table 

	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;  //  该设备驱动支持的所有次设备的地址数组
	struct list_head clients;  //  用来挂接与该i2c_driver匹配成功的i2c_client (次设备)的一个链表头
};

5。关键文件(drivers\i2c)

(1)i2c-core.c: i2c核心层

(2)busses目录:这个文件中是已经编写好的各种向i2c核心层注册的适配器

(3)algos目录:这个目录里面是一些I2C通信算法

2. i2c_adapter适配器分析

2.1设备树文件device

在dts文件中可以看到关于i2c控制器(adapter)的device定义如下

i2c1: i2c@48070000 {
   
   
			compatible = "ti,omap4-i2c";    //适配码
			reg = <0x48070000 0x100>;           //寄存器基地址  
			interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;  //中断
			#address-cells = <1>;
			#size-cells = <0>;
			ti,hwmods = "i2c1";
			status = "disabled";
		};

		i2c2: i2c@48072000 {
   
   
                ...
		};

		i2c3: i2c@48060000 {
   
   
                ...
		};

		i2c4: i2c@4807a000 {
   
   
                ...
		};

		i2c5: i2c@4807c000 {
   
   
			compatible = "ti,omap4-i2c";
			reg = <0x4807c000 0x100>;
			interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
			#address-cells = <1>;
			#size-cells = <0>;
			ti,hwmods = "i2c5";
			status = "disabled";
		};

i2c的子节点在系统初始后自动从设备树读取i2c控制器的数据然后创建一个platform device (总线设备)。

2.2适配器驱动driver

Platform机制的本身使用并不复杂,由两部分组成:platform_device 和 platfrom_driver。所以上面已经创建了platform_device下面就应该创建platfrom_driver

drivers\i2c\busses\i2c-omap.c:

1.适配机制

 static struct platform_driver omap_i2c_driver = 
 {
   
     
	.probe		= omap_i2c_probe,
	.remove		= omap_i2c_remove,
	.driver		= {
   
   
		.name	= "omap_i2c",
		.pm	= OMAP_I2C_PM_OPS,
		.of_match_table = of_match_ptr(omap_i2c_of_match),  //适配函数
	},
};

static const struct of_device_id omap_i2c_of_match[] = {
   
      //结构体数组
	{
   
   
		.compatible = "ti,omap4-i2c",   
		.data = &omap4_pdata,
	},
	{
   
   
		.compatible = "ti,omap3-i2c",
		.data = &omap3_pdata,
	},
	{
   
   
		.compatible = "ti,omap2430-i2c",
		.data = &omap2430_pdata,
	},
	{
   
   
		.compatible = "ti,omap2420-i2c",
		.data = &omap2420_pdata,
	},
	{
   
    },
};

device和driver根据compatible属性进行适配。上面提到的描述i2c的dtb文件中compatible = “ti,omap4-i2c”。所以device和driver是因为.compatible = "ti,omap4-i2c"而适配上。

2.omap_i2c_probe函数分析

函数在总线上驱动和设备的名字匹配后,就会调用驱动的probe函数,

(1)开始通过Platform机制提供的API接口获取Platform device 的中断号和寄存器基地址

(2)给omap_i2c_dev数据结构分配存储空间,并且初始化该结构

(3)存储dev->driver_data数据i2c寄存器进行初始化

(4)给i2c指定中断处理函数,用于i2c总线的数据传输

(5)初始化结构体i2c_adapter 将i2c_adapter注册到i2c-core层

omap_i2c_probe(struct platform_device *pdev)
{
   
   
	...

	/* (1) 获取i2c adapter的中断号,在dts中定义的"interrupts"属性 */
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
   
   
		dev_err(&pdev->dev, "no irq resource?\n");
		return irq;
	}  //中断判断

	omap = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL);//申请一个内存空间存放dev
	if (!omap)
		return -ENOMEM;
	/* (2) 获取i2c adapter的寄存器基地址,在dts中定义的"reg"属性 */
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //ioresource_mem
	omap->base = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值