I2C驅動2

一、i2c_client、i2c_driver和i2c_adapter結構的創建

 

我們先來看看這三個結構是如何被定義的。

 

在mach-zhaocj2440.c文件中的zhaocj2440_init函數內,有下面一句:

i2c_register_board_info(0,zhaocj2440_i2c_devs,ARRAY_SIZE(zhaocj2440_i2c_devs));

 

i2c_register_board_info函數是在drivers/i2c/i2c-boardinfo.c中被定義,作用是靜態註冊聲明開發板上的IIC信息。其中該函數的參數zhaocj2440_i2c_devs的作用是定義板上的IIC設備信息,它是在mach-zhaocj2440.c內定義的。由於我的開發板上的IIC設備爲AT24C02的eeprom,因此zhaocj2440_i2c_devs應該重新定義爲:

static struct i2c_board_info zhaocj2440_i2c_devs[] __initdata = {

       {

              I2C_BOARD_INFO("24c02", 0x50),   //宏定義:.type=24c02, .addr=0x50,

              .platform_data= &at24c02,

       },

};

 

而at24c02定義爲:

static struct at24_platform_data at24c02 = {

       .byte_len = SZ_2K / 8,                //容量大小

       .page_size       = 8,                      //每頁的字節數

};

 

i2c_board_info.type就是i2c_client.name變量,i2c_board_info.addr就是i2c_client.addr變量,所以只要定義了zhaocj2440_i2c_devs,也就是定義了i2c_client結構。關於這一點,我們在後面會講到。

 

我們再來看drivers/misc/eeprom/at24.c文件中的模塊初始化函數at24_init:

static int __init at24_init(void)

{

       if(!io_limit) {

              pr_err("at24:io_limit must not be 0!\n");

              return -EINVAL;

       }

 

       io_limit= rounddown_pow_of_two(io_limit);

       //爲IIC添加驅動

       return i2c_add_driver(&at24_driver);

}

 

i2c_add_driver函數通過宏定義爲i2c_register_driver函數,它的作用是註冊IIC驅動,這裏的驅動是at24_driver,它的定義爲:

static struct i2c_driver at24_driver = {

       .driver= {

              .name= "at24",

              .owner= THIS_MODULE,

       },

       .probe= at24_probe,

       .remove= __devexit_p(at24_remove),

       .id_table= at24_ids,

};

 

IIC適配器是通過i2c-s3c2410.c文件內的s3c24xx_i2c_probe函數創建的。該函數調用了i2c_add_numbered_adapter函數,它又調用了i2c_register_adapter函數,從而完成了對i2c_adapter的創建和註冊。

 

二、i2c_client、i2c_driver和i2c_adapter三者的綁定

 

這樣,i2c_client、i2c_driver和i2c_adapter都被創建了,並都各自放到了總線上,那它們是如何關聯在一起的呢?

 

在s3c24xx_i2c_probe函數內調用了of_i2c_register_devices函數,該函數提取出了i2c_board_info設備信息,並且又調用了i2c_new_device函數,該函數主要完成了兩項工作,一是通過把i2c_board_info賦值給i2c_client,從而創建了i2c_client,因此正如我們在前面介紹過的,定義了i2c_board_info,也就是定義了i2c_client;二是把剛剛創建的IIC設備與在s3c24xx_i2c_probe函數創建的IIC適配器綁定在了一起,如下面這句:

client->adapter= adap;

 

如前文所述,i2c_register_adapter函數用於註冊i2c_adapter,i2c_register_driver函數用於註冊i2c_driver。i2c_register_adapter函數有下面一句:

adap->dev.bus= &i2c_bus_type;

i2c_register_driver函數有下面一句:

driver->driver.bus= &i2c_bus_type;

 

總之,都定義了IIC的總線類型——i2c_bus_type:

struct bus_type i2c_bus_type = {

       .name             ="i2c",

       .match            =i2c_device_match,

       .probe            =i2c_device_probe,

       .remove          =i2c_device_remove,

       .shutdown       = i2c_device_shutdown,

       .pm         = &i2c_device_pm_ops,

};

 

當設備或驅動添加到總線上時,會調用match指向的函數,用於找到是否有設備和驅動相匹配;當設備或驅動添加到總線上時,也會調用probe指向的函數,用於初始化與驅動相匹配的設備。因此無論是在創建i2c_adapter,還是i2c_driver的時候,都會調用這兩個函數。

static int i2c_device_match(struct device *dev, structdevice_driver *drv)

{

       structi2c_client      *client = i2c_verify_client(dev);

       structi2c_driver     *driver;

 

       if(!client)

              return0;

 

       /*Attempt an OF style match */

       //設備和驅動是否匹配

       if(of_driver_match_device(dev, drv))

              return1;

 

       driver= to_i2c_driver(drv);

       /*match on an id table if there is one */

       //如果驅動有設備列表,則用下面判斷設備和驅動是否匹配

       if(driver->id_table)

              returni2c_match_id(driver->id_table,client) != NULL;

 

       return0;

}

 

i2c_device_match函數返回非零值,表示設備和驅動匹配上了。at24_driver中有一個設備列表項——at24_ids:

static const struct i2c_device_id at24_ids[] = {

       /*needs 8 addresses as A0-A2 are ignored */

       {"24c00",AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },

       /*old variants can't be handled with this generic entry! */

       {"24c01",AT24_DEVICE_MAGIC(1024 / 8, 0) },

       {"24c02",AT24_DEVICE_MAGIC(2048 / 8, 0) },

       /*spd is a 24c02 in memory DIMMs */

       {"spd", AT24_DEVICE_MAGIC(2048 / 8,

              AT24_FLAG_READONLY| AT24_FLAG_IRUGO) },

       {"24c04",AT24_DEVICE_MAGIC(4096 / 8, 0) },

       /*24rf08 quirk is handled at i2c-core*/

       {"24c08",AT24_DEVICE_MAGIC(8192 / 8, 0) },

       {"24c16",AT24_DEVICE_MAGIC(16384 / 8, 0) },

       {"24c32",AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },

       {"24c64",AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },

       {"24c128",AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },

       {"24c256",AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },

       {"24c512",AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },

       {"24c1024",AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },

       {"at24", 0 },

       {/* END OF LIST */ }

};

 

上面結構中的一項"24c02",與我們前面定義的i2c_client.name一致,因此i2c_client和i2c_driver匹配上了。

 

static int i2c_device_probe(struct device *dev)

{

       structi2c_client      *client = i2c_verify_client(dev);

       structi2c_driver     *driver;

       intstatus;

 

       if(!client)

              return0;

 

       //提取IIC驅動

       driver = to_i2c_driver(dev->driver);

       if(!driver->probe || !driver->id_table)

              return-ENODEV;

       //i2c_client和i2c_driver綁定在了一起

       client->driver= driver;

       if(!device_can_wakeup(&client->dev))

              device_init_wakeup(&client->dev,

                                   client->flags& I2C_CLIENT_WAKE);

       dev_dbg(dev,"probe\n");

 

       //調用probe函數,也就是at24_probe函數

       status= driver->probe(client, i2c_match_id(driver->id_table,client));

       if(status) {

              client->driver= NULL;

              i2c_set_clientdata(client, NULL);

       }

       returnstatus;

}

 

經過上面的分析,可以看出IIC設備中的驅動指向了IIC驅動,IIC設備中的適配器指向了IIC適配器,這樣三者就都綁定在了一起,成爲了一個整體。

 

三、IIC設備的操作

 

系統對IIC設備的操作是在at24_probe函數完成的:

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

       structat24_platform_data chip;

       boolwritable;

       intuse_smbus = 0;

       structat24_data *at24;

       interr;

       unsignedi, num_addresses;

       kernel_ulong_tmagic;

 

       if(client->dev.platform_data) {

              chip= *(struct at24_platform_data *)client->dev.platform_data;

       }else {

              if(!id->driver_data) {

                     err= -ENODEV;

                     gotoerr_out;

              }

              magic = id->driver_data;

              chip.byte_len= BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));

              magic>>= AT24_SIZE_BYTELEN;

              chip.flags= magic & AT24_BITMASK(AT24_SIZE_FLAGS);

              /*

               * This is slow, but we can't know all eeproms,so we better

               * play safe. Specifying custom eeprom-typesvia platform_data

               * is recommended anyhow.

               */

              chip.page_size= 1;

 

              /*update chipdata if OF is present */

              at24_get_ofdata(client,&chip);

 

              chip.setup= NULL;

              chip.context= NULL;

       }

 

       //判斷IIC設備(這裏指的就是eeprom)容量是否爲2的次冪

       if(!is_power_of_2(chip.byte_len))

              dev_warn(&client->dev,

                     "byte_lenlooks suspicious (no power of 2)!\n");

       //判斷eeprom的每頁的大小

       if(!chip.page_size) {

              dev_err(&client->dev,"page_size must not be 0!\n");

              err= -EINVAL;

              gotoerr_out;

       }

       //判斷eeprom每頁的大小是否爲2的次冪

       if(!is_power_of_2(chip.page_size))

              dev_warn(&client->dev,

                     "page_sizelooks suspicious (no power of 2)!\n");

 

       /*Use I2C operationsunless we're stuck with SMBus extensions. */

       if(!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {

              if(chip.flags & AT24_FLAG_ADDR16) {

                     err= -EPFNOSUPPORT;

                     gotoerr_out;

              }

              if(i2c_check_functionality(client->adapter,

                            I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {

                     use_smbus= I2C_SMBUS_I2C_BLOCK_DATA;

              }else if (i2c_check_functionality(client->adapter,

                            I2C_FUNC_SMBUS_READ_WORD_DATA)) {

                     use_smbus= I2C_SMBUS_WORD_DATA;

              }else if (i2c_check_functionality(client->adapter,

                            I2C_FUNC_SMBUS_READ_BYTE_DATA)) {

                     use_smbus= I2C_SMBUS_BYTE_DATA;

              }else {

                     err= -EPFNOSUPPORT;

                     gotoerr_out;

              }

       }

 

       if(chip.flags & AT24_FLAG_TAKE8ADDR)

              num_addresses= 8;

       else

              num_addresses=    DIV_ROUND_UP(chip.byte_len,

                     (chip.flags &AT24_FLAG_ADDR16) ? 65536 : 256);

 

       at24= kzalloc(sizeof(struct at24_data) +

              num_addresses* sizeof(struct i2c_client *),GFP_KERNEL);

       if(!at24) {

              err= -ENOMEM;

              gotoerr_out;

       }

 

       mutex_init(&at24->lock);

       at24->use_smbus= use_smbus;

       at24->chip= chip;

       at24->num_addresses= num_addresses;

 

       /*

        * Export the EEPROM bytes through sysfs, sincethat's convenient.

        * By default, only root should see the data(maybe passwords etc)

        */

       //下面是定義一些關於sysfs的屬性

       sysfs_bin_attr_init(&at24->bin);

       at24->bin.attr.name ="eeprom";          //名稱

       at24->bin.attr.mode = chip.flags &AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

       at24->bin.read= at24_bin_read;          //讀操作

       at24->bin.size = chip.byte_len;

 

       at24->macc.read= at24_macc_read;

 

       writable= !(chip.flags & AT24_FLAG_READONLY);

       if(writable) {

              if(!use_smbus || i2c_check_functionality(client->adapter,

                            I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

 

                     unsignedwrite_max = chip.page_size;

 

                     at24->macc.write= at24_macc_write;

 

                     at24->bin.write =at24_bin_write;               //寫操作

                     at24->bin.attr.mode|= S_IWUSR;

 

                     if(write_max > io_limit)

                            write_max= io_limit;

                     if(use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)

                            write_max= I2C_SMBUS_BLOCK_MAX;

                     at24->write_max= write_max;

 

                     /*buffer (data + address at the beginning) */

                     at24->writebuf= kmalloc(write_max + 2, GFP_KERNEL);

                     if(!at24->writebuf) {

                            err= -ENOMEM;

                            gotoerr_struct;

                     }

              }else {

                     dev_warn(&client->dev,

                            "cannotwrite due to controller restrictions.");

              }

       }

 

       at24->client[0]= client;

 

       /*use dummy devices for multiple-address chips */

       for (i = 1; i < num_addresses; i++){

              at24->client[i]= i2c_new_dummy(client->adapter,

                                   client->addr+ i);

              if(!at24->client[i]) {

                     dev_err(&client->dev,"address 0x%02x unavailable\n",

                                   client->addr+ i);

                     err= -EADDRINUSE;

                     gotoerr_clients;

              }

       }

 

       //爲sysfs創建二進制文件

       err= sysfs_create_bin_file(&client->dev.kobj, &at24->bin);

       if(err)

              gotoerr_clients;

 

       i2c_set_clientdata(client, at24);

 

       dev_info(&client->dev,"%zu byte %s EEPROM, %s, %u bytes/write\n",

              at24->bin.size,client->name,

              writable? "writable" : "read-only", at24->write_max);

       if(use_smbus == I2C_SMBUS_WORD_DATA||

           use_smbus == I2C_SMBUS_BYTE_DATA) {

              dev_notice(&client->dev,"Falling back to %s reads, "

                        "performance will suffer\n",use_smbus ==

                        I2C_SMBUS_WORD_DATA? "word" : "byte");

       }

 

       /*export data to kernel code */

       if(chip.setup)

              chip.setup(&at24->macc,chip.context);

 

       return0;

 

err_clients:

       for(i = 1; i < num_addresses; i++)

              if(at24->client[i])

                     i2c_unregister_device(at24->client[i]);

 

       kfree(at24->writebuf);

err_struct:

       kfree(at24);

err_out:

       dev_dbg(&client->dev,"probe error %d\n", err);

       returnerr;

}

 

在這裏,讀文件的函數爲at24_bin_read,它又調用了at24_read函數,最後又調用at24_eeprom_read函數;同樣的,寫文件的函數爲at24_bin_write,它又調用了at24_write函數,最後又調用at24_eeprom_write函數。at24_eeprom_read和at24_eeprom_write最終都要調用i2c_transfer函數,來真正完成對AT24C02的讀寫操作。

 

i2c_transfer函數已經在上一篇文章分析過了,這裏就不再重複。

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