OMAP3630 Linux I2C总线驱动分析

1 Linux I2C驱动架构
Linux下I2C驱动的架构图如下:
 

 
图1.1 Linux下I2C驱动架构


    如上图所示,每条I2C总线会对应一个adapter,而每条I2C总线上则可以有多个client,在linux kernel中,通过I2C core层将I2C client与I2C adapter关联起来,Linux 中I2C驱动代码位于drivers/i2c目录。
    Linux中I2C可以分为三个层次,分别为I2C core层、I2C adapter driver层、I2C device driver层。

1.1 I2C core层
    I2C core是用于维护Linux的I2C核心部分,提供了核心的数据结构,I2C适配器驱动和设备驱动的注册、注销管理等API,同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现)。该层为硬件平台无关层,向下屏蔽了物理总线适配器的差异,定义了统一的访问策略和接口;向上则提供了统一的接口,以便I2C设备驱动可以通过总线适配器进行数据收发。
    Linux中,I2C core层的代码位于driver/i2c/ i2c-core.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。

 

1.2 I2C adapter driver层
    I2C adapter driver层即I2C适配器驱动层,每种处理器平台都有自己的适配器驱动,属于平台移植相关层。它的职责是为系统中每条I2C总线实现相应的读写方法。但是适配器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。
    在系统开机时,I2C适配器驱动被首先装载。一个适配器驱动用于支持一条特定的I2C总线的读写。一个适配器驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述。
    i2c adapter 构造一个对I2C core层接口的数据结构,并通过相应的接口函数向I2C core注册一个适配器。i2c_algorithm主要实现对I2C总线访问的算法,master_xfer和smbus_xfer即I2C adapter底层对I2C总线读写方法的实现,相关的数据结构如下:
1./* 
2. * The following structs are for those who like to implement new bus drivers: 
3. * i2c_algorithm is the interface to a class of hardware solutions which can 
4. * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 
5. * to name two of the most common. 
6. */ 
struct i2c_algorithm {  
2.       /* If an adapter algorithm can't do I2C-level access, set master_xfer  to NULL.

3.       If an adapter algorithm can  do SMBus access, set smbus_xfer.

          If set to NULL, the SMBus protocol is simulated  using common I2C messages 
6.       master_xfer should return the number of messages successfully 
processed, or a negative value on error */ 
8.    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,   int num);  

9.    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,  unsigned short flags, char read_write,  

12.               u8 command, int size, union i2c_smbus_data *data);  

 

13.    /* To determine what the adapter supports */ 

14.    u32 (*functionality) (struct i2c_adapter *); 

15.}; 

    主要就是master_xfer方法,其和具体的总线控制器相关,不同的CPU在实现上会有差异。
1./* 
2. * i2c_adapter is the structure used to identify a physical i2c bus along 
3. * with the access algorithms necessary to access it. 
4. */ 
5.struct i2c_adapter {  
2.    struct module *owner;

3.    unsigned int id;  

4.    unsigned int class;       /* classes to allow probing for */ 

5.    const struct i2c_algorithm *algo; /* the algorithm to access the bus */ 

6.    void *algo_data;    /* data fields that are valid for all devices   */ 

8.    struct rt_mutex bus_lock;  

9.    int timeout;            /* in jiffies */ 

10.  int retries;  

11.  struct device dev;      /* the adapter device */ 

12.  int nr;  

13.  char name[48];  

14.  struct completion dev_released;  

15.  struct list_head userspace_clients;  

16.}; 

    Algo是和底层硬件的接口,标识了具体的物理总线传输的实现。
    Userspace_clients为使用该总线的client链表。
    Nr为该适配器也就是某条I2C总线占据的全局编号。
    bus_lock总线的互斥锁,防止总线冲突。
    Linux中,I2C adapter driver层的代码位于drivers/i2c/busses目录,第3章会详细介绍该层的内容。

1.3 I2C device driver层
    I2C device driver层为用户接口层,其为用户提供了通过I2C总线访问具体设备的接口。
    I2C的device driver层可以用两个模块来描述,struct i2c_driver和struct i2c_client。

    i2c_client和i2c_driver分别构造对I2C core层接口的数据结构,并且通过相关的接口函数向 I2C Core注册I2C设备驱动。相关的数据结构如下: 
 
1./** 
2. * struct i2c_driver - represent an I2C device driver 
3. * @class: What kind of i2c device we instantiate (for detect) 
4. * @attach_adapter: Callback for bus addition (for legacy drivers) 
5. * @detach_adapter: Callback for bus removal (for legacy drivers) 
6. * @probe: Callback for device binding 
7. * @remove: Callback for device unbinding 
8. * @shutdown: Callback for device shutdown 
9. * @suspend: Callback for device suspend 
10. * @resume: Callback for device resume 
11. * @command: Callback for bus-wide signaling (optional) 
12. * @driver: Device driver model driver 
13. * @id_table: List of I2C devices supported by this driver 
14. * @detect: Callback for device detection 
15. * @address_list: The I2C addresses to probe (for detect) 
16. * @clients: List of detected clients we created (for i2c-core use only) 
17. * 
18. * The driver.owner field should be set to the module owner of this driver. 
19. * The driver.name field should be set to the name of this driver. 
20. * 
21. * For automatic device detection, both @detect and @address_data must 
22. * be defined. @class should also be set, otherwise only devices forced 
23. * with module parameters will be created. The detect function must 
24. * fill at least the name field of the i2c_board_info structure it is 
25. * handed upon successful detection, and possibly also the flags field. 
26. * 
27. * If @detect is missing, the driver will still work fine for enumerated 
28. * devices. Detected devices simply won't be supported. This is expected 
29. * for the many I2C/SMBus devices which can't be detected reliably, and 
30. * the ones which can always be enumerated in practice. 
31. * 
32. * The i2c_client structure which is handed to the @detect callback is 
33. * not a real i2c_client. It is initialized just enough so that you can 
34. * call i2c_smbus_read_byte_data and friends on it. Don't do anything 
35. * else with it. In particular, calling dev_dbg and friends on it is 
36. * not allowed. 
37. */ 
38.struct i2c_driver {  

39.    unsigned int class;  

40.     /* Notifies the driver that a new bus has appeared or is about to be 

41.     * removed. You should avoid using this if you can, it will probably 
42.     * be removed in a near future. 
43.     */ 
44.    int (*attach_adapter)(struct i2c_adapter *);  

45.    int (*detach_adapter)(struct i2c_adapter *);  

46.    /* Standard driver model interfaces */ 

47.    int (*probe)(struct i2c_client *, const struct i2c_device_id *);  

48.    int (*remove)(struct i2c_client *);  

 

49.    /* driver model interfaces that don't relate to enumeration  */ 

50.    void (*shutdown)(struct i2c_client *);  

51.    int (*suspend)(struct i2c_client *, pm_message_t mesg);  

52.    int (*resume)(struct i2c_client *);  

53.    /* Alert callback, for example for the SMBus alert protocol. 

54.     * The format and meaning of the data value depends on the protocol. 
55.     * For the SMBus alert protocol, there is a single bit of data passed 
56.     * as the alert response's low bit ("event flag"). 
57.     */ 
58.    void (*alert)(struct i2c_client *, unsigned int data);  

59.    /* a ioctl like command that can be used to perform specific functions 

60.     * with the device. 
61.     */ 
62.    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);  

63.    struct device_driver driver;  

64.    const struct i2c_device_id *id_table;  

65.    /* Device detection callback for automatic device creation */ 

66.    int (*detect)(struct i2c_client *, struct i2c_board_info *);  

67.    const unsigned short *address_list;  

68.    struct list_head clients;  

69.}; 

        Driver是为device服务的,i2c_driver注册时会扫描i2c bus上的设备,进行驱动和设备的绑定。主要有两种接口:attach_adapter和probe,二者分别针对旧的和新式的驱动。 

 
1./** 
2. * struct i2c_client - represent an I2C slave device 
3. * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address; 
4. *  I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking 
5. * @addr: Address used on the I2C bus connected to the parent adapter. 
6. * @name: Indicates the type of the device, usually a chip name that's 
7. *  generic enough to hide second-sourcing and compatible revisions. 
8. * @adapter: manages the bus segment hosting this I2C device 
9. * @driver: device's driver, hence pointer to access routines 
10. * @dev: Driver model device node for the slave. 
11. * @irq: indicates the IRQ generated by this device (if any) 
12. * @detected: member of an i2c_driver.clients list or i2c-core's 
13. *  userspace_devices list 
14. * 
15. * An i2c_client identifies a single device (i.e. chip) connected to an 
16. * i2c bus. The behaviour exposed to Linux is defined by the driver 
17. * managing the device. 
18. */ 
19.struct i2c_client {  

20.    unsigned short flags;       /* div., see below      */ 

21.    unsigned short addr;  /* chip address - NOTE: 7bit   addresses are stored in the  _LOWER_ 7 bits       */  24.    char name[I2C_NAME_SIZE];  

25.    struct i2c_adapter *adapter;    /* the adapter we sit on    */ 

26.    struct i2c_driver *driver;  /* and our access routines  */ 

27.    struct device dev;      /* the device structure     */ 

28.    int irq;            /* irq issued by device     */ 

29.    struct list_head detected;  

30.}; 

    通常来说i2c_client对应着I2C总线上某个特定的slave或者是user space的某个用户对应,而此时的slave可以动态变化。
    Linux中,I2C device driver层的代码位于drivers/i2c/chips目录,第4章将详细介绍该层的内容。



 

2 OMAP3630 I2C控制器 
    OMAP3630具有4个高速I2C控制器,每个控制器都通过I2C串行总线为本地主机即OAMP3630 MPU和I2C总线兼容设备提供了一个通讯接口,支持多达8-bit的数据传送和接收。
    
每个I2C控制器都能配置成一个主机或者从机设备,而且他们都能配置成在一个2线的串行的摄像头控制总线(SCCB总线)上作为主设备,I2C2和I2C3还能配置成在一个3线的SCCB总线上作为主设备。
    I2C4控制器位于PRCM模块,可以进行动态电压控制和电源序列测定。
    OMAP3630的I2C控制器模块图如下:

 

 图2.1 OMAP3630 I2C控制器模块图

 
控制器1,2,3具有以下特征:
? 兼容飞利浦I2C 2.1版本
? 支持标准I2C标准模式(100Kbps)和快速模式(400Kpbs)
? 支持高达3.4Mbps的高速发送模式
? 支持I2C2和I2C3 模块的3线/2线的SCCB主从模式,I2C1 模块的2线的SCCB主从模式,高达100kbit/s
? 7-bit和10bit的设备地址模式
? 多主控发送/从接收模式
? 多主控接收/从发送模式
? 联合的主机发送/接收和接收/发送模式
? 内置FIFO(8,16,32,64字节大小)用于缓存读取和接收
? 模块使能/关闭
? 可编程的时钟
? 8-bit的数据存取
? 低功耗的设计
? 两个DMA通道
? 支持中断机制
? 自动空闲机制
? 空闲请求和应答握手机制
主从的发送机I2C4控制器有以下特征:
? 支持高速和快速模式
? 只能支持7-bit地址模式
? 只支持主发送模式
关于I2C控制器的详细介绍请参考OMAP36XX_ES1.1_NDA_TRM_V_G.pdf的第17章。

 

3 OMAP3630 I2C adapter驱动
    在Linux内核中,I2C adapter驱动位于drivers/i2c/busses目录下,OMAP3630 的I2C adapter驱动程序为i2c-omap.c。
    I2C adapter驱动,本质上就是实现了具体的总线传输算法并向核心层注册适配器。该驱动的注册采用Platform驱动和设备机制。

 

 


3.1 I2C adapter的Platform device
       Andrord 2.1中Platform device的注册的代码位于内核的arch/arm/plat-omap/i2c.c,arch/arm/mach-omap2/board-xxxx.c中。
3.1.1 Platform device的定义
       在文件arch/arm/plat-omap/i2c.c中,Platform device定义如下:

1.#define OMAP_I2C_SIZE       0x3f  
2.#define OMAP1_I2C_BASE      0xfffb3800  

3.#define OMAP2_I2C_BASE1     0x48070000  

4.#define OMAP2_I2C_BASE2     0x48072000  

5.#define OMAP2_I2C_BASE3     0x48060000  

6.static const char name[] = "i2c_omap";  

7. 
8.#define I2C_RESOURCE_BUILDER(base, irq)         \  

9.    {                       \  
10.        .start  = (base),           \  
11.        .end    = (base) + OMAP_I2C_SIZE,   \  
12.        .flags  = IORESOURCE_MEM,       \  
13.    },                      \  
14.    {                       \  
15.        .start  = (irq),            \  
16.        .flags  = IORESOURCE_IRQ,       \  
17.    },  
18. 
19.static struct resource i2c_resources[][2] = {  

20.    { I2C_RESOURCE_BUILDER(0, 0) },  
21.#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)  

22.    { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE2, INT_24XX_I2C2_IRQ) },  
23.#endif  

24.#if defined(CONFIG_ARCH_OMAP34XX)  

25.    { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE3, INT_34XX_I2C3_IRQ) },  
26.#endif  

27.};  
28. 
29.#define I2C_DEV_BUILDER(bus_id, res, data)      \  

30.    {                       \  
31.        .id = (bus_id),         \  
32.        .name   = name,             \  
33.        .num_resources  = ARRAY_SIZE(res),  \  
34.        .resource   = (res),        \  
35.        .dev        = {         \  
36.            .platform_data  = (data),   \  
37.        },                  \  
38.    }  
39. 
40.static u32 i2c_rate[ARRAY_SIZE(i2c_resources)];  

41.static struct platform_device omap_i2c_devices[] = {  

42.    I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_rate[0]),  
43.#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)  

44.    I2C_DEV_BUILDER(2, i2c_resources[1], &i2c_rate[1]),  
45.#endif  

46.#if defined(CONFIG_ARCH_OMAP34XX)  

47.    I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_rate[2]),  
48.#endif  

49.}; 

     可以看到,这边定义了三个I2C适配器的Platform device,id分别为“1,2,3”,name都为“i2c_omap”,变量resource中定义了适配器的寄存器基地址,irq中断号等。
3.1.2 Platform device的注册
    Platform device的注册是由内核启动后,具体产品的板级初始化完成的。xxxx项目的I2C adapter的Platform device注册过程如下图:

 

图3.1 Platform device注册过程
    函数omap_i2c_add_bus()中,通过函数platform_device_register()注册Platform device到platform bus上,代码如下:
1.static int __init omap_i2c_add_bus(int bus_id)  
2.{  
3.    struct platform_device *pdev;  
2.    struct resource *res;  

3.    resource_size_t base, irq;  

4.    ……  
5.    ……  
6.    return platform_device_register(pdev);  

7.} 

    注册完成后,中断号及寄存器的基地址等信息会在设备树中描述了,此后只需利用platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。

3.2 I2C adapter的Platform driver
    Andrord 2.1中Platform driver的注册的代码位于内核的drivers/i2c/busses/ i2c-omap.c中,该驱动的注册目的是初始化OMAP3630的I2C adapter,提供I2C总线传输的具体实现,并且向I2C core注册I2C adapter。

3.2.1 Platform driver的定义
    在文件drivers/i2c/busses/ i2c-omap.c中,platform driver定义如下:
1.static struct platform_driver omap_i2c_driver = {  
2.    .probe      = omap_i2c_probe,  
3.    .remove     = omap_i2c_remove,  
4.    .driver     = {  
5.        .name   = "i2c_omap",  
2.        .owner  = THIS_MODULE,  
3.    },  
4.}; 

3.2.2 Platform driver的注册
    在文件drivers/i2c/busses/ i2c-omap.c中,platform driver注册如下:
1./* I2C may be needed to bring up other drivers */ 
2.static int __init   .omap_i2c_init_driver(void)  

3.{  
4.    return platform_driver_register(&omap_i2c_driver);  

5.}  
6.subsys_initcall(omap_i2c_init_driver); 

    通过platform_driver_register()函数注册Platform driver omap_i2c_driver时,会扫描platform bus上的所有设备,由于匹配因子是name即"i2c_omap",而之前已经将name为"i2c_omap"的Platform device注册到platform bus上,因此匹配成功,调用函数omap_i2c_probe将设备和驱动绑定起来。
    在drivers/i2c/busses/ i2c-omap.c中会涉及到一个数据结构omap_i2c_dev,这个结构定义了omap3630的I2C控制器,结构如下:
1.struct omap_i2c_dev {  
2.    struct device       *dev;  
2.    void __iomem        *base;      /* virtual */ 

3.    int         irq;  

4.    struct clk      *iclk;      /* Interface clock */ 

5.    struct clk      *fclk;      /* Functional clock */ 

6.    struct completion   cmd_complete;  

7.    struct resource     *ioarea;  

8.    u32         speed;      /* Speed of bus in Khz */ 

9.    u16         cmd_err;  
10.    u8          *buf;  
11.    size_t          buf_len;  
12.    struct i2c_adapter  adapter;  

13.    u8          fifo_size;  /* use as flag and value 

14.                         * fifo_size==0 implies no fifo 
15.                         * if set, should be trsh+1 
16.                         */ 
17.    u8          rev;  
18.    unsigned        b_hw:1;     /* bad h/w fixes */ 

19.    unsigned        idle:1;  
20.    u16         iestate;    /* Saved interrupt register */ 

21.    u16         pscstate;  
22.    u16         scllstate;  
23.    u16         sclhstate;  
24.    u16         bufstate;  
25.    u16         syscstate;  
26.    u16         westate;  
27.}; 
Base对应I2C控制器寄存器的虚拟地址。
Irq对应I2C控制器的中断号。
Buf对应上层传下来的需要发送数据或者I2C控制接收到数据的缓存空间,buf_len是其大小。
Adapter对应I2C控制器的适配器结构。
U16类型的各个state变量是用于对应I2C控制器的寄存器的值。
    函数omap_i2c_probe的执行流程如下图: 

 

图3.2 omap_i2c_probe的执行流程

 


    函数omap_i2c_probe的简要代码如下:
1.static int __init  
2.omap_i2c_probe(struct platform_device *pdev)  
2.{  
3.    struct omap_i2c_dev *dev;  

4.    struct i2c_adapter  *adap;  

5.    struct resource     *mem, *irq, *ioarea;  

6.    irq_handler_t isr;  
7.    ……  
8. 
9.    /* NOTE: driver uses the static register mapping */ 

10.    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
11.    ……  
12.    irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
13.    ……  
14.    dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);  

15.    ……  
16.    dev->dev = &pdev->dev;  
17.    dev->irq = irq->start;  
18.    dev->base = ioremap(mem->start, mem->end - mem->start + 1);  

19.    ……  
20.    /* reset ASAP, clearing any IRQs */ 

21.    omap_i2c_init(dev);  
22. 
23.    isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;  
24.    r = request_irq(dev->irq, isr, 0, pdev->name, dev);  
25.    ……  
26.    adap = &dev->adapter;  
27.    i2c_set_adapdata(adap, dev);  
28.    adap->owner = THIS_MODULE;  
29.    adap->class = I2C_CLASS_HWMON;  

30.    strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));  

31.    adap->algo = &omap_i2c_algo;  
32.    adap->dev.parent = &pdev->dev;  
33.    /* i2c device drivers may be active on return from add_adapter() */ 

34.    adap->nr = pdev->id;  
35.    r = i2c_add_numbered_adapter(adap);  
36.    ……  
37.    return 0;  

38.    ……  
39.} 

     这里定义了I2C adapter的中断处理函数omap_i2c_isr(),该函数对I2C控制器的中断事件进行响应,主要实现了对I2C数据收发中断事件的处理。
     这边还涉及到了一个i2c_algorithm结构的变量omap_i2c_algo,该变量的定义如下:
1.static const struct i2c_algorithm omap_i2c_algo = {  
2.    .master_xfer    = omap_i2c_xfer,  
3.    .functionality  = omap_i2c_func,  
4.}; 

 omap_i2c_xfer接口函数实现了底层I2C数据传输的方法。

 omap_i2c_probe函数最后使用了  i2c_add_numbered_adapter()将adapter注册到i2c-core层,adapter的总线号保存在平台设备数组 omap_i2c_devices中,见3.1.1节,由于该数组中有三个成员,即三条I2C总线,所以这里会建立三个I2C adapter,总线号分别为1,2,3。

 

4 OMAP3630 I2C device驱动
    在Linux内核中,I2C device驱动位于drivers/i2c/chips目录下,可以看到该目录下有很多相关的device驱动,这里以xxxx项目的mma7455为例介绍device驱动的注册过程,对应的device驱动程序为mma7455.c。

    既然有device驱动,那么必定有相应的device,I2C的device是什么呢?其实就是我们在1.3节中提到的i2c_client,所以在device驱动注册之前先来了解下i2c_client的注册过程。
4.1 Mma7455 device注册
    Mma7455 device即i2c_client的创建以及注册分为两步。
4.1.1 将mma7455设备信息加入到设备链表
    在板级初始化时将I2C device的名称,地址和相关的信息加入到链表__i2c_board_list中,该链表记录了具体开发板上的I2C设备信息。
    在board-xxxx.c中,定义了mma7455的设备信息定义如下:
1.static struct i2c_board_info __initdata xxxx_i2c_bus3_info[] = {  
2.……  
3.#ifdef CONFIG_SENSORS_MMA7455  
2.        {  
3.        I2C_BOARD_INFO("mma7455", 0x1D),  

4.        .platform_data = &xxxx_mma7455_platform_data,  
5.        },  
6.#endif   7.}; 

    Mma7455加入到设备链表__i2c_board_list的流程图如下图:

 

图4.1 mma7455加入到I2C设备链表的过程

 

    i2c_register_board_info()函数的定义如下:

1.int __init i2c_register_board_info(int busnum,  
2.    struct i2c_board_info const *info, unsigned len)  

3.{  
4.    ……  
5.    for (status = 0; len; len--, info++) {  

6.        struct i2c_devinfo  *devinfo;  

7.        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);   8.        ……  
9.        devinfo->busnum = busnum;  
10.        devinfo->board_info = *info;  
11.        list_add_tail(&devinfo->list, &__i2c_board_list);  
12.    }  
13.    ……  
14.} 

4.1.2 创建并注册i2c_client
    i2c_client的创建和注册在I2C adapter驱动注册过程中完成,I2C adapter驱动的注册可以参考3.2.2节,i2c_add_numbered_adapter()函数在注册I2C adapter驱动的同时会扫描4.1.1中提到的I2C设备链表__i2c_board_list,如果该总线上有对应的I2C设备,则创建相应的i2c_client,并将其注册到I2C core中。流程图如下所示:

 

图4.2创建并注册i2c_client

 

    相应的代码位于i2c-core.c如下:

1.static void i2c_scan_static_board_info(struct i2c_adapter *adapter)  
2.{  
3.    ……  
4.    list_for_each_entry(devinfo, &__i2c_board_list, list) {  
5.        if (devinfo->busnum == adapter->nr  

6.                && !i2c_new_device(adapter,  
7.                        &devinfo->board_info))  
8.            ……  
9.    }  
10.    ……  
11.} 

    在i2c_scan_static_board_info()函数中遍历I2C设备链表__i2c_board_list,设备的总线号和adapter的总线号相等,则使用函数i2c_new_device()创建该设备。

1.struct i2c_client *  
2.i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)   3.{  
4.    ……  
5.    client = kzalloc(sizeof *client, GFP_KERNEL);  

6.    if (!client)  

7.        return NULL;  

8.    client->adapter = adap;  
9.    client->dev.platform_data = info->platform_data;  
10.    if (info->archdata)  

11.      client->dev.archdata = *info->archdata;  
12.    client->flags = info->flags;  
13.    client->addr = info->addr;  
14.    client->irq = info->irq;  
15.    strlcpy(client->name, info->type, sizeof(client->name));  

16.    ……  
17.    status = i2c_attach_client(client);  
18.    ……  
19.} 

     在函数i2c_new_device()中创建一个i2c_client,初始化该结构体的adapter,addr,name等变量,这里的client->name被初始化为info->type,在4.1.1中,info->type初始化为“mma7455”, client->name后面会用于I2C device和I2C driver匹配时使用,最后调用i2c_attach_client()将该client注册到I2C core。

1.int i2c_attach_client(struct i2c_client *client)  
2.{  
3.    struct i2c_adapter *adapter = client->adapter;   4.    ……  
5.    client->dev.parent = &client->adapter->dev;  
6.    client->dev.bus = &i2c_bus_type;  
7. 
8.    ……  
9.    res = device_register(&client->dev);  
10.    ……  
11.} 

    函数i2c_attach_client()进一步初始化i2c_client结构体,将该设备的总线初始化为i2c_bus_type,说明该设备被放在I2C总线上,用于后面跟I2C driver匹配时使用,最后使用device_register(&client->dev)注册该i2c_client设备。
4.2 Mma7455 device驱动注册
    在mma7455.c中,定义了mma7455的device驱动,代码如下:

1.static struct i2c_driver mma7455_driver = {  
2.    .driver     = {  
3.                .name = "mma7455",  

4.            },  
5.    .class      = I2C_CLASS_HWMON,  

6.    .probe      = mma7455_probe,  
7.    .remove     = mma7455_remove,  
8.    .id_table   = mma7455_id,  
9.    ……  
10.}; 

    注册的简要示意图如下:

 
图4.3 device驱动的注册

    相应的代码位于mma7455.c和i2c-core.c。

1.static int __init init_mma7455(void)  
2.{  
3.    ……  
4.    res = i2c_add_driver(&mma7455_driver);  
5.    ……  
6.    return (res);  

7.} 

    在模块加载的时候首先调用init_mma7455(),然后init_mma7455()调用函数i2c_add_driver()注册mma7455_driver结构体。

1.int i2c_register_driver(struct module *owner, struct i2c_driver *driver)  
2.{  
3.    …….  
4.    /* add the driver to the list of i2c drivers in the driver core */ 

5.    driver->driver.owner = owner;  
6.    driver->driver.bus = &i2c_bus_type;  
7. 
8.    ……  
9.    res = driver_register(&driver->driver);  
10.    if (res)  

11.        return res;  

12. 
13.    ……  
14.} 

    函数i2c_register_driver()初始化该驱动的总线为i2c_bus_type,然后使用函数driver_register(&driver->driver)注册该驱动,因此内核会在I2C总线上遍历所有I2C设备,由于该mma7455 device驱动的匹配因子name变量为“mma7455”,因此正好和在4.1.2里创建的name也为“mma7455”的i2c client匹配。因此总线的probe函数将会被调用,I2C总线的probe函数为i2c_device_probe(),具体代码如下:

1.static int i2c_device_probe(struct device *dev)  
2.{  
3.    struct i2c_client   *client = to_i2c_client(dev);  

4.    struct i2c_driver   *driver = to_i2c_driver(dev->driver);  

5.    int status;   6. 
7.    if (!driver->probe || !driver->id_table)  

8.        return -ENODEV;  

9.    client->driver = driver;  
10.    …….  
11.    status = driver->probe(client, i2c_match_id(driver->id_table, client));  
12.    ……  
13.    return status;   14.} 

    在i2c_device_probe()函数中,语句client->driver = driver将I2C device和I2C driver绑定,然后直接调用具体设备的probe函数,这里即mma7455的probe函数mma7455_probe()。
    在mma7455_probe()函数会完成一些具体I2C设备相关的初始化等操作,这边就不再详述。


 

5 用户空间的支持
    图1.1Linux I2C的架构图中的i2c-dev部份是一个通用的I2C设备的驱动程序,通过一个带有操作集file_operations的标准字符设备驱动为用户空间提供了访问接口,使用户空间可以通过I2C core,进而访问I2C adapter。
 

5.1 I2c-dev的注册
    该部分的源代码位于drivers/i2c/i2c-dev.c,首先定义了I2C device驱动i2cdev_driver:

1.static struct i2c_driver i2cdev_driver = {  
2.    .driver = {  
3.        .name   = "dev_driver",  

4.    },  
5.    .attach_adapter = i2cdev_attach_adapter,  
6.    .detach_adapter = i2cdev_detach_adapter,  
7.}; 

    i2cdev_driver注册代码如下:

1.static int __init i2c_dev_init(void)  
2.{  
3.    ……  
4.    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);   5.    ……  
6.    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");   7.    ……  
8.    res = i2c_add_driver(&i2cdev_driver);  
9.    ……  
10.} 

    首先注册了一个主设备号为I2C_MAJOR,操作集为i2cdev_fops,名字为“i2c”的字符设备。在文件drivers/i2c/i2c-dev.h中,I2C_MAJOR被定义为89。在i2c-dev.c中i2cdev_fops的定义如下:

1.static const struct file_operations i2cdev_fops = {  
2.    .owner      = THIS_MODULE,  
3.    .llseek     = no_llseek,  
4.    .read       = i2cdev_read,  
5.    .write      = i2cdev_write,  
6.    .unlocked_ioctl = i2cdev_ioctl,  
7.    .open       = i2cdev_open,  
8.    .release    = i2cdev_release,  
9.}; 
 
    该操作集是用户空间访问该字符设备的接口。
   然后调用函数i2c_add_driver(&i2cdev_driver)将i2cdev_driver驱动注册到i2c core中,i2cdev_driver驱动注册的流程图如下:

 

图5.1 i2cdev_driver注册过程


 

注册i2c_driver时,会将驱动和adapter绑定起来,然后将调用i2c_driver 的attach_adapter 方法,即i2cdev_attach_adapter()函数,建立dev设备节点,每个adapter都会对应一个dev设备节点,并维护了一个i2c_dev链表保存设备节点和adapter的关系。
    i2cdev_attach_adapter()函数的代码如下:
 
 
1.static int i2cdev_attach_adapter(struct i2c_adapter *adap)  
2.{  
3.    ……  
4.    /* register this i2c device with the driver core */  5.    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,  
6.                     MKDEV(I2C_MAJOR, adap->nr), NULL,  
7.                     "i2c-%d", adap->nr);   8.    ……  
9.    res = device_create_file(i2c_dev->dev, &dev_attr_name);  
10.    ……  
11.} 

    以I2C_MAJOR和adap->nr为主从设备号创建并注册设备节点,如果系统有udev或者是hotplug,那么就会在/dev下自动创建相关的设备节点了。
5.2 I2c-dev的打开
    I2c-dev的open函数如下:

1.static int i2cdev_open(struct inode *inode, struct file *file)  
2.{  
3.    ……  
4.    i2c_dev = i2c_dev_get_by_minor(minor);  
5.    if (!i2c_dev) {  

6.        ret = -ENODEV;  
7.        goto out;  

8.    }  
9. 
10.    adap = i2c_get_adapter(i2c_dev->adap->nr);  
11.    ……  
12.    client = kzalloc(sizeof(*client), GFP_KERNEL);  

13.    if (!client) {  

14.        i2c_put_adapter(adap);  
15.        ret = -ENOMEM;  
16.        goto out;   17.    }  
18.    snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);  

19.    client->driver = &i2cdev_driver;  
20.    client->adapter = adap;  
21.    file->private_data = client;  
22.    ……  
23.} 

    Open操作是用户空间程序和内核驱动交换的第一步,最终返回给用户空间的就是struct file结构体。对于I2C 驱动来说,用户空间所获得的就是client这个关键信息,在其中可以找到所有有关的信息如client所在的adapter及i2c_driver。
    用open函数将i2c-dev设备打开以后,就可以通过ioctl函数的各种命令来设定要访问从设备的地址,I2C设备读写等操作,也可以通过 read和write函数完成对I2C设备的读写。
    对I2C设备的具体操作在这里不再具体阐述,可以参看i2c-dev.c源代码。


 

6 I2C数据收发的框架
    I2C架构的读写支持两种协议类型,I2C协议与SMBUS协议。
    I2C协议和SMBUS协议不完成等同,SMBUS是I2C的子集,SMBUS由I2C衍生而来。SMBUS总线上传输的数据一定是I2C的格式的,但是SMBUS上传输的数据不一定能满足具体某个I2C从设备的通信要求。

6.1 SMBUS协议的数据收发
    如果控制器不支持SMBUS协议,框架层可以用i2c_transfer模拟SMBUS协议的实现,系统默认的I2C传输函数一般都是基于I2C模拟的SMBUS方法传输的,如i2c_smbus_write_byte_data(),i2c_smbus_read_byte_data()等。
    在设备mma7455的驱动程序中,使用了SMBUS协议,而OMAP3630控制器使用的是I2C协议,因此在mma7455的驱动程序中就用到了基于I2C模拟的SMBUS方法。
    下面以函数i2c_smbus_write_byte_data()为例来说明SMBUS协议下的数据发送的过程。
    在i2c-core.c中,函数i2c_smbus_write_byte_data()的定义如下:
1./** 
2. * i2c_smbus_write_byte_data - SMBus "write byte" protocol 
3. * @client: Handle to slave device 
4. * @command: Byte interpreted by slave 
5. * @value: Byte being written 
6. * 
7. * This executes the SMBus "write byte" protocol, returning negative errno 
8. * else zero on success. 
9. */ 
10.s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)  

11.{  
12.    union i2c_smbus_data data;  

13.    data.byte = value;  
14.    return i2c_smbus_xfer(client->adapter,client->addr,client->flags,  

15.                          I2C_SMBUS_WRITE,command,  
16.                          I2C_SMBUS_BYTE_DATA,&data);  
17.} 

    函数i2c_smbus_write_byte_data()的调用流程图如下:

 


图6.1 SMBUS协议下的数据发送过程

 

    从图6.1可以看到,走哪条分支取决于I2C控制器的i2c_algorithm算法,当定义了方法smbus_xfer,则直接调用该方法,如果没有则通过先调用i2c_smbus_xfer_emulated(),进而通过i2c_transfer()最终调用I2C协议下的master_xfer方法,所以我们说SMBUS总线上传输的数据一定是I2C的格式的。
6.2 I2C协议的数据收发
    I2C协议下的数据收发函数就是常用的I2C传输函数:i2c_master_send()和i2c_master_recv()。
    下面以函数i2c_master_send ()为例来说明I2C协议下的数据发送的过程。
    在i2c-core.c中,函数i2c_master_send()定义如下:

 
1./** 
2. * i2c_master_send - issue a single I2C message in master transmit mode 
3. * @client: Handle to slave device 
4. * @buf: Data that will be written to the slave 
5. * @count: How many bytes to write 
6. * 
7. * Returns negative errno, or else the number of bytes written. 
8. */ 
9.int i2c_master_send(struct i2c_client *client,const char *buf ,int count)  

10.{  
11.    int ret;  

12.    struct i2c_adapter *adap=client->adapter;  

13.    struct i2c_msg msg;  

14. 
15.    msg.addr = client->addr;  
16.    msg.flags = client->flags & I2C_M_TEN;  
17.    msg.len = count;  
18.    msg.buf = (char *)buf;  

19.    ret = i2c_transfer(adap, &msg, 1);  
20. 
21.    /* If everything went ok (i.e. 1 msg transmitted), return #bytes 

22.       transmitted, else error code. */ 
23.    return (ret == 1) ? count : ret;   24.} 

    从源代码中可以看出,函数i2c_master_send()先构造i2c_msg结构体,然后直接调用函数i2c_transfer。简单的示意图如下:


图6-2 I2C协议下的数据发送过程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值