I2C Driver of Communication Protocol I2C Subsystem

2022.06.23
I2C Driver of Communication Protocol I2C Subsystem

The I2C subsystem separates the i2c device driver and the i2c bus driver through i2c-core, so that the i2c device driver does not need to care about the details of the i2c bus transmission, and focuses on the realization of the i2c device logic.

Now to write the I2C Driver part.

The I2C driver is written in four parts:

I2C SW Architecture
I2C Data Structure
I2C Register Flow
I2C Data Transfer
  • 1.
  • 2.
  • 3.
  • 4.

The article takes the MTK platform as an example. The code comes from the Xiaomi open source project. Every time Xiaomi makes a mobile phone project, it will open the kernel part because it must follow the GPL agreement.

https://github.com/MiCode/Xiaomi_Kernel_OpenSource
  • 1.

I2C driver source code directory.

/kernel-5.10/drivers/i2c/i2c-core-base.c //Linux common 驱动
/kernel-5.10/drivers/i2c/i2c-core.h
/kernel-5.10/include/linux/i2c.h
/kernel-5.10/drivers/i2c/busses/i2c-mt65xx.c //i2c 控制器驱动
/kernel-5.10/arch/arm64/boot/dts/
demo
/kernel-5.10/drivers/input/touchscreen/
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

1. I2C SW Architecture

picture

[driver driver layer] is responsible for ordinary driver engineers, [i2c core layer] is provided by Linux, and the following [i2c core layer] is responsible for the original chip factory.

The I2C subsystem separates the i2c device driver and  the i2c bus driver through i2c-core  , so that the i2c device driver does not need to care about the details of the i2c bus transmission, and focuses on the realization of the i2c device logic.

The abstraction is as follows:

picturepicture

The focus of the I2C bus driver is the I2C adapter (controller) driver, where two important data structures are used: i2c_adapter and i2c_algorithm. Among them, the Linux kernel abstracts the I2C adapter (controller) of the SOC into i2c_adapter, and i2c_algorithm is just a collection of implementation functions of some i2c transmission.

There are the following nodes in the Linux system:

2. I2C Data Structure

If we want to understand a Linux subsystem, we must study its data structure and understand what each structure stores, in order to sort out the architecture of the subsystem.

The I2C subsystem has several main structures:

I2C 控制器:i2c_adapter、i2c_algorithm、mtk_i2c
I2C 设备驱动:i2c_client、i2c_driver
I2C 传输:i2c_msg
  • 1.
  • 2.
  • 3.

i2c_adapter: The i2c-core layer describes an I2C controller. If a chip has 8 I2C buses, there are 8 i2c_adapters. Please see the blogger's comments on the code in detail .

struct i2c_adapter {
 struct module *owner;
 unsigned int class; /* 该 I2C bus 支持哪些类型的从设备 */
 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;/* 超过该时间无法重发 */
 int retries;/* I2C发送失败重试次数 */
 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;/*I2C bus id*/
 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;
 struct regulator *bus_regulator;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.
  • twenty three.
  • twenty four.
  • 25.

i2c_algorithm: A collection of I2C transfer functions, of which master_xfer is the real transfer function, which must be implemented when the original chip factory writes the I2C controller driver. The functionality function will return what communication protocol the I2C controller supports, and it also needs to be implemented. Even if other functions are specified by Linux, the original chip factory may not implement it because it is not commonly used.

struct i2c_algorithm {
 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
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

MTK implements only two of them.

picture

picture

i2c_client: describe device information:

struct i2c_client {
 unsigned short flags;/* I2C 传输标志位如下*/
#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;/* 所处的那一路 I2C bus */
 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
 void *devres_group_id;  /* ID of probe devres group */
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.
  • twenty three.
  • twenty four.

i2c_driver: When an ordinary driver engineer writes a driver, the probe function and remove function must be implemented, and the rest of the functions are generally not used.

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);
 int (*probe_new)(struct i2c_client *client);
 void (*shutdown)(struct i2c_client *client);
 void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,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 *client, struct i2c_board_info *info);
 const unsigned short *address_list;
 struct list_head clients;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

mtk_i2c: MTK platform uses this structure to represent the I2C controller, which is defined in /kernel-5.10/drivers/i2c/busses/i2c-mt65xx.c.

struct mtk_i2c {
 struct i2c_adapter adap; /* i2c host adapter */
 struct device *dev;
 struct completion msg_complete;
 /* set in i2c probe */
 void __iomem *base;  /* i2c base addr */
 void __iomem *pdmabase;  /* dma base address*/
 struct clk *clk_main;  /* main clock for i2c bus */
 struct clk *clk_dma;  /* DMA clock for i2c via DMA */
 struct clk *clk_pmic;  /* PMIC clock for i2c from PMIC */
 bool have_pmic;   /* can use i2c pins from PMIC */
 bool use_push_pull;  /* IO config push-pull mode */
 u16 irq_stat;   /* interrupt status */
 unsigned int clk_src_div;
 unsigned int speed_hz;  /* The speed in transfer */
 enum mtk_trans_op op;
 u16 timing_reg;
 u16 high_speed_reg;
 unsigned char auto_restart;
 bool ignore_restart_irq;
 const struct mtk_i2c_compatible *dev_comp;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • twenty one.
  • twenty two.

i2c_msg: When I2C is read and written, i2c_msg must be filled.

Flag bit: write as 0, read as I2C_M_RD, you can refer to other flags.

The I2C single transfer is up to 64KB, and the length of len is explained in the comments by the blogger.

struct i2c_msg {
 __u16 addr;
 __u16 flags;
#define I2C_M_RD  0x0001 /* guaranteed to be 0x0001! */
#define I2C_M_TEN  0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
#define I2C_M_DMA_SAFE  0x0200 /* use only in kernel space */
#define I2C_M_RECV_LEN  0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
#define I2C_M_NO_RD_ACK  0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART  0x4000 /* use only if I2C_FUNC_NOSTART */
#define I2C_M_STOP  0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
 __u16 len;//无符号16位,65536个byte,一次 I2C 传输最大 64KB
 __u8 *buf;
};
I2C_M_NO_RD_ACK:忽略所有 ACK/NACK 一直读
I2C_M_IGNORE_NAK:忽略所有的 NACK 继续读
I2C_M_NOSTART:没有 START 信号