- 编写 I2C controller driver(转载:Linux I2C framework(2)_I2C provider)
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。