linux i2c framework(2) -- I2C controller driver

1.The Linux I2C subsystem

  The Linux I2C subsystem is the interface through which the system running Linux can interact with devices connected on the system’s I2C bus. It is designed in such a manner that the system running Linux is always the I2C master. It consists of the following subsections.

  • I2C adapter: There can be multiple I2C buses on the board, so each bus on the system is represented in Linux using the struct i2c_adapter (defined in include/linux/i2c.h). The following are the important fields present in this structure.
    bus number: Each bus in the system is assigned a number that is present in the I2C adapter structure which represents it.

  • I2C algorithm: Each I2C bus operates with a certain protocol for communicating between devices. The algorithm that the bus uses is defined by this field. There are currently three algorithms for the I2C bus, which are pca, pcf and bitbanging. These algorithms are used to communicate with devices when the driver requests to write or read data from the device.

  • I2C client: Each device that is connected to the I2C bus on the system is represented using the struct i2c_client (defined in include/linux/i2c.h).

  • I2C driver: For each device on the system, there should be a driver that controls it. For the I2C device, the corresponding driver is represented by struct i2c_driver (defined in include/linux/i2c.h). The following are the important fields defined in this structure.

1.1.I2C 控制器

  I2C framework 使用struct i2c_adapter抽象出一个 I2C 控制器,用于标识物理的I2C总线(physical i2c bus)。该结构体如下:

   include/linux/i2c.h:
   669 /*
   670  * i2c_adapter is the structure used to identify a physical i2c bus along
   671  * with the access algorithms necessary to access it.
   672  */
   673 struct i2c_adapter {
   674     struct module *owner;
   675     unsigned int class;       /* classes to allow probing for */
   676     const struct i2c_algorithm *algo; /* the algorithm to access the bus */
   677     void *algo_data;
   678 
   679     /* data fields that are valid for all devices   */
   680     const struct i2c_lock_operations *lock_ops;
   681     struct rt_mutex bus_lock;
   682     struct rt_mutex mux_lock;
   683 
   684     int timeout;            /* in jiffies */
   685     int retries;
   686     struct device dev;      /* the adapter device */
   687     unsigned long locked_flags; /* owned by the I2C core */
   688 #define I2C_ALF_IS_SUSPENDED        0
   689 #define I2C_ALF_SUSPEND_REPORTED    1
   690 
   691     int nr;
   692     char name[48];
   693     struct completion dev_released;
   694 
   695     struct mutex userspace_clients_lock;
   696     struct list_head userspace_clients;
   697 
   698     struct i2c_bus_recovery_info *bus_recovery_info;
   699     const struct i2c_adapter_quirks *quirks;
   700                                                                                                       
   701     struct irq_domain *host_notify_domain;
   702 };
  • algo:总线定义一套用于访问slave设备的算法,该算法定义发送和接收数据的方法,保存struct i2c_algorithm。

  • retries/timeout:I2C不一定每一次总线访问(发送或者接收数据)都会成功,在传输失败的时候,可以选择重试。重试的逻辑由I2C core实现,I2C controller driver需要设定重试的次数。另外,有些consumer对结果的返回是有时间要求的,因此不能无节制的重试,timeout字段(单位为jiffies)在retries基础上,增加了时间限制,超过这个时间,就不能重试了。

  • nr:I2C bus ID,具体查看sysfs目录(/sys/bus/i2c/devices/i2c-n中的‘n’),可由I2C controller driver在注册adapter时指定,或者通过DTS解析,或者自动分配。

  • class:I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体类型包括(include/linux/i2c.h):

    I2C_CLASS_HWMON,硬件监控类,如lm_sensors等; 
    I2C_CLASS_DDC,DDC是数字显示通道(Digital Display Channel)的意思, 通常用于显示设备信息的获取; 
    I2C_CLASS_SPD,存储类的模组; 
    I2C_CLASS_DEPRECATED,不再使用的class。
  • dev:I2C framework将I2C adapter当做了I2C bus中的一类特殊的设备,因此dev变量是它在设备模型中的体现。

1.2.struct i2c_algorithm

  struct i2c_algorithm实现访问I2C总线设备的接口,定义如下:

   514 struct i2c_algorithm {
   524     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
   525                int num);
   526     int (*master_xfer_atomic)(struct i2c_adapter *adap,
   527                    struct i2c_msg *msgs, int num);
   528     int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
   529               unsigned short flags, char read_write,
   530               u8 command, int size, union i2c_smbus_data *data);
   531     int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
   532                  unsigned short flags, char read_write,
   533                  u8 command, int size, union i2c_smbus_data *data);
   534 
   535     /* To determine what the adapter supports */
   536     u32 (*functionality)(struct i2c_adapter *adap);
   537 
   538 #if IS_ENABLED(CONFIG_I2C_SLAVE)
   539     int (*reg_slave)(struct i2c_client *client);
   540     int (*unreg_slave)(struct i2c_client *client);                                                    
   541 #endif
   542 };
  • functionality:通过一个bitmap,告诉调用者该I2C adapter支持的功能,包括:

    • I2C_FUNC_I2C,支持传统的I2C功能;
    • I2C_FUNC_10BIT_ADDR,支持10bit地址;
    • I2C_FUNC_PROTOCOL_MANGLING,支持非标准的协议行为;
    • I2C_FUNC_NOSTART,支持不需要发送START信号的I2C传输;
    • I2C_FUNC_SMBUS_xxx,SMBUS相关的功能。
  • master_xfer:I2C协议有关的数据传输接口,输入参数是struct i2c_msg类型的数组(大小由num指定)。返回值是成功传输的msg的个数,如有错误返回负值。(编写控制器驱动重点需要实现)

  • smbus_xfer,SMBUS有关的数据传输接口,如果为NULL,I2C core会尝试使用master_xfer模拟。

  drivers/i2c/busses/i2c-pxa.c:
  1141 static const struct i2c_algorithm i2c_pxa_algorithm = {                                               
  1142     .master_xfer    = i2c_pxa_xfer,
  1143     .functionality  = i2c_pxa_functionality,
  1144 };

1.3.struct i2c_msg

  I2C数据传输(读、写)以i2c msg为单位,结构体定义如下:

 69 struct i2c_msg {
 70     __u16 addr;         /* slave address  */
 71     __u16 flags;                                                                                         
 72 #define I2C_M_RD        0x0001  /* read data, from slave to master */
 73                     /* I2C_M_RD is guaranteed to be 0x0001! */
 74 #define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
 75 #define I2C_M_DMA_SAFE      0x0200  /* the buffer of this message is DMA safe */
 76                     /* makes only sense in kernelspace */
 77                     /* userspace buffers are copied anyway */
 78 #define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
 79 #define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
 80 #define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
 81 #define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
 82 #define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_NOSTART */
 83 #define I2C_M_STOP      0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
 84     __u16 len;      /* msg length               */
 85     __u8 *buf;      /* pointer to msg data          */
 86 };
  • addr:I2C slave device的地址。

  • flags:

    • I2C_M_TEN,支持10-bit的slave地址;
    • I2C_M_RD,此次传输是读操作;
  • len:数据传输的长度,单位为byte

  • buf:数据buf

1.4.I2C传输有关的flags

  正常情况下,如果I2C msg中的flags为0,adapter将按照标准I2C协议操作总线,进行传输操作,参考Documentation/i2c/i2c-protocol。

1.5.struct i2c_board_info

struct i2c_board_info {   创建I2C设备的模版
     char  type[I2C_NAME_SIZE];  设备类型,用于填充i2c_client.name
     unsigned short flags;       用于填充i2c_client.flags
     unsigned short addr;        用于填充i2c_client.addr
     void  *platform_data;       存储i2c_client.dev.platform_data
     struct dev_archdata *archdata; 拷贝到i2c_client.dev.archdata
#ifdef CONFIG_OF             指向打开固件的设备节点
     struct device_node *of_node;
#endif
     int  irq;                  存储到i2c_client.irq
};

  i2c_board_info 被用来创建当前系统中I2C设备的列表,这些信息用来驱动模型树,对于主板会使用i2c_register_board_info来注册这些设备。(具体详见Linux I2C framework(4) – I2C device创建)。

1.6.struct i2c_client(i2c slave device)

struct i2c_client {   
     unsigned short flags;  I2C_CLIENT_TEN表示设备使用的是10位的地址,I2C_CLIENT_PEC表示使用SMBus包用在错误检查
     unsigned short addr;   I2C设备在总线上的的地址  
   
     char name[I2C_NAME_SIZE]; 设备名
     struct i2c_adapter *adapter;   指向该I2C设备挂载的I2C适配器
     struct i2c_driver *driver;     指向支持该I2C设备的驱?
     struct device dev;             用于总线设备驱动模型
     int irq;                       该设备能产生的中断号
     struct list_head detected;
};

1.7.struct i2c_driver

struct i2c_driver {
     unsigned int class;

     int (*attach_adapter)(struct i2c_adapter *);
     int (*detach_adapter)(struct i2c_adapter *);

     //标准的驱动模型 
     int (*probe)(struct i2c_client *, const struct i2c_device_id *);
     int (*remove)(struct i2c_client *);
     void (*shutdown)(struct i2c_client *);
     int (*suspend)(struct i2c_client *, pm_message_t mesg);
     int (*resume)(struct i2c_client *);

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

    //类似ioctl 
     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    //用于设备驱动模型 
    struct device_driver driver;
    //列出该设备驱动所支持的设备 
    const struct i2c_device_id *id_table;

   //设备探测的回调函数 
   int (*detect)(struct i2c_client *, struct i2c_board_info *);
   //用于探测的i2c设备地址 
   const unsigned short *address_list;
 
   struct list_head clients;   /* 链接所有探测出的i2c设备 */
};

2.结构体间的内在关系

2.1.i2c_client 、i2c_driver

  i2c_client,代表具体的挂载在i2c总线上的从设备,如EEPROM。i2c_client结构体内嵌i2c_adapter,主要是用于记录client所依附的adapter,当对从设备读写时就能找到对应的adapter以及读写设备的Algorithm

  i2c_driver,主要用于记录该client所使用的i2c_driver。i2c_driver是一个抽象的驱动,不对应任何的物理实体内容。如此设计,主要可能是因为driver和client是一对多的关系,即i2c_client对应的物理设备所使用驱动方法可能是相同的,那么抽象出来就能够复用了。i2c_driver内有一个clients链表,就是用来记录该抽象i2c_driver的client的,同时i2c_driver提供了client用于注册到和注销相应adapter的方法。

  An example of a struct i2c_driver structure defnition for an I2C accelerometer device:

static struct i2c_driver ioaccel_driver = {
	.driver = {
		.name = "mma8451",
		.owner = THIS_MODULE,
		.of_match_table = ioaccel_dt_ids,
	},
	.probe = ioaccel_probe,
	.remove = ioaccel_remove,
	.id_table = i2c_ids,
};

  在添加新的设备时,需要构建对应的i2c_client结构体,至于i2c_driver结构体,如果没有适合的i2c_driver可以复用的话,那么也是需要工程师进行添加。

2.2.i2c_adapter,i2c_algorithm

  i2c_adapter,代表物理适配器,一般这个适配器会集成在芯片内,而i2c_algorithm就是为特定的i2c_adapter提供通信方法的,如果没有为i2c_adapter提供对应的i2c_algorithm的话,那么这个i2c_adapter就什么都做不了。所以在i2c_adapter结构体内会存在一个记录i2c_algorithm的指针。而i2c_algorithm中的master_xfer和smbus_xfer指针就是记录对应的通信方法的。在执行I2C通信的时候会调用到。

  那么对于工程师来说,如果如果没有提供对应的适配器方法就需要自己填写i2c_adapter结构体,既然适配器的内容是自行添加的,那么对应的i2c通信方法也需要根据具体的时序和芯片手册标注的寄存器进行编程。

2.3.i2c_adapter,i2c_client

  i2c_client要依附于i2c_adapter。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client的链表。

小结(关系图如下所示):
在这里插入图片描述

3.编写I2C adapter 驱动

3.1.I2C adapter APIs

  I2C adapter定义之后,注册到kernel中去,相关的API如下:

  1: /* include/linux/i2c.h */
  2: 
  3: extern int i2c_add_adapter(struct i2c_adapter *);
  4: extern void i2c_del_adapter(struct i2c_adapter *);
  5: extern int i2c_add_numbered_adapter(struct i2c_adapter *);
  6: 
  7: static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
  8: static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
  9: static inline int i2c_adapter_id(struct i2c_adapter *adap)
 10: 
 11: extern struct i2c_adapter *i2c_get_adapter(int nr);
 12: extern void i2c_put_adapter(struct i2c_adapter *adap);
 13: 
 14: /* must call put_device() when done with returned i2c_adapter device */
 15: extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
  • i2c_add_adapter和i2c_add_numbered_adapter是I2C adapter的注册接口,它们区别:

    • i2c_add_adapter会自动分配adapter ID(adapter->nr);
    • i2c_add_numbered_adapter 可以指定adapter ID。
  • i2c_del_adapter将I2C adapter从内核中删除。

  • i2c_get_functionality获取指定adapter所支持的功能,i2c_check_functionality可用于检查指定adapter是否具备指定功能。

  • i2c_adapter_id可以获取指定adapter的ID。

  • i2c_get_adapter通过ID获得指定adapter的指针,由于该接口会尝试调 try_module_get增加模块的引用计数,因此使用完毕后,需要调用i2c_put_adapter将引用计数减去。

  • of_find_i2c_adapter_by_node,通过device的device_node查找相应的adapter结构,使用完后需要调用put_device将adapter->dev所在的模块引用计数减去。

3.2.编写步骤

1>.定义一个struct i2c_algorithm变量,并根据I2C controller的特性,实现其中的回调函数。

2>.定义I2C controller相关的DTS node,例如:

  1: /* arch/arm/boot/dts/am33xx.dtsi
  3: i2c0: i2c@44e0b000 {
  4:         compatible = "ti,omap4-i2c";
  5:         #address-cells = <1>;
  6:         #size-cells = <0>;
  7:         ti,hwmods = "i2c1";
  8:         reg = <0x44e0b000 0x1000>;
  9:         interrupts = <70>;
 10:         status = "disabled";
 11: };
 12: 
 13: i2c1: i2c@4802a000 {
 14:         compatible = "ti,omap4-i2c";
 15:         #address-cells = <1>;
 16:         #size-cells = <0>;
 17:         ti,hwmods = "i2c2";
 18:         reg = <0x4802a000 0x1000>;
 19:         interrupts = <71>;
 20:         status = "disabled";
 21: };
 23: ...

3>.在drives/i2c/busses目录下,以i2c-xxx.c的命名方式,编写I2C controller的platform driver,并提供match id,probe,remove等接口。

4>.在platform driver的probe接口中,分配一个adapter结构,并进行必要的初始化操作后,调用i2c_add_adapter或者i2c_add_numbered_adapter接口,将其注册到kernel中即可。

4.函数i2c_add_adapter分析

  该函数完成:

  • adapter ID的分配;
  • 设备模型有关的内容;
  • I2C slave device的创建和注册。

4.1.分配adapter ID

  I2C adapter ID(adapter->nr)有两种方法分配:

  • i2c_add_adapter(),使用的是动态总线号,即由系统给其分析一个总线号;
  • i2c_add_numbered_adapter(),使用自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。

  第一种方法: i2c_add_adapter

1342 int i2c_add_adapter(struct i2c_adapter *adapter)
1343 {
1344     struct device *dev = &adapter->dev;
1345     int id;
1346 
1347     if (dev->of_node) {
1348         id = of_alias_get_id(dev->of_node, "i2c");
1349         if (id >= 0) {
1350             adapter->nr = id;
1351             return __i2c_add_numbered_adapter(adapter);
1352         }
1353     }
1354 
1355     mutex_lock(&core_lock);
1356     id = idr_alloc(&i2c_adapter_idr, adapter,
1357                __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);                                             
1358     mutex_unlock(&core_lock);
1359     if (WARN(id < 0, "couldn't get idr"))
1360         return id;
1361 
1362     adapter->nr = id;
1363 
1364     return i2c_register_adapter(adapter);
1365 }

  动态分配也有两种手段:

1348~1351行,通过of_alias_get_id获取。该方法会通过DTS中的alias解析指定I2C adapter的ID,例如:

aliases { 
        i2c0 = &i2c0; 
};

i2c0: i2c@44e0b000 { 
        compatible = "ti,omap4-i2c"; 
        … 
};
/* arch/arm/boot/dts/am33xx.dtsi */ 

1356 ~1358,通过idr_alloc分配。

  第二种方法:i2c_add_numbered_adapter()

  I2C adapter ID(adapter->nr)通过driver直接赋值,并经过下面的函数调用注册adapter:

i2c_add_numbered_adapter—>__i2c_add_numbered_adapter—>i2c_register_adapter

调用i2c_register_adapter注册adapter:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0;
    ...
    dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    adap->dev.bus = &i2c_bus_type;    // adapter 注册在 i2c_bus_type 总线下
    adap->dev.type = &i2c_adapter_type;    // 设置 dev 的类型是 i2c_adapter_type 类型,用以区分其他设备类型
    res = device_register(&adap->dev);    // 注册进kernel设备框架
    ...
exit_recovery:
    /* create pre-declared device nodes */
    of_i2c_register_devices(adap);    // 扫描设备树配置,构造i2c_board_info创建 i2c_client并注册
    acpi_i2c_register_devices(adap);
    acpi_i2c_install_space_handler(adap);

    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);    // 根据前期静态配置好的board info,创建并注册i2c_client

    /* Notify drivers */
    mutex_lock(&core_lock);
    bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);  // 扫描 i2c_bus_type 总线下的驱动i2c_driver,探测并创建driver支持的 i2c_client,注册之
    mutex_unlock(&core_lock);
    return 0;
}

4.2.设备模型有关流程

  如上所示,首先i2c_register_adapter在“/sys/devices/xxxxxxxx.i2c/”中创建该adapter的目录(/sys/devices/xxxxxxxx.i2c/i2c-n),如下:

dev_set_name(&adap->dev, "i2c-%d", adap->nr); 
adap->dev.bus = &i2c_bus_type; 
adap->dev.type = &i2c_adapter_type; 
res = device_register(&adap->dev);

  由此可知,I2C adapter被挂到i2c总线(i2c_bus_type)上了,同时,通过“device_register—>device_add—>bus_add_device—>sysfs_create_link”的调用,在/sys/bus/i2c/devices/中创建对应的符号链接(/sys/bus/i2c/devices/i2c-n/)。

4.3.I2C slave device创建和注册

1>.在DTS的支持下,I2C adapter注册的时候,会为它下面所有的slave device创建struct i2c_client结构,并注册到I2C bus上,调用流程是:

i2c_register_adapter
  —>of_i2c_register_devices
    —>of_i2c_register_device
      —>i2c_new_device

  其中of_i2c_register_devices()函数中会遍历设备数dtsi i2c控制器下的节点,然后通过of_i2c_register_device()函数把 i2c 控制器下的设备注册进去。

2>. i2c_scan_static_board_info(adap);

  根据前期静态配置好的board info,创建并注册i2c_client。

3>.__process_new_adapter

  扫描 i2c_bus_type 总线下的驱动i2c_driver,探测并创建driver支持的 i2c_client,注册之。

  当调用i2c_register_adapter或i2c_add_driver时,都会遍历已经注册的i2c_driver或i2c_adapter作为参数调用__process_new_adapter或 __process_new_driver(分析详见Linux I2C framework(3) – I2C consumer driver)。

调用流程:

__process_new_adapter
  —>i2c_do_add_adapter; // 匹配 i2c_driver 下的 address_list,探测 i2c_client
    —>i2c_detect // 探测 i2c_client
      —>address_list = driver->address_list; // 驱动支持的 i2c_client address
        -->temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); // 创建临时 i2c_client
          // 根据 driver 支持的 address_list,执行探测函数
          -->err = i2c_detect_address(temp_client, driver);
            —>if (!i2c_default_probe(adapter, addr)) // 执行默认的探测函数
            —>err = driver->detect(temp_client, &info); // 执行驱动定义的探测函数
            —>client = i2c_new_device(adapter, &info); // 创建并注册这个正式的 i2c_client
            —>list_add_tail(&client->detected, &driver->clients); // 将该 i2c_client 链入 driver->clients链表

由上面的代码流程可见,在注册 i2c_adapter的过程中,i2c核心层会根据设备树配置,静态board info 配置去创建该 i2c_adapter下的子设备 i2c_client并匹配相应的 i2c_driver,最后注册进 i2c_bus_type。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值