一. 简介
现在,我们来学习一下如何在 Linux 下开发 I2C 接口器件 驱动,重点是学习 Linux 下的 I2C 驱动框架,按照指定的框架去编写 I2C 设备驱动。
本文来了解以下 Linux下 I2C 驱动框架,框架下的 i2C总线驱动(也就是I2C控制器驱动)。
二. Linux下I2C驱动实验: I2C驱动框架与I2C总线驱动
1. Linux I2C 驱动框架简介
回想一下我们在裸机篇中是怎么编写 AP3216C 驱动的,我们编写了四个文件: bsp_i2c.c 、
bsp_i2c.h 、 bsp_ap3216c.c 和 bsp_ap3216c.h 。其中前两个是 I.MX6U 的 IIC 接口驱动,后两个文 件是 AP3216C 这个 I2C 设备驱动文件。相当于有两部分驱动:
(1) I2C 主机驱动。
(2) I2C 设备驱动。
对于 I2C 主机驱动,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此, Linux 内核也将 I2C 驱动分为两部分:
(1) I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
(2) I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
2. I2C 总线驱动(I2C控制器驱动)
首先来看一下 I2C 总线,在讲 platform 的时候就说过, platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。
对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C 总线即可。 I2C 总线驱动重点是 I2C 适配器 ( 也就是 SOC 的 I2C 接口控制器 ) 驱动。
(1) I2C总线驱动的结构体
这里要用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm。 Linux 内核将 SOC 的 I2C 适配器 ( 控制器 ) 抽象成 i2c_adapter , i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
第 4 行, i2c_algorithm 类型的指针变量 algo ,对于一个 I2C 适配器,肯定要对外提供读 写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。 i2c_algorithm 就是 I2C 适 配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下 ( 删除条件编译 ) :
struct 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);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
......
};
第 3 行, master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
第 6 行, smbus_xfer 就是 SMBUS 总线的传输函数。
综上所述,I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。
(2) I2C 总线驱动注册与注销
完成以后通过 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)
函数参数和返回值含义如下:
adap :要删除的 I2C 适配器。
返回值: 无。
三. 总结
关于 I2C 的总线 ( 控制器或适配器 ) 驱动就讲解到这里,一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,比如, I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了,这个不需要用户去编 写。
因此, I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱 动即可。除非你是在半导体公司上班,工作内容就是写 I2C 适配器驱动。