msm7227平台linux I2C驱动分析(2.6.29)

    技术2026-05-14  8

    原文:

    http://blog.csdn.net/tjd0227/archive/2010/07/21/5753606.aspx

     

     

    Revision History   Date Issue Description Author    <08/07/2010> <1.0> Msm7227平台I2C驱动分析 滕景东                目录 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 plain copy to clipboard print ? 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 plain copy to clipboard print ? 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从设备的通信要求(数据序列)。 下图以i2c_smbus_write_byte_data介绍数据流程:   10. References [1]. http://blog.chinaunix.net/u1/51562/showart_1403925.html [2]. 《msm7227-I2C设备驱动实现要点.doc》 滕景东

    最新回复(0)