前言

代码

本文的分析对象为NXP的IMX8MM的代码,内核版本为4.14.98

说明

I2C实际上有两部分驱动并且是分层的。

  • I2C主机驱动。
  • 2C设备驱动。
    对于I2C主机驱动,一旦编写完成就不需要再做修改,其他的I2C设备直接调用主机驱动提供的API函数完成读写操作即可。这个正好符合Linux的驱动分离与分层的思想,因此Linux内核也将I2C驱动分为两部分:
  1. I2C总线驱动,I2C总线驱动就是SOC的I2C控制器驱动,也叫做I2C适配器驱动。
  2. I2C设备驱动,I2C设备驱动就是针对具体的I2C设备而编写的驱动。

整体驱动构架理解:

方式概述:实际上有两个步骤:

  1. client(bord_info)会先和adpater匹配绑定成一体(可以理解为主机驱动和设备驱动关联)。
  2. 然后如果有驱动的匹配信息和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驱动分析相关推荐

  1. 【驱动】linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  2. linux下I2C驱动发送IO时序,I2C驱动情景分析——怎样控制I2C时序

    内核版本:linux-3.4.2 源程序:    linux-3.4.2\drivers\i2c\busses\I2c-s3c2410.c 这次要解决的问题是:如何配置soc的I2C模块,输出想要的时 ...

  3. linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  4. linux下I2C驱动体系结构及在FL2440开发板上的具体实现

    最近一段时间,我在网上看了一些关于linux下i2c的文档,对i2c有了一些较浅层次了解.写这篇博客,主要是对现在已经掌握知识的巩固. Linux下I2C驱动体系结构 Linux下I2C驱动体系结构由 ...

  5. Linux下I2C驱动框架全面解析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  6. i2c驱动架构 davinc dm368 i2c驱动分析

    预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c).标题党请见谅! 其实i2c接口非常的简单,即 ...

  7. linux powerpc i2c驱动 之 i2c设备层的注册过程

    Linux下i2c驱动的加载过程,分为i2c设备层.i2c adapter层与i2c核心层 i2c设备驱动层也就是我们为特定i2c设备编写的驱动,下面是我自己理解的i2c驱动的注册过程 在我们写的i2 ...

  8. linux下I2C驱动

    linux下I2C驱动(-) by good02xaut 最近的一段时间,总结一下linux下开发I2C设备驱动的要点.内容随想,没有多加整理. I2C协议规定了主机和从机的概念,在驱动中采用的多是适 ...

  9. Linux的 i2c 驱动框架分析

    1.基本概念 总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工 ...

  10. linux下i2c驱动架构全面分析,linux I2C驱动 :整体架构(1)

    有一篇很不错的文章,可以看这篇: 本文更重要的是一些要点的记录: I2C 驱动分为: I2C 核心.I2C总线驱动. I2C 设备驱动 I2C 核心: 提供I2C总线驱动和设备驱动的注册.注销方法等等 ...

最新文章

  1. android自定义View-垂直滚动的TextView
  2. 如何只用2GB内存从20/40/80亿个整数中找到出现次数最多的数
  3. 存储过程 传 datatable
  4. 2.2 string
  5. Android笔记 notification
  6. 云计算正在“抹杀”开源?
  7. 解决办法:对‘operator delete(void*)’未定义的引用
  8. Nginx伪静态配置和常用Rewrite伪静态规则
  9. 回溯(backtrack)
  10. 5G协议下载地址及介绍
  11. ENSP静态路由配置
  12. 举个栗子!Tableau 技巧(126):学几个常用的日期函数
  13. snmpwalk与snmpget的区别
  14. PHP 简易聊天室 利用redis的订阅发布功能
  15. RED LION 1GS00000
  16. 国内计算机类学术期刊投稿指南
  17. Android Google原生系统刷机
  18. 使用STC-ISP向KEIL添加STC芯片头文件
  19. 投机者怎样于2019年3月18日前后的证券市场中利用底部放量实现程序化交易
  20. 相对论或能解释为什么中微子速度比光快

热门文章

  1. 利用鱼群算法求解最值问题(一元或多元)MATLAB编程实现
  2. 基于Android设备的 Kali Linux渗透测试教程(内部资料)
  3. QT从字体名获取字库文件路径(从宋体获取到simsun.ttc)
  4. 老旧小区智慧用电改造方案
  5. 如何查看局域网内所有的IP
  6. TOMCAT安装指南
  7. 刺客信条3重制版修改器|刺客信条3重制版十项修改器风灵月影版下载
  8. JDK下载 JVM调优工具jvisualvm下载
  9. Gradle下载及安装
  10. Android Studio Gradle下载慢解决方法