总的意思就是i2c使用的这么频繁,既然是搞研发,怎么说也得把i2c框架了解一下吧。
2,那就开始吧
i2c这个框架中有几个名词 adaptr, algorithm, client
1. adapter : 这个对应一个i2c适配器,就是你的三星、mtk、高通啦这些soc里的i2c硬件模块
2. algorithm: 算法的意思,有点高大上的感觉。接地气的说法就是对应控制i2c发送start end等信号的东西,就当是i2c硬件模块中的控制模块
3. client:对应外设i2c器件
以上的说法比较俗,也不是非常的完善,暂时先有个大体的框架
我们再来说说这几个名词的对应关系吧
adapter这个肯定是独一无二的了,那么algorithm也是独一无二的
adapter <---> algorithm
这个client,外设吗!外设肯定不止一个了,一个i2c线上可以挂好多设备啊
adapter <---> 多个client
algorithm <---> 多个client
3,其实我不想贴代码
说来说去,还是贴代码来的的实在,一是说服力强,二是占空间!!!
3.1板级支持包
板级支持包里,一般都会有这样的一段代码(请原谅我还没有使用dts...)
static struct i2c_board_info i2c0_boardinfo[] = {
{
//I2C_BOARD_INFO("tlv320aic3x", 0x18),
I2C_BOARD_INFO("wm8523", 0x1a),
}
}
对就是这个i2c_board_info这个意思是我要把这个"wm8523"i2c设备挂在i2c0上
int __init
i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
down_write(&__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);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
这个函数做了啥?就是把外设的信息,外设要挂载的信息通过一个链表挂在了和i2c还没产生啥关系啊。。。
3.2 内核注册i2c设备
既然要使用i2c,那么就去创建i2c的框架吧
删删删 终于把代码删减到现在这么多了
static int __devinit
omap_i2c_probe(struct platform_device *pdev)
{
/* dev的类似和平台厂商有关,不用过多纠结 */
struct omap_i2c_dev *dev;
/* 这个就是adapter了,实例化的i2c硬件模块 */
struct i2c_adapter *adap;
...
dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
...
/* 初始化i2c的设备 不关心 */
omap_i2c_init(dev);
...
/* 申请i2c的中断 不关心 */
r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
...
adap = &dev->adapter;
...
/* 这个就是algorithm了,发送、结束、应答全靠它! */
adap->algo = &omap_i2c_algo;
...
/* 增加一个有编号的i2c适配器,
* 可以理解 soc还有好几个i2c呢
* i2c0,i2c1,i2c2.... i2cX
*/
adap->nr = pdev->id;
r = i2c_add_numbered_adapter(adap);
...
}
看看i2c_add_numbered_adapter,再删删
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
status = i2c_register_adapter(adap);
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
...
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
/* 这里成功注册一个 /sys/bus/platform/devices/xxxxx/i2c-1 */
res = device_register(&adap->dev);
...
/* create pre-declared device nodes */
/* 上面板级支持包里挂在在__i2c_board_list上信息要起作用了 */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
...
/* Notify drivers */
/* 这里主要是找driver */
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
...
}
ok 我们看i2c_scan_static_board_info
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
这里当注册i2c0的适配器adapter的时候,会到__i2c_board_list链表找到要挂在i2c0的外设"wm8523" 。
哎呀,好巧,那么就去i2c_new_device吧
这个i2c_new_device就会产生我们经常说的device和driver匹配的时候会调用probe函数,对就是那个device.
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
/* 其实这个就是我们在驱动里面操作的client, 这里早早的就出现了 */
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
/* 找到adapter 发信息离不开它 */
client->adapter = adap;
...
client->flags = info->flags;
/* 外设自己的地址自己记住,只有自己知道 */
client->addr = info->addr;
client->irq = info->irq;
/* client->name = "wm8523" */
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
/* 检测地址是否有效 */
status = i2c_check_client_addr_validity(client);
/* Check for address business */
/* 检测地址是否和这个i2c上的其他器件冲突 */
status = i2c_check_addr_busy(adap, client->addr);
/* 做人不能忘本,一定要记住自己挂在哪条总线
* 因为device找到driver后的probe还要靠总线呢
*/
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
/* 这里把dev的name命名成了 "0-001a",为什么不是"wm8523"
* 这样driver岂不是通过名字找不到device了???
* 注意看上面红色的行
* client->name = "wm8523"
*/
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
/* dev带着"0-001a"而不是"wm8523"的名字遗憾的去注册了 */
status = device_register(&client->dev);
if (status)
goto out_err;
/* return也没人用啊
* client在这里了等待
* 等到有设备找到带着"0-001a"名字注册的dev的时候,
* 就是client再现江湖的时候
*/
return client;
out_err:
kfree(client);
return NULL;
}
这里整理一下client有什么?没有driver谁去操作client啊,所以去找driver吧
3.3 去找driver吧
static const struct i2c_device_id wm8523_i2c_id[] = {
{ "wm8523", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
static struct i2c_driver wm8523_i2c_driver = {
.driver = {
.name = "wm8523-codec",
.owner = THIS_MODULE,
.of_match_table = wm8523_of_match,
},
.probe = wm8523_i2c_probe,
.remove = __devexit_p(wm8523_i2c_remove),
.id_table = wm8523_i2c_id,
};
#endif
static int __init wm8523_modinit(void)
{
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8523_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
ret);
}
#endif
return 0;
}
代码是贴了但是probe怎么开始执行的?#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
...
/* 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 */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
记住不能忘本,不能忘记自己挂在i2c_bus_type上 -> i2c_bus_type.match <--> i2c_device_match
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
看到没那个带着"0-001a"名字注册的dev找到了