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總線讀寫方法的實現,相關的數據結構如下:

/*  
 * The following structs are for those who like to implement new bus drivers:  
 * i2c_algorithm is the interface to a class of hardware solutions which can  
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584  
 * to name two of the most common.  
 */  
struct i2c_algorithm {   
    /* If an adapter algorithm can't do I2C-level access, set master_xfer  
       to NULL. If an adapter algorithm can do SMBus access, set  
       smbus_xfer. If set to NULL, the SMBus protocol is simulated  
       using common I2C messages */  
    /* 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);   
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,   
               unsigned short flags, char read_write,   
               u8 command, int size, union i2c_smbus_data *data);   
    /* To determine what the adapter supports */  
    u32 (*functionality) (struct i2c_adapter *);   
}; 

    主要就是master_xfer方法,其和具體的總線控制器相關,不同的CPU在實現上會有差異。

/*  
 * i2c_adapter is the structure used to identify a physical i2c bus along  
 * with the access algorithms necessary to access it.  
 */  
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;   
    /* data fields that are valid for all devices   */  
    struct rt_mutex bus_lock;   
    int timeout;            /* in jiffies */  
    int retries;   
    struct device dev;      /* the adapter device */  
    int nr;   
    char name[48];   
    struct completion dev_released;   
    struct list_head userspace_clients;   
}; 

    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設備驅動。相關的數據結構如下: 
 
/**  
 * struct i2c_driver - represent an I2C device driver  
 * @class: What kind of i2c device we instantiate (for detect)  
 * @attach_adapter: Callback for bus addition (for legacy drivers)  
 * @detach_adapter: Callback for bus removal (for legacy drivers)  
 * @probe: Callback for device binding  
 * @remove: Callback for device unbinding  
 * @shutdown: Callback for device shutdown  
 * @suspend: Callback for device suspend  
 * @resume: Callback for device resume  
 * @command: Callback for bus-wide signaling (optional)  
 * @driver: Device driver model driver  
 * @id_table: List of I2C devices supported by this driver  
 * @detect: Callback for device detection  
 * @address_list: The I2C addresses to probe (for detect)  
 * @clients: List of detected clients we created (for i2c-core use only)  
 *  
 * The driver.owner field should be set to the module owner of this driver.  
 * The driver.name field should be set to the name of this driver.  
 *  
 * For automatic device detection, both @detect and @address_data must  
 * be defined. @class should also be set, otherwise only devices forced  
 * with module parameters will be created. The detect function must  
 * fill at least the name field of the i2c_board_info structure it is  
 * handed upon successful detection, and possibly also the flags field.  
 *  
 * If @detect is missing, the driver will still work fine for enumerated  
 * devices. Detected devices simply won't be supported. This is expected  
 * for the many I2C/SMBus devices which can't be detected reliably, and  
 * the ones which can always be enumerated in practice.  
 *  
 * The i2c_client structure which is handed to the @detect callback is  
 * not a real i2c_client. It is initialized just enough so that you can  
 * call i2c_smbus_read_byte_data and friends on it. Don't do anything  
 * else with it. In particular, calling dev_dbg and friends on it is  
 * not allowed.  
 */  
struct i2c_driver {   
    unsigned int class;   
    /* Notifies the driver that a new bus has appeared or is about to be  
     * removed. You should avoid using this if you can, it will probably  
     * be removed in a near future.  
     */  
    int (*attach_adapter)(struct i2c_adapter *);   
    int (*detach_adapter)(struct i2c_adapter *);   
    /* Standard driver model interfaces */  
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);   
    int (*remove)(struct i2c_client *);   
    /* driver model interfaces that don't relate to enumeration  */  
    void (*shutdown)(struct i2c_client *);   
    int (*suspend)(struct i2c_client *, pm_message_t mesg);   
    int (*resume)(struct i2c_client *);   
    /* Alert callback, for example for the SMBus alert protocol.  
     * The format and meaning of the data value depends on the protocol.  
     * For the SMBus alert protocol, there is a single bit of data passed  
     * as the alert response's low bit ("event flag").  
     */  
    void (*alert)(struct i2c_client *, unsigned int data);   
    /* a ioctl like command that can be used to perform specific functions  
     * with the device.  
     */  
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);   
    struct device_driver driver;   
    const struct i2c_device_id *id_table;   
    /* Device detection callback for automatic device creation */  
    int (*detect)(struct i2c_client *, struct i2c_board_info *);   
    const unsigned short *address_list;   
    struct list_head clients;   
}; 

    Driver是爲device服務的,i2c_driver註冊時會掃描i2c bus上的設備,進行驅動和設備的綁定。主要有兩種接口attach_adapter和probe,二者分別針對舊的和新式的驅動。 

 
/**  
 * struct i2c_client - represent an I2C slave device  
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;  
 *  I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking  
 * @addr: Address used on the I2C bus connected to the parent adapter.  
 * @name: Indicates the type of the device, usually a chip name that's  
 *  generic enough to hide second-sourcing and compatible revisions.  
 * @adapter: manages the bus segment hosting this I2C device  
 * @driver: device's driver, hence pointer to access routines  
 * @dev: Driver model device node for the slave.  
 * @irq: indicates the IRQ generated by this device (if any)  
 * @detected: member of an i2c_driver.clients list or i2c-core's  
 *  userspace_devices list  
 *  
 * An i2c_client identifies a single device (i.e. chip) connected to an  
 * i2c bus. The behaviour exposed to Linux is defined by the driver  
 * managing the device.  
 */  
struct i2c_client {   
    unsigned short flags;       /* div., see below      */  
    unsigned short addr;        /* chip address - NOTE: 7bit    */  
                    /* addresses are stored in the  */  
                    /* _LOWER_ 7 bits       */  
    char name[I2C_NAME_SIZE];   
    struct i2c_adapter *adapter;    /* the adapter we sit on    */  
    struct i2c_driver *driver;  /* and our access routines  */  
    struct device dev;      /* the device structure     */  
    int irq;            /* irq issued by device     */  
    struct list_head detected;   
}; 

    通常來說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定義如下:

#define OMAP_I2C_SIZE       0x3f   
#define OMAP1_I2C_BASE      0xfffb3800   
#define OMAP2_I2C_BASE1     0x48070000   
#define OMAP2_I2C_BASE2     0x48072000   
#define OMAP2_I2C_BASE3     0x48060000   
static const char name[] = "i2c_omap";   
  
#define I2C_RESOURCE_BUILDER(base, irq)         /   
    {                       /   
        .start  = (base),           /   
        .end    = (base) + OMAP_I2C_SIZE,   /   
        .flags  = IORESOURCE_MEM,       /   
    },                      /   
    {                       /   
        .start  = (irq),            /   
        .flags  = IORESOURCE_IRQ,       /   
    },   
  
static struct resource i2c_resources[][2] = {   
    { I2C_RESOURCE_BUILDER(0, 0) },   
#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)   
    { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE2, INT_24XX_I2C2_IRQ) },   
#endif   
#if defined(CONFIG_ARCH_OMAP34XX)   
    { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE3, INT_34XX_I2C3_IRQ) },   
#endif   
};   
  
#define I2C_DEV_BUILDER(bus_id, res, data)      /   
    {                       /   
        .id = (bus_id),         /   
        .name   = name,             /   
        .num_resources  = ARRAY_SIZE(res),  /   
        .resource   = (res),        /   
        .dev        = {         /   
            .platform_data  = (data),   /   
        },                  /   
    }   
  
static u32 i2c_rate[ARRAY_SIZE(i2c_resources)];   
static struct platform_device omap_i2c_devices[] = {   
    I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_rate[0]),   
#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)   
    I2C_DEV_BUILDER(2, i2c_resources[1], &i2c_rate[1]),   
#endif   
#if defined(CONFIG_ARCH_OMAP34XX)   
    I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_rate[2]),   
#endif   
}; 

     可以看到,這邊定義了三個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上,代碼如下:

static int __init omap_i2c_add_bus(int bus_id)   
{   
    struct platform_device *pdev;   
    struct resource *res;   
    resource_size_t base, irq;   
    ……   
    ……   
    return platform_device_register(pdev);   

    註冊完成後,中斷號及寄存器的基地址等信息會在設備樹中描述了,此後只需利用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定義如下:
static struct platform_driver omap_i2c_driver = {   
    .probe      = omap_i2c_probe,   
    .remove     = omap_i2c_remove,   
    .driver     = {   
        .name   = "i2c_omap",   
        .owner  = THIS_MODULE,   
    },   
}; 

 

3.2.2 Platform driver的註冊
    在文件drivers/i2c/busses/ i2c-omap.c中,platform driver註冊如下:

/* I2C may be needed to bring up other drivers */  
static int __init   
omap_i2c_init_driver(void)   
{   
    return platform_driver_register(&omap_i2c_driver);   
}   
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控制器,結構如下:

struct omap_i2c_dev {   
    struct device       *dev;   
    void __iomem        *base;      /* virtual */  
    int         irq;   
    struct clk      *iclk;      /* Interface clock */  
    struct clk      *fclk;      /* Functional clock */  
    struct completion   cmd_complete;   
    struct resource     *ioarea;   
    u32         speed;      /* Speed of bus in Khz */  
    u16         cmd_err;   
    u8          *buf;   
    size_t          buf_len;   
    struct i2c_adapter  adapter;   
    u8          fifo_size;  /* use as flag and value  
                         * fifo_size==0 implies no fifo  
                         * if set, should be trsh+1  
                         */  
    u8          rev;   
    unsigned        b_hw:1;     /* bad h/w fixes */  
    unsigned        idle:1;   
    u16         iestate;    /* Saved interrupt register */  
    u16         pscstate;   
    u16         scllstate;   
    u16         sclhstate;   
    u16         bufstate;   
    u16         syscstate;   
    u16         westate;   
}; 

Base對應I2C控制器寄存器的虛擬地址。
Irq對應I2C控制器的中斷號。
Buf對應上層傳下來的需要發送數據或者I2C控制接收到數據的緩存空間,buf_len是其大小。
Adapter對應I2C控制器的適配器結構。
U16類型的各個state變量是用於對應I2C控制器的寄存器的值。
    函數omap_i2c_probe的執行流程如下圖: 

 


圖3.2 omap_i2c_probe的執行流程

 


    函數omap_i2c_probe的簡要代碼如下:

static int __init   
omap_i2c_probe(struct platform_device *pdev)   
{   
    struct omap_i2c_dev *dev;   
    struct i2c_adapter  *adap;   
    struct resource     *mem, *irq, *ioarea;   
    irq_handler_t isr;   
    ……   
  
    /* NOTE: driver uses the static register mapping */  
    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);   
    ……   
    irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);   
    ……   
    dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);   
    ……   
    dev->dev = &pdev->dev;   
    dev->irq = irq->start;   
    dev->base = ioremap(mem->start, mem->end - mem->start + 1);   
    ……   
    /* reset ASAP, clearing any IRQs */  
    omap_i2c_init(dev);   
  
    isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;   
    r = request_irq(dev->irq, isr, 0, pdev->name, dev);   
    ……   
    adap = &dev->adapter;   
    i2c_set_adapdata(adap, dev);   
    adap->owner = THIS_MODULE;   
    adap->class = I2C_CLASS_HWMON;   
    strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));   
    adap->algo = &omap_i2c_algo;   
    adap->dev.parent = &pdev->dev;   
    /* i2c device drivers may be active on return from add_adapter() */  
    adap->nr = pdev->id;   
    r = i2c_add_numbered_adapter(adap);   
    ……   
    return 0;   
    ……   

     這裏定義了I2C adapter的中斷處理函數omap_i2c_isr(),該函數對I2C控制器的中斷事件進行響應,主要實現了對I2C數據收發中斷事件的處理。
     這邊還涉及到了一個i2c_algorithm結構的變量omap_i2c_algo,該變量的定義如下:

static const struct i2c_algorithm omap_i2c_algo = {   
    .master_xfer    = omap_i2c_xfer,   
    .functionality  = omap_i2c_func,   
}; 

 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的設備信息定義如下:

static struct i2c_board_info __initdata xxxx_i2c_bus3_info[] = {   
……   
#ifdef CONFIG_SENSORS_MMA7455   
        {   
        I2C_BOARD_INFO("mma7455", 0x1D),   
        .platform_data = &xxxx_mma7455_platform_data,   
        },   
#endif   
}; 

    Mma7455加入到設備鏈表__i2c_board_list的流程圖如下圖:

圖4.1 mma7455加入到I2C設備鏈表的過程

 

    i2c_register_board_info()函數的定義如下:

int __init i2c_register_board_info(int busnum,   
    struct i2c_board_info const *info, unsigned len)   
{   
    ……   
    for (status = 0; len; len--, info++) {   
        struct i2c_devinfo  *devinfo;   
        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);   
        ……   
        devinfo->busnum = busnum;   
        devinfo->board_info = *info;   
        list_add_tail(&devinfo->list, &__i2c_board_list);   
    }   
    ……   

 

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如下:

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)   
{   
    ……   
    list_for_each_entry(devinfo, &__i2c_board_list, list) {   
        if (devinfo->busnum == adapter->nr   
                && !i2c_new_device(adapter,   
                        &devinfo->board_info))   
            ……   
    }   
    ……   

    在i2c_scan_static_board_info()函數中遍歷I2C設備鏈表__i2c_board_list,設備的總線號和adapter的總線號相等,則使用函數i2c_new_device()創建該設備。

struct i2c_client *   
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)   
{   
    ……   
    client = kzalloc(sizeof *client, GFP_KERNEL);   
    if (!client)   
        return NULL;   
    client->adapter = adap;   
    client->dev.platform_data = info->platform_data;   
    if (info->archdata)   
        client->dev.archdata = *info->archdata;   
    client->flags = info->flags;   
    client->addr = info->addr;   
    client->irq = info->irq;   
    strlcpy(client->name, info->type, sizeof(client->name));   
    ……   
    status = i2c_attach_client(client);   
    ……   

     在函數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。

int i2c_attach_client(struct i2c_client *client)   
{   
    struct i2c_adapter *adapter = client->adapter;   
    ……   
    client->dev.parent = &client->adapter->dev;   
    client->dev.bus = &i2c_bus_type;   
  
    ……   
    res = device_register(&client->dev);   
    ……   

    函數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驅動,代碼如下:

static struct i2c_driver mma7455_driver = {   
    .driver     = {   
                .name = "mma7455",   
            },   
    .class      = I2C_CLASS_HWMON,   
    .probe      = mma7455_probe,   
    .remove     = mma7455_remove,   
    .id_table   = mma7455_id,   
    ……   
}; 

    註冊的簡要示意圖如下:

 
圖4.3 device驅動的註冊

    相應的代碼位於mma7455.c和i2c-core.c。

static int __init init_mma7455(void)   
{   
    ……   
    res = i2c_add_driver(&mma7455_driver);   
    ……   
    return (res);   

    在模塊加載的時候首先調用init_mma7455(),然後init_mma7455()調用函數i2c_add_driver()註冊mma7455_driver結構體。

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)   
{   
    …….   
    /* add the driver to the list of i2c drivers in the driver core */  
    driver->driver.owner = owner;   
    driver->driver.bus = &i2c_bus_type;   
  
    ……   
    res = driver_register(&driver->driver);   
    if (res)   
        return res;   
  
    ……   

    函數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(),具體代碼如下:

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);   
    int status;   
  
    if (!driver->probe || !driver->id_table)   
        return -ENODEV;   
    client->driver = driver;   
    …….   
    status = driver->probe(client, i2c_match_id(driver->id_table, client));   
    ……   
    return status;   

    在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:

static struct i2c_driver i2cdev_driver = {   
    .driver = {   
        .name   = "dev_driver",   
    },   
    .attach_adapter = i2cdev_attach_adapter,   
    .detach_adapter = i2cdev_detach_adapter,   
}; 

    i2cdev_driver註冊代碼如下:

static int __init i2c_dev_init(void)   
{   
    ……   
    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);   
    ……   
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");   
    ……   
    res = i2c_add_driver(&i2cdev_driver);   
    ……   

    首先註冊了一個主設備號爲I2C_MAJOR,操作集爲i2cdev_fops,名字爲“i2c”的字符設備。在文件drivers/i2c/i2c-dev.h中,I2C_MAJOR被定義爲89。在i2c-dev.c中i2cdev_fops的定義如下:

static const struct file_operations i2cdev_fops = {   
    .owner      = THIS_MODULE,   
    .llseek     = no_llseek,   
    .read       = i2cdev_read,   
    .write      = i2cdev_write,   
    .unlocked_ioctl = i2cdev_ioctl,   
    .open       = i2cdev_open,   
    .release    = i2cdev_release,   
}; 
 
    該操作集是用戶空間訪問該字符設備的接口。
   然後調用函數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()函數的代碼如下:
 
 

static int i2cdev_attach_adapter(struct i2c_adapter *adap)   
{   
    ……   
    /* register this i2c device with the driver core */  
    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,   
                     MKDEV(I2C_MAJOR, adap->nr), NULL,   
                     "i2c-%d", adap->nr);   
    ……   
    res = device_create_file(i2c_dev->dev, &dev_attr_name);   
    ……   

    以I2C_MAJOR和adap->nr爲主從設備號創建並註冊設備節點,如果系統有udev或者是hotplug,那麼就會在/dev下自動創建相關的設備節點了。

5.2 I2c-dev的打開
    I2c-dev的open函數如下:

static int i2cdev_open(struct inode *inode, struct file *file)   
{   
    ……   
    i2c_dev = i2c_dev_get_by_minor(minor);   
    if (!i2c_dev) {   
        ret = -ENODEV;   
        goto out;   
    }   
  
    adap = i2c_get_adapter(i2c_dev->adap->nr);   
    ……   
    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;   
    ……   

    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()的定義如下:
/**  
 * i2c_smbus_write_byte_data - SMBus "write byte" protocol  
 * @client: Handle to slave device  
 * @command: Byte interpreted by slave  
 * @value: Byte being written  
 *  
 * This executes the SMBus "write byte" protocol, returning negative errno  
 * else zero on success.  
 */  
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)   
{   
    union i2c_smbus_data data;   
    data.byte = value;   
    return i2c_smbus_xfer(client->adapter,client->addr,client->flags,   
                          I2C_SMBUS_WRITE,command,   
                          I2C_SMBUS_BYTE_DATA,&data);   

    函數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()定義如下:

 
/**  
 * i2c_master_send - issue a single I2C message in master transmit mode  
 * @client: Handle to slave device  
 * @buf: Data that will be written to the slave  
 * @count: How many bytes to write  
 *  
 * Returns negative errno, or else the number of bytes written.  
 */  
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)   
{   
    int ret;   
    struct i2c_adapter *adap=client->adapter;   
    struct i2c_msg msg;   
  
    msg.addr = client->addr;   
    msg.flags = client->flags & I2C_M_TEN;   
    msg.len = count;   
    msg.buf = (char *)buf;   
    ret = i2c_transfer(adap, &msg, 1);   
  
    /* If everything went ok (i.e. 1 msg transmitted), return #bytes  
       transmitted, else error code. */  
    return (ret == 1) ? count : ret;   

    從源代碼中可以看出,函數i2c_master_send()先構造i2c_msg結構體,然後直接調用函數i2c_transfer。簡單的示意圖如下:


圖6-2 I2C協議下的數據發送過程



本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文鏈接:http://www.linuxidc.com/Linux/2011-05/35577.htm

發佈了0 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章