S3C6410使用---25 I2C设备驱动分析
2016-08-17 21:57
459 查看
注:
转自:http://www.programgo.com/article/83144546859/;jsessionid=0BE6120516C6BEFFEA6CE4410474380D
一. I2C设备 的定义及其添加过程
1 .1 数据结构
include/linux/i2c.h: struct i2c_board_info { char type[I2C_NAME_SIZE]; //设备名 unsigned short flags; // unsigned short addr; // 设备地址 void *platform_data; // struct dev_archdata *archdata; // struct device_node *of_node; // int irq; // }; drivers/i2c/i2c-core.h: struct i2c_devinfo { struct list_head list; //i2c设备列表头 int busnum; //i2c总线并不是只有一条 struct i2c_board_info board_info; // };
1.2 全局变量
drivers/i2c/i2c-core.h: extern struct rw_semaphore __i2c_board_lock; // extern struct list_head __i2c_board_list; // extern int __i2c_first_dynamic_bus_num; // 全局变量的初始化,在driver/i2c/i2c-boardinfo.c中 DECLARE_RWSEM(__i2c_board_lock); //读写锁 EXPORT_SYMBOL_GPL(__i2c_board_lock); LIST_HEAD(__i2c_board_list); //双向链表 EXPORT_SYMBOL_GPL(__i2c_board_list); int __i2c_first_dynamic_bus_num; EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
1.3 I2C设备的定义
在arch/arm/mach-s3c64xx/mach-smdk6410.c中,定义了I2C的设备 static struct i2c_board_info i2c_devs0[] __initdata = { { I2C_BOARD_INFO("ov965x", 0x30), }, // gjl }; static struct i2c_board_info i2c_devs1[] __initdata = { { I2C_BOARD_INFO("24c128", 0x57), }, /* Samsung S524AD0XD1 */ }; 其中 #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中 static void __init smdk6410_machine_init(void) { i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); //将bus 0上的设备添加到i2c的设备链表中 i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); //将bus 1上的设备添加到i2c的设备链表中 } 在driver/i2c/i2c-boardinfo.c中: int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { int status; down_write(&__i2c_board_lock); 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); devinfo->busnum = busnum; //新建一个i2c_devinfo结构体,初始化之后 devinfo->board_info = *info; //初始化后,插入到i2c设备链表的尾部 list_add_tail(&devinfo->list, &__i2c_board_list); //在全局变量i2c_board_list中添加新的设备 } up_write(&__i2c_board_lock); return status; }
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 static __init int ov965x_init(void) { return i2c_add_driver(&ov965x_i2c_driver); } module_init(ov965x_init) ov965x_init --> i2c_add_driver static inline int i2c_add_driver(struct i2c_driver *driver) { return i2c_register_driver(THIS_MODULE, driver); } 在driver/i2c/i2c-core.c中 int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; res = driver_register(&driver->driver); //注册driver并进入I2C的probe函数 INIT_LIST_HEAD(&driver->clients); //对i2c上的每一个设备执行一次__process_new_driver i2c_for_each_dev(driver, __process_new_driver); //但__process_new_driver好像什么事也没有干 return 0; } EXPORT_SYMBOL(i2c_register_driver); ov965x_init --> i2c_add_driver --> i2c_device_probe driver/i2c/i2c-core.c static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); //这儿己经是ov965x的dev了,己经得到dev->addr=0x30 struct i2c_driver *driver; int status; driver = to_i2c_driver(dev->driver); client->driver = driver; if (!device_can_wakeup(&client->dev)) device_init_wakeup(&client->dev, client->flags & I2C_CLIENT_WAKE); status = driver->probe(client, i2c_match_id(driver->id_table, client)); //开始调用具体设备的probe return status; }
下面接着分析
三. I2C设备驱动的写过程
i2c_device_probe - - > ov965x_probe static int ov965x_probe(struct i2c_client *c, const struct i2c_device_id *id) { ov965x_data.client = c; s3c_fimc_register_camera(&ov965x_data); //向fimc注册 for (i = 0; i < OV965X_INIT_REGS; i++) { //通过i2c写ov9650的寄存器 ret = i2c_smbus_write_byte_data(c, OV965X_init_reg[i].subaddr, OV965X_init_reg[i].value); } return 0; } i2c_device_probe - - > ov965x_probe --> i2c_smbus_write_byte_data driver/i2c/i2c-core.c s32 i2c_smbus_write_byte_data(const 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); } s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data) { flags &= I2C_M_TEN | I2C_CLIENT_PEC; i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data); } i2c_device_probe - - > ov965x_probe --> i2c_smbus_write_byte_data --> i2c_smbus_xfer_emulated driver/i2c/i2c-core.c static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; int num = read_write == I2C_SMBUS_READ ? 2 : 1; struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, { addr, flags | I2C_M_RD, 0, msgbuf1 } }; msgbuf0[0] = command; switch (size) { case I2C_SMBUS_BYTE_DATA: msg[0].len = 2; msgbuf0[1] = data->byte; break; } status = i2c_transfer(adapter, msg, num); //把要传输的信息组装成msg,进行传输 //i==0 && I2C_SMBUS_READ } i2c_device_probe - - > ov965x_probe --> i2c_smbus_write_byte_data --> i2c_smbus_xfer_emulated --> i2c_transfer driver/i2c/i2c-core.c int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { unsigned long orig_jiffies; int ret, try; if (in_atomic() || irqs_disabled()) { ret = i2c_trylock_adapter(adap); if (!ret) return -EAGAIN; } else i2c_lock_adapter(adap); //加把锁 orig_jiffies = jiffies; for (ret = 0, try = 0; try <= adap->retries; try++) { //如果传输失败,则重试adap->retries次 ret = adap->algo->master_xfer(adap, msgs, num); //传输函数,在文件i2c-s3c2410.c中 if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) //如果超时了,不管成不成功立即返回 break; } i2c_unlock_adapter(adap); //unlock return ret; }
注: ret = adap - > algo - > master_xfer ( adap , msgs , num ) ; 是i2c控制器的数据传输函数,下一篇分析
相关文章推荐
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- new style I2C设备驱动分析,probe,struct i2c_device_id
- Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(1)
- linux-i2c驱动 之 i2c设备层的注册过程probe函数如何被调用分析
- [转载]Linux设备驱动之I2C架构分析 - linux设备驱动 - Linux内核学习
- Linux设备驱动之I2C架构分析
- Linux设备驱动之I2C架构分析
- new style I2C设备驱动分析,probe,struct i2c_device_id
- Linux设备驱动之I2C架构分析
- Linux I2C子系统分析-I2C设备驱动
- Linux设备驱动之I2C架构分析
- new style I2C设备驱动分析,probe,struct i2c_device_id
- Linux I2C驱动分析 S3C6410
- Linux设备驱动之I2C架构分析
- 四极管: Android开发调试I2C设备驱动 i2cdetect 工具的使用
- i2c设备驱动实例分析-pca9541(上)
- i2c设备驱动实例分析-pca9541(下)
- Linux I2C子系统分析-I2C总线驱动&&Linux I2C子系统分析-I2C设备驱动
- new style I2C设备驱动分析,probe,struct i2c_device_id