2

技术 | I2C 子系统(八)

 2 years ago
source link: https://www.eefocus.com/embedded/520041
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

I2C 子系统(八)-嵌入式系统-与非网

723cecf435af19afdaf5da218ac59181.jpg
323ff4e6e9e0e61b62a6de60c585c240.jpg
2616a9857065d040a0e793e69696eff5.jpg

I2C spec 和 I3C spec 已经写完了(5+2=7篇),现在来写 I2C Driver 部分。

I2C driver 分四个部分来写:

I2C SW Architecture
I2C Data Structure
I2C Register Flow
I2C Data Transfer

文章以 MTK 平台为例,code 来源于小米开源项目,小米每做一个手机项目,都会将 kernel 部分开源,因为必须遵循 GPL 协议

https://github.com/MiCode/Xiaomi_Kernel_OpenSource

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、I2C SW Architecture

forward?url=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FhgnOITBSQYtprZqWuMUxW3XYSaFg5CV80YMrDEGRSSo1eic1A1sEbjQm8dUnwo3cR0ticwqxI2txqqIbKG1GFdCw%2F640%3Fwx_fmt%3Dpng&s=67f9d5

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

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

抽象如下:

forward?url=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FhgnOITBSQYtprZqWuMUxW3XYSaFg5CV8hZriaib6PBc8CGOMLhBMeic36zkbRt7hDa6MolRJtWKjWryMBicJOWfia0Q%2F640%3Fwx_fmt%3Dpng&s=e0df5b

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

在 Linux 系统中有如下节点:

forward?url=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FhgnOITBSQYtprZqWuMUxW3XYSaFg5CV86XiaJKJAaEubIm0AKq29kJZnpUqPYJt8GnWuqg5q1sTVjpGym8Jaxkw%2F640%3Fwx_fmt%3Dpng&s=2f93f4

2、I2C Data Structure

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

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

I2C 控制器:i2c_adapter、i2c_algorithm、mtk_i2c

I2C 设备驱动:i2c_client、i2c_driver

I2C 传输:i2c_msg

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

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

MTK 只实现了其中两个

forward?url=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FhgnOITBSQYtprZqWuMUxW3XYSaFg5CV8icEPqAibqCKrRnicdHvHglFlzzc3GyHibbBuWvp79hn06zVqzLvlvb0Wag%2F640%3Fwx_fmt%3Dpng&s=2214d1
forward?url=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FhgnOITBSQYtprZqWuMUxW3XYSaFg5CV8ehU6YOaN26AVpia8xBCWhZreycewP18cj6SnwozOLn4MliccIhfnLHHA%2F640%3Fwx_fmt%3Dpng&s=06fe4b

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 */
};

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

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

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK