1. I2C 子系统框架图
从框架中看,用户应用通过/sys或者/dev中的设备节点来访问I2C设备。内核空间中的I2C分I2C client driver、I2C-Core与I2C Adapter driver三部分。其中I2C-Core是linux内核实现好的代码,I2C Adapter driver是具体CPU硬件平台的host 控制器驱动,一般平台商提供这部分驱动。I2C Client driver是具体的外设驱动,我们一般关心的是这部分代码。
2. I2C 设备驱动框架图
以下是I2C框架流程图:
3. I2C 驱动相关接口函数
(1). I2C注册、注销函数
static inline int i2c_add_driver(struct i2c_driver *driver); /* 注册一个I2C设备驱动 */
void i2c_del_driver(struct i2c_driver *driver); /* 注销一个I2C设备驱动 */
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len); //注册I2C设备板级信息。
(2). I2C读写数据函数
a. 接收(read)数据函数
//接收一个字节,寄存器为上次发送的地址。
s32 i2c_smbus_read_byte(const struct i2c_client *client);
//从具体I2C设备的寄存器中读一个字节,command表示寄存器地址。
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
//从具体I2C设备的寄存器中读2个字节,command表示寄存器地址。
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
//从具体I2C设备的寄存器中读一段数据, command表示寄存器地址。
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values);
b. 发送(write)数据函数
//向具体外设发送一个字节,寄存器为上次发送的地址。
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
//向具体寄存器发送一个字节。
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
//向具体寄存器发送2个字节。
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value);
//向具体寄存器发送一段数据。
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
4. I2C 驱动相关数据结构
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
int irq;
};
I2C 设备信息数据结构,蓝色的type与addr成员必须初始化具体的值,其中type是具体的设备名称,addr是I2C地址。
注意:addr=chip_address>>1,意思就是addr是实际I2C地址的高7位。
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, 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 *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
I2C 驱动相关数据结构,我们通常要实现此结构体的蓝色部分标示的成员。
driver:驱动名称
id_table:此驱动支持的设备ID表
4个函数指针:
probe: 驱动的侦测函数接口,由总线控制器驱动调用。
remove: 卸载驱动的函数接口。
suspend: 休眠函数接口,用于节省电能等场合。
resume: 唤醒函数接口,使从休眠中醒来。
如果没有实现相应函数的功能,那置为NULL值即可。
5. I2C 例子驱动及实现代码
(1). 包含头文件
#include <linux/i2c.h>
(2). 定义及实现probe函数,比如:
int akm8975_probe(struct i2c_client *client, const struct i2c_device_id *id)
(3). 定义及实现remove函数
static int akm8975_remove(struct i2c_client *client)
(4). 定义及初始化i2c_device_id
static const struct i2c_device_id akm8975_id[]= {
{"akm8975", 0 },
{ }
};
(5). 定义及初始化i2c_driver数据结构
static struct i2c_driver akm8975_driver = {
.probe = akm8975_probe,
.remove = akm8975_remove,
.id_table = akm8975_id,
.driver = {
.name = "akm8975",
},
};
(6). 在模块初始化函数中调用 i2c_add_driver 把I2C驱动加入I2C子系统
static int __init akm8975_init(void)
{
return i2c_add_driver(&akm8975_driver);
}
(7). 在模块卸载函数中调用i2c_del_driver删掉I2C驱动
static void __exit akm8975_exit(void)
{
i2c_del_driver(&akm8975_driver);
}
(8). 定义及初始化i2c_board_info
static struct i2c_board_info akm8975_i2c_info[] __initdata = {
{
I2C_BOARD_INFO("akm8975", 0x0e),
.platform_data = &akm_platform_data_8975,
.flags = I2C_CLIENT_WAKE,
},
};
(9). 调用函数i2c_register_board_info注册i2c_board_info到系统
i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID, akm8975_i2c_info, ARRAY_SIZE(akm8975_i2c_info));