一、Linux I2C设备体系
Linux源码中I2C驱动目录介绍:
| 目录/文件 | 介绍 |
|---|---|
| i2c-core.c | I2C核心功能以及proc/bus/i2c*接口 |
| i2c-dev.c | I2C适配器的设备文件,每一个I2C适配器都视为一个设备,主设备号都是89,并提供通用的open、read、write接口,用户层可以直接调用这些接口访问挂在此适配器下的真实I2C设备。 |
| busses文件夹 | 包含不同芯片的I2C主机控制器的驱动 |
| algos文件夹 | 实现了一些I2C适配器的通信方法 |
Linux I2C的设备体系主要分为3个部分:
1.I2C核心
I2C核心提供了I2C总线驱动和设备驱动注册、注销函数,I2C通信函数、探测设备、检测设备地址函数等。
//Linux/drivers/i2c/i2c-core.c
/*注册和注销i2c驱动*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);
/*注测和注销i2c适配器*/
int i2c_add_adapter(struct i2c_adapter *adapter);
void i2c_del_adapter(struct i2c_adapter *adap);
/*i2c传输函数*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/*i2c地址探测函数*/
static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_driver *driver)
2.I2C总线驱动
I2C总线驱动就是SOC中I2C控制器(在Linux中称为I2C适配器)驱动,其主要包括下面几个部分:
a.I2C适配器数据结构i2c_adapter
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
b.I2C适配器Algorithm数据结构i2c_algorithm
//kernel/include/linux/i2c.h
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
3.I2C设备驱动
I2C设备驱动就是真实物理设备的驱动程序,这些设备通常都是挂在I2C适配器下面的,用户程序不能直接访问设备,必须通过I2C适配器来访问,而访问用到的接口函数都是由I2C核心层提供。
I2C设备驱动有两个重要的数据结构:i2c_client和i2c_driver。
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future.
*/
int (*attach_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 *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
i2c_driver结构体类似platform_driver,是我们编写 I2C设备驱动重点要处理的内容。
4.I2C设备和驱动匹配过程
i2c设备和驱动的匹配过程是依靠I2C核心完成的,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,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
.match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数,此函数内容如下::
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 (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
与platform总线设备与驱动匹配很类似,I2C设备和驱动匹配有三种方式:
-
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 设备和驱动匹配。
二、Linux I2C 4个重要数据结构之间的联系
i2c4个重要的数据结构就是上文提到的:i2c_adapter、i2c_algorithm、i2c_driver和i2c_client。
1.i2c_adapter和i2c_algorithm
i2c_adapter对应物理上的1个i2c接口(主机),i2c_algorithm是I2C主机通信的一套方法,i2c_adapter需要使用i2c_algorithm提供的通信函数控制适配器产生特定的波形。i2c_adapter结构体中包含其所使用i2c_algorithm的指针。
i2c_algorithm数据结构中关键的方法master_xfer()用于产生I2C通信的波形,以i2c_msg(I2C消息)为单位。
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
i2c_msg定义了I2C通信的地址,方向,缓存区等信息。
2.i2c_driver和i2c_client
i2c_driver是i2c设备驱动,i2c_client是真实的设备,每个设备都需要一个i2c_client描述。当I2C设备和驱动匹配后,驱动中的probe函数就会被执行。每个i2c_driver驱动可以对应多个i2c_client,也就是同一个驱动程序,可以驱动多个同类型的物理设备。
3.i2c_adapter和i2c_client
i2c_client是挂在i2c_adapter下面的,因为物理设备肯定是直接接在主机I2C接口上的。一个适配器可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adapter中包括依附于他的i2c_client的链表。
三、I2C适配器驱动
I2C适配器驱动是一个标准的platform驱动,它的作用是向内核注册适配器,这里以RK3288开发板的I2C适配器驱动为例。驱动源码位置:kernel/drivers/i2c/busses/i2c-rk3x.c
rk3x_i2c结构体包含了RK3288 I2C控制器的所有私有数据(时钟,寄存器,i2c信息块,i2c状态)

本文深入探讨了Linux I2C设备驱动架构,详细介绍了I2C核心、总线驱动、设备驱动及设备和驱动匹配过程。通过分析RK3288开发板的I2C适配器驱动和自定义AT24xx EEPROM设备驱动,揭示了I2C设备在Linux系统中的工作原理。
最低0.47元/天 解锁文章

1387

被折叠的 条评论
为什么被折叠?



