Linux字符設備驅動框架(六):Linux IIC總線驅動框架

/************************************************************************************html

*本文爲我的學習記錄,若有錯誤,歡迎指正。node

*本文參考資料: 
算法

*        https://blog.csdn.net/li_wen01/article/details/51657040
數組

*        https://blog.csdn.net/jscese/article/details/44003393
數據結構

*        https://blog.csdn.net/mcgrady_tracy/article/details/51288138框架

*        http://www.javashuo.com/article/p-yhwqmnkr-m.htmlide

*        https://blog.csdn.net/xie0812/article/details/22984527函數

*        https://blog.csdn.net/zqixiao_09/article/details/50916916學習

************************************************************************************/this

1. I2C簡介

(1)IIC物理總線的構成 

IIC總線是由數據線SDA和時鐘SCL構成的串行總線,可發送和接收數據。在CPU與被控IC之間、IC與IC之間進行雙向傳送,最高傳送速率100kbps。

(2)IIC通訊的特色

同步、串行、電平信號、低速率、近距離。

(3)IIC通訊時序

開始信號:SCL爲高電平時,SDA由高電平向低電平跳變,開始傳送數據;

結束信號:SCL爲高電平時,SDA由低電平向高電平跳變,結束傳送數據;

數據傳輸信號:在開始條件之後,時鐘信號SCL的高電平週期期間,數據線SDA的數據有效,即數據能夠被讀走,開始進行讀操做。在時鐘信號SCL的低電平週期期間,數據線SDA的數據才容許改變。

應答信號:接收數據的IC在接收到8bit數據後,向發送數據的IC發出特定的低電平脈衝,表示已收到數據。CPU向受控單元發出一個信號後,等待受控單元發出一個應答信號,CPU接收到應答信號後,根據實際狀況做出是否繼續傳遞信號的判斷。若未收到應答信號,由判斷爲受控單元出現故障。

 

(4)IIC通訊操做(s5pv210)

2. 相關數據結構

2.1 數據結構

(1)i2c_client

Linux內核使用i2c_client來描述一個掛載在I2C總線上的I2C設備。

struct  i2c_client
{
    unsigned short  flags;            //標誌位
    unsigned short  addr;        //設備的地址,低7位爲芯片地址
    char name[I2C_NAME_SIZE];     //設備的名稱,最大爲20個字節
    struct  i2c_adapter *adapter;   //依附的適配器i2c_adapter,適配器指明所屬的總線
    struct  i2c_driver *driver;    //指向設備對應的驅動程序
    struct device  dev;         //設備結構體
    int irq;                //設備申請的中斷號
    struct list_head  list;       //鏈接到總線上的全部設備
    struct list_head   detected;   //已經被發現的設備鏈表
    struct completion  released;    //是否已經釋放的完成量
};

(2)i2c_driver

Linux內核使用i2c_driver來描述一個IIC設備的驅動程序。每一個i2c_client對應一個i2c_driver。

struct  i2c_driver
{
    int id;                                                  //驅動標識ID
    unsigned int class;                                      //驅動的類型
    int (*attach_adapter)(struct i2c_adapter *);             //當檢測到適配器時調用的函數
    int (*detach_adapter)(struct i2c_adapter*);              //卸載適配器時調用的函數
    int (*detach_client)(struct i2c_client *)   __deprecated;//卸載設備時調用的函數
  
    /*如下是一種新類型驅動須要的函數,這些函數支持IIC設備動態插入和拔出。若是不想支持只實現上面3個。
      要不實現上面3個。要麼實現下面5個。不能同時定義*/
    int  (*probe)(struct i2c_client *,const struct  i2c_device_id *);     //新類型設備探測函數
    int  (*remove)(struct i2c_client *);                                   //新類型設備的移除函數
    void (*shutdown)(struct i2c_client *);                                 //關閉IIC設備
    int  (*suspend)(struct  i2c_client *,pm_messge_t mesg);                //掛起IIC設備
    int  (*resume)(struct  i2c_client *);                                  //恢復IIC設備

    int  (*command)(struct i2c_client *client,unsigned int cmd,void *arg);//使用命令使設備完成特殊的功能。相似ioctl()函數

    struct devcie_driver  driver;                                          //設備驅動結構體
    const struct  i2c_device_id *id_table;                                 //設備ID表
    int (*detect)(struct i2c_client *,int  kind,struct  i2c_board_info *);//自動探測設備的回調函數
 
    const  struct i2c_client_address_data          *address_data;          //設備所在的地址範圍
    struct  list_head    clients;                                          //指向驅動支持的設備
};

(3)i2c_adapter

Linux內核使用i2c_adapter來描述一個IIC總線適配器。IIC總線適配器就是SoC內部的IIC總線控制器,在物理上鍊接若干個IIC設備。IIC總線適配器本質上是一個物理設備,其主要功能是完成IIC總線控制器相關的數據通訊。

struct i2c_adapter
{
    struct module *owner;                         //模塊計數
    unsigned  int id;                             //alogorithm的類型,定義於i2c_id.h中
    unsigned  int  class;                         //容許探測的驅動類型
    const struct i2c_algorithm *algo;             //指向適配器的驅動程序
    void *algo_data;                              //指向適配器的私有數據,根據不一樣的狀況使用方法不一樣
    int (*client_register)(struct  i2c_client *); //設備client註冊時調用
    int (*client_unregister(struct  i2c_client *);//設備client註銷時調用
    u8 level;                                                         
    struct  mutex  bus_lock;                      //對總線進行操做時,將得到總線鎖
    struct  mutex  clist_lock ;                   //鏈表操做的互斥鎖
    int timeout;                                  //超時
    int retries;                                  //重撥次數
    struct device dev;                            //指向適配器的設備結構體
    int  nr ;                                                          
    struct  list_head      clients;               //鏈接總線上的設備的鏈表
    char name[48];                                //適配器名稱
    struct completion     dev_released;           //用於同步的完成量
};

(4)i2c_algorithm

Linux內核使用i2c_algorithm來描述IIC適配器與IIC設備的通訊方法。

struct  i2c_algorithm
{
  /*傳輸函數指針,指向實現IIC總線通訊協議的函數,用來肯定適配器支持那些傳輸類型    */
  int  (*master_xfer)(struct  i2c_adapter *adap,  struct  i2c_msg *msg, int num);    
  /*smbus方式傳輸函數指針,指向實現SMBus總線通訊協議的函數。SMBus和IIC之間能夠經過軟件方式兼容,因此這裏提供了一個函數,可是通常都賦值爲NULL*/ 
   int  (*smbus_xfer)(struct  i2c_adapter *adap, u16  addr, unsigned  short flags, char  read_write, 
          u8 command, int size, union  i2c_smbus_data  *data);    
  /*返回適配器支持的功能*/
   u32  (*functionality)(struct  i2c_adapter *);                  
};

2.2 數據結構之間的聯繫

(1)i2c_driver和i2c_client

i2c_client對應真實的IIC物理設備,每一個IIC設備都須要一個i2c_client來描述;而i2c_driver對應一套驅動方法。i2c_driver與i2c_client的關係是一對多,即一個i2c_driver上能夠支持多個同等類型的i2c_client。

(2)i2c_adapter與i2c_algorithm

i2c_adapter對應一個IIC總線適配器(SoC內部的IIC總線控制器),而i2c_algorithm對應一套通訊方法。一個IIC適配器須要i2c_algorithm中提供的通訊函數來控制適配器上產生特定的訪問週期。缺乏i2c_algorithm的i2c_adapter什麼也作不了,所以i2c_adapter中包含其使用i2c_algorithm的指針。

(3)i2c_adapter和i2c_client

i2c_adapter和i2c_client的關係與IIC硬件體系中適配器和設備的關係一致,即i2c_client依附於i2c_adapter,因爲一個適配器上能夠鏈接多個i2c設備,因此i2c_adapter中包含依附於它的i2c_client的鏈表。

3. Linux I2C驅動框架

Linux內核中的IIC總線不一樣於SoC內部的物理IIC總線 ,內核中的IIC總線是虛擬出來的,目的是管理內核中的IIC從設備及其驅動。

Linux的I2C體系結構分爲3個組成部分:

(1)IIC核心

IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法、IIC通訊方法(algorithm)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。

(2)IIC總線驅動

IIC總線驅動是對IIC硬件體系結構中適配器端(SoC內部的IIC總線控制器)的實現。IIC總線驅動主要包含了IIC適配器數據結構i2c_adapter,IIC適配器的通訊方法數據結構i2c_algorithm和控制I2C適配器產生通訊信號的函數。經由IIC總線驅動的代碼,咱們能夠控制IIC適配器以主控方式產生開始位,中止位,讀寫週期,以及以從設備方式被讀寫,產生ACK等。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

(3)IIC設備驅動

IIC設備驅動是對IIC硬件體系結構中設備端的實現,與掛在I2C總線上的具體的設備通信的驅動。經過I2C總線驅動提供的函數,設備驅動能夠忽略不一樣IIC總線適配器的差別,不考慮其實現細節地與硬件設備通信。這部分代碼通常由驅動工程師完成。

3.1 IIC核心

IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法、IIC通訊方法(algorithm)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。

IIC核心的實現代碼:/kernel/drivers/i2c/i2c-core.c。

3.1.1 IIC bus初始化

Linux內核初始化階段,調用i2c_init() 函數來初始化IIC總線。

static int __init i2c_init(void)
{
    int retval;
 
    retval = bus_register(&i2c_bus_type);      //註冊IIC總線
    if (retval)
        return retval;

  ... ...

    retval = i2c_add_driver(&dummy_driver);   //添加一個空驅動,不知爲什麼要添加這個空驅動
    if (retval)
        goto class_err;
    return 0;
 
  ... ...

    return retval;
}

i2c_init() 函數中調用bus_register()函數註冊IIC總線。IIC總線定義以下:

struct bus_type i2c_bus_type = 
{
    .name     = "i2c",
    .match    = i2c_device_match,//match方法用來進行 device 和driver 的匹配,在向總線註冊設備或是驅動的的時候會調用此方法
    .probe    = i2c_device_probe,//probe方法在完成設備和驅動的配對以後調用執行
    .remove   = i2c_device_remove,
    .shutdown = i2c_device_shutdown,
    .suspend  = i2c_device_suspend,
    .resume   = i2c_device_resume,
};

IIC總線提供的match方法:match方法用來進行 i2c_driver 和 i2c_client 的匹配,在向總線註冊i2c_driver或i2c_client的的時候會調用此方法。匹配的方法是拿id_table 中的每一項與 i2c_client 的name 進行匹配,若是名字相同則匹配成功。其函數定義以下:

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client    *client = i2c_verify_client(dev); 
    struct i2c_driver    *driver;
 
    if (!client)
        return 0; 
 
    driver = to_i2c_driver(drv);
    /* match on an id table if there is one */
    if (driver->id_table)//若是IIC驅動的id_table 存在的話,使用i2c_match_id 進行函數進行匹配。
        return i2c_match_id(driver->id_table, client) != NULL;    
    return 0;
}

 i2c_driver 和 i2c_client匹配成功後,IIC總線提供的probe方法將被調用執行,即執行i2c_device_probe()函數。實質上,最終調用執行的是IIC設備驅動中的probe函數,即i2c_driver->probe。

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;
    int status;
 
    if (!client)
        return 0;
 
    driver = to_i2c_driver(dev->driver);
    if (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
  
  ... ...
 
    status = driver->probe(client, i2c_match_id(driver->id_table, client));//調用IIC設備驅動中的probe函數

  if (status)
        client->driver = NULL;
    return status;
}

3.1.2 IIC核心提供的接口函數

(1)增長/刪除IIC總線適配器

/*增長一個IIC總線適配器*/
int i2c_add_adapter(struct i2c_adapter *adapter);
/*刪除一個IIC總線適配器*/ int i2c_del_adapter(struct i2c_adapter *adap);

(2)增長/刪除IIC從設備驅動

/*增長一個IIC從設備驅動*/
int i2c_add_driver(struct i2c_driver *driver);
/*刪除一個IIC從設備驅動*/ void i2c_del_driver(struct i2c_driver *driver);

(3)IIC數據傳輸

/*
*參數:    struct i2c_adapter *adap:IIC總線適配器
*            struct i2c_msg*msgs:
*            int num:
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num) ;

/*從如下代碼可知,IIC的數據傳輸是調用i2c_adapter->i2c_algorithm->master_xfer完成*/
int i2c_transfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)  
{  
    ... ...
    if (adap->algo->master_xfer)
    {  
         for (ret = 0, try = 0; try <=adap->retries; try++) 
        {  
             ret = adap->algo->master_xfer(adap, msgs,num);  
        }  
    }
    ... ...
} 

IIC總線上的數據傳輸是以字節爲單位的,有讀和寫兩種通訊模式。IIC子系統爲了實現這種通訊方法,提供了i2c_msg結構,對於每個START信號,都對應一個i2c_msg對象,實際操做中咱們會將全部的請求封裝成一個struct i2c_msg[],一次性將全部的請求經過i2c_transfer()發送給匹配到的client的從屬的adapter,由adapter根據相應的algo域以及master_xfer域經過主機驅動來將這些請求發送給硬件上的設備。

struct i2c_msg 
{
    __u16 addr;    //IIC從設備地址
    __u16 flags;  //操做標誌位,I2C_M_RD爲讀(1),寫爲0

#define I2C_M_TEN           0x0010    /* this is a ten bit chip address */
#define I2C_M_RD            0x0001    /* read data, from slave to master */
#define I2C_M_NOSTART       0x4000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400    /* length will be first received byte */

    __u16 len;      //傳輸的數據長度,字節爲單位
    __u8 *buf;      //存放read或write的數據的buffer
};

 

3.2 IIC總線驅動

IIC總線驅動是對IIC硬件體系結構中適配器端(SoC內部的IIC總線控制器)的實現。IIC總線驅動主要包含了IIC適配器數據結構i2c_adapter,IIC適配器的通訊方法數據結構i2c_algorithm和控制I2C適配器產生通訊信號的函數。經由IIC總線驅動的代碼,咱們能夠控制IIC適配器以主控方式產生開始位,中止位,讀寫週期,以及以從設備方式被讀寫,產生ACK等。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

IIC總線驅動的核心代碼:/kernel/drivers/i2c/busses/i2c-s3c2410.c(SAMSUNG 2410平臺)。

i2c-s3c2410.c利用platform總線來實現IIC總線驅動,將IIC適配器的硬件信息掛載在platform device,將IIC總線適配器的驅動程序掛載在platform driver下。不一樣的CPU平臺對應着不一樣的I2C總線驅動。

3.2.1 IIC總線適配器的platform device初始化

platform device的對象是IIC總線適配器,即SoC內部的IIC總線控制器。

構建IIC總線控制器的設備信息。

/*IIC總線控制器的硬件資源*/
static struct resource s3c_i2c_resource[] = 
{
    [0] = {
        .start = S3C_PA_IIC,            //IIC總線控制器寄存器開始地址
        .end   = S3C_PA_IIC + SZ_4K - 1//IIC總線控制器寄存器結束地址
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_IIC,
        .end   = IRQ_IIC,
        .flags = IORESOURCE_IRQ,
    },
};

/*IIC總線控制器的設備信息*/
struct platform_device s3c_device_i2c0 = 
{
    .name          = "s3c2410-i2c"//名稱,與platform driver匹配時使用
    .id            = 1,
    .num_resources = ARRAY_SIZE(s3c_i2c_resource),
    .resource      = s3c_i2c_resource,
};

/*IIC總線控制器的默認平臺數據*/
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = 
{
    .flags        = 0,
    .slave_addr   = 0x10//從設備的設備地址
    .frequency    = 400*1000//IIC的時鐘頻率
    .sda_delay    = S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON,
};

/*設置IIC總線控制器的平臺數據*/
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
    struct s3c2410_platform_i2c *npd;

    if (!pd)
        pd = &default_i2c_data0;

    npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
    if (!npd)
        printk(KERN_ERR "%s: no memory for platform data\n", __func__);
    else if (!npd->cfg_gpio)
        npd->cfg_gpio = s3c_i2c0_cfg_gpio;

    s3c_device_i2c0.dev.platform_data = npd;
}

向內核註冊IIC總線控制器的platform device。

/*開發板的全部設備信息*/
static struct platform_device *smdkc110_devices[] __initdata = 
{
    ... ...
    &s3c_device_i2c0,
    ... ...
}

static void __init smdkc110_machine_init(void)
{
    ... ...
    platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));//向內核註冊開發板的全部設備信息
    s3c_i2c0_set_platdata(NULL);//設置IIC總線控制器的平臺數據,NULL表示使用默認的平臺數據
    ... ...
}

3.2.2  IIC總線適配器的platform driver初始化

 platform driver的對象是IIC總線適配器的驅動程序。分析/kernel/driver/i2c/busees/i2c_s3c2410.c。

註冊IIC總線適配器的驅動程序。

從以下代碼可知,IIC總線適配器的驅動註冊函數i2c_adap_s3c_init()被subsys_initcall修飾,則i2c_adap_s3c_init()函數將在內核初始化階段被調度執行。

/*platform driver支持的設備表*/
static struct platform_device_id s3c24xx_driver_ids[] = 
{
    {
        .name        = "s3c2410-i2c",
        .driver_data    = TYPE_S3C2410,
    }, 
  {
        .name        = "s3c2440-i2c",
        .driver_data    = TYPE_S3C2440,
    }, 
  { },
};

/*使用MODULE_DEVICE_TABLE 宏聲明,s3c24xx_driver_ids 是platform類型的一個設備表*/
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);

/*IIC總線適配器的驅動信息*/
static struct platform_driver s3c24xx_i2c_driver = {
    .probe     = s3c24xx_i2c_probe,
    .remove    = s3c24xx_i2c_remove,
    .id_table  = s3c24xx_driver_ids, //與IIC總線適配器的platform device匹配
    .driver    = 
  {
        .owner = THIS_MODULE,
        .name  = "s3c-i2c",
        .pm    = S3C24XX_DEV_PM_OPS,
    },
};

/*註冊IIC總線適配器的驅動*/
static int __init i2c_adap_s3c_init(void)
{
    return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);

在IIC總線適配器的platform device與platform driver匹配成功以後,platform driver中的probe函數將被調度運行,即s3c24xx_i2c_probe()函數。

s3c24xx_i2c_probe()函數的主要工做是對IIC總線適配器進行硬件初始化,並向內核註冊一個i2c_adapter。

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c;
    struct s3c2410_platform_i2c *pdata;
    struct resource *res;
    int ret;
 
    pdata = pdev->dev.platform_data;//獲取IIC總線適配器的平臺數據
 
    i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
    
  /*填充i2c變量*/
    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner   = THIS_MODULE;
    i2c->adap.algo    = &s3c24xx_i2c_algorithm;//初始化IIC總線適配器的控制算法
    i2c->adap.retries = 2;                     //設置重播次數
    i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
    i2c->tx_setup     = 50;
 
    spin_lock_init(&i2c->lock);
    init_waitqueue_head(&i2c->wait);//初始化一個等待隊列
 
    /* find the clock and enable it */
   /*初始化IIC總線適配器的時鐘*/
    i2c->dev = &pdev->dev;
    i2c->clk = clk_get(&pdev->dev, "i2c");
    dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
    clk_enable(i2c->clk);
 
    /* map the registers */
    //獲取IIC適配器的內存資源信息
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   //申請內存資源
    i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
   //虛擬地址映射
    i2c->regs = ioremap(res->start, resource_size(res));
 
    /* setup info block for the i2c core */
 
    i2c->adap.algo_data = i2c;
    i2c->adap.dev.parent = &pdev->dev;
 
    /* initialise the i2c controller */
    //初始化IIC總線適配器,主要針對相關寄存器進行操做
    ret = s3c24xx_i2c_init(i2c);
 
    /* find the IRQ for this unit (note, this relies on the init call to
     * ensure no current IRQs pending
     */
    //獲取IIC總線適配器的中斷資源信息
    i2c->irq = ret = platform_get_irq(pdev, 0);
   //申請中斷
    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
 
    ret = s3c24xx_i2c_register_cpufreq(i2c);
 
    /* Note, previous versions of the driver used i2c_add_adapter()
     * to add the bus at any number. We now pass the bus number via
     * the platform data, so if unset it will now default to always
     * being bus 0.
     */
 
    i2c->adap.nr = pdata->bus_num;
   
   //向內核註冊一個i2c adapter
    ret = i2c_add_numbered_adapter(&i2c->adap);
 
    ... ...
    return 0;
 
 ... ...
}
static int s3c24xx_i2c_probe(struct platform_device *pdev)

 

3.3 IIC從設備驅動

在咱們實際開發中,I2C 總線驅動通常芯片原廠會提供,咱們開發通常是設計設備驅動。

IIC從設備驅動是對IIC硬件體系結構中設備端的實現,與掛在I2C總線上的具體的設備通信的驅動。經過I2C總線驅動提供的函數,設備驅動能夠忽略不一樣IIC總線適配器的差別,不考慮其實現細節地與硬件設備通信。這部分代碼通常由驅動工程師完成。

IIC從設備驅動掛載在IIC總線下,IIC總線管理着IIC從設備的設備信息(i2c_client)與設備驅動(i2c_driver)。所以,IIC從設備驅動的編寫分爲兩個部分:註冊IIC從設備信息、編寫IIC從設備驅動程序。

Linux內核提供了struct i2c_board_info與i2c_register_board_info()函數,方便驅動工程師構建IIC從設備的設備信息 。

(1)struct i2c_board_info

 Linux內核封裝了一個i2c_board_info結構體變量,描述一個IIC從設備的基本信息。內核使用i2c_board_info來構建i2c_client。

struct i2c_board_info 
{
    char            type[I2C_NAME_SIZE];//設備名稱,對應i2c_client.name
    unsigned short  flags;              //標誌位,對應i2c_client.flags
    unsigned short  addr;               //從設備地址,對應i2c_client.addr
    void           *platform_data;      //平臺數據,對應i2c_client.dev.platform_data
    struct dev_archdata  *archdata;     //對應i2c_client.dev.archdata
#ifdef CONFIG_OF
    struct device_node   *of_node;
#endif
    int   irq;//中斷號,對應i2c_client.irq
};

(2)i2c_register_board_info()函數

 Linux內核維護了一個__i2c_board_list鏈表,用以管理內核中的全部IIC從設備信息。i2c_register_board_info()函數的工做是向__i2c_board_list鏈表添加一條IIC從設備信息。

/*
*  參數:  int busnum:IIC總線編號,表示IIC從設備所掛載的IIC適配器編號
*       struct i2c_board_info const *info:IIC從設備信息
         unsigned len:i2c_board_info數組的大小,即IIC從設備的個數
*/
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
    int status;
     ... ... 
     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);//向鏈表添加一條IIC從設備信息
     }
     ... ...
     return status;
}

 在i2c_adapter註冊的時候,會利用__i2c_board_list鏈表中的IIC從設備信息(i2c_board_info)來構建i2c_client。具體調用關係以下:

static int s3c24xx_i2c_probe()【IIC總線驅動的probe函數】

  --i2c_add_numbered_adapter()【註冊i2c_adapter】

    --i2c_register_adapter()【實質註冊i2c_adapter】

      --i2c_scan_static_board_info()【掃描__i2c_board_list鏈表】

        --i2c_new_device()【構建i2c_client,並向內核添加IIC從設備】

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client    *client;
    int            status;

   //申請client內存空間 
  client = kzalloc(sizeof *client, GFP_KERNEL);
    ... ...
  
  /*****************填充i2c_client*******************/
    client->adapter = adap;                         //與i2c_adapter關聯
    client->dev.platform_data = info->platform_data;//填充平臺數據
    client->flags = info->flags;                    //填充標誌位
    client->addr = info->addr;                      //填充從設備地址
    client->irq = info->irq;                        //填充中斷號
    strlcpy(client->name, info->type, sizeof(client->name));//填充從設備名稱

    ... ...
  /*****************初始化client->dev*****************/
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
  ... ...
    status = device_register(&client->dev);//建立IIC從設備
    
    return client;

  ... ...
}

IIC從設備的驅動程序的實例,詳見驅動程序實例(六):mpu6050(IIC + cdev)

相關文章
相關標籤/搜索