I2C驱动框架分析(1):I2C重要概念与数据结构
I2C驱动框架分析(2):I2C框架源码分析
I2C驱动框架分析(3):DW_I2C驱动分析
前言
i2c是低速模块中最常见的模块之一,也是linux系统的最基础的框架,本系列内容主要针对dw厂商的i2c IP进行分析,其驱动位置在drivers/i2c/busses/i2c-designware-platdrv.c
。
第一章:I2C重要概念与数据结构
在分析dw厂商的i2c驱动前,先简单介绍linux下i2c相关概念。
1.1 I2C字符设备
I2C驱动本身是属于字符设备驱动,其设备结构体内容定义为:
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device dev; //表明这是一个设备
struct cdev cdev;
};
设备对应的操作符为:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.compat_ioctl = compat_i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
1.2 I2C总线
i2c总线使用的是linux中struct bus_type
,具体的i2c总线结构体定义为:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
1.3 I2C设备驱动
i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体,例如eeprom的驱动。I2C驱动结构体定义部分内容为:
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
void (*shutdown)(struct i2c_client *client);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver; //linux驱动结构体,表明这是一个驱动
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;
};
例如eeprom的驱动:
static struct i2c_driver i2c_slave_eeprom_driver = {
.driver = {
.name = "i2c-slave-eeprom",
},
.probe = i2c_slave_eeprom_probe,
.remove = i2c_slave_eeprom_remove,
.id_table = i2c_slave_eeprom_id,
};
module_i2c_driver(i2c_slave_eeprom_driver);
1.4 I2C设备
区别于前面的i2c控制器设备,i2c设备是指支持i2c协议的设备,例如at24c02存储。linux系统为了方便管理,将其统一用结构体表示,定义为:
struct i2c_client {
unsigned short flags;
unsigned short addr; //设备地址
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev; //表明这是一个设备
int irq;
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
1.5 I2C适配器
linux为了实现i2c控制器与i2c设备通信,使用i2c适配器来实现,其内容定义为:
struct i2c_adapter {
struct module *owner;
unsigned int class;
const struct i2c_algorithm *algo;
int timeout;
int retries;
struct device dev; //这是一个设备
char name[48];
...
};
1.6 I2C通信函数
在i2c适配器中,提供了i2c通信函数,对i2c设备读写功能,都由此实现,其定义为:
struct i2c_algorithm {
//i2c控制器作主机的接口
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
//i2c控制器作从机的接口
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
1.7 I2C消息
i2c在进行读写操作时,其数据通过i2c消息进行管理,其定义为:
struct i2c_msg {
__u16 addr; //从机地址
__u16 flags;
#define I2C_M_RD 0x0001 //读标志位
#define I2C_M_TEN 0x0010 //从机地址位数:10位
__u16 len; //消息长度
__u8 *buf; //消息内容
};
1.8 小结
-
struct i2c_driver:是i2c设备驱动,比如eeprom设备的驱动,实现eeprom的读写操作;
static struct i2c_driver i2c_slave_eeprom_driver = { .driver = { .name = "i2c-slave-eeprom", }, .probe = i2c_slave_eeprom_probe, .remove = i2c_slave_eeprom_remove, .id_table = i2c_slave_eeprom_id, }; module_i2c_driver(i2c_slave_eeprom_driver);
-
struct i2c_client: i2c_client用于描述真实的i2c设备。在设备树上每一个i2c设备都会分配一个i2c_client,可有如下函数获取
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
一般在open函数打开i2c设备时,会创建一个i2c_client,供后续使用。
-
struct i2c_adapter:对应i2c控制器,例如dw_i2c。在写i2c控制器驱动时,只需要开辟i2c_adapte。一个i2c_adapte下可挂载多个i2c_client,即下一个i2c控制器的设备树下,挂载多个i2c设备。