文章目录
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 =

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

被折叠的 条评论
为什么被折叠?



