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.  */  
  7. struct i2c_algorithm {  
  8.     /* If an adapter algorithm can't do I2C-level access, set master_xfer 
  9.        to NULL. If an adapter algorithm can do SMBus access, set 
  10.        smbus_xfer. If set to NULL, the SMBus protocol is simulated 
  11.        using common I2C messages */  
  12.     /* master_xfer should return the number of messages successfully 
  13.        processed, or a negative value on error */  
  14.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,  
  15.                int num);  
  16.     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,  
  17.                unsigned short flags, char read_write,  
  18.                u8 command, int size, union i2c_smbus_data *data);  
  19.     /* To determine what the adapter supports */  
  20.     u32 (*functionality) (struct i2c_adapter *);  
  21. };  

    主要就是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 {  
  6.     struct module *owner;  
  7.     unsigned int id;  
  8.     unsigned int class;       /* classes to allow probing for */  
  9.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */  
  10.     void *algo_data;  
  11.     /* data fields that are valid for all devices   */  
  12.     struct rt_mutex bus_lock;  
  13.     int timeout;            /* in jiffies */  
  14.     int retries;  
  15.     struct device dev;      /* the adapter device */  
  16.     int nr;  
  17.     char name[48];  
  18.     struct completion dev_released;  
  19.     struct list_head userspace_clients;  
  20. };  

    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    */  
  22.                     /* addresses are stored in the  */  
  23.                     /* _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;  
  4.     struct resource *res;  
  5.     resource_size_t base, irq;  
  6.     ……  
  7.     ……  
  8.     return platform_device_register(pdev);  
  9. }  

    註冊完成後,中斷號及寄存器的基地址等信息會在設備樹中描述了,此後只需利用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",  
  6.         .owner  = THIS_MODULE,  
  7.     },  
  8. };  

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  
  3. omap_i2c_init_driver(void)  
  4. {  
  5.     return platform_driver_register(&omap_i2c_driver);  
  6. }  
  7. 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;  
  3.     void __iomem        *base;      /* virtual */  
  4.     int         irq;  
  5.     struct clk      *iclk;      /* Interface clock */  
  6.     struct clk      *fclk;      /* Functional clock */  
  7.     struct completion   cmd_complete;  
  8.     struct resource     *ioarea;  
  9.     u32         speed;      /* Speed of bus in Khz */  
  10.     u16         cmd_err;  
  11.     u8          *buf;  
  12.     size_t          buf_len;  
  13.     struct i2c_adapter  adapter;  
  14.     u8          fifo_size;  /* use as flag and value 
  15.                          * fifo_size==0 implies no fifo 
  16.                          * if set, should be trsh+1 
  17.                          */  
  18.     u8          rev;  
  19.     unsigned        b_hw:1;     /* bad h/w fixes */  
  20.     unsigned        idle:1;  
  21.     u16         iestate;    /* Saved interrupt register */  
  22.     u16         pscstate;  
  23.     u16         scllstate;  
  24.     u16         sclhstate;  
  25.     u16         bufstate;  
  26.     u16         syscstate;  
  27.     u16         westate;  
  28. };  

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)  
  3. {  
  4.     struct omap_i2c_dev *dev;  
  5.     struct i2c_adapter  *adap;  
  6.     struct resource     *mem, *irq, *ioarea;  
  7.     irq_handler_t isr;  
  8.     ……  
  9.   
  10.     /* NOTE: driver uses the static register mapping */  
  11.     mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  12.     ……  
  13.     irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  14.     ……  
  15.     dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);  
  16.     ……  
  17.     dev->dev = &pdev->dev;  
  18.     dev->irq = irq->start;  
  19.     dev->base = ioremap(mem->start, mem->end - mem->start + 1);  
  20.     ……  
  21.     /* reset ASAP, clearing any IRQs */  
  22.     omap_i2c_init(dev);  
  23.   
  24.     isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;  
  25.     r = request_irq(dev->irq, isr, 0, pdev->name, dev);  
  26.     ……  
  27.     adap = &dev->adapter;  
  28.     i2c_set_adapdata(adap, dev);  
  29.     adap->owner = THIS_MODULE;  
  30.     adap->class = I2C_CLASS_HWMON;  
  31.     strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));  
  32.     adap->algo = &omap_i2c_algo;  
  33.     adap->dev.parent = &pdev->dev;  
  34.     /* i2c device drivers may be active on return from add_adapter() */  
  35.     adap->nr = pdev->id;  
  36.     r = i2c_add_numbered_adapter(adap);  
  37.     ……  
  38.     return 0;  
  39.     ……  
  40. }  

     這裏定義了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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章