您的位置:首页 > 运维架构 > Linux

Linux I2C子系统分析整理

2012-12-28 16:18 155 查看
原文出處: Linux I2C子系统分析整理

参考:
http://www.linuxidc.com/Linux/2011-02/32497.htm http://www.linuxidc.com/Linux/2011-11/47651.htm等。
一、I2C总线硬件特性

I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA)。由于I2C总线仅需要两根线,因此在电路板上占用的空间更少,带来的问题是带宽较窄。I2C在标准模式下传输速率最高100Kb/s,在快速模式下最高可达400kb/s。属于半双工。在嵌入式系统中,I2C应用非常广泛,大多数微控制器中集成了I2C总线,一般用于和RTC,EEPROM,智能电池电路,传感器,LCD以及其他类似设备之间的通信。





[I2C总线传输时序]

[I2C总线的信号状态]
1、 空闲状态:SDA和SCL都是高电平;
2、 开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;
3、 结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;
4、 数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;
5、 ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。

[I2C通信]
由于I2C总线只有SDA和SCL两根线,并不像spi等其它总线有片选脚。所以在I2C总线上的设备用7位或10位的地址来区分。在通信时主机发送开始信号后跟着要与之通行的从设备的地址再加上以为数据读写方向(R/W)就可和总线上对应的设备通信。具体格式如下图:



二、linux I2C子系统

1、I2C核心(i2c-core)
I2C核心注册I2C总线类型、适配器类等并提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。所有的I2C驱动代码位于drivers/i2c目录下:

I2c-core.c 实现I2C核心的功能
I2c-dev.c 实现I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备
Chips 特定的I2C设备驱动
Busses I2C总线的驱动
Algos 实现了一些I2C总线适配器的algorithm

主要接口函数:
1) 增加/删除I2C适配器

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adap)
2) 增加/删除I2C从设备驱动

int i2c_register_driver(struct module *owner, structi2c_driver *driver)
static inline int i2c_add_driver(struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
i2c_add_driver是对i2c_register_driver简单的封装

3) i2c传输,发送和接收

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num)
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)
int i2c_master_recv(const struct i2c_client *client, char*buf, int count)
i2c_master_send和i2c_master_recv是i2c_transfer的封装

2、I2C总线驱动(I2Cadapter/Algo driver)
I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至集成在CPU内部(大多数微控制器都这么做)。适配器就是我们经常所说的控制器,一条I2C总线对应一个adapter。经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位,停止位,读写周期,以及以从设备方式被读写,产生ACK等。I2C总线驱动由i2c_adapter和i2c_algorithm来描述。一个I2C适配器需要I2C_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。I2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg为单位。

3、I2C客户驱动程序(I2Cclient driver)
I2C客户驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在收CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。I2C客户驱动程序由i2c_driver来描述,而设备由i2c_client来描述。
I2c_driver与i2c_client是一对多的关系,一个i2c_driver上可以支持多个同等类型的i2c_client。
I2c_adapter与i2c_client的关系与I2C硬件体系中适配器和从设备的关系一致,i2c_client依附在i2c_adapter上。

三、I2C总线/adapter驱动
现在大部分的微控制器cpu中都会集成I2C控制器,在总线驱动中一般也都是用platform虚拟总线来写的。在这里就不再讨论platform的部分。

1、首先先看一个重要的结构i2c_adapter,这个结构用来描述具体的I2C控制器。I2C总线的驱动就是对该结构的实现与注册。

struct i2c_adapter {
struct module *owner;
unsigned int id; //适配器编号也是bus编号,第几条i2c总线
unsigned int class;
const struct i2c_algorithm *algo; /* the algorithm to access the bus */ //该适配器的通信指针
void *algo_data; //algorithm 数据
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout;
int retries; /* 重试次数 */
struct device dev; /* the adapter device */ //内嵌的标准device
int nr;
struct list_head clients; /* DEPRECATED */
char name[48]; /* 适配器名字 */
struct completion dev_released; /* 用于同步 */
};

该结构中包含了控制器的一些信息,主要注意其中包含了一个i2c_algorithm结构的指针。该结构对应一套具体通信的方法。I2C的通信最终都必须调用该结构中master_xfer的方法来实现。这套通信方法是跟底层I2C控制器硬件相关的。对cpu上集成的控制器的操作就需要根据datasheet来操作。

struct i2c_algorithm {
/* 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传输函数指针 ,在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 *); /*返回适配器支持的通信协议 */
};

2、adapter注册

在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已.
对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.
分别来看一下这两个函数的代码:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
mutex_unlock(&core_lock);
if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}
adapter->nr = id;
return i2c_register_adapter(adapter);
}
在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅http://www.linuxidc.com<<
linux文件系统之文件的读写>>中有关radix tree的分析.
注意一下idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,然后将对应的id号存放在adapter->nr中.调用i2c_register_adapter(adapter)对这个adapter进行进一步注册.

看一下另外一人注册函数: i2c_add_numbered_adapter( ),如下所示:
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status;
if (adap->nr & ~MAX_ID_MASK)
return -EINVAL;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == 0 && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry;
if (status == 0)
status = i2c_register_adapter(adap);
return status;
}
对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.

进一步跟踪i2c_register_adapter().代码如下:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* let legacy drivers scan this bus for matching devices */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter);
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
首先对adapter和adapter中内嵌的struct device结构进行必须的初始化.之后将adapter内嵌的struct device注册.
在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c + 总线号。
在i2c_register_adapter中,可以看到其对new-style和legacy形式驱动的不同处理。New-style drivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2c driver的匹配上。

2.1 new-style 形式的adapter注册
对于第一种,也就是new-style drivers,将相关代码再次列出如下:
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
如果adap->nr 小于__i2c_first_dynamic_bus_num的话,就会进入到i2c_scan_static_board_info().
结合我们之前分析的adapter的两种注册分式: i2c_add_adapter()所分得的总线号肯会不会小于__i2c_first_dynamic_bus_num.只有i2c_add_numbered_adapter()才有可能满足:(adap->nr < __i2c_first_dynamic_bus_num)
而且必须要调用i2c_register_board_info()将板子上的I2C设备信息预先注册时才会更改__i2c_first_dynamic_bus_num的值.在x86上只没有使用i2c_register_board_info()的.因此,x86平台上的分析可以忽略掉new-style driver的方式。

跟踪i2c_scan_static_board_info():代码如下:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr // I2C设备信息中的busnum与适配器的id(nr)相等也就是该设备在这个适配器总线上就创建一个对应的I2C设备
&& !i2c_new_device(adapter,
&devinfo->board_info))
printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
i2c_adapter_id(adapter),
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
该函数遍历挂在__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息。__i2c_board_list在下一小节分析。
如果指定设备是位于adapter所在的I2C总线上,那么,就调用i2c_new_device().代码如下:
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);
client->flags = info->flags & ~I2C_CLIENT_WAKE;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* a new style driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe
* hotplugging will load the driver module). and the device
* refcount model is the standard driver model one.
*/
status = i2c_attach_client(client);
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}

在这里我们又看到了一个新的结构struct i2c_client,这个结构就是一个嵌入struct device的I2C设备的封装。也就是说一个I2C设备就对应着一个i2c_client。这个i2c_client将在i2c_attach_client中被挂载到apapter结构中的链表中。

int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
//初始化client内嵌的dev结构
//父结点为所在的adapter,所在bus为i2c_bus_type
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
//如果client已经指定了driver,将driver和内嵌的dev关联起来
if (client->driver)
client->dev.driver = &client->driver->driver;
//指定了driver, 但不是newstyle的
if (client->driver && !is_newstyle_driver(client->driver)) {
client->dev.release = i2c_client_release;
client->dev.uevent_suppress = 1;
} else
client->dev.release = i2c_client_dev_release;
//clinet->dev的名称
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
//将内嵌的dev注册
res = device_register(&client->dev);
if (res)
goto out_err;
//将clinet链到adapter->clients中
mutex_lock(&adapter->clist_lock);
list_add_tail(&client->list, &adapter->clients);
mutex_unlock(&adapter->clist_lock);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
//如果adapter->cleinet_reqister存在,就调用它
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x\n",
client->name, client->addr);
}
}
return 0;
out_err:
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, res);
return res;
}
参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?怎么没看到它的关系过程呢?
这是因为:在代码中设置了client->dev所在的bus为i2c_bus_type .以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2c driver的时候再来详细分析.

小结:mew-style注册一个I2C适配器时,该适配器将遍历BSP板级信息中的I2C设备链表,为该链表中属于本适配器的设备建立i2c_client,并将该i2c_client挂在本适配器结构中维护的链表中。

2.2 legacy形式的adapter注册
Legacy形式的adapter注册代码片段如下:
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, i2c_do_add_adapter);

这段代码遍历挂在i2c_bus_type上的驱动,然后对每一个驱动和adapter调用i2c_do_add_adapter().
代码如下:
static int i2c_do_add_adapter(struct device_driver *d, void *data)
{
struct i2c_driver *driver = to_i2c_driver(d);
struct i2c_adapter *adap = data;
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
if (driver->attach_adapter) {
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}
该函数很简单,就是调用driver的attach_adapter()接口.
到此为止,adapter的注册已经分析完了.

3. __i2c_board_list板级信息

int __init i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
mutex_lock(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
mutex_unlock(&__i2c_board_lock);
return status;
}
这个函数比较简单, struct i2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表。在系统启动时会创建这个链表,在之后adapter注册时就会被用到。

四、I2C设备驱动

4.1 在分析i2c driver的时候,有必要先分析一下i2c架构的初始化。
代码如下:
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
retval = class_register(&i2c_adapter_class);
if (retval)
goto bus_err;
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
class_unregister(&i2c_adapter_class);
bus_err:
bus_unregister(&i2c_bus_type);
return retval;
}
subsys_initcall(i2c_init);
很明显,i2c_init()会在系统初始化的时候被调用.
在i2c_init中,先注册了i2c_bus_type的bus,i2c_adapter_class的class.然后再调用i2c_add_driver()注册了一个i2c driver.
I2c_bus_type结构如下:
static struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};这个结构先放在这里吧,以后还会用到里面的信息的.

4.2 从上面的初始化函数里也看到了,注册i2c driver的接口为i2c_add_driver().代码如下:
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
继续跟踪:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* new style driver methods can't mix with legacy ones */
//如果是一个newstyle的driver.但又定义了attach_adapter/detach_adapter.非法
if (is_newstyle_driver(driver)) {
if (driver->attach_adapter || driver->detach_adapter
|| driver->detach_client) {
printk(KERN_WARNING
"i2c-core: driver [%s] is confused\n",
driver->driver.name);
return -EINVAL;
}
}
/* add the driver to the list of i2c drivers in the driver core */
//关联到i2c_bus_types
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* for new style drivers, when registration returns the driver core
* will have called probe() for all matching-but-unbound devices.
*/
//注册内嵌的driver
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lock);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
/* legacy drivers scan i2c busses directly */
//遍历所有的adapter,对其都调用driver->attach_adapter
if (driver->attach_adapter) {
struct i2c_adapter *adapter;
down(&i2c_adapter_class.sem);
list_for_each_entry(adapter, &i2c_adapter_class.devices,
dev.node) {
driver->attach_adapter(adapter);
}
up(&i2c_adapter_class.sem);
}
mutex_unlock(&core_lock);
return 0;
}

这里也有两种形式的区分,对于第一种,只需要将内嵌的driver注册就可以了,对于legacy的情况,对每一个adapter都调用driver->attach_adapter().
现在,我们可以将adapter和i2c driver关联起来考虑一下了:
如果是news style形式的,在注册adapter的时候,将它上面的i2c 设备转换成了struct client,而struct client->dev->bus又指定了和i2c driver同一个bus.因为,它们可以发生probe.
如果是legacy形式,就直接找到对应的对象,调用driver->attach_adapter().

4.3 i2c_bus_type的相关操作
I2c_bus_type的操作主要存在于new-style形式的驱动中.接下来分析一下对应的probe过程:
4.3.1:match过程分析
Match对应的操作函数为i2c_device_match().代码如下
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);
/* make legacy i2c drivers bypass driver model probing entirely;
* such drivers scan each i2c adapter/bus themselves.
*/
if (!is_newstyle_driver(driver))
return 0;
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
如果该驱动不是一个new-style形式的.或者driver没有定义匹配的id_table.都会匹配失败.
继续跟踪进i2c_match_id():
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
由此可见.如果client的名字和driver->id_table[]中的名称匹配即为成功.

4.3.2 probe过程分析
Probe对应的函数为: i2c_device_probe()
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
const struct i2c_device_id *id;
int status;
if (!driver->probe)
return -ENODEV;
client->driver = driver;
dev_dbg(dev, "probe\n");
if (driver->id_table)
id = i2c_match_id(driver->id_table, client);
else
id = NULL;
status = driver->probe(client, id);
if (status)
client->driver = NULL;
return status;
}
这个函数也很简单,就是将probe流程回溯到i2c driver的probe()。

好,I2C框架的分析就到这。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: