firefly-rk3288开发板Linux驱动——AT24C02 E2PROM驱动

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

一、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状态)


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知否,知否

来一杯冰美式把

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值