1、I2C(Inter-Integrated Circuit)总线:一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。
2、常用IIC接口通用器件的器件地址是由“类型号”及“寻址码”组成的,共7位:
D7 D6 D5 D4 D3 D2 D1 D0
器件类型:D7-D4 共4位决定的,半导公司生产时即已固定此类型。
用户自定义地址码:D3-D1共3位。
最低一位就是R/W位:“0”表示写,“1”表示读。
3、根据开漏输出或集电极开路输出信号的“线与”逻辑,IIC总线上任意器件输出低电平都会使相应总线上的信号变低。
4、I2C驱动由3部分组成:I2C核心、I2C总线驱动和I2C设备驱动。
5、I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法和I2C通信方法(algorithm),与具体适配器无关以及探测设备、检测设备地址的上层代码等。是I2C 总线驱动和设备驱动之间的枢纽。当一个具体的client 被侦测到并被关联的时候,设备和sysfs 文件将被注册。相反的,在client 被取消关联的时候,sysfs 文件和设备也被注销(在设备模型中,sysfs文件系统用来表示设备的结构.将设备的层次结构形象的反应到用户空间中.用户空间可以修改sysfs中的文件属性来修改设备的属性值)。
6、I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部,一般由CPU 厂家提供,busses 文件夹。(I2C适配器:i2c_adapter和I2C适配器的algorithm:i2c_algorithm)
I2C总线驱动功能:控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及从设备方式被读写、产生ACK等。
7、I2C设备驱动:对I2C硬件体系结构中设备slave的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。(i2c_driver和i2c_client)
8、Linux内核源码中/opt/kernel/drivers/i2c:
algos busses i2c-boardinfo.c i2c-core.c chips i2c-core.h i2c-dev.c
8.1 i2c_core.c:提供了一组不依赖与硬件平台的接口函数(一般不需要修改)和/proc/bus/i2c*接口。
8.2 i2c_dev.c :实现了I2C适配器设备文件的功能,每个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255.应用程序通过"i2c-%d"(i2c-0,i2c-1,...,i2c-10,...)文件名,使用文件操作接口open(),write(),ioctl()和close()等来访问该设备。
i2c_dev.c :并没有针对特定的设备设计,只是提供了通用的read(),write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。
i2c_dev.c :提供 i2cdev_read()和i2dev_write() 函数来对应用户空间来使用read()和write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()和i2c_master_send函数来构造一条I2C消息并引发适配器algorithm 通信函数的调用,完成消息的传输。(必须是一条消息,非ReqStart;不支持两条甚至更多的消息来进行一次的读写周期)
static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, loff_t *offset)
static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count, loff_t *offset)
static long i2cdev_ioctl (struct file *file, unsigned int cmd, unsigned long arg)//指定I2C:设备地址、超时、重试次数等
8.3 chips文件夹:包含了一些特定的i2C 设备驱动。在具体的I2C设备驱动中,调用的都是I2C 核心提供的API,因此,这使得具体的I2C 设备驱动不依赖于CPU的类型和I2C 适配器的硬件驱动。
8.4 busses 文件夹:包含了一些I2C总线的驱动,针对处理器I2C控制器的驱动。
8.5 algos文件夹:实现了一些I2C总线适配器的algorithm。
9、/opt/kernel/include/linux/i2c.h 四个重要结构体。
适配器结构体i2c_adapter:
> structi2c_adapter {
struct module *owner; /* 所属模块 */
unsigned int id; /* algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始 */
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */
void *algo_data; /*algorithm的数据:类似私有数据 */
/* --- administration stuff. */
int (*client_register)(struct i2c_client *) __deprecated; /*client 注册时调用 */
int (*client_unregister)(struct i2c_client *) __deprecated;/* client 注销时调用 */
/* data fields that are valid for all devices*/
u8 level; /* 控制并发访问的自旋锁 */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout;/* in jiffies */
int retries; /* 重试次数 */
struct device dev;/* 适配器设备 */
int nr;
struct list_head clients;/* client链表头 */
char name[48]; /* 适配器名称 */
struct completion dev_released; /* 用于同步 */
};
I2C适配器的通信方法i2c_algorithm :
> struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,/* 以i2c_msg消息为单位 */
int num); /* i2c传输函数指针:用于产生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传输函数指针 */
u32 (*functionality) (struct i2c_adapter *);/* 返回适配器支持的功能 */
};
struct i2c_msg{
__u16 addr;/* 设备地址 */
__u16 flags;/* 标志 */
__u16 len;/* 消息长度 */
__u8 *buf;/* 消息数据 */
};
I2C 驱动i2c_driver:
> struct i2c_driver {
struct module *owner;//HJ_add
char name[32];//HJ_add
int id;
unsigned int class;
unsigned int flags;//HJ_add
int (*attach_adapter)(struct i2c_adapter *); /* 依附i2c_adapter 函数指针 */
int (*detach_adapter)(struct i2c_adapter *); /* 脱离i2c_adapter 函数指针 */
int (*detach_client)(struct i2c_client *) __deprecated;/* i2c_client 脱离函数指针 */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table; /* 该驱动支持的设备ID表 */
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
> i2c_client :
struct i2c_client {
unsigned short flags;/* div., see below标志 */
unsigned short addr;/* 7bit低7位为芯片地址 */
char name[I2C_NAME_SIZE]; /* 设备名称 */
struct i2c_adapter *adapter;/* 依附的 i2c_adapter */
struct i2c_driver *driver;/* 依附的i2c_driver */
struct device dev;/* 设备结构体 */
int irq; /* 设备使用的中断号 */
struct list_head list;/* 链表头 */
struct list_head detected;
struct completion released; /* 用于同步 */
};
10、四个数据结构之间的关系:
1)i2c_adapter与i2c_algorithm:
i2c_adapter对应于物理上的适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器i2c_adapter需i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。故i2c_adapter中包含要使用的i2c_algorithm的指针。
2)i2c_driver和i2c_client:
i2c_driver 对应一套的驱动方法;i2c_client对应真实的物理设备,每个I2C设备都需一个i2c_client来描述。i2c_driver与i2c_client的关系是一对多,一个i2c_driver上可以支持多个同等类型的i2c_client。
i2c_client 信息通常在BSP(介于主板硬件和操作系统中间一层) 的板文件中通过i2c_board_info 填充。例如:
static struct i2c_board_info _initdata xxx_i2c_board_info[] = {
#if defined(CONFIG_JOYSTICK_AD7142) || defined(CONFIG_JOYSTICK_AD7141_MODULE)
{
I2C_BOARD_INFO("ad7142_joystick", 0x2C),
.irq = IRQ_PF5,
},
...
}
3)i2c_adapter和i2c_client:
i2c_adapter和i2c_client对应于I2C硬件体系中适配器和设备。i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个I2C设备,故一个i2c_adapter可以被多个i2c_client 依附,i2c_adapter 中包括依附于它的i2c_client 的链表。
十一、I2C硬件体系虽然简单,但是I2C体系结构在Linux中实现却相当复杂(实现步骤):
1)提供I2C 适配器的硬件驱动,探测、初始化I2C 适配器(申请I2C的I/O地址和中断号)、驱动CPU控制的I2C 适配器从硬件上产生各种信号以及处理I2C中断等。(总线驱动)
2)提供I2C适配器的algorithm,用具体适配器的 xxx_xfe()函数填充 i2c_algorithm 的master_xfer指针,并把 i2c_algorithm 指针赋值给 i2c_adapter 的algo 指针。(总线驱动)
3)实现I2C设备驱动中的i2c_driver 接口,用具体设备yyy的yyy_probe()、yyy_remove()、yyy_suspend()、yyy_resume()函数指针和 i2c_device_id 设备ID表赋值给 i2c_driver 的 probe、remove、suspend、resume和id_table指针。
4)实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的。例如,如果是字符设备,就是吸纳文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等;如果是声卡,就实现ALSA驱动。
十二、I2C 核心
1)增加/删除i2c_adapter:
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adap)
2)增加/删除i2c_driver:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
3)i2c_client 依附/脱离:
int i2c_attach_client(struct i2c_client *client)
int i2c_detach_client(struct i2c_client *client)
4)I2C 数据传输(发送和接收)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)/* 用于I2C适配器和I2C设备之间的消息交互,本身不具备驱动适配器物理硬件完成消息交互的能力,最终调用i2c_adapter 对应的i2c_algorithm 中的master_xfer()函数真正驱动硬件 */
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)/* 构造消息并调用i2c_transfer()完成一条发送消息 */
int i2c_master_recv(struct i2c_client *client, char *buf ,int count) /*构造消息并调用i2c_transfer()完成一条接收消息 */
十三、I2C 总线驱动
I2C 适配器驱动加载和卸载:
> I2C 总线驱动模块的加载:
1)初始化I2C适配器所使用的硬件资源,如申请I/O地址、中断号等。
2)通过 i2c_add_adapter()添加 i2c_adapter 的数据结构(已经被xxx适配器相应函数初始化过)
> I2C 总线驱动模块的卸载:
1)释放I2C 适配器所使用的硬件资源,如释放I/O地址、中断号等。
2)通过i2c_del_adapter() 删除i2c_adapter 的数据结构。
I2C 总线通信方法:需要为特定的I2C适配器实现其通信方法,i2c_adapter_xxx_xfer()判断核心层传递过来的msg消息类型,并处理消息进而完成真正的数据传输(开始位、停止位、读写周期,以及从设备方式被读写、产生ACK等)。
> 多数I2C 总线驱动会定义一个xxx_i2c 结构体,作为i2c_adapter 的 algo_data(类似“私有数据”),其中包含I2C 消息数据指针、数组索引及I2C 适配器 algorithm 访问控制用的自旋锁、等待队列等,而 master_xfer() 函数完成消息数组中消息的处理也可通过对 xxx_i2c 结构体相关成员的访问来控制。
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
enum s3c24xx_i2c_state state;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *irq;
struct resource *ioarea;
struct i2c_adapter adap;
};
十四、smbus总线驱动接口
* This is the very generalized SMBus access routine. You probably do not
want to use this, though; one of the functions below may be much easier,
and probably just as fast.
Note that we use i2c_adapter here, because you do not need a specific
smbus adapter to call this function. */
extern s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data);
/* Now follow the 'nice' access routines. These also document the calling
conventions of i2c_smbus_xfer. */
s32 i2c_smbus_read_byte(struct i2c_client *client);
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client *client,u8 command, u8 value);
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(struct i2c_client *client,u8 command, u16 value);
/* Returns the number of read bytes */
s32 i2c_smbus_read_block_data(struct i2c_client *client,u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_client *client,u8 command, u8 length, const u8 *values);
/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client,u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,u8 command, u8 length,const u8 *values);
十五、I2C 体系结构实现流程:
1)初始化:
i2c_client 通过 i2c_board_info 注册给I2C内核:主要设备名、地址和中断等信息。
2)用户空间到内核空间的调用:
i2c_driver 通过 smbus 接口将数据提交内核,内核将数据交给给I2C总线驱动(适配器驱动)。
适配器i2c_adapter 重组消息msg,通过通信接口i2c_algorithm调用其master_xfer函数,解析消息指令,操作I2C设备硬件。
3)I2C核心不用修改,I2C总线驱动通过内核提供的接口向核心层进行注册。
4)I2C总线驱动中要根据硬件时序,提供硬件操作接口(开始、读写、应答等消息机制)。
5)I2C设备驱动:向用户空间提供 xxx_read、xxx_write等接口和xxx_ioctl接口实现设备的配置工作以及中断等机制。