通訊協議I2C 子系統之I2C Driver

2022.06.23
通訊協議I2C 子系統之I2C Driver

I2C 子系統通過i2c-core 將i2c 設備驅動和i2c 總線驅動進行了分離,從而使得i2c 設備驅動中不用關心i2c 總線傳輸細節,專注於i2c 設備邏輯的實現。

現在來寫I2C Driver 部分。

I2C driver 分四個部分來寫:

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

文章以MTK 平台為例,code 來源於小米開源項目,小米每做一個手機項目,都會將kernel 部分開源,因為必須遵循GPL 協議。

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

I2C driver 源碼目錄。

/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

圖片

【driver 驱动层】由普通驱动工程师负责,【i2c 核心层】由 Linux 提供,【i2c 核心层】以下由芯片原厂负责。

I2C 子系统通过 i2c-core 将 i2c 设备驱动和 i2c 总线驱动进行了分离,从而使得 i2c 设备驱动中不用关心 i2c 总线传输细节,专注于 i2c 设备逻辑的实现。

抽象如下:

圖片圖片

I2C 总线驱动重点是 I2C 适配器(控制器)驱动,这里用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm。其中,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,i2c_algorithm 只是一些 i2c 传输的实现函数合集。

在 Linux 系统中有如下节点:

2、I2C Data Structure

我们要搞懂一个 Linux 子系统,必须研究它的数据结构,搞懂每个结构体存储了什么东西,才能梳理清楚该子系统的架构。

I2C 子系统有几个主要的结构体:

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

i2c_adapter:i2c-core 层描述一个 I2C 控制器,假如一个芯片有 8 路 I2C bus,则有 8 个 i2c_adapter。请详细看博主对 code 的注释说明。

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.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

i2c_algorithm:I2C 传输函数合集,其中 master_xfer 是真正的传输函数,芯片原厂写 I2C 控制器驱动时必须实现。functionality 函数会返回该 I2C 控制器支持什么通信协议,也需要实现,其他的函数即便 Linux 规定了,芯片原厂也可以不实现,因为不常用。

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 只实现了其中两个。

圖片

圖片

i2c_client:描述设备信息:

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.
  • 21.
  • 22.
  • 23.
  • 24.

i2c_driver:普通驱动工程师写驱动时,必须实现其中的 probe 函数和 remove 函数,其余的函数一般用不到。

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 平台用该结构体表示 I2C 控制器,定义在/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.
  • 21.
  • 22.

i2c_msg:I2C 读写时,必须填充 i2c_msg。

标志位:写为 0 ,读为 I2C_M_RD,其他的 flag 大家可以参考。

I2C 单笔传输最大 64KB,len 的长度博主在注释中有说明。

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 信号