内核i2c框架分析
前言
本章基于 linux-4.19.193源码讲述。
I2C协议是主从结构
I2C控制器一般位于片上,是I2C的master端,是一个芯片
I2C软件框架主要包括:
主机端:I2C控制器驱动、I2C适配器(I2C控制器的抽象)
设备端:I2C 设备驱动、I2C client(I2C设备的抽象)
相关目录
drivers/i2c/busses/: 各个I2C controller drivers的集合,位于drivers/i2c/busses/目录下,驱动工程师常说的“I2C driver”就是指它们
I2C总线协议
空闲时:上拉电阻使SDA和SCL线都保持高电平
开始:当SCL稳定在高电平时,SDA由高到低的变化将产生一个开始位
主设备访问从设备时,首先根据从设备的访问地址长度发送地址信息+读写位,一般为7位地址+1位读写位
从设备收到后,回复1位低位ACK,表示允许主设备读写数据;回复高位NACK时,表示不能再接收主设备发送的数据
读:从设备在回复ACK后,紧接着跟着待传输的一字节数据,然后再一个ACK,再一字节数据,直到数据传输完毕,回复NACK完成读操作,主设备收到NACK后,发送停止位
写:从设备回复ACK后,按字节读取主设备发来的数据,每读一字节,从设备回复一个ACK,主设备把待写数据发送完毕后,发送停止位
停止:SDA数据线由低变高,表示停止
注意:i2C按8位1字节传输数据
开始位和停止位由I2C主设备产生
I2C整体框架
CPU::XX_I2C控制器芯片
--------------------------------------------------
||
i2c adapter
||
==================platform bus====================
||
i2c controller driver
||
==================I2C bus(虚拟) ================
|| ||
I2C xx device i2c xx device driver
||
==========I2C 总线 (硬件)=======
||
--------------------------------------------------
I2C设备
设备树匹配方式的I2C框架
以i2c/busses/i2c-rk3x.c为例
自上层至而下层进行说明
i2c控制器
i2c控制器device描述在设备树中,设备树中的I2C主节点会转换为device_node->platform_device,并注册到platform总线上。I2C控制器下的子节点,转换为device_node
i2c adapter和driver匹配
即i2c控制器设备(device抽象)和i2c控制器驱动匹配
使用platform总线进行匹配。
i2c 控制器device: struct platform_device 或者是设备树中的i2c设备描述
i2c 控制器driver: struct platform_driver
module_platform_driver(rk3x_i2c_driver);
...//省略,驱动开发platform框架章节有详细内容
platform_match
of_driver_match_device //实际使用了设备树(打开了CONFIG_OF),所以走该匹配流程
of_match_device(drv->of_match_table, dev)
of_match_node
__of_match_node(const struct of_device_id *matches,const struct device_node *node)
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {
best_match = matches;
best_score = score;
}
}
__of_device_is_compatible
匹配dts的compatible属性和rk3x_i2c_driver.driver.of_match_table.compatible
匹配过程关键的函数为:__of_match_node
参数:
const struct of_device_id *matches : 即controller driver中的成员
const struct device_node *node: 设备树文件中设备节点转换的device_node结构体
I2C虚拟总线
在i2c-core-base.c:i2c_init中初始化,提供操作总线上设备的一些接口。
retval = bus_register(&i2c_bus_type);
i2c控制器和i2c设备驱动建立关联
主要目的是遍历虚拟i2c总线上i2c设备,将其与i2c控制器驱动进行关联起来。
由上述可知,当I2C控制器设备和I2C控制器驱动match后,将执行控制器驱动的probe函数,过程如下:
drivers/base:dd.c
__driver_attach
ret = driver_match_device //ret > 0,则表示匹配成功
driver_probe_device(drv, dev);
really_probe
drv->probe //这个drv是struct device_driver
platform.c::platform_drv_probe
drv->probe(dev) //这个drv是platform_driver
最终执行:
i2c-rk3x.c::rk3x_i2c_probe(struct platform_device *pdev)
1、实现struct i2c_adapter:i2c控制器的抽象
2、i2c_add_adapter:
将adapter和I2C设备驱动关联,同时对I2C设备和I2C设备驱动进行匹配。
过程如下:
rk3x_i2c_probe
i2c->adap.dev.parent = &pdev->dev;
i2c_add_adapter
如果采用设备树方式则执行:
of_alias_get_id(dev->of_node, "i2c") //设备树上有没有I2C主节点
__i2c_add_numbered_adapter //添加I2C下的子节点
i2c_register_adapter
of_i2c_register_devices //将设备树I2C下的子节点(I2C设备)转换为i2c_client
of_i2c_register_device
i2c_new_device
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = of_node_get(info->of_node);
device_register //将i2c设备(i2c_client)注册到I2C虚拟总线上。
此时,I2C_client已经注册到了I2C虚拟总线上了,最终会匹配i2c驱动,具体过程可查看i2c设备驱动注册过程。
I2C设备驱动和I2C client(I2C设备的抽象)匹配
当二者加入到I2C虚拟总线上时,进行匹配操作。
以rtc-ds1307.c为例:
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "rtc-ds1307",
.of_match_table = of_match_ptr(ds1307_of_match),
.acpi_match_table = ACPI_PTR(ds1307_acpi_ids),
},
.probe = ds1307_probe,
.id_table = ds1307_id,
};
module_i2c_driver(ds1307_driver);
i2c_register_driver
driver->driver.bus = &i2c_bus_type; // i2c设备挂在i2c虚拟总线上,这里提供操作总线的接口
driver_register(&driver->driver);
bus_add_driver(drv);
struct driver_private *priv;
priv->driver = drv;
drv->p = priv;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
driver_attach(drv);
因为是将驱动添加到I2C虚拟总线的,所以最终会使用I2C虚拟总线提供的接口进行匹配
i2c-core-base.c: i2c_device_match
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
i2c_of_match_device(drv->of_match_table, client)
i2c_of_match_device(drv->of_match_table, client)
其中:
drv->of_match_table:ds1307_driver.driver.of_match_table
client: struct i2c_client, 即上面提到的在i2c_add_adapter时,将设备树中的I2C子节点转换为的i2c_client。
相关接口
i2c_register_device和i2c_add_driver
在Linux内核中,I2C子系统提供了对i2c设备的支持。在I2C子系统中,通过i2c_register_driver函数注册一个I2C设备驱动程序,该函数会将驱动程序加入内核I2C驱动程序列表中,以便在后续的设备匹配过程中被调用。
当系统启动时,内核会扫描系统I2C总线上的所有设备,根据设备的I2C地址和ID等唯一信息进行匹配。在匹配过程中,内核会依次调用已注册的驱动程序的probe函数,尝试与当前扫描到的设备进行匹配,以找到正确的设备实例并进行加载和初始化。
具体来说,驱动程序通过i2c_new_device接口向内核注册一个I2C设备,该接口会通过i2c_register_device函数将设备添加到I2C设备列表中,并指定对应的驱动程序。接着,在I2C子系统进行设备匹配时,内核会遍历I2C设备列表,尝试将扫描到的设备地址与设备列表中的设备地址进行匹配,匹配成功后会调用对应驱动程序的probe函数进行设备识别和初始化。如果匹配不成功,则会继续遍历I2C设备列表,直到匹配成功或者遍历完全部设备。
需要注意的是,I2C子系统中的设备匹配过程是基于设备地址等唯一标识信息进行匹配的,因此在实际使用中需要根据设备的实际情况进行相应的设置。同时,在驱动程序的probe函数中,需要对设备进行一系列的初始化和配置等操作,以确保设备能够正确工作。
总结
综上,i2c adapter和i2c controler driver匹配,i2c adapter和i2c device driver通过I2C虚拟总线关联,i2c device driver和i2c device匹配,最终完成对i2c设备的访问。
- 分享
- 举报
-
浏览量:1357次2023-09-18 11:05:06
-
浏览量:4777次2018-05-07 20:47:52
-
2020-08-05 20:23:18
-
浏览量:10607次2021-03-03 17:39:18
-
浏览量:3790次2020-08-30 09:57:38
-
浏览量:871次2023-06-12 14:34:15
-
浏览量:4536次2021-05-14 09:48:13
-
浏览量:1163次2024-02-05 11:02:54
-
浏览量:1697次2020-06-23 17:33:04
-
浏览量:3350次2020-08-30 15:44:39
-
浏览量:1620次2023-10-26 13:56:58
-
浏览量:1310次2023-10-12 14:39:21
-
浏览量:905次2023-10-26 13:41:10
-
浏览量:5213次2021-05-19 15:43:27
-
浏览量:3779次2020-08-06 20:14:59
-
浏览量:5610次2021-08-05 13:55:51
-
浏览量:1684次2024-05-24 17:53:56
-
浏览量:6348次2020-08-30 12:34:17
-
浏览量:1423次2024-01-09 17:43:28
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
阿帅
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明