I2C子系统框架,i2c子系统
I2C子系统框架,i2c子系统
1 I2C子系统框架
Linux I2C子系统分成三部分:I2C核心层、I2C总线驱动和I2C设备驱动。
(1)I2C核心层
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即algorithm)上层的与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。核心层的代码在drivers/i2c/i2c-core.c中实现。
(2)I2C总线驱动
I2C总线驱动是对I2C硬件适配器端的实现,适配器可由CPU控制。I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter、I2C适配器的Algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
Linux在drivers/i2c/下建立了busses目录用于存放各种已实现的I2C总线驱动,其中包括samsung S3C系列芯片的I2C总线驱动实现i2c-s3c2410.c。
(3)I2C设备驱动
I2C设备驱动是对I2C硬件设备端的实现,设备一般挂接在受CPU控制的I2C控制器上,通过I2C适配器与CPU交换数据。I2C设备驱动主要包含数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。
【温馨提示】i2c-dev.c实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255。i2c-dev.c并不是针对特定的设备而设计的,只是提供了通用的read()、write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或者寄存器,并控制I2C设备的工作方式。
2 I2C子系统数据结构
(1)i2c_adapter结构
I2C子系统用struct i2c_adapter来描述一个物理的适配器,适配器的具体通信方法由struct i2c_adapter的一个类型为struct i2c_algorithm的成员来描述。定义在include/linux/i2c.h。
1 struct i2c_adapter { 2 struct module *owner; 3 unsigned int id; // 适配器ID,这个在适配器驱动中不常用 4 unsigned int class; // 适配器的类类型 5 const struct i2c_algorithm *algo; // 指向通信方法数据的指针 6 void *algo_data; 7 8 /* data fields that are valid for all devices */ 9 u8 level; /* nesting level for lockdep */ 10 struct mutex bus_lock; 11 12 int timeout; // 传输超时时间 13 int retries; // 重试次数 14 struct device dev; // 内嵌的device结构 15 16 int nr; // 总线编号(也是适配器编号),同时对应设备节点/dev/i2c-x(x=0,1,2...)来访问 17 char name[48]; // 适配器名称,这个名称可以通过/sys/bus/i2c/devices/i2c-x/name(x=0,1,2...)来访问 18 struct completion dev_released; 19 };
(2)i2c_algorithm结构
struct i2c_adapter中用于描述通信方法的成员i2c_algorithm定义如下,该成员及其内核方法的实现是开发I2C总线驱动的核心任务。此结构体定义在include/linux/i2c.h。
1 struct i2c_algorithm { 2 /* 指向具体的I2C传输函数的指针,对应的传输一般会通过直接操作适配器硬件来发起。这个 3 函数的传入参数分别是使用该传输方法的适配器adap,待传输的消息msgs和消息数量num。 */ 4 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, 5 int num); 6 /* 指向具体的SMBus传输函数的指针。SMBus协议大部分基于I2C总线规范,并在基础上做了扩展,在访问 7 时序上由一些差异。如果这个指针置NULL,基于SMBus协议的通信将通过I2C传输来模拟 */ 8 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, 9 unsigned short flags, char read_write, 10 u8 command, int size, union i2c_smbus_data *data); 11 12 /* 指向返回适配器支持功能的函数的指针,这些功能定义在incude/linux/i2c.h中以I2C_FUNC开头 13 的宏表示,常用的有I2C_FUNC_I2C、I2C_FUNC_SMBUS_EMUL、I2C_FUNC_PROTOCOL_MANGLING */ 14 u32 (*functionality) (struct i2c_adapter *); 15 };
(3)i2c_msg结构
i2c_msg结构用来描述一个用于传输I2C的消息。此结构体定义在include/linux/i2c.h。
1 struct i2c_msg { 2 __u16 addr; // 从机地址 3 __u16 flags; // 反映消息特定的标志,当I2C_M_RD位被设置时,表示消息方向是主机从从机去读,如果 4 // 不设置该位(0),则消息方向是主机向从机写入。I2C_M_RD可以被所有适配器处理。I2C_M_NOSTART等 5 // 几个消息则需要适配器支持I2C_FUNC_PROTOCOL_MANGLING功能。 6 #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ 7 #define I2C_M_RD 0x0001 /* read data, from slave to master */ 8 #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ 9 #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ 10 #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ 11 #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ 12 #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ 13 __u16 len; // 消息数据长度,单位是字节 14 __u8 *buf; // 指向存放消息数据缓冲区 15 };
一个i2c_msg对象代表一个底层I2C传输单元。在驱动程序中,i2c_msg对象通过函数i2c_transfer()进行处理,而i2c_transfer()则调用适配器通信方法的master_xfer()来传输消息数据。
(4)i2c_driver结构
i2c_driver结构定义在include/linux/i2c.h中。它对应于一套驱动方法,其主要成员函数是probe()、remove()、suspend()、resume()等,另外,struct i2c_device_id形式的id_table是该驱动所支持的I2C设备的ID表。
1 struct i2c_driver { 2 unsigned int class; 3 4 /* 分别是依附和脱离i2c_adapter的函数指针,驱动注册函数会遍历适配器设备类i2c_adapter_class中的所有设备 5 并调用该驱动的attach_adapter方法进行依附。相应的,在添加i2c_adapter时,适配器注册函数会遍历总线i2c_bus_type 6 上所有的驱动,如果驱动定义了attach_adapter方法,他也将得到调用 */ 7 int (*attach_adapter)(struct i2c_adapter *) __deprecated; 8 int (*detach_adapter)(struct i2c_adapter *) __deprecated; 9 10 /* 在设备驱动中,当总线i2c_bus_type上的设备与设备驱动匹配后被调用。 */ 11 int (*probe)(struct i2c_client *, const struct i2c_device_id *); 12 int (*remove)(struct i2c_client *); 13 14 /* driver model interfaces that don't relate to enumeration */ 15 void (*shutdown)(struct i2c_client *); 16 int (*suspend)(struct i2c_client *, pm_message_t mesg); 17 int (*resume)(struct i2c_client *); 18 19 void (*alert)(struct i2c_client *, unsigned int data); 20 21 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); 22 23 /* 内嵌的driver结构。在注册i2c_driver对象时,i2c_driver->driver的总线类型被指定为i2c_bus_type */ 24 struct device_driver driver; 25 /* 存放该驱动支持的设备列表。i2c_device id结构在include/linux/mod_devicetable.h 26 中定义。包括一个char name[I2C_NAME_SIZE]和一个kernel_ulong_t driver_data的成员。 27 name用于匹配设备和驱动,i2c_bus_type的match()方法会遍历驱动id_table中的每一项,通过 28 比较设备名称和这个name成员,找到与设备匹配的驱动。*/ 29 const struct i2c_device_id *id_table; 30 31 int (*detect)(struct i2c_client *, struct i2c_board_info *); 32 const unsigned short *address_list; 33 struct list_head clients; 34 };
(5)i2c_client结构
i2c_client结构定义在inlcude/linux/i2c.h中。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client描述。i2c_client的信息通常在BSP的板级文件中通过i2c_board_info填充或者设备树填充。
1 struct i2c_client { 2 unsigned short flags; // 两个主要的标识是:I2C_CLIENT_TEN表示设备使用10-bit地址; 3 // I2C_CLIENT_PEC表示设备使用SMBus包错误检查 4 unsigned short addr; // 设备地址,7-bit地址格式下,地址存放该成员的低7位 5 char name[I2C_NAME_SIZE]; // 设备名 6 struct i2c_adapter *adapter; // 依附的适配器 7 struct i2c_driver *driver; // 设备绑定的驱动 8 struct device dev; // 内嵌device结构。 9 int irq; 10 struct list_head detected; 11 };
(1)BSP板级文件的i2c_board_info填充:
I2C设备ID为“ad7142_joystick”、地址为0x2C、中断号为IRQ_PF5。
1 static struct i2c_board_info __initdata xxx_i2c_board_info[] = { 2 { 3 I2C_BOARD_INFO("ad7142_joystick", 0x2C); 4 .irq = IRQ_PF5. 5 }, 6 ... 7 }
(2)设备树填充:
I2C设备ID为“invensense,mpu6050”、地址为0x68、中断号为3。
i2c@138B0000 { samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; pinctrl-0 = <&i2c5_bus>; pinctrl-names = "default"; status = "okay"; mpu6050-3-axis@68 { compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpx3>; interrupts = <3 2>; }; };
在I2C总线驱动i2c_bus_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配板级文件中定义的ID和i2c_driver所支持的ID表。
3 I2C子系统接口
(1)i2c_add_adapter()
i2c_add_adapter()向系统注册一个i2c_adapter对象,不必指定其nr成员,总线编号自动在i2c_add_adapter中分配并赋值给nr。
函数原形 |
int i2c_add_adapter(struct i2c_adapter *adapter); |
函数参数 |
adaper:i2c_adapter结构指针 |
(2)i2c_del_adaper()
i2c_del_adapter()向系统注销一个i2c_adapter对象。
函数原形 |
int i2c_del_adapter(struct i2c_adapter *adapter); |
函数参数 |
adaper:i2c_adapter结构指针 |
(3)i2c_add_numbered_adapter()
i2c_add_numbered_adapter()向系统注册一个i2c_adapter结构。此函数必须静态指定一个总线编号给nr成员。
函数原形 |
int i2c_add_numbered_adapter(struct i2c_adapter *adapter); |
函数参数 |
adaper:i2c_adapter结构指针 |
(4)i2c_add_driver()
i2c_add_driver()向系统添加一个i2c_driver对象。
函数原形 |
int i2c_add_driver(struct i2c_driver *driver); |
函数参数 |
driver:i2c_driver结构指针 |
(5)i2c_del_driver()
i2c_del_driver()向系统注销一个i2c_driver对象。
函数原形 |
int i2c_del_driver(struct i2c_driver *driver); |
函数参数 |
driver:i2c_driver结构指针 |
(6)i2c_master_send()
i2c_master_send()函数用来主机向i2c_client设备对象发送数据。在使用它们收发数据时,必须先设置设备地址。
函数原形 |
int i2c_master_send(const struct i2c_client *client, const char *buf, int count); |
函数参数 |
client:i2c_client结构指针 |
buf:存放发送数据的缓冲区 |
|
count:发送数据的大小 |
(7)i2c_master_recv()
i2c_master_recv()函数用来主机获取从机的数据。在它们收发数据时,必须先设置设备节点。
函数原形 |
int i2c_master_recv(const struct i2c_client *client, const char *buf, int count); |
函数参数 |
client:i2c_client结构指针 |
buf:存放接收数据的缓冲区 |
|
count:接收数据的大小 |
(8)i2c_transfer()
i2c_transfer()函数用于执行单个或组合的MSG消息。
函数原形 |
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); |
函数参数 |
adapter:I2C适配器的结构指针 |
msgs:i2c_msg结构,存放发送和接收的数据 |
|
num:msg的数量 |
【温馨提示】i2c_transfer()一次可以传输多个i2c_msg(考虑到很多外设的读写波形比较复杂)。而对于时序比较简单的外设,i2c_master_send()函数和i2c_master_recv()函数内部调用i2c_transfer()函数分别完成一条写消息和一条读消息。
评论暂时关闭