一. 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控制器的数据传输函数,下一篇分析
阅读(2338) | 评论(0) | 转发(0) |