1. 概述
学习完IIC总线驱动模型后,重点还是I2C设备驱动,因为一般芯片原厂已经把总线驱动给实现好了,在实际开发过程中,设备开发才是重点,相比总线驱动而已,也是必须掌握的。
2. I2C设备驱动
I2C 设备驱动重点关注两个数据结构:i2c_client 和 i2c_driver ,根据总线、设备和驱动模型,I2C 总线上一小节已经讲了。还剩下设备和驱动,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver 。
2.1 I2C设备树(以RK3399为例)
&i2c1 {
status = "okay";
i2c-scl-rising-time-ns = <265>;
i2c-scl-falling-time-ns = <11>;
clock-frequency = <400000>;
es8316: es8316@10 {
#sound-dai-cells = <0>;
compatible = "everest,es8316";
reg = <0x10>;
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "mclk";
spk-con-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_LOW>;
};
};
clock-frequency : 默认 frequency 为 100k 可不配置,其它 I2C 频率需要配置,最大可配置频率由 i2c-scl-rising-time-ns 决定;例如配置 400k ,clock-frequency=<400000> 。i2c-scl-rising-time-ns :SCL 上升沿时间由硬件决定,改变上拉电阻可调节该时间,需通过示波器 量测,参考下图;例如测得 SCL 上升沿 365ns ,i2c-scl-rising-time-ns=<365> 。(默认可以不配 置,但必须保证当前的上升沿时间不能超过所配置频率下的 I2C 标准所定义的最大上升沿时间) i2c-scl-falling-time-ns : SCL 下降沿时间, 一般不变, 等同于 i2c-sda-falling-time-ns 。(默认也可以 不配置)es8316 : 为I2C 从设备信息
compatible :用于匹配设备driver reg :从设备的从机地址
3. i2c_client 结构体
一个设备对应一个 i2c_client ,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client 。i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:
struct i2c_client {
unsigned short flags;
#define I2C_CLIENT_PEC 0x04
#define I2C_CLIENT_TEN 0x10
#define I2C_CLIENT_SLAVE 0x20
#define I2C_CLIENT_HOST_NOTIFY 0x40
#define I2C_CLIENT_WAKE 0x80
#define I2C_CLIENT_SCCB 0x9000
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev;
int init_irq;
int irq;
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb;
#endif
};
flags : 标志addr : 芯片地址,7 位,存在低 7 位name :名字adapter : 对应的 I2C 适配器dev :设备结构体irq :中断
4. i2c_driver 结构体
i2c_driver 类似 platform_driver ,是我们编写 I2C 设备驱动重点要处理的内容,i2c_driver 结 构体定义在 include/linux/i2c.h 文件中,内容如下:
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
int (*probe_new)(struct i2c_client *client);
void (*shutdown)(struct i2c_client *client);
void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,
unsigned int data);
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 *client, struct i2c_board_info *info);
const unsigned short *address_list;
struct list_head clients;
};
probe :当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。device_driver : 驱动结构体,如果使用设备树的话,需要设置 device_driver 的of_match_table 成员变量,也就是驱动的兼容(compatible)属性。id_table 是传统的、未使用设备树的设备匹配 ID 表
4.1 i2c_driver的注册
对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver ,构建完成以后需要向Linux 内核注册这个 i2c_driver 。i2c_driver 注册函数为 int i2c_register_driver ,此函数原型如下:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
函数参数和返回值含义如下:
owner :一般为 THIS_MODULE 。driver :要注册的 i2c_driver 。返回值 :0 ,成功;负值,失败。
除了i2c_register_driver 外,i2c_add_driver 也常常用于注册 i2c_driver ,i2c_add_driver 是一个宏,i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver 。定义如下:
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
4.2 i2c_driver的注销
注销 I2C 设备驱动的时候需要将前面注册的 i2c_driver 从 Linux 内核中注销掉,需要用到 i2c_del_driver 函数,此函数原型如下:
void i2c_del_driver(struct i2c_driver *driver)
函数参数和返回值含义如下:
driver :要注销的 i2c_driver 。返回值 :无。
4.3 i2c_driver的注册示例代码如下(出自正点原子):
static int xxx_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
return 0;
}
static int xxx_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id xxx_id[] = {
{"xxx", 0},
{}
};
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx" },
{ }
};
static struct i2c_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
5. I2C 设备和驱动匹配过程
I2C 设备和驱动的匹配过程是由 I2C 核心来完成的,drivers/i2c/i2c-core.c 就是 I2C 的核心部分,I2C 核心提供了一些与具体硬件无关的 API 函数,比如前面提到的i2c注册和注销函数: ①:i2c_adapter 注册/注销函数
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)
②:i2c_driver 注册/注销函数
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 总线完成的,I2C 总线的数据结构为 i2c_bus_type ,定义 在 drivers/i2c/i2c-core.c 文件,i2c_bus_type 内容如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数,此 函数内容如下drivers\i2c\i2c-core-base.c :
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (i2c_of_match_device(drv->of_match_table, client))
return 1;
if (acpi_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
if (i2c_match_id(driver->id_table, client))
return 1;
return 0;
}
函数分析如下:
of_driver_match_device :函数用于完成设备树设备和驱动匹配。比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C 设备和驱动匹配acpi_driver_match_device :函数用于 ACPI 形式的匹配。i2c_match_id :函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C 设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。
返回总目录
|