27 IIC(五)i2c体系结构

1. I2C体系结构组成

在linux中i2c驱动由三部分组成:i2c-core(i2c核心)、i2c总线驱动、i2c设备驱动

结合上图描述可知

  • 应用态访问内核态可以使用特定的I2C driver,也可以使用通用的i2c_dev

    i2c_dev.c文件内部实现的是基本的通用的i2c驱动程序。它对外只提供基本的i2c操作,相当于把部分驱动操作放到了应用层去实现

  • i2c设备驱动通过i2c-core与i2c总线驱动建立连接

  • i2c总线驱动包含:algorithum、i2c adapter、特定的硬件相关的代码

  • driver并不一定需要访问i2c adapter,还可以走gpio模拟的i2c

  • I2C设备驱动是设备端的实现,主要为i2c_driver和i2c_client

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);

2. I2C文件

在内核中

linux-5.10.150/drivers/i2c$ tree
.
├── algos
│   ├── i2c-algo-bit.c
│   ├── i2c-algo-pca.c
│   ├── i2c-algo-pcf.c
│   ├── i2c-algo-pcf.h
│   ├── Kconfig
│   └── Makefile
├── busses
│   ├── i2c-acorn.c
│   ├── i2c-ali1535.c
│   ├── i2c-ali1563.c
│   ├── i2c-ali15x3.c
│   ├── i2c-altera.c
│   ├── i2c-amd756.c
...
│   ├── Kconfig
│   ├── Makefile
│   └── scx200_acb.c
├── i2c-boardinfo.c
├── i2c-core-acpi.c
├── i2c-core-base.c
├── i2c-core.h
├── i2c-core-of.c
├── i2c-core-slave.c
├── i2c-core-smbus.c
├── i2c-dev.c
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-slave-testunit.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
    ├── i2c-arb-gpio-challenge.c
    ├── i2c-demux-pinctrl.c
    ├── i2c-mux-gpio.c
    ├── i2c-mux-gpmux.c
    ├── i2c-mux-ltc4306.c
    ├── i2c-mux-mlxcpld.c
  • algos

    iic总线驱动的通信方法

  • busses

    iic主控芯片的驱动

  • i2c-core.c

    iic-core的核心功能

  • i2c-dev.c

    通用iic adapter驱动,主设备号都为89,此设备号为0~255。注册类型为/dev/i2c-%d

linux-5.10.150/include/linux/i2c.h
  • i2c.h

    此文件包含i2c_adapter、i2c_algorithm、i2c_driver、i2c_client等重要结构体

linux-5.10.150/include/uapi/linux/i2c.h
  • i2c_msg

    iic消息,包含iic的传输地址、方向、缓冲区、缓冲长度。

    位于uapi,意味着可供用户空间使用

3. 主要数据结构

3.1 i2c_adapter

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
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	*/
	const struct i2c_lock_operations *lock_ops;
	struct rt_mutex bus_lock;
	struct rt_mutex mux_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */
	unsigned long locked_flags;	/* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED		0
#define I2C_ALF_SUSPEND_REPORTED	1

	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;

	struct irq_domain *host_notify_domain;
};

3.2 i2c_client

client中指定了client的地址、name和adapter

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
#define I2C_CLIENT_PEC		0x04	/* Use Packet Error Checking */
#define I2C_CLIENT_TEN		0x10	/* we have a ten bit chip address */
					/* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE	0x20	/* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY	0x40	/* We want to use I2C host notify */
#define I2C_CLIENT_WAKE		0x80	/* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB		0x9000	/* Use Omnivision SCCB protocol */
					/* Must match I2C_M_STOP|IGNORE_NAK */

	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 init_irq;			/* irq set at initialization	*/
	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
};

3.3 i2c_driver

i2c_driver成员中包含of_match_type和id_table

struct i2c_driver {
	unsigned int class;

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
	int (*remove)(struct i2c_client *client);

	/* New driver model interface to aid the seamless removal of the
	 * current probe()'s, more commonly unused than used second parameter.
	 */
	int (*probe_new)(struct i2c_client *client);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *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").
	 * For the SMBus Host Notify protocol, the data corresponds to the
	 * 16-bit payload data reported by the slave device acting as master.
	 */
	void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,
		      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);

	/* driver中含有of_match_type */
	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
	const unsigned short *address_list;
	struct list_head clients;
};

3.4 i2c_algorithm

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 (*master_xfer_atomic)(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);
	int (*smbus_xfer_atomic)(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 *adap);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

3.5 i2c_msg

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_RD		0x0001	/* read data, from slave to master */
					/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_DMA_SAFE		0x0200	/* the buffer of this message is DMA safe */
					/* makes only sense in kernelspace */
					/* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

3.6 数据结构之间的关系

3.6.1 i2c_adapter、i2c_algorithm

一个i2c_adapter对应一个物理上的适配器;一个i2c_algorithm对应一套通信算法

一个i2c_adapter应该包含一个i2c_algorithm。即一个i2c适配器,需要对应的algorithm来实现

3.6.2 i2c_adapter、i2c_client

i2c_adapter与i2c_client为一对多关系

3.6.3 i2c_driver、i2c_client

i2c_driver与i2c_client为一对多关系

4 i2c bus匹配机制

i2c设备的driver与client的匹配机制是由i2c_device_match去实现的了。整体实现机制与platform类似

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 */
	/* 设备树中指定信息匹配
	 * devicetree compatible与driver中of_match_type中的compatible进行比较
	 */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then ACPI style match */
	/* 使用较少略过 */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* 比较client的name与id_table进行比较 */
	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;
}
posted @ 2023-05-23 22:36  人民广场的二道贩子  阅读(108)  评论(0编辑  收藏  举报