msm7227平台linux I2C驱动分析

来源:互联网 发布:淘宝开店ps教程 编辑:程序博客网 时间:2024/05/16 02:44

目录
1. 摘要 3
2. 简介 3
3. I2C架构 3
4. I2C总线初始化 4
5. I2C适配器驱动 5
6. I2C设备驱动 9
7. 用户空间驱动支持 12
8. 数据传输框架 16
9. References 16

1. 摘要
主要介绍Msm7227平台上I2C驱动原理,多数部分是29内核标准架构。
2. 简介
I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL。I2C是一种多主机控制总线,同一总线上可允许多个master. 
i2c总线适配器(adapter)就是一条i2c总线的控制器,在物理连接上若干i2c设备。在linux驱动中,每种处理器平台有自己的适配器驱动。
3. I2C架构

内核中i2c相关代码可以分为三个层次:
i2c框架层:i2c.h和i2c-core.c为其主体框架代码,提供了核心数据结构的定义、i2c适配器驱动和设备驱动的注册、注销管理等;i2c-dev.c用于创建i2c适配器的/dev/i2c-%d设备节点,提供i2c设备的用户空间访问方法等。
i2c总线适配器驱动:i2c/busses/目录下,如i2c-msm.c。定义描述具体i2c总线适配器的i2c_adapter数据结构、实现在具体i2c适配器上的i2c总线通信的具体实现,并由i2c_algorithm数据结构描述与i2c设备通信的方法。
i2c设备驱动:定义描述具体设备的i2c_client和可能的私有数据结构。

                     
 
 上图展示了内核I2C结构大整体框架,以下根据内核加载顺序介绍I2C总线初始化,I2C总线适配器驱动,I2C设备驱动和用户空间驱动支持及数据传输框架五部分介绍。
4. I2C总线初始化
 
该过程主要完成了sysfs总线结构,最终形成如下结构:
/sys/bus/i2c/
|-- devices
|-- drivers
|   |-- dummy
|      |-- bind
|      |-- uevent
|      `-- unbind
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent

/sys/class/i2c-adapter/
dummy_driver仅仅是注册了一个空的设备驱动,注册驱动时会遍历加载/sys/class/i2c-adapter/中的所有设备,该过程在初始话总线过程中完成,/sys/class/i2c-adapter/基本为空,所以我认为这里的驱动注册只是验证i2c总线结构的完整性考虑的。
5. I2C适配器驱动


Linux内核的所有适配器驱动程序都在driver/i2c/busses/目录下,当前高通的驱动是i2c-msm.c,适配器驱动的注册过程如下:

 
在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号。对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分配一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。高通的adapter驱动使用了i2c_add_numbered_adapter()注册,总线号最初保存在platform_data中。
I2C adapter以platform_device方式注册进系统,在proble函数中初始化了struct i2c_adapter结构:


struct i2c_adapter {   
 struct module *owner;   
 unsigned int id;   
 unsigned int class;    /* classes to allow probing for */  
 const struct i2c_algorithm *algo; /* the algorithm to access the bus */  
 void *algo_data;   
  
 /* --- administration stuff. */  
 int (*client_register)(struct i2c_client *);   
 int (*client_unregister)(struct i2c_client *);   
  
 /* data fields that are valid for all devices */  
 u8 level;    /* nesting level for lockdep */  
 struct mutex bus_lock;   
 struct mutex clist_lock;   
  
 int timeout;   /* in jiffies */  
 int retries;   
 struct device dev;  /* the adapter device */  
  
 int nr; /*该成员描述了总线号*/  
 struct list_head clients; /* i2c_client结构链表,该结构包含device,driver和  
adapter结构*/  
 char name[48];   
 struct completion dev_released;   
};  
struct i2c_adapter {
 struct module *owner;
 unsigned int id;
 unsigned int class;    /* classes to allow probing for */
 const struct i2c_algorithm *algo; /* the algorithm to access the bus */
 void *algo_data;

 /* --- administration stuff. */
 int (*client_register)(struct i2c_client *);
 int (*client_unregister)(struct i2c_client *);

 /* data fields that are valid for all devices */
 u8 level;    /* nesting level for lockdep */
 struct mutex bus_lock;
 struct mutex clist_lock;

 int timeout;   /* in jiffies */
 int retries;
 struct device dev;  /* the adapter device */

 int nr; /*该成员描述了总线号*/
 struct list_head clients; /* i2c_client结构链表,该结构包含device,driver和
adapter结构*/
 char name[48];
 struct completion dev_released;
};

其中nr的值是在arch/arm/mach-msm/devices.c中定义的:
struct platform_device msm_device_i2c = {   
 .name  = "msm_i2c",   
 .id  = 0,   
 .num_resources = ARRAY_SIZE(resources_i2c),   
 .resource = resources_i2c,   
};   
struct platform_device msm_device_i2c_2 = {   
 .name  = "msm_i2c",   
 .id  = 2,   
 .num_resources = ARRAY_SIZE(resources_i2c_2),   
 .resource = resources_i2c_2,   
};  
struct platform_device msm_device_i2c = {
 .name  = "msm_i2c",
 .id  = 0,
 .num_resources = ARRAY_SIZE(resources_i2c),
 .resource = resources_i2c,
};
struct platform_device msm_device_i2c_2 = {
 .name  = "msm_i2c",
 .id  = 2,
 .num_resources = ARRAY_SIZE(resources_i2c_2),
 .resource = resources_i2c_2,
};

该结构以参数形式传进i2c_add_numbered_adapter(),下一步将进入


static int i2c_register_adapter(struct i2c_adapter *adap)   
{   
 int res = 0, dummy;   
  
 /* Can't register until after driver model init */  
 if (unlikely(WARN_ON(!i2c_bus_type.p)))   
  return -EAGAIN;   
  
 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;/*父设备是platform_bus*/  
  pr_debug("I2C adapter driver [%s] forgot to specify "  
    "physical device/n", adap->name);   
 }   
 dev_set_name(&adap->dev, "i2c-%d", adap->nr);/*设备节点名字*/  
 adap->dev.release = &i2c_adapter_dev_release;   
 adap->dev.class = &i2c_adapter_class;   
 res = device_register(&adap->dev); /*注册adapter这个设备本身*/  
 if (res)   
  goto out_list;   
  
 dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);   
  
 /*以下部分完成i2c设备和驱动的注册*/  
 if (adap->nr < __i2c_first_dynamic_bus_num)/*主板初始化时的动态总线号,该值已导出符号表*/  
  i2c_scan_static_board_info(adap);/*完成新类型i2c设备的注册,一般只在主板初始化时*/  
  
 /* Notify drivers */  
 dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,   
     i2c_do_add_adapter);  /*探测总线上的所有i2c设备驱动,同时完成client、driver、device、adapter的绑定,但driver->address_data非空的情况下有用,而这又意味着只对旧的i2c机制有效*/  
  
out_unlock:   
 mutex_unlock(&core_lock);   
 return res;   
  
out_list:   
 idr_remove(&i2c_adapter_idr, adap->nr);   
 goto out_unlock;   
}  
static int i2c_register_adapter(struct i2c_adapter *adap)
{
 int res = 0, dummy;

 /* Can't register until after driver model init */
 if (unlikely(WARN_ON(!i2c_bus_type.p)))
  return -EAGAIN;

 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;/*父设备是platform_bus*/
  pr_debug("I2C adapter driver [%s] forgot to specify "
    "physical device/n", adap->name);
 }
 dev_set_name(&adap->dev, "i2c-%d", adap->nr);/*设备节点名字*/
 adap->dev.release = &i2c_adapter_dev_release;
 adap->dev.class = &i2c_adapter_class;
 res = device_register(&adap->dev); /*注册adapter这个设备本身*/
 if (res)
  goto out_list;

 dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);

 /*以下部分完成i2c设备和驱动的注册*/
 if (adap->nr < __i2c_first_dynamic_bus_num)/*主板初始化时的动态总线号,该值已导出符号表*/
  i2c_scan_static_board_info(adap);/*完成新类型i2c设备的注册,一般只在主板初始化时*/

 /* Notify drivers */
 dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
     i2c_do_add_adapter);  /*探测总线上的所有i2c设备驱动,同时完成client、driver、device、adapter的绑定,但driver->address_data非空的情况下有用,而这又意味着只对旧的i2c机制有效*/

out_unlock:
 mutex_unlock(&core_lock);
 return res;

out_list:
 idr_remove(&i2c_adapter_idr, adap->nr);
 goto out_unlock;
}

i2c_scan_static_board_info对应的初始化过程在board-msm7x27.c中完成,
i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
6. I2C设备7. 驱动
驱动的编写方法已在《msm7227-I2C设备驱动实现要点.doc》中介绍,此节分析驱动和设备的注册过程。
 
本还想详细分析代码,但发现,这张图已经足够说明i2c驱动的注册过程了,下面对我看代码时碰到的一些问题简要分析。
设备和驱动的关联
大家知道,对于一个驱动程序有两个元素不可或缺,即设备和驱动,一般驱动都是通过设备名和驱动名的匹配建立关系的,我从i2c/chips/里看到的示例代码了只能发现驱动的注册,却不见设备注册的踪影,令人疑惑,跟踪发现,在i2c adapter注册时会遍历i2c_board_info这样一个结构,而这个结构在29以前或更早的内核里是不存在的,该数据结构在board-msm7x27.c中初始化了i2c设备名及设备地址,这便解决了驱动与设备的匹配问题,同时器件地址的提供也有所改变,旧的内核是在驱动中使用一个normal_i2c数组保存地址的。
名字匹配
一个i2c驱动是可以有多个名字的,即一个驱动程序可以支持多个设备,该机制是通过 struct i2c_device_id实现的,驱动中建立这么一个结构体数组,i2c架构层便会扫描该数组,与设备名去匹配,匹配成功的都会进入相应probe函数。
进入probe
该过程困惑了我一段时间,其实要进入自己驱动的probe首先需要进入总线的probe,而进入总线probe的前提是与总线的match成功,具体实现大家可以根据上面的图看一下相应代码便知。
设备模型
I2C的架构充分利用的设备模型的原理及sysfs的实现,我认为理解i2C架构前先了解一下设备模型是很有必要的。这里将我的个人理解总结一下:
 Kobject是设备模型的最小单位,kset是对kobject的集合,struct driver_private、struct device等结构都内嵌了kobject,kset也内嵌kobject用于表征自己。相同特性的kset的合集又构成了subsys,举个不太恰当的类比:
kobject之于设备或驱动;kset之于某一类设备,如i2c;subsys之于子系统,如输入子系统。其实在29内核中subsys就是一个kset结构,贴两张图理解一下:
  
8. 用户空间驱动支持
这部分在i2c-dev.c中实现,这部分内容简单的说就是通过内嵌一个具有file_operations的标准字符设备驱动来虚拟i2c设备,这样,就可以在用户空间直接操作i2c设备了。
流程如下图:
 
余下的就是常规file_operation了,open操作:
static int i2cdev_open(struct inode *inode, struct file *file)   
{   
 unsigned int minor = iminor(inode);   
 struct i2c_client *client;   
 struct i2c_adapter *adap;   
 struct i2c_dev *i2c_dev;   
 int ret = 0;   
  
 lock_kernel();/*内核上锁,一般只在多cpu是有用*/  
 i2c_dev = i2c_dev_get_by_minor(minor);/*因为有两个adapter,同一个主设备号*/  
 if (!i2c_dev) {   
  ret = -ENODEV;   
  goto out;   
 }   
  
 adap = i2c_get_adapter(i2c_dev->adap->nr);   
 if (!adap) {   
  ret = -ENODEV;   
  goto out;   
 }   
  
 /* This creates an anonymous i2c_client, which may later be  
  * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.  
  *  
  * This client is ** NEVER REGISTERED ** with the driver model  
  * or I2C core code!!  It just holds private copies of addressing  
  * information and maybe a PEC flag.  
  */  
 client = kzalloc(sizeof(*client), GFP_KERNEL);   
 if (!client) {   
  i2c_put_adapter(adap);   
  ret = -ENOMEM;   
  goto out;   
 }   
 snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);   
 client->driver = &i2cdev_driver;/*绑定字符设备驱动*/  
  
 client->adapter = adap;   
 file->private_data = client;   
  
out:   
 unlock_kernel();   
 return ret;   
}  
static int i2cdev_open(struct inode *inode, struct file *file)
{
 unsigned int minor = iminor(inode);
 struct i2c_client *client;
 struct i2c_adapter *adap;
 struct i2c_dev *i2c_dev;
 int ret = 0;

 lock_kernel();/*内核上锁,一般只在多cpu是有用*/
 i2c_dev = i2c_dev_get_by_minor(minor);/*因为有两个adapter,同一个主设备号*/
 if (!i2c_dev) {
  ret = -ENODEV;
  goto out;
 }

 adap = i2c_get_adapter(i2c_dev->adap->nr);
 if (!adap) {
  ret = -ENODEV;
  goto out;
 }

 /* This creates an anonymous i2c_client, which may later be
  * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
  *
  * This client is ** NEVER REGISTERED ** with the driver model
  * or I2C core code!!  It just holds private copies of addressing
  * information and maybe a PEC flag.
  */
 client = kzalloc(sizeof(*client), GFP_KERNEL);
 if (!client) {
  i2c_put_adapter(adap);
  ret = -ENOMEM;
  goto out;
 }
 snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
 client->driver = &i2cdev_driver;/*绑定字符设备驱动*/

 client->adapter = adap;
 file->private_data = client;

out:
 unlock_kernel();
 return ret;
}
 
注意这里分配并初始化了一个struct i2c_client结构.但是没有注册这个clinet.此外,这个函数中还有一个比较奇怪的操作.不是在前面已经将i2c_dev->adap指向要操作的adapter么?为什么还要以adapter->nr为关键字从i2c_adapter_idr去找这个操作的adapter呢?注意了,调用i2c_get_adapter()从总线号nr找到操作的adapter的时候,还会增加module的引用计数.这样可以防止模块意外被释放掉.也许有人会有这样的疑问,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被释放掉的话,不是一样会引起系统崩溃么?这里因为,在i2cdev_attach_adapter()间接的增加了一次adapter的一次引用计数.如下:
view plaincopy to clipboardprint?
static int i2cdev_attach_adapter(struct i2c_adapter *adap)   
{   
......   
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,   
                     MKDEV(I2C_MAJOR, adap->nr),   
                     "i2c-%d", adap->nr);   
......   
}  
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
......
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr),
                     "i2c-%d", adap->nr);
......

看到了么,i2c_dev内嵌的device是以adap->dev为父结点,在device_create()中会增次adap->dev的一次引用计数.
好了,open()操作到此就完成了.
使用方法:(参考kernel-test/i2c-msm-test.c)
1、构造struct i2c_msg
[读] struct i2c_msg msgs[] = {   
  [0] = {   
   .addr = slave_address,   
   .flags = 0,   
   .buf = (void *)offset_data,   
   .len = ARRAY_SIZE(offset_data),   
  },   
  [1] = {   
   .addr = slave_address,   
   .flags = I2C_M_RD,   
   .buf = (void *)buf,   
   .len = count,   
  },   
 };   
[写] struct i2c_msg msgs[] = {   
  [0] = {   
   .addr = slave_address,   
   .flags = 0,   
   .buf = (void *)data,   
   .len = (2 + len) * sizeof(*data),   
  },   
  }  
[读] struct i2c_msg msgs[] = {
  [0] = {
   .addr = slave_address,
   .flags = 0,
   .buf = (void *)offset_data,
   .len = ARRAY_SIZE(offset_data),
  },
  [1] = {
   .addr = slave_address,
   .flags = I2C_M_RD,
   .buf = (void *)buf,
   .len = count,
  },
 };
[写] struct i2c_msg msgs[] = {
  [0] = {
   .addr = slave_address,
   .flags = 0,
   .buf = (void *)data,
   .len = (2 + len) * sizeof(*data),
  },
  } 
2、通过ioctl操作设备


view plaincopy to clipboardprint?
static int do_rdwr(int fd, struct i2c_msg *msgs, int nmsgs)   
{   
 struct i2c_rdwr_ioctl_data msgset = {   
  .msgs = msgs,   
  .nmsgs = nmsgs,  /* msgs 个数*/  
 };   
  
 if (msgs == NULL || nmsgs <= 0)   
  return -1;   
  
 if (ioctl(fd, I2C_RDWR, &msgset) < 0)   
  return -1;   
  
 return 0;   
}  
static int do_rdwr(int fd, struct i2c_msg *msgs, int nmsgs)
{
 struct i2c_rdwr_ioctl_data msgset = {
  .msgs = msgs,
  .nmsgs = nmsgs,  /* msgs 个数*/
 };

 if (msgs == NULL || nmsgs <= 0)
  return -1;

 if (ioctl(fd, I2C_RDWR, &msgset) < 0)
  return -1;

 return 0;
}
 

3、ioctl命令字:


#define I2C_SMBUS_READ 1  
#define I2C_SMBUS_WRITE 0  
 
#define I2C_SMBUS_QUICK 0  
#define I2C_SMBUS_BYTE 1  
#define I2C_SMBUS_BYTE_DATA 2   
#define I2C_SMBUS_WORD_DATA 3  
#define I2C_SMBUS_PROC_CALL 4  
#define I2C_SMBUS_BLOCK_DATA 5  
#define I2C_SMBUS_I2C_BLOCK_DATA 6  
#define I2C_SMBUS_BLOCK_PROC_CALL 7    
 
#define I2C_RETRIES 0x0701    
#define I2C_TIMEOUT 0x0702    
#define I2C_SLAVE 0x0703    
#define I2C_SLAVE_FORCE 0x0706    
#define I2C_TENBIT 0x0704    
#define I2C_FUNCS 0x0705    
#define I2C_RDWR 0x0707    
#define I2C_PEC 0x0708    
#define I2C_SMBUS 0x0720    
#define I2C_SMBUS_READ 1
#define I2C_SMBUS_WRITE 0

#define I2C_SMBUS_QUICK 0
#define I2C_SMBUS_BYTE 1
#define I2C_SMBUS_BYTE_DATA 2 
#define I2C_SMBUS_WORD_DATA 3
#define I2C_SMBUS_PROC_CALL 4
#define I2C_SMBUS_BLOCK_DATA 5
#define I2C_SMBUS_I2C_BLOCK_DATA 6
#define I2C_SMBUS_BLOCK_PROC_CALL 7 

#define I2C_RETRIES 0x0701  
#define I2C_TIMEOUT 0x0702  
#define I2C_SLAVE 0x0703  
#define I2C_SLAVE_FORCE 0x0706  
#define I2C_TENBIT 0x0704  
#define I2C_FUNCS 0x0705  
#define I2C_RDWR 0x0707  
#define I2C_PEC 0x0708  
#define I2C_SMBUS 0x0720 

9. 数据传输框架
I2C架构的读写支持两种类型,默认实现的操作是smbus协议,该协议与i2c协议类似,如果控制器不支持smbus,框架层可以用i2c_transfer模拟smbus的实现,系统默认的i2c传输函数一般都是基于i2c模拟的smbus方法传输的,如i2c_smbus_write_byte_data,i2c_smbus_read_byte_data等。
I2C协议的总线实现应该是I2C控制器,而不是SMBUS控制器, I2C协议和SMBUS协议不完成等同,SMBUS是I2C的子集,smbus由I2C衍生而来。smbus总线上传输的数据一定是I2C的格式的,但是SMBUS上传输的数据不一定能满足具体某个I2C从设备的通信要求(数据序列)。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 w7系统反应慢怎么办 思维缓慢发呆书呆子怎么办 苹果手机网速慢怎么办? 在美国警察杀人怎么办? 征兵填写不知道怎么办 长虹子画面关怎么办 土地被村长霸占怎么办 半夜 手机卡掉了怎么办 淘宝号买家违规怎么办 镍氢电池没电了怎么办 地下室对讲机信号不好怎么办 cf不能说话了怎么办 cf没办法说话怎么办 cf对讲机没声音怎么办 交警用的对讲机怎么办 cf语音没有声音怎么办 去上海没有流量怎么办 去外地没流量怎么办 外地流量不够用怎么办 手机在外地流量怎么办 联通网卡在外地怎么办 套餐流量不够用怎么办 省内流量去省外怎么办 在外省流量不够怎么办 榨汁机开关坏了怎么办 健伍dvd没遥控器怎么办 佳能 显示屏关不了怎么办 iqos充电闪红灯怎么办 航班取消了乘客怎么办 摩托罗拉电话静音了怎么办 对讲机话筒坏了怎么办 摩托罗拉xt1570费电怎么办 主板没有rgb接口怎么办 对讲机频段没了怎么办 怀孕查出宫颈囊怎么办 办养殖场没地怎么办 宝宝睡觉衣服湿透怎么办 开衫衣服往下滑怎么办 迷你世界没有牛怎么办 火龙果树烂了怎么办 误喝鸵鸟墨水怎么办