Linux内核---32.I2C设备驱动分析

来源:互联网 发布:ubuntu 安装svnserver 编辑:程序博客网 时间:2024/06/01 11:12
一. I2C设备的定义及其添加过程
1.1 数据结构
include/linux/i2c.h:
  1. struct i2c_board_info {
  2.     char        type[I2C_NAME_SIZE];            //设备名   
  3.     unsigned short    flags;                    //   
  4.     unsigned short    addr;                     //     
  5.     void        *platform_data;                 // 
  6.     struct dev_archdata    *archdata;           // 
  7.     struct device_node *of_node;                // 
  8.     int        irq;                             // 
  9. };
drivers/i2c/i2c-core.h:
  1. struct i2c_devinfo {
  2.     struct list_head    list;                 //i2c设备列表头           
  3.     int            busnum;                    //i2c总线并不是只有一条
  4.     struct i2c_board_info    board_info;      //
  5. };

1.2 全局变量
drivers/i2c/i2c-core.h:
  1. extern struct rw_semaphore    __i2c_board_lock     //    
  2. extern struct list_head    __i2c_board_list;         // 
  3. extern int        __i2c_first_dynamic_bus_num;       //
全局变量的初始化,在driver/i2c/i2c-boardinfo.c中
  1. DECLARE_RWSEM(__i2c_board_lock)                    //读写锁
  2. EXPORT_SYMBOL_GPL(__i2c_board_lock);

  3. LIST_HEAD(__i2c_board_list);                         //双向链表
  4. EXPORT_SYMBOL_GPL(__i2c_board_list);

  5. int __i2c_first_dynamic_bus_num;
  6. EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
1.3 I2C设备的定义
在arch/arm/mach-s3c64xx/mach-smdk6410.c中,定义了I2C的设备
  1. static struct i2c_board_info i2c_devs0[] __initdata = {
  2.     { I2C_BOARD_INFO("ov965x", 0x30), }, // gjl
  3. };

  4. static struct i2c_board_info i2c_devs1[] __initdata = {
  5.     { I2C_BOARD_INFO("24c128", 0x57), },    /* Samsung S524AD0XD1 */
  6. };
其中 #define I2C_BOARD_INFO(dev_type, dev_addr) \
    .type = dev_type, .addr = (dev_addr)
.type 是name 
.addr 是设备在i2c上的地址

在arch/arm/mach-s3c64xx/mach-smdk6410.c中
  1. static void __init smdk6410_machine_init(void)
  2. {
  3.     i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));   //将bus 0上的设备添加到i2c的设备链表中
  4.     i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));   //将bus 1上的设备添加到i2c的设备链表中
  5. }
在driver/i2c/i2c-boardinfo.c中:
  1. int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
  2. {
  3.     int status;

  4.     down_write(&__i2c_board_lock);
  5.     
  6.     if (busnum >= __i2c_first_dynamic_bus_num)
  7.         __i2c_first_dynamic_bus_num = busnum + 1;

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

  10.         devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);    
  11.         
  12.         devinfo->busnum = busnum;                           //新建一个i2c_devinfo结构体,初始化之后
  13.         devinfo->board_info = *info;                        //初始化后,插入到i2c设备链表的尾部
  14.         list_add_tail(&devinfo->list, &__i2c_board_list);   //在全局变量i2c_board_list中添加新的设备
  15.     }

  16.     up_write(&__i2c_board_lock);

  17.     return status;
  18. }

1.4 I2C设备的添加过程
s3c24xx_i2c_probe
    --> i2c_add_numbered_adapter
        --> i2c_register_adapter
            --> i2c_scan_static_board_info
                --> i2c_new_device
i2c设备的添加过程是随着i2c控制器的添加而添加的,这个在下一篇中详细说一下,这儿就省了!

二. I2C设备驱动的注册过程
这儿以ov9650为例说明一下,(为什么呢? 因为ok6410上面没有其它的i2c设备了!)
drivers/media/video/samsung/fimc/ov965x.c
  1. static __init int ov965x_init(void)
  2. {
  3.     return i2c_add_driver(&ov965x_i2c_driver);
  4. }
  5. module_init(ov965x_init)
ov965x_init
    --> i2c_add_driver
  1. static inline int i2c_add_driver(struct i2c_driver *driver)
  2. {
  3.     return i2c_register_driver(THIS_MODULE, driver);
  4. }

  5. 在driver/i2c/i2c-core.c中
  6. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  7. {
  8.     driver->driver.owner = owner;
  9.     driver->driver.bus = &i2c_bus_type;
  10.     res = driver_register(&driver->driver);        //注册driver并进入I2C的probe函数
  11.     INIT_LIST_HEAD(&driver->clients);
  12.                                                     //对i2c上的每一个设备执行一次__process_new_driver                 
  13.     i2c_for_each_dev(driver, __process_new_driver)//但__process_new_driver好像什么事也没有干
  14.     return 0; 
  15. }
  16. EXPORT_SYMBOL(i2c_register_driver);

ov965x_init
    --> i2c_add_driver
            --> i2c_device_probe
driver/i2c/i2c-core.c
  1. static int i2c_device_probe(struct device *dev)
  2. {
  3.     struct i2c_client    *client = i2c_verify_client(dev);   //这儿己经是ov965x的dev了,己经得到dev->addr=0x30
  4.     struct i2c_driver    *driver;
  5.     int status;

  6.     driver = to_i2c_driver(dev->driver);
  7.     client->driver = driver;
  8.     if (!device_can_wakeup(&client->dev))
  9.         device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE);
  10.     status = driver->probe(client, i2c_match_id(driver->id_table, client));    //开始调用具体设备的probe
  11.     return status;
  12. }
下面接着分析

三. I2C设备驱动的写过程
i2c_device_probe
    --> ov965x_probe
  1. static int ov965x_probe(struct i2c_client *c, const struct i2c_device_id *id)
  2. {
  3.     ov965x_data.client = c;
  4.     s3c_fimc_register_camera(&ov965x_data);            //向fimc注册
  5.     for (= 0; i < OV965X_INIT_REGS; i++) {           //通过i2c写ov9650的寄存器
  6.         ret = i2c_smbus_write_byte_data(c, OV965X_init_reg[i].subaddr, OV965X_init_reg[i].value);
  7.     }
  8.     return 0;
  9. }

i2c_device_probe
    --> ov965x_probe
        --> i2c_smbus_write_byte_data
driver/i2c/i2c-core.c
  1. s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)
  2. {
  3.     union i2c_smbus_data data;
  4.     data.byte = value;
  5.     return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
  6.              I2C_SMBUS_WRITE, command, I2C_SMBUS_BYTE_DATA, &data);
  7. }

  8. s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
  9.          char read_write, u8 command, int protocol,
  10.          union i2c_smbus_data *data)
  11. {
  12.     flags &= I2C_M_TEN | I2C_CLIENT_PEC;    
  13.     i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data);
  14. }

i2c_device_probe
    --> ov965x_probe
        --> i2c_smbus_write_byte_data
            -->  i2c_smbus_xfer_emulated
driver/i2c/i2c-core.c
  1. static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
  2.              unsigned short flags,
  3.              char read_write, u8 command, int size,
  4.              union i2c_smbus_data *data)
  5. {
  6.     unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
  7.     unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
  8.     int num = read_write == I2C_SMBUS_READ ? 2 : 1;
  9.     struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
  10.      { addr, flags | I2C_M_RD, 0, msgbuf1 }
  11.      };

  12.     msgbuf0[0] = command;
  13.     switch (size) {
  14.     case I2C_SMBUS_BYTE_DATA:       
  15.             msg[0].len = 2;
  16.             msgbuf0[1] = data->byte;        
  17.         break;
  18.     }

  19.     status = i2c_transfer(adapter, msg, num);    //把要传输的信息组装成msg,进行传输
  20.     //i==&& I2C_SMBUS_READ
  21. }

i2c_device_probe
    --> ov965x_probe
        --> i2c_smbus_write_byte_data
            -->  i2c_smbus_xfer_emulated
                --> i2c_transfer
driver/i2c/i2c-core.c
  1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
  2. {
  3.     unsigned long orig_jiffies;
  4.     int ret, try;
  5.     if (in_atomic() || irqs_disabled()) {
  6.         ret = i2c_trylock_adapter(adap);
  7.         if (!ret)
  8.             return -EAGAIN;
  9.     } else 
  10.         i2c_lock_adapter(adap);       //加把锁
  11.         
  12.     orig_jiffies = jiffies;
  13.     for (ret = 0, try = 0; try <= adap->retries; try++) {          //如果传输失败,则重试adap->retries次
  14.         ret = adap->algo->master_xfer(adap, msgs, num);            //传输函数,在文件i2c-s3c2410.c中
  15.         if (ret != -EAGAIN)
  16.             break;
  17.         if (time_after(jiffies, orig_jiffies + adap->timeout))    //如果超时了,不管成不成功立即返回
  18.             break;
  19.     }
  20.     i2c_unlock_adapter(adap);        //unlock
  21.     return ret; 
  22. }
注: ret = adap->algo->master_xfer(adap, msgs, num); 是i2c控制器的数据传输函数,下一篇分析
0 0
原创粉丝点击