linux之I2C驱动分析
前言
代码
本文的分析对象为NXP的IMX8MM的代码,内核版本为4.14.98
说明
I2C实际上有两部分驱动并且是分层的。
- I2C主机驱动。
- 2C设备驱动。
对于I2C主机驱动,一旦编写完成就不需要再做修改,其他的I2C设备直接调用主机驱动提供的API函数完成读写操作即可。这个正好符合Linux的驱动分离与分层的思想,因此Linux内核也将I2C驱动分为两部分:
- I2C总线驱动,I2C总线驱动就是SOC的I2C控制器驱动,也叫做I2C适配器驱动。
- I2C设备驱动,I2C设备驱动就是针对具体的I2C设备而编写的驱动。
整体驱动构架理解:
方式概述:实际上有两个步骤:
- client(bord_info)会先和adpater匹配绑定成一体(可以理解为主机驱动和设备驱动关联)。
- 然后如果有驱动的匹配信息和client一致时,会调用驱动的probe函数。
设备树描述的是设备信息,不是驱动的信息。设备树文件会自动被内核展开,生成对应的设备信息,并且插入到对应的链表中。注意:在i2c设备(client)树描述中,会有和adapter设备树描述进行绑定(关联)的信息(number),(int nr;和int busnum;)。通过这个信息client和adapter进行绑定,最后会形成client->adapter这样的结构。
PS:i2c从设备(client)成功注册后会主动查找合适的adapter。adapter成功注册后,会主动遍历i2c从设备链表,找到可以用这个adatper的设备。
i2c_driver 可以驱动多个client,每当有一个client的匹配信息和这个driver匹配信息一致时,driver的的probe函数都会被调用一次。并将client的相关信息传递到probe()函数参数中。
设备树
adapter对应的的信息,实际上就是i2c的控制器。它是可以通过内存映射从而cpu能访问的设备。
531 i2c3: i2c@30a40000 {532 #address-cells = <1>;533 #size-cells = <0>;534 compatible = "fsl,imx8mm-i2c", "fsl,imx21-i2c";535 reg = <0x0 0x30a40000 0x0 0x10000>;536 interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;537 clocks = <&clk IMX8MM_CLK_I2C3_ROOT>;538 status = "disabled";539 };
i2c从设备(client)对应的信息,它不属于可内存映射的设备,cpu也不能访问它。
745 &i2c3 {746 clock-frequency = <400000>;747 pinctrl-names = "default";748 pinctrl-0 = <&pinctrl_i2c3>;749 status = "okay"; 750 /* 751 pca6416: gpio@20 {752 compatible = "ti,tca6416"; //主要是这个匹配信息753 reg = <0x20>; //主要是这个i2c地址信息 754 gpio-controller;755 #gpio-cells = <2>;756 }; 757 */ 758 759 };
PS:代码是米尔科技imx8mm源码,algos目录里面有imx对应的i2c发送算法。
/drivers/i2c
tree -L 1
├── algos
├── busses
├── i2c-boardinfo.c
├── i2c-core-acpi.c
├── i2c-core-base.c //i2c核心基本函数
├── i2c-core.h
├── i2c-core-of.c //i2c设备数据相关的操作函数
├── i2c-core-slave.c
├── i2c-core-smbus.c
├── i2c-dev.c //创建了适配器的节点,为用户空间访问i2c适配器的方法。可用于调试
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
相关的数据结构
431 struct i2c_algorithm {...
438 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
439 int num);
440 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
441 unsigned short flags, char read_write,
442 u8 command, int size, union i2c_smbus_data *data);
443
444 /* To determine what the adapter supports */
445 u32 (*functionality) (struct i2c_adapter *);
446
447 #if IS_ENABLED(CONFIG_I2C_SLAVE)
448 int (*reg_slave)(struct i2c_client *client);
449 int (*unreg_slave)(struct i2c_client *client);
450 #endif
451 };570 struct i2c_adapter {
571 struct module *owner;
572 unsigned int class; /* classes to allow probing for */
573 const struct i2c_algorithm *algo; /* the algorithm to access the bus */
574 void *algo_data;
575
576 /* data fields that are valid for all devices */
577 const struct i2c_lock_operations *lock_ops;
578 struct rt_mutex bus_lock;
579 struct rt_mutex mux_lock;
580
581 int timeout; /* in jiffies */
582 int retries;
583 struct device dev; /* the adapter device */
584
585 int nr;
586 char name[48];
587 struct completion dev_released;
588
589 struct mutex userspace_clients_lock;
590 struct list_head userspace_clients;
591
592 struct i2c_bus_recovery_info *bus_recovery_info;
593 const struct i2c_adapter_quirks *quirks;
594
595 struct irq_domain *host_notify_domain;
596 };597 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)175 struct i2c_driver {
176 unsigned int class;
177
181 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
182
183 /* Standard driver model interfaces */
184 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
185 int (*remove)(struct i2c_client *);
190 int (*probe_new)(struct i2c_client *);
191
192 /* driver model interfaces that don't relate to enumeration */
193 void (*shutdown)(struct i2c_client *);
202 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
203 unsigned int data);
208 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
209
210 struct device_driver driver;
211 const struct i2c_device_id *id_table;
212
213 /* Device detection callback for automatic device creation */
214 int (*detect)(struct i2c_client *, struct i2c_board_info *);
215 const unsigned short *address_list;
216 struct list_head clients;
217
218 bool disable_i2c_core_irq_mapping;
219 };220 #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)241 struct i2c_client {
242 unsigned short flags; /* div., see below */
243 unsigned short addr; /* chip address - NOTE: 7bit */
244 /* addresses are stored in the */
245 /* _LOWER_ 7 bits */
246 char name[I2C_NAME_SIZE];
247 struct i2c_adapter *adapter; /* the adapter we sit on */
248 struct device dev; /* the device structure */
249 int irq; /* irq issued by device */
250 struct list_head detected;
251 #if IS_ENABLED(CONFIG_I2C_SLAVE)
252 i2c_slave_cb_t slave_cb; /* callback for slave mode */
253 #endif
254 };327 struct i2c_board_info {
328 char type[I2C_NAME_SIZE];
329 unsigned short flags;
330 unsigned short addr;
331 void *platform_data;
332 struct dev_archdata *archdata;
333 struct device_node *of_node;
334 struct fwnode_handle *fwnode;
335 const struct property_entry *properties;
336 const struct resource *resources;
337 unsigned int num_resources;
338 int irq;
339 };17 struct i2c_devinfo {18 struct list_head list;19 int busnum;//这个很重要,根据这个获取对应的adapter20 struct i2c_board_info board_info; 21 };说明:busnum
1099 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
1100 {
1101 struct i2c_devinfo *devinfo;
1102
1103 down_read(&__i2c_board_lock);
1104 list_for_each_entry(devinfo, &__i2c_board_list, list) {
1105 if (devinfo->busnum == adapter->nr //设备和adapter匹配
1106 && !i2c_new_device(adapter,
1107 &devinfo->board_info))
1108 dev_err(&adapter->dev,
1109 "Can't create device at 0x%02x\n",
1110 devinfo->board_info.addr);
1111 }
1112 up_read(&__i2c_board_lock);
1113 }
理解i2c驱动,理解上面的结构体以及它们的关系非常重要。然而它们又是错综复杂的。i2c驱动复杂的原因是什么呢?主要还是因为i2c要驱动各种繁杂的外围设备。采用上面的方式可以灵活的配置来满足不同的需求。
术语解释:
adapter 适配器对应的是SOC外围的外设,比如说i2c0/i2c1等部分,因为它们的功能有可能不同,所以要适配叫适配器。
algorithm 算法,adapter会驱动各种外围的i2c设备,它们各自的要求不一样,所以要用不同的算法来适配。
client 客户端或者从端设备,是指SOC外接的i2c设备。比如说AT24XX的EEPROM、使用i2c配置的摄像头等。
driver 驱动则是包含上面的所有的集成者。
我们的疑问是i2c_algorhtm是怎么来配置的?
主要是通过里面的i2c时序来实现的,也和相应的soc相关。
i2c_client会作为i2c_driver的参数来使用,而i2c_client中包含有i2c_adapter,最后i2c_adapter会调用i2c_algorithm来实现i2c的数据传输。
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
上面这条语句很重要,直接判断client中的adapter是否支持需要的功能。
另外需要注意的是不同的soc会在驱动的Makefile文件中选用对应的板级驱动,比如imx8mm会在buses中编译i2c_imx.c文件。
疑问:在probe函数中的c->adapter是怎么来的?
参考前面驱动构架
--drivers/i2c/busses/i2c-imx.c
言归正传:i2c的adatper总线驱动(platform驱动)在匹配后会调用i2c_imx_probe()函数,会依次调用i2c_add_numbered_adapter
-> __i2c_add_numbered_adapter
-> i2c_register_adapter
-> of_i2c_register_devices
-> of_i2c_register_device
-> i2c_new_device()这个函数中实际分配内存创建了client,并将client和dapter进行绑定。
{
client = kzalloc(sizeof *client, GFP_KERNEL);
client->adapter = adap;
}
其中在void of_i2c_register_devices(struct i2c_adapter *adap)中
100 for_each_available_child_of_node(bus, node) {
101 if (of_node_test_and_set_flag(node, OF_POPULATED))
102 continue;
103
104 client = of_i2c_register_device(adap, node);
105 if (IS_ERR(client)) {
106 dev_warn(&adap->dev,
107 "Failed to create I2C device for %pOF\n",
108 node);
109 of_node_clear_flag(node, OF_POPULATED);
110 }
111 }
会遍历所有的i2c总线信息的子节点,找到所有的i2c子节点,最终调用i2c_new_device()函数来创建i2c的设备,创建的所有i2c_client的adapter都指向当前的adapter(client->adapter = adap;)。
所以在i2c_driver的probe()(struct i2c_client *c,const struct i2c_device_id *id)函数的参数i2c_client->dapater已经有实实在在的适配器了,并且adapter也有指定的algorithem。
疑问:i2c的从设备(client)和adapter怎么配对的?
在设备树文件中,client的信息是依附于adapter的,本身是一体的,设备树会解析相关的信息。
疑问:i2c的adapter设备信息是怎么创建的?哪里创建的?用的什么函数?
应该是platform平台设备驱动展开的。
疑问+验证:通过设备树来创建设备的过程是怎样的?
724 struct i2c_client * 725 i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)726 {727 struct i2c_client *client;728 int status;729 730 client = kzalloc(sizeof *client, GFP_KERNEL);731 if (!client)732 return NULL; 733 734 client->adapter = adap;1099 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
1100 {
1101 struct i2c_devinfo *devinfo;
1102
1103 down_read(&__i2c_board_lock);
1104 list_for_each_entry(devinfo, &__i2c_board_list, list) {
1105 if (devinfo->busnum == adapter->nr
1106 && !i2c_new_device(adapter,
1107 &devinfo->board_info))
1108 dev_err(&adapter->dev,
1109 "Can't create device at 0x%02x\n",
1110 devinfo->board_info.addr);
1111 }
1112 up_read(&__i2c_board_lock);
1113 }
i2c驱动主要分为2个部分:i2c总线驱动、i2c设备驱动。前者一般由soc厂商实现了的,开发者一般要根据需要实现后者。
i2c总线驱动:实际上是依附于plantform总线的总线。比如imx的i2c驱动/drivers/i2c/busses/i2c-imx.c,其中就实现了algorithm。
总线匹配:i2c总线驱动和i2c设备驱动,在其中任意一个成功注册后都会去匹配另外一个,匹配完成后,i2c_client->adapter会指向匹配的adapter。
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
...
}
这个函数用于i2c设备的匹配。
i2c设备驱动的cilent参数是怎么传递的?
实际上是在下面的函数中实现的:
static int i2c_device_probe(struct device *dev)
{
...if (!driver->id_table &&!i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&!i2c_of_match_device(dev->driver->of_match_table, client))return -ENODEV;
...
}
我们在某个i2c总线上添加了一个外部设备,在外部设备的驱动中probe()中的cilent参数,总线是怎么知道使用的哪一个soc上的具体的i2c呢?
这个实际上是具体看对应的设备树文件就知道了,比如:
i2c2: i2c@30a30000 { #address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx8mm-i2c", "fsl,imx21-i2c";reg = <0x0 0x30a30000 0x0 0x10000>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clk IMX8MM_CLK_I2C2_ROOT>;status = "disabled";};
而挂在这个总线上的外部设备描述如下:
&i2c2 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c2>;status = "okay";wm8904: wm8904@1a {compatible = "wlf,wm8904";reg = <0x1a>;clocks = <&clk IMX8MM_CLK_SAI3_ROOT>;clock-names = "mclk";};rtc: rtc@32 {compatible = "epson,rx8025";reg = <0x32>;};ov5640_mipi: ov5640_mipi@3c {compatible = "ovti,ov5640_mipi";//reg = <0x3c>; //ov5640 addr: 0x78>>1 = 0x3creg = <0x44>; //0x88>>1 = 0x44...};tp9950_mipi: tp9950_mipi@3c {compatible = "techpoint,tp9950_mipi";reg = <0x45>; //0x8a>>1 = 0x45...}
可以看到所有的外挂的i2c外部设备,从设备树信息结构上看都是依附于i2c2的。当我们匹配了某个i2c外部设备wm8904、rtc、ov5640_mipi、tp9950_mipi等设备时,系统中的i2c总线通过设备树是有依据来知道使用的是soc的哪个i2c的,上面列举的几个外部i2c设备驱动的probe函数的参数必然是i2c2的cilent。
linux之I2C驱动分析相关推荐
- 【驱动】linux下I2C驱动架构全面分析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- linux下I2C驱动发送IO时序,I2C驱动情景分析——怎样控制I2C时序
内核版本:linux-3.4.2 源程序: linux-3.4.2\drivers\i2c\busses\I2c-s3c2410.c 这次要解决的问题是:如何配置soc的I2C模块,输出想要的时 ...
- linux下I2C驱动架构全面分析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- linux下I2C驱动体系结构及在FL2440开发板上的具体实现
最近一段时间,我在网上看了一些关于linux下i2c的文档,对i2c有了一些较浅层次了解.写这篇博客,主要是对现在已经掌握知识的巩固. Linux下I2C驱动体系结构 Linux下I2C驱动体系结构由 ...
- Linux下I2C驱动框架全面解析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- i2c驱动架构 davinc dm368 i2c驱动分析
预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c).标题党请见谅! 其实i2c接口非常的简单,即 ...
- linux powerpc i2c驱动 之 i2c设备层的注册过程
Linux下i2c驱动的加载过程,分为i2c设备层.i2c adapter层与i2c核心层 i2c设备驱动层也就是我们为特定i2c设备编写的驱动,下面是我自己理解的i2c驱动的注册过程 在我们写的i2 ...
- linux下I2C驱动
linux下I2C驱动(-) by good02xaut 最近的一段时间,总结一下linux下开发I2C设备驱动的要点.内容随想,没有多加整理. I2C协议规定了主机和从机的概念,在驱动中采用的多是适 ...
- Linux的 i2c 驱动框架分析
1.基本概念 总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工 ...
- linux下i2c驱动架构全面分析,linux I2C驱动 :整体架构(1)
有一篇很不错的文章,可以看这篇: 本文更重要的是一些要点的记录: I2C 驱动分为: I2C 核心.I2C总线驱动. I2C 设备驱动 I2C 核心: 提供I2C总线驱动和设备驱动的注册.注销方法等等 ...
最新文章
- android自定义View-垂直滚动的TextView
- 如何只用2GB内存从20/40/80亿个整数中找到出现次数最多的数
- 存储过程 传 datatable
- 2.2 string
- Android笔记 notification
- 云计算正在“抹杀”开源?
- 解决办法:对‘operator delete(void*)’未定义的引用
- Nginx伪静态配置和常用Rewrite伪静态规则
- 回溯(backtrack)
- 5G协议下载地址及介绍
- ENSP静态路由配置
- 举个栗子!Tableau 技巧(126):学几个常用的日期函数
- snmpwalk与snmpget的区别
- PHP 简易聊天室 利用redis的订阅发布功能
- RED LION 1GS00000
- 国内计算机类学术期刊投稿指南
- Android Google原生系统刷机
- 使用STC-ISP向KEIL添加STC芯片头文件
- 投机者怎样于2019年3月18日前后的证券市场中利用底部放量实现程序化交易
- 相对论或能解释为什么中微子速度比光快