201@實例分析,i2c 設備註冊失敗,懷疑是i2c 地址衝突,如何打印出來衝突設備

一:
mtk6589 平臺碰到 akm09911 指南針sensor i2c註冊失敗,懷疑是i2c 設備地址衝突。
實例分析,i2c 設備註冊失敗,懷疑是i2c 地址衝突,如何打印出來衝突設備

1:


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

     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));

     /* Check for address validity */
     status = i2c_check_client_addr_validity(client);
     if (status) {
          dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
               client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
          goto out_err_silent;
     }

     /* Check for address business */
     status = i2c_check_addr_busy(adap, client->addr);
     if( 0x0c == client->addr )
         printk("pei_add_akm0 %s,%x ,%s\n" ,  client->name ,client->addr ,   __func__   );
//pei_add 這裏打印出來了 正好爲 0x0c 我們的msensor akm09911  這顆sensor而不是其它sensor
         
     if (status)
          {
          printk("pei_add_akm1 %d,%x ,%s\n" ,status,info->addr ,__func__    );//pei_add    這裏打印
          goto out_err;
          }
    
     client->dev.parent = &client->adapter->dev;
     client->dev.bus = &i2c_bus_type;
     client->dev.type = &i2c_client_type;
     client->dev.of_node = info->of_node;
     ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);

     /* For 10-bit clients, add an arbitrary offset to avoid collisions */
     dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
               client->addr | ((client->flags & I2C_CLIENT_TEN)
                         ? 0xa000 : 0));
     status = device_register(&client->dev);
     if (status)
          {
          printk("pei_add_akm2 %d ,%s ,%s\n" ,status,dev_name(&client->dev),__func__);//pei_add
          goto out_err;
          }
    
     dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
          client->name, dev_name(&client->dev));

     return client;

out_err:
     dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
          "(%d)\n", client->name, client->addr, status);
out_err_silent:
     kfree(client);
     return NULL;
}



2:定位從哪裏 出來後,在對應地方加log,打印出來對應的 name 即可
static int __i2c_check_addr_busy(struct device *dev, void *addrp)
{
     struct i2c_client     *client = i2c_verify_client(dev);
     int               addr = *(int *)addrp;

     if (client && client->addr == addr) //pei_add
          {
          printk("pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!!  %s, client->addr=0x%x , %s\n" ,client->name ,client->addr ,__func__);//pei_add
          return -EBUSY;
          }
     return 0;
}


3:log發現:設備 AD5820AF 此 af 設備佔用了 msensor akm09911 的   i2c 導致了衝突問題,很奇怪的就是我們
在 i2c_new_device 中有添加當 i2c 設備地址爲 0x0c 的時候那麼就打印出來 設備名稱,所以 af的註冊方式應該不一樣
沒有走 這個流程。


<4>[    3.762691].(1)[1:swapper/0] pei_add akm09911 0
<4>[    4.018498].(1)[1:swapper/0]pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!!  AD5820AF, client->addr=0xc , __i2c_check_addr_busy
<4>[    4.018507].(1)[1:swapper/0]pei_add_akm0 akm09911,c ,i2c_new_device
<4>[    4.018513].(1)[1:swapper/0]pei_add_akm1 -16,c ,i2c_new_device
<4>[    4.027913].(2)[1:swapper/0]pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!!  kd_camera_hw, client->addr=0x7f , __i2c_check_addr_busy
<4>[    4.027921].(2)[1:swapper/0]pei_add_akm1 -16,7f ,i2c_new_device
<4>[    6.424342].(1)[1:swapper/0]pei_add_akm09911_0  akm09911_local_init
<4>[    6.424355].(1)[1:swapper/0]pei_add_akm09911_1  akm09911_local_init
<4>[    6.424868].(1)[1:swapper/0]pei_add_akm09911_3  akm09911_local_init

4:

     查看 對應 af 確實爲 0x0c 通信地址!!!
     g_pstAD5820AF_I2Cclient->addr = (0x18 >> 1);

5:但是正常情況下我們經常用 假地址去註冊,放置出現 異常的情況,比如衝突。這個情況仍然出現可能是因爲 af 註冊太早了。




二:
mtk6572 android4
kernel-3.10\drivers\misc\mediatek\mach\mt6752\mt_devs.c

#if defined(CONFIG_MTK_I2C)
     //i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
     //i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
     //i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
          for (i = 0; i < ARRAY_SIZE(mt_device_i2c); i++){
               retval = platform_device_register(&mt_device_i2c[i]);
               if (retval != 0){
                    return retval;
               }
          }
#endif


kernel/mediatek/platform/mt6572/kernel/drivers/ldvt/i2c/i2c-mt6573.c
static int mt6573_i2c_probe(struct platform_device *pdev)
{
     int ret, irq;

     struct mt6573_i2c *i2c = NULL;

    

     /* Request IO memory */
     if (!request_mem_region(pdev->resource[0].start,
                                pdev->resource[0].end - pdev->resource[0].start + 1,
                                pdev->name)) {
          return -EBUSY;
     }
    
     if (NULL == (i2c = kzalloc(sizeof(struct mt6573_i2c), GFP_KERNEL)))
          return -ENOMEM;


     /* initialize mt6573_i2c structure */
    irq = pdev->resource[1].start;
    i2c->id = pdev->id;
    i2c->base = pdev->resource[0].start;
    i2c->irqnr = irq;
     i2c->clk    = I2C_CLK_RATE;    
     i2c->adap   = &mt6573_i2c_adaptor[pdev->id];
     i2c->dev    = &mt6573_i2c_adaptor[pdev->id].dev;
     i2c->adap->dev.parent = &pdev->dev;
    
     if(0 == i2c->id)
          i2c->pdmabase = AP_DMA_BASE + 0x0480;
     else
          i2c->pdmabase = AP_DMA_BASE + 0x0500;
     spin_lock_init(&i2c->lock);    
     init_waitqueue_head(&i2c->wait);

     ret = request_irq(irq, mt6573_i2c_irq, 0, DRV_NAME, i2c);

     if (ret){
          dev_err(&pdev->dev, "Can Not request I2C IRQ %d\n", irq);
          goto free;
     }

    mt6573_i2c_init_hw(i2c);

     i2c_set_adapdata(i2c->adap, i2c);

     ret = i2c_add_adapter(i2c->adap);  // adpater 註冊到 i2c core中去,adapter 包含了mtk 寫的adapter的i2c通信方法

     if (ret){
          dev_err(&pdev->dev, "failed to add i2c bus to i2c core\n");
          goto free;
     }

     platform_set_drvdata(pdev, i2c);
    

     return ret;
    
free:
     mt6573_i2c_free(i2c);
     return ret;
}




mtk6797 android8.0
kernel-3.18/drivers/i2c/busses/i2c-mt65xx.c
static int mtk_i2c_probe(struct platform_device *pdev)
{
     const struct of_device_id *of_id;
     int ret = 0;
     struct mtk_i2c *i2c;
     struct clk *clk;
     struct resource *res;
     int irq;

     i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
     if (!i2c)
          return -ENOMEM;

     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     i2c->base = devm_ioremap_resource(&pdev->dev, res);
     if (IS_ERR(i2c->base))
          return PTR_ERR(i2c->base);

     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
     if (IS_ERR(i2c->pdmabase))
          return PTR_ERR(i2c->pdmabase);

     irq = platform_get_irq(pdev, 0);
     if (irq <= 0)
          return irq;

     init_completion(&i2c->msg_complete);

     of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
     if (!of_id)
          return -EINVAL;

     i2c->dev_comp = of_id->data;
     i2c->adap.dev.of_node = pdev->dev.of_node;
     i2c->dev = &pdev->dev;
     i2c->adap.dev.parent = &pdev->dev;
     i2c->adap.owner = THIS_MODULE;
     i2c->adap.algo = &mtk_i2c_algorithm;  // mtk adapter  算法填充 
     i2c->adap.quirks = i2c->dev_comp->quirks;
     i2c->adap.timeout = 2 * HZ;
     i2c->adap.retries = 1;

     ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
     if (ret)
          return -EINVAL;

     if (i2c->dev_comp->timing_adjust)
          i2c->clk_src_div = I2C_DEFAULT_CLK_DIV;

     if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
          return -EINVAL;

     i2c->clk_main = devm_clk_get(&pdev->dev, "main");
     if (IS_ERR(i2c->clk_main)) {
          dev_err(&pdev->dev, "cannot get main clock\n");
          return PTR_ERR(i2c->clk_main);
     }

     i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
     if (IS_ERR(i2c->clk_dma)) {
          dev_err(&pdev->dev, "cannot get dma clock\n");
          return PTR_ERR(i2c->clk_dma);
     }

     clk = i2c->clk_main;
     if (i2c->have_pmic) {
          i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
          if (IS_ERR(i2c->clk_pmic)) {
               dev_err(&pdev->dev, "cannot get pmic clock\n");
               return PTR_ERR(i2c->clk_pmic);
          }
          clk = i2c->clk_pmic;
     }

     strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));

     ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
     if (ret) {
          dev_err(&pdev->dev, "Failed to set the speed.\n");
          return -EINVAL;
     }

     if (i2c->dev_comp->support_33bits) {
          ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
          if (ret) {
               dev_err(&pdev->dev, "dma_set_mask return error.\n");
               return ret;
          }
     }

     ret = mtk_i2c_clock_enable(i2c);
     if (ret) {
          dev_err(&pdev->dev, "clock enable failed!\n");
          return ret;
     }

     mtk_i2c_init_hw(i2c);
     mtk_i2c_clock_disable(i2c);

     ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
                      IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
     if (ret < 0) {
          dev_err(&pdev->dev,
               "Request I2C IRQ %d fail\n", irq);
          return ret;
     }

     i2c_set_adapdata(&i2c->adap, i2c);
     ret = i2c_add_adapter(&i2c->adap);
     if (ret) {
          dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
          return ret;
     }

     platform_set_drvdata(pdev, i2c);

     return 0;
}

static struct platform_driver mtk_i2c_driver = {
     .probe = mtk_i2c_probe,
     .remove = mtk_i2c_remove,
     .driver = {
          .name = I2C_DRV_NAME,
          .pm = &mtk_i2c_pm,
          .of_match_table = of_match_ptr(mtk_i2c_of_match),
     },
};

module_platform_driver(mtk_i2c_driver);




三:開始分析架構,根據實例1的問題。


linux的 i2c 分爲3個層次, 分別爲  i2c core 層linux系統內核自帶的, i2c adapter driver 層 芯片供應商寫的, i2c device driver 層 android驅動開發工程師來寫。


I2C core 是用於維護 Linux 的 I2C 核心部分,提供了核心的數據結構, I2C 適配器驅動和設備驅動的註冊、註銷管理等 API
同時還提供了 I2C 總線讀寫訪問的一般接口(具體的實現在與 I2C 控制器相關 的  C adapter 中實現)。

該層爲硬件平臺無關層,向下屏蔽了物理總線適配器的差異 , 定義了統一的訪問策略和接口 ; 向上則提供了統一的接口,
以便 I2C 設備驅動可以通過總線適配器進行數據收發。Linux 中 , I2C core 層的代碼位於 driver/i2c/ i2c-core.c 。 


static int mtk_i2c_probe(struct platform_device *pdev)
{
     const struct of_device_id *of_id;
     int ret = 0;
     struct mtk_i2c *i2c;
     struct clk *clk;
     struct resource *res;
     int irq;

     i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
     if (!i2c)
          return -ENOMEM;

     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     i2c->base = devm_ioremap_resource(&pdev->dev, res);
     if (IS_ERR(i2c->base))
          return PTR_ERR(i2c->base);

     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
     if (IS_ERR(i2c->pdmabase))
          return PTR_ERR(i2c->pdmabase);

     irq = platform_get_irq(pdev, 0);
     if (irq <= 0)
          return irq;

     init_completion(&i2c->msg_complete);

     of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
     if (!of_id)
          return -EINVAL;

     i2c->dev_comp = of_id->data;
     i2c->adap.dev.of_node = pdev->dev.of_node;
     i2c->dev = &pdev->dev;
     i2c->adap.dev.parent = &pdev->dev;
     i2c->adap.owner = THIS_MODULE;
     i2c->adap.algo = &mtk_i2c_algorithm;  // mtk adapter
     i2c->adap.quirks = i2c->dev_comp->quirks;
     i2c->adap.timeout = 2 * HZ;
     i2c->adap.retries = 1;

     ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
     if (ret)
          return -EINVAL;

     if (i2c->dev_comp->timing_adjust)
          i2c->clk_src_div = I2C_DEFAULT_CLK_DIV;

     if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
          return -EINVAL;

     i2c->clk_main = devm_clk_get(&pdev->dev, "main");
     if (IS_ERR(i2c->clk_main)) {
          dev_err(&pdev->dev, "cannot get main clock\n");
          return PTR_ERR(i2c->clk_main);
     }

     i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
     if (IS_ERR(i2c->clk_dma)) {
          dev_err(&pdev->dev, "cannot get dma clock\n");
          return PTR_ERR(i2c->clk_dma);
     }

     clk = i2c->clk_main;
     if (i2c->have_pmic) {
          i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
          if (IS_ERR(i2c->clk_pmic)) {
               dev_err(&pdev->dev, "cannot get pmic clock\n");
               return PTR_ERR(i2c->clk_pmic);
          }
          clk = i2c->clk_pmic;
     }

     strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));

     ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
     if (ret) {
          dev_err(&pdev->dev, "Failed to set the speed.\n");
          return -EINVAL;
     }

     if (i2c->dev_comp->support_33bits) {
          ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
          if (ret) {
               dev_err(&pdev->dev, "dma_set_mask return error.\n");
               return ret;
          }
     }

     ret = mtk_i2c_clock_enable(i2c);
     if (ret) {
          dev_err(&pdev->dev, "clock enable failed!\n");
          return ret;
     }

     mtk_i2c_init_hw(i2c);
     mtk_i2c_clock_disable(i2c);

     ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
                      IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
     if (ret < 0) {
          dev_err(&pdev->dev,
               "Request I2C IRQ %d fail\n", irq);
          return ret;
     }

     i2c_set_adapdata(&i2c->adap, i2c);
     ret = i2c_add_adapter(&i2c->adap);  // mtk adapter register 
     if (ret) {
          dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
          return ret;
     }

     platform_set_drvdata(pdev, i2c);

     return 0;
}



Linux的I2C體系結構分爲3個組成部分:
1·I2C核心:
I2C核心提供了I2C總線驅動和設備驅動的註冊、註銷方法,I2C通信方法(即“algorithm”)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。
這部分是與平臺無關的。

2·I2C總線驅動:
I2C總線驅動是對I2C硬件體系結構中適配器端的實現。I2C總線驅動主要包含了I2C適配器數據結構i2c_adapterI2C適配器的algorithm數據結構i2c_algorithm和控制I2C適配器產生通信信號的函數。經由I2C線驅動的代碼,我們可以控制I2C適配器以主控方式產生開始位、停止位、讀寫週期,以及以從設備方式被讀寫、產生ACK等。不同的CPU平臺對應着不同的I2C總線驅動。 
總線驅動的職責,是爲系統中每個I2C總線增加相應的讀寫方法。但是總線驅動本身並不會進行任何的通訊,它只是存在在那裏,等待設備驅動調用其函數。
kernel-3.18/drivers/i2c/busses/i2c-mt65xx.c
這部分在MTK 6516中是由MTK已經幫我們實現了的,不需要我們更改。


3· I2C設備驅動:
I2C設備驅動是對I2C硬件體系結構中設備端的實現。設備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數據。I2C設備驅動主要包含了數據結構i2c_driver和i2c_client,我們需要根據具體設備實現其中的成員函數。在Linux內核源代碼中的drivers目錄下的i2c_dev.c文件,實現了I2C適配器設備文件的功能,應用程序通過“i2c-%d”文件名並使用文件操作接口open()、write()、read()、ioctl()和close()等來訪問這個設備。應用層可以借用這些接口訪問掛接在適配器上的I2C設備的存儲空間或寄存器並控制I2C設備的工作方式。
設備驅動則是與掛在I2C總線上的具體的設備通訊的驅動。通過I2C總線驅動提供的函數,設備驅動可以忽略不同總線控制器的差異,不考慮其實現細節地與硬件設備通訊。

 
struct i2c_client
代表一個掛載到i2c總線上的i2c從設備,該設備所需要的數據結構,其中包括該i2c從設備所依附的i2c主設備 struct i2c_adapter *adapter 該i2c從設備的驅動程序struct i2c_driver *driver 作爲i2c從設備所通用的成員變量,比如addr, name等 該i2c從設備驅動所特有的數據,依附於dev->driver_data下
struct i2c_adapter:
代表主芯片所支持的一個i2c主設備。
struct i2c_algorithm *algo:
是該i2c主設備傳輸數據的一種算法,或者說是在i2c總線上完成主從設備間數據通信的一種能力。

Linux的i2c子系統新、舊架構並存。主要分爲舊架構(Legacy)也有人稱之爲adapter方式,和新的架構new-style的方式。

這倆者的區別主要在於設備註冊和驅動註冊的不同。對於Legacy的設備註冊是在驅動運行的時候動態的創建,而新式的new-style則是採用靜態定義的方式。
注:MTK在Android2.1版上用的是Legacy的架構,而在Android2.2版上用的是new-style的架構。(在這裏我就只說明Android2.2new-style的實現方法)
 
要完成I2C設備的驅動,我們可以分三步走:
第一步:完成適配器的註冊(總線);
第二步:完成I2C client的設備註冊(設備);
第三步:完成I2C client驅動的註冊(驅動);
 
我們分別給予介紹:

I2C-mt6516.ckernel-3.18/drivers/i2c/busses/i2c-mt65xx.c
就總線而言,其本質只需要我們填充倆個結構體就可以了:
i2c_adapter;i2c_algorithm;
i2c_add_adapter(i2c->adap); 往總線上添加對應的適配器;

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;  
}; 
static struct i2c_algorithm mt6516_i2c_algorithm = {    
 .master_xfer   = mt6516_i2c_transfer,
.smbus_xfer    = NULL,
.functionality = mt6516_i2c_functionality,
};

2、設備註冊
第一步:
記得以前的i2c設備驅動,設備部分喜歡驅動運行的時候動態創建,
新式的驅動傾向於向傳統的linux下設備驅動看齊,採用靜態定義的方式來註冊設備,使用接口爲

int __init i2c_register_board_info(int busnum,   struct i2c_board_info const *info, unsigned len)
{
    int status;

    mutex_lock(&__i2c_board_lock);

    /* dynamic bus numbers will be assigned after the last static one */

    if (busnum >= __i2c_first_dynamic_bus_num)
        __i2c_first_dynamic_bus_num = busnum + 1; 

    for (status = 0; len; len--, info++) {
        struct i2c_devinfo    *devinfo;

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);       //申請表示i2c設備的結構體空間
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }
        /* 填寫i2c設備描述結構 */
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);          //添加到全局鏈表__i2c_board_list
    }

    mutex_unlock(&__i2c_board_lock);

    return status;
}

在系統初始化的過程中,我們可以通過 i2c_register_board_info,將所需要的I2C從設備加入一個名爲   __i2c_board_list    雙向循環鏈表
系統在成功加載I2C主設備adapter  後,就會對這張鏈表裏所有I2C從設備逐一地完成  i2c_client  的註冊


第二步:
系統初始化的時候,會根據板級i2c設備配置信息,創建i2c客戶端設備(i2c_client),  添加到i2c子系統中:

static void i2c_scan_static_board_info (struct i2c_adapter *adapter)
{
    struct i2c_devinfo    *devinfo;

    mutex_lock(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {    //遍歷全局鏈表__i2c_board_list
        if (devinfo->busnum == adapter->nr   && !i2c_new_device(adapter,    &devinfo->board_info))

            printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",    i2c_adapter_id(adapter),    devinfo->board_info.addr);
    }
    mutex_unlock(&__i2c_board_lock);
}


struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
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));
 
/* Check for address business */
status = i2c_check_addr(adap, client->addr);
if (status)
     goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
 
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),  client->addr);
status = device_register(&client->dev);
if (status)
     goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",  client->name, dev_name(&client->dev));
return client;

out_err:
     dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "  "(%d)\n", client->name, client->addr, status);
kfree(client);
return NULL;
}
IDR機制:完成的是設備ID和結構體的關聯。
__i2c_first_dynamic_bus_num:當前系統允許的動態總線的最大值。

i2c_scan_static_board_info(adap);        /*完成新類型i2c設備的註冊,一般只在主板初始化時*/ 
此函數爲整個I2C子系統的核心,它會去遍歷一個由I2C從設備組成的雙向循環鏈表,並完成所有I2C從設備的i2c_client的註冊

struct i2c_devinfo *devinfo;     //已經建立好了的I2C從設備鏈表
status = i2c_check_addr(adap, client->addr);
注:

特別要提一下的是這個“i2c_check_addr”,引用<<i2c 源代碼情景分析>>裏的話:“i2c 設備的7 位地址是就當前i2c 總線而言的,是“相對地址”。
不同的i2c 總線上的設備可以使用相同的7 位地址,但是它們所在的i2c 總線不同。
所以在系統中一個i2c 設備的“絕對地址”由二元組(i2c 適配器的ID 和設備在該總線上的7 位地址)表示。”,
所以這個函數的作用主要是排除同一i2c總線上出現多個地址相同的設備。
 
3、I2C驅動註冊:
第一步:
static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;

/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;

/* When registration returns, the driver core
 * will have called probe() for all matching-but-unbound devices.
 */
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
 
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
mutex_unlock(&core_lock);

return 0;
}
 
設備和驅動的關聯過程:首先當I2C從設備和I2C驅動如果處於同一條總線上,那麼其在設備和驅動註冊之後,將會促使I2C_bus_type中的match獲得調用;()如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
繼續跟進i2c_device_match
i2c_match_id(driver->id_table, client) != NULL;
我們回到i2c_device_probe
這個函數的關鍵是:
status = driver->probe(client, i2c_match_id(driver->id_table, client));
它將函數的流程交回到了driver->probe的手中;

圖解如下:


過程分享:

1、設備和驅動的關聯
大家知道,對於一個驅動程序有兩個元素不可或缺,即   設備和驅動,一般驅動都是通過     設備名和驅動名的匹配   建立關係的,
最開始我從代碼中只能發現    驅動的註冊,卻不見設備註冊的蹤影,令人疑惑,跟蹤發現,
在  i2c adapter   註冊時會遍歷   i2c_board_info   這樣一個結構,而這個結構在29以前或更早的內核裏是不存在的,它會完成驅動與設備的匹配問題, 

2、名字匹配
一個i2c驅動是可以有多個名字的,即一個驅動程序可以支持多個設備,該機制是通過 struct i2c_device_id    實現的,
驅動中建立這麼一個結構體數組,i2c架構層便會掃描該數組,與  設備名去匹配,匹配成功的都會進入相應probe函數

3、進入probe
該過程困惑了我一段時間,其實要進入自己   驅動的probe首先需要進入總線的probe,而進入總線 probe的前提是與總線的match  成功。

 
 

 Legacy 的相關知識:


(一)Linux的I2C驅動框架中的主要數據結構及其關係

Linux的I2C驅動框架中的主要數據結構包括:i2c_driver、i2c_client、i2c_adapter和i2c_algorithm。

i2c_adapter對應於物理上的一個適配器,這個適配器是基於不同的平臺的,
一個I2C適配器需要i2c_algorithm   中提供的通信函數來控制適配器,因此i2c_adapter中包含其使用的i2c_algorithm的指針。
i2c_algorithm中的關鍵函數     master_xfer()以i2c_msg爲  單位產生I2C訪問需要的信號。
不同的平臺所對應的master_xfer()   是不同的,開發人員需要根據所用平臺的硬件特性實現自己的XXX_xfer()方法以填充i2c_algorithm的master_xfer指針。


i2c_ driver對應一套驅動方法,不對應於任何的物理實體。i2c_client  對應於真實的物理設備,每個I2C設備都需要一個i2c_client   來描述。
i2c_client   依附於i2c_adpater ,這與I2C   硬件體系中適配器和設備的關係一致。i2c_driver提供了i2c-client與i2c-adapter產生聯繫的函數。
當    attach a_dapter()函數探測物理設備時,如果確定存在一個client,則把該client使用的i2c_client數據結構的adapter指針指向對應的i2e_ adapter,
driver指針指向該i2c_driver,並調用i2e_adapter的client_register()函數來註冊此設備。相反的過程發生在i2c_ driver的detach_client()函數被調用的時候。



(二)Linux的I2C體系結構中三個組成部分的作用

I2C核心提供了一組不依賴於硬件平臺的接口函數,I2C總線驅動和設備驅動之間依賴於I2C核心作爲紐帶。
I2C核心提供了i2c_adapter的增加和刪除函數、i2c_driver的增加和刪除函數、i2c_client的依附和脫離函數以及i2c傳輸、發送和接收函數。
i2c傳輸函數i2c_transfer()用於進行I2C適配器和I2C設備之間的一組消息交互i2c_master_send()函數和i2c_master_recv()  
      函數內部會調用i2c_ transfer()函數分別完成一條寫消息和一條讀消息.


I2C總線驅動包括I2C適配器驅動加載與卸載以及I2C總線通信方法。
其中I2C適配器驅動加載(與卸載)要完成初始化(釋放)I2C適配器所使用的硬件資源,申請I/0地址、中斷號、
通過i2c_add_ adapter()添加i2c_adapter的數據結構(通過i2c_del_adapter()刪除i2c_adapter的數據結構)的工作。
12C總線通信方法主要對特定的I2C適配器實現i2c_algorithm的master_xfer()方法來實現i2c_ msg的傳輸。
不同的適配器對應的master_xfer()方法由其處理器的硬件特性決定。

I2C設備驅動主要用於I2C設備驅動模塊加載與卸載以及提供I2C設備驅動文件操作接口。

I2C設備驅動的模塊加載通用的方法遵循以下流程:首先通過   register_chrdev()   
將I2C設備註冊爲一個字符設備,然後利用I2C核心中的i2c_add_a_dapter()添加i2c_driver。
調用i2c_add_adapter()過程中會引發i2c_driver結構體中的YYY_attach_adapter()的執行,它通過調用I2C核心的i2e_probe()實現物理設備的探測。
i2c_probe()會引發yyy_detect()的調用。yyy_detect()中會初始化i2c_ client,然後調用內核的i2e_attach_client()通知I2C核心此時系統中包含了一個新的I2C設備。
之後會引發I2C設備驅動中yyy_init_client()來初始化設備。卸載過程執行相反的操作。


I2C設備驅動模塊加載與卸載的流程 如圖2 所示。



Devices.c 裏面有  i2c_borad_info 





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