必要的概念和已有的总结还是要拷贝来的:
I2C架构概述
Linux的I2C体系结构分为3个组成部分:
I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。
I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据
linux驱动中i2c驱动架构
上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂
架构层次分类
第一层:提供i2c adapter
的硬件驱动,探测、初始化i2c adapter
(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter
在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层
第二层:提供i2c adapter
的algorithm,用具体适配器的xxx_xferf()
数来填充i2c_algorithm
的master_xfer
函数指针,并把赋值后的i2c_algorithm
再赋值给i2c_adapte
的algo指针。覆盖图中的访问抽象层、i2c核心层
第三层:实现i2c设备驱动中的i2c_driver
接口,用具体的i2c device
设备的attach_adapter()
、detach_adapter()
方法赋值给i2c_driver
的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层
第四层:实现i2c设备所对应的具体device的驱动,i2c_driver
只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()
、read()
、ioctl()
等方法,赋值给file_operations
,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层
I2C调用图示:
Linux下I2C体系文件构架
在Linux内核源代码中的driver目录下包含一个i2c目录
i2c-core.c
这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。
i2c-dev.c
实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访设备时的主设备号都为89,次设备号为0-255。i2c-dev.c
并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。
busses文件夹这个文件中包含了一些I2C总线的驱动,如针对S3C2410,S3C2440,S3C6410等处理器的I2C控制器驱动为i2c-s3c2410.c.
algos文件夹实现了一些I2C总线适配器的algorithm.
以上总结可知:
在Linux驱动中I2C系统中主要包含以下几个成员:
i2c_adapter
即I2C适配器
i2c_driver
某个I2C设备的设备驱动,可以以driver理解。
i2c_client
某个I2C设备的设备声明,可以以device理解。
i2c_algorithm
某个I2C设备数据的传输算法,对应一套通信方法。
I2C adapter
i2c_adapter
对应与物理上的一个适配器,而i2c_algorithm
对应一套通信方法,一个i2c适配器需要i2c_algorithm
中提供的(i2c_algorithm
中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm
的i2c_adapter
什么也做不了,因此i2c_adapter
中包含其使用i2c_algorithm
的指针。
i2c_algorithm
中的关键函数master_xfer()
用于产生i2c访问周期需要的start stop ack信号,以i2c_msg
(即i2c消息)为单位发送和接收通信数据。
i2c_adapter
是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,但最主要的工作是需要完成i2c_algorithm
结构体。这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型。i2c_algorithm结构体如下:
/**
* struct i2c_algorithm - represent I2C transfer method
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter
* defined by the msgs array, with num messages available to transfer via
* the adapter specified by adap.
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
* is not present, then the bus layer will try and convert the SMBus calls
* into I2C transfers instead.
* @functionality: Return the flags that this algorithm/adapter pair supports
* from the I2C_FUNC_* flags.
* @reg_slave: Register given client to I2C slave mode of this adapter
* @unreg_slave: Unregister given client from I2C slave mode of this adapter
*
* The following structs are for those who like to implement new bus drivers:
* i2c_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*
* The return codes from the @master_xfer field should indicate the type of
* error code that occurred during the transfer, as documented in the kernel
* Documentation file Documentation/i2c/fault-codes.
*/
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); //I2C传输函数指针
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data); //smbus传输函数指针
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *); //返回适配器支持的功能
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
如果一个I2C适配器不支持I2C通道,那么就将master_xfer
成员设为NULL。如果适配器支持SMBUS协议,那么需要去实现smbus_xfer
,如果smbus_xfer
指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。master_xfer
指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。
在内核的drivers/i2c/i2c-stub.c中实现了一个i2c adapter的例子,其中实现的是更为复杂的SMBUS。
SMBus 与 I2C的区别
通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。
时钟速度对比:
I2C | SMBus |
---|---|
最小 | 无 |
最大 | 100kHZ(标准)400kHz(快速模式)2MHz(高速模式) |
超时 | 无 |
在电气特性上他们也有所不同,SMBus要求的电压范围更低。
以下为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 */
struct rt_mutex bus_lock; //控制并发访问的自旋锁
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;//重试次数
struct device dev;//适配器设备/* the adapter device */
int nr;
char name[48];//适配器名称
struct completion dev_released;//用于同步
struct mutex userspace_clients_lock;
struct list_head userspace_clients; //client链表头
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
void (*lock_bus)(struct i2c_adapter *, unsigned int flags);
int (*trylock_bus)(struct i2c_adapter *, unsigned int flags);
void (*unlock_bus)(struct i2c_adapter *, unsigned int flags);
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
I2C driver
具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C协议与主机进行数据传输、控制。结构体如下:
/**
* struct i2c_driver - represent an I2C device driver
* @class: What kind of i2c device we instantiate (for detect)
* @attach_adapter: Callback for bus addition (deprecated)
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @shutdown: Callback for device shutdown
* @alert: Alert callback, for example for the SMBus alert protocol