I2C总线驱动
概要
一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,我们只要专注于 I2C 设备驱动即可。对于 I2C 主机驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此 Linux内核也将 I2C 驱动分为两部分:
①、 I2C 总线驱动,I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、I2C 设备驱动,I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
整体架构流程
当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。Linux 内核中大量的驱动程序都采用总线、驱动和设备模式, platform 驱动就是这一思想下的产物。
1、i2c总线驱动
i2c_adapter是一种用于连接和控制I2C设备的接口。I2C是一种串行通信协议,用于在电子设备之间传输数据。i2c_adapter允许主机系统与I2C设备进行通信,以便读取和写入数据。它提供了一组函数和方法,使得开发人员可以轻松地与I2C设备进行交互。通过i2c_adapter,我们可以实现对I2C设备的配置、读取和写入操作,以满足特定的应用需求。
i2c总线驱动除了匹配设备和设备驱动外,还用到了 i2c_adapter适配器。
1. i2c_adapter 注册/注销函数:
(platform_driver、pci_driver、usb_driver(probe函数内使用了)注册函数)
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2. i2c_driver 注册/注销函数:
(基本都是init里实现的内容)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
拓展:下面为和i2c驱动同级别的usb驱动的,代码结构。
示例:
I2C 适配器i2c_adapter和i2c_algorithm介绍
–>i2c_adapter
-->i2c_algorithm /* 总线访问算法 */
-->int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, int num);
-->int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
master_xfer 就是 I2C 适配器的传输函数,此函数完成与 IIC 设备间的通信。
smbus_xfer 就是 SMBUS 总线的传输函数。
I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter。
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap) 这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。
adapter 或 adap:要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。
返回值:0,成功;负值,失败。
如果要删除 I2C 适配器的话使用 i2c_del_adapter 函数即可,函数原型如下:
void i2c_del_adapter(struct i2c_adapter * adap)
2、设备驱动:
1、i2c_client 和 i2c_driver
I2C 设备驱动重点关注两个数据结构:i2c_client 和 i2c_driver,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver。
①、i2c_client:存放设备信息,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
②、i2c_driver:类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容。
–>i2c_driver
-->driver //基类,提供最基础的驱动框架
-->name
-->owner
--> i2c_device_id *of_match_table //设备树匹配
-->probe //驱动和设备匹配成功,probe会执行。
-->remove
--> i2c_device_id *id_table //id_table表用来匹配驱动和设备
当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。probe 函数里面基本就是标准的字符设备驱动那一套了。
i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。
2、I2C 总线的数据结构为 i2c_bus_type
of_driver_match_device 函数用于完成设备树设备和驱动匹配。
①设备树采用的匹配:i2c_driver–>driver–> i2c_device_id(*of_match_table)和设备树compatible比较
② ACPI 匹配方式
③比较 I2C设备名字和 i2c_driver–>i2c_device_id(*id_table) 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。 虽然 I2C 总线为i2c设备提供了一种总线驱动框架,但是 I2C 适配器却是 platform驱动。
当设备和驱动匹配成功以后 i2c_imx_probe 函数就会执行,i2c_imx_probe 函数就会完成 I2C 适配器初始化工作。
补充:i2c_imx_probe 内部有:i2c_imx_start、i2c_imx_read、i2c_imx_write 和 i2c_imx_stop 这些函数就是 I2C 寄存器的具体操作函数。
platform_get_resource 函数从设备树中获取 I2C1 控制器寄存器物理基地址,(返回值)
devm_ioremap_resource 函数对其进行内存映射,得到可在 Linux 内核中使用的虚拟地址。
3、i2c_transfer适配器
int i2c_transfer (struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
1.读的过程:(是①②连在一起)
struct i2c_msg msg[2] = {
{
.addr = i2c->addr, //从机地址7位
.flags = 0, //写标记,(0为写)
.len = 3, //寄存器地址长度,3字节
.buf = ®, //寄存器地址
},
{
.addr = i2c->addr, //从机地址7位
.flags = 1, .//读标记(1为读)
.len = 2, //表示期望读到数据的字节长度(寄存器长度),是以byte为单位
.buf = buffer, //将读取到的数据保存在buffer中
},
};
i2c_transfer(i2c->i2c_adap, msg, 2); //返回值是实际传输数据的个数(第三个参数)
2.写的过程:
msg.addr = client->addr;
msg.flags = 0;
msg.len = length + 1; //+1,是因为&be_data第一个元素为寄存器的地址,
//length代表要写入的数据长度。
msg.buf = (u8 *)&be_data;
i2c_transfer(client->adapter, &msg, 1);
重要结构体
重要结构体:
struct bus_type { //总线
2 const char *name;
3 struct bus_attribute *bus_attrs;
4 struct device_attribute *dev_attrs;
5 struct driver_attribute *drv_attrs;
6 int (*match)(struct device *dev, struct device_driver *drv);
7 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
8 int (*probe)(struct device *dev);
9 int (*remove)(struct device *dev);
10 void (*shutdown)(struct device *dev);
11 int (*suspend)(struct device *dev, pm_message_t state);
12 int (*resume)(struct device *dev);
13 const struct dev_pm_ops *pm;
14 struct bus_type_private *p;
15 };
设备和驱动的匹配过程也是由 I2C 总线完成的,I2C 总线的数据结构为 i2c_bus_type
platform 总线是 bus_type 的一个具体实例
struct platform_driver { //总线的驱动
2 int (*probe)(struct platform_device *); //驱动和设备匹配成功,probe会执行。
3 int (*remove)(struct platform_device *); //
4 void (*shutdown)(struct platform_device *);
5 int (*suspend)(struct platform_device *, pm_message_t state);
6 int (*resume)(struct platform_device *);
7 struct device_driver driver; //基类,提供最基础的驱动框架
8 const struct platform_device_id *id_table; //id_table表用来匹配驱动和设备
9 bool prevent_deferred_probe;
10 };
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
struct i2c_adapter { //i2c适配器
2 struct module owner;
3 unsigned int class; / classes to allow probing for */
4 const struct i2c_algorithm algo; / 总线访问算法 */
5 void algo_data;
6
7 / data fields that are valid for all devices /
8 struct rt_mutex bus_lock;
9
10 int timeout; / in jiffies /
11 int retries;
12 struct device dev; / the adapter device */
13
14 int nr;
15 char name[48];
16 struct completion dev_released;
17
18 struct mutex userspace_clients_lock;
19 struct list_head userspace_clients;
20
21 struct i2c_bus_recovery_info *bus_recovery_info;
22 const struct i2c_adapter_quirks *quirks;
23 };
struct i2c_driver { //I2c的驱动
2 unsigned int class;
3 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
4 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
5 int (*remove)(struct i2c_client *);
6 void (*shutdown)(struct i2c_client *);
7 void (*alert)(struct i2c_client *, unsigned int data);
8 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
9 struct device_driver driver;
10 const struct i2c_device_id *id_table;
11 int (*detect)(struct i2c_client , struct i2c_board_info );
12 const unsigned short address_list;
13 struct list_head clients;
14 };
struct i2c_client {
2 unsigned short flags; / 标志 /
3 unsigned short addr; / 芯片地址,7 位,存在低 7 位/
4 …
5 char name[I2C_NAME_SIZE]; / 名字 */
6 struct i2c_adapter adapter; / 对应的 I2C 适配器 /
7 struct device dev; / 设备结构体 /
8 int irq; / 中断 */
9 struct list_head detected;
10 …
11 };
struct device_driver {
2 const char *name;
3 struct bus_type *bus;
4
5 struct module *owner;
6 const char mod_name; / used for built-in modules /
7
8 bool suppress_bind_attrs; / disables bind/unbind via sysfs */
9
10 const struct of_device_id *of_match_table;
11 const struct acpi_device_id *acpi_match_table;
12
13 int (*probe) (struct device *dev);
14 int (*remove) (struct device *dev);
15 void (*shutdown) (struct device *dev);
16 int (*suspend) (struct device *dev, pm_message_t state);
17 int (*resume) (struct device *dev);
18 const struct attribute_group **groups;
19
20 const struct dev_pm_ops *pm;
21
22 struct driver_private *p;
23 };
struct class { //类
2 const char *name;
3 struct module *owner;
4
5 struct class_attribute *class_attrs;
6 const struct attribute_group **dev_groups;
7 struct kobject *dev_kobj;
8
9 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
10 char *(*devnode)(struct device *dev, umode_t *mode);
11
12 void (*class_release)(struct class *class);
13 void (*dev_release)(struct device *dev);
14
15 int (*suspend)(struct device *dev, pm_message_t state);
16 int (*resume)(struct device *dev);
17 int (*shutdown)(struct device *dev);
18
19 const struct kobj_ns_type_operations *ns_type;
20 const void *(*namespace)(struct device *dev);
21
22 const struct dev_pm_ops *pm;
23
24 struct subsys_private *p;
25 };
struct i2c_algorithm { //i2c的算法结构体
2 …
3 int (*master_xfer)(struct i2c_adapter *adap,
4 struct i2c_msg *msgs,
5 int num);
6 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
7 unsigned short flags, char read_write,
8 u8 command, int size, union i2c_smbus_data data);
9
10 / To determine what the adapter supports */
11 u32 (*functionality) (struct i2c_adapter *);
12 …
13 };
struct kobj_map { // 设备号的存放位置
2 . struct probe {
3 struct probe next; / 这样形成了链表结构 /
4 dev_t dev; / 设备号 /
5 unsigned long range; / 设备号的范围 */
6 struct module *owner;
7 kobj_probe_t *get;
8 int (*lock) (dev_t, void *);
9 void data; / 指向struct cdev对象 */
10 } *probes[255];
11 struct mutex *lock;
12 }
小结
记录比较乱,没有太多时间打磨,关于i2c驱动部分的介绍如果能看懂这篇文章,基本可以做到读懂I2C驱动代码。后续在实际项目中遇到I2C调试的相关问题,可以用到i2c_tools工具,比较简单,后续可以自行了解。