红联Linux门户
Linux帮助

Linux i2c adapter驱动

发布时间:2016-08-30 10:02:23来源:linux网站作者:stevin_liang
1、概览
之前的文章中说过,struct i2c_adapter在i2c子系统中就代表了一个i2c控制器,也就是一条i2c总线。其实实现i2c总线或控制器驱动,其实就是实现一个struct i2c_adapter结构以及结构中的与平台相关的发送数据的接口,这些接口涉及到操作具体的寄存器。下面简单看一下struct i2c_adapter这个结构。
struct i2c_adapter {
struct module *owner;
unsigned int class;
conststructi2c_algorithm *algo;
structrt_mutexbus_lock;
int timeout;
int retries;
struct device dev;
intnr;
char name[48];
struct completion dev_released;
structmutexuserspace_client_lock;
structlist_headuserspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
};
 
2、struct i2c_adapter中各个字段的含义
2.1) algo:
驱动作者需要实现这个字段中的接口,主要是传输数据的接口。
struct i2c_algorithm {
int (*master_xfer) (struct i2c_adapter *adap, struct i2c_msg *msg, intnum);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned long flags, char read_write,
u8 command, int size, union i2c_bus_data *data);
u32 (*functionality) (struct i2c_adapter *);
}
其中master_xfer()是i2c协议的发送和接收数据的接口,smbus_xfer()是smbus协议的传输数据的接口。而functionality()是检查该i2c总线支持的功能。这里面的接口在实现i2c client驱动的时候会间接的用到。
2.2)timeout和retries
timeout指的是传输数据的时间限制,超过这个时间就认为传输数据失败。
retries字段记录的是当传输数据失败重试的次数。
 
3、注册struct i2c_adapter到内核中
所以实现了上述的几个字段,然后调用函数i2c_add_adapter()或i2c_add_numbered_adapter()就可以将这个i2c总线注册到i2c子系统中了。
下面来看看s3c24xx系列cpu的i2c总线的驱动是如何实现的。首先i2c控制器是个平台设备,所以驱动从注册平台设备开始。
static struct platform_driver s3c24xx_i2c_driver = {
.probe   = s3c24xx_i2c_probe,
.remove  = s3c24xx_i2c_remove,
.driver = {
.owner  = THIS_MODULE,
.name   = "s3c-i2c",
.pm=S3C24XX_DEV_PM_OPS,   
},
};
static int __init i2c_adap_s3c_init(void)
{   
return platform_driver_register(&s3c24xx_i2c_driver);
}   
subsys_initcall(i2c_adap_s3c_init);
static void __exit i2c_adap_s3c_exit(void)
{   
platform_driver_unregister(&s3c24xx_i2c_driver);
}   
module_exit(i2c_adap_s3c_exit);
而struct i2c_adapter这个结构是在平台驱动probe的过程中被创建和注册的。
下面看看s3c24xx_i2c_probe()这个函数。
首先是struct s3c24xx_i2c这个结构,这个结构中包含很多平台相关的信息,不过最主要的还是包含了struct i2c_adapter adap。
struct s3c24xx_i2c {
…...
struct i2c_msg  *msg;
……
struct i2c_adapter  adap;
……
};   
在probe()函数中的第一步就是构建structs3c24xx_i2c结构的一个变量。然后接下来就是设置struct s3c24xx_i2c的内嵌结构adap的相关字段。
/* 设置adapter的名字*/
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner   = THIS_MODULE;
/* 设置algo字段,主要包含传输数据的算法*/
i2c->adap.algo= &s3c24xx_i2c_algorithm;
/* 发送失败重试次数*/
i2c->adap.retries = 2;
/* 何种类型的i2c, 主要说明这个接口的用途*/
i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
/*然后是设置总线的编号和device tree node的一些信息*/
i2c->adap.nr = i2c->pdata->bus_num;
i2c->adap.dev.of_node = pdev->dev.of_node;
最后一步就是注册这个struct i2c_adapter的这个结构到i2c子系统中:
ret = i2c_add_numbered_adapter(&i2c->adap);
其实到这一步,整个i2c adapter的驱动就已经完成了。不过为了兼容现在device tree,最后还调用了of_i2c_register_devices()函数将连接在该i2c_adapter上的设备注册到系统中。以前的做法是在平台的代码中注册i2c_board_info结构。关于这两种方法注册i2c设备,将在下篇文章中详细讲解。
 
4、总结
下面来总结一下写一个i2c控制器的驱动大致需要的步骤:
1、编写与i2c控制器相关的平台驱动(structplatform_driver),这一步不是必须的,但是通用的做法是这样的;
2、在平台驱动的probe()函数中构建struct i2c_adapter结构,可以使直接创建这个结构,也可以将这个结构内嵌到一个表示某个平台的i2c控制器的一个结构体中。
3、实现struct i2c_algorithm中的传输数据的函数和functionality()函数;
4、设置struct i2c_adapter结构的一些信息,包括.algo、.retries、.class等;
5、注册struct i2c_adapter结构到i2c子系统中;
6、如果要支持device tree那么就要调用of_i2c_register_devices()注册连接在该总线上的i2c设备。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/23732.html