硬件平台:飞思卡尔IMX6
内核版本:kernel3.0.35
Linux的I2C子系统分为三层,I2C核心层,I2C总线驱动层和I2C设备驱动层。I2C核心层由内核开发者提供,I2C总线驱动层有芯片厂商提供,而I2C设备驱动层由于设备的差异性,就只能是具体的开发需求具体实现了。而本实例是拿eeprom芯片AT24C02进行的具体分析。
I2C核心层管理所有关于I2C的数据结构,提供I2C总线驱动层和I2C设备驱动层的操作接口函数,I2C总线驱动层提供I2C核心层访问硬件设备的具体方法,I2C设备驱动层向I2C核心层添加新的数据(总线设备节点)并且提供用户层访问接口。
注意:I2C核心层其实已经提供了用户访问接口函数,每一个I2C总线对应一个设备,/dev/i2c-x(0,1,2...),一般不选择使用,而是自己编写特定的设备驱动
本文档只针对I2C设备驱动的编写方法进行描述,而I2C核心层实现可见driver/i2c/i2c-core.c中有具体实现,I2C总线驱动层具体平台具体分析,比如IMX6平台为drivers\i2c\busses/i2c-imx.c
下面开始正题:I2C设备驱动编写分两部分
第一、在平台文件board-mx6q_sabresd.c中添加i2c_board_info,以及将其注册到I2C核心层
第二、编写I2C设备驱动
1 在平台文件board-mx6q_sabresd.c中添加i2c_board_info,以及将其注册到I2C核心层
步骤:
a、初始化i2c_board_info 类型全局变量
//分配初始化i2c_board_info
static struct i2c_board_info at24c02[] = {
{
I2C_BOARD_INFO("at24c02", 0x50) //“at24c02“用来匹配
}
};
b、在平台设备初始化函数(IMX6平台为mx6_sabresd_board_init)中注册i2c_board_info
//注册AT24C02的硬件信息
i2c_register_board_info(0,at24c02, ARRAY_SIZE(at24c02));//参数描述,第一个参数为busnum,第二个参数为struct i2c_board_info const *info,即挂接在总线上的设备集合数组,第三个参数,数组元素个数
2 编写I2C设备驱动
步骤:
a、初始化i2c_driver变量
static struct i2c_device_id at24c02_id[] = {
{"at24c02", 0},
//”at24c02“必须和i2c_board_info的type一致,匹配靠它进行
};
static struct i2c_driver at24c02_drv = {
.driver = {
.name = "tarena" //不重要,匹配不靠它
},
.probe = at24c02_probe, //匹配成功执行,i2c-core.c的probe函数会来回调这个函数执行
.remove = at24c02_remove,
.id_table = at24c02_id
};
b、注册i2c_driver变量
i2c_add_driver(&at24c02_drv);
c、在at24c02_probe函数中获取i2c_client变量,以及注册混杂设备,以用于后面的数据传输
static struct i2c_client *g_client;
static int at24c02_probe(
struct i2c_client *client,
struct i2c_device_id *id)
{
//1.注册混杂设备驱动
misc_register(&at24c02_dev);
//2.记录匹配成功的i2c_client
g_client = client;
return 0; //成功返回0,失败返回负值
}
d、编写混杂设备操作函数
static struct miscdevice at24c02_dev = {
.minor = MISC_DYNAMIC_MINOR, //自动分配次设备号
.name = "at24c02", //dev/at24c02
.fops = &at24c02_fops
};
static struct file_operations at24c02_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = at24c02_ioctl
};
e、实现at24c02_ioctl函数
ioctl函数实现数据传输接口,这里使用SMBUS接口将数据(地址,数据,设备地址(g_client->addr))丢给I2C总线驱动,启动I2C总线的硬件传输。
具体方法:
(1)打开SMBUS文档:内核源码\Documentation\i2c\smbus-protocol,找到对应的SMBUS接口函数
(2)打开芯片操作时序图,根据时序图找对应的SMBUS操作函数
(3)将addr,data和at24c02_probe函数中匹配成功的i2c_client通过对应的函数丢给I2C总线驱动然后启动I2C总线的硬件传输
比如AT24C02的读写函数分别为
i2c_smbus_read_byte_data(g_client, addr);
i2c_smbus_write_byte_data(g_client, addr, data);
具体代码可见以下地址点击打开链接