i2c子系统整理的图
参考上图,可以整理上看清i2c子系统的脉络框架。下面后部分代码跟踪,或补充各个细节的地方。
代码补充
这里是不使用设备树情况下,设备信息的代码
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("24c08", 0x50),
.platform_data = &at24c08,
},
};
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_i2c0_resource),
.resource = s3c_i2c0_resource,
};
static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_rtc,
...
};
__init mini2440_init(void)
{
i2c_register_board_info(0, mini2440_i2c_devs,
ARRAY_SIZE(mini2440_i2c_devs));
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
}
注意这里为什么,24c08使用的是i2c_board_info ,而rtc和i2c适配器使用的都是platform_device 。原因是24c08使用了是i2c子系统,rtc和i2c使用的platform子系统。
一下代码中可以看出,以海思的i2c适配器为例,代码在busses目录中:
struct platform_device hi_i2c0_device = {
.name = HI_I2C,
.id = 0,
.resource = hi_i2c0_resources,
.num_resources = ARRAY_SIZE(hi_i2c0_resources),
.dev = {
.platform_data = &hi_i2c0_platform_data,
}
};
static struct platform_device *hi_i2c_devices[] __initdata = {
&hi_i2c0_device,
};
static int __init hi_i2c_module_init(void)
{
int ret;
ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices));
if (ret) {
hi_err("i2c device register failed!\n");
return ret;
}
ret = platform_driver_register(&hi_i2c_driver);
if (ret) {
platform_device_unregister(&hi_i2c0_device);
hi_err("i2c driver register failed!\n");
return ret;
}
return ret;
}
这里可以看出来i2c适配器的驱动使用的platform设备,尤其需要关心的参数是id号,后面i2c设备驱动,需要匹配i2c适配器,需要使用这个id,单个i2c不需要考虑,多个i2c需要关心,不要对应错啦。
继续看i2c_dev.c这里为i2c适配器,创建了,设备节点:
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);//设备节点的序号
}
//这里提供了file_operation的接口
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
static int __init i2c_dev_init(void)
{
int res;
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);//遍历创建设备节点
return 0;
}
这里贴出的部分代码详细介绍了,/dev/i2c-x,节点创建的原理,过程。
接下来看一下i2c_core.c函数:
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,
};
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
}
这里主要是注册了i2c总线
下面在看一下,i2c_core.c提供给设备驱动的接口:
首先看驱动代码,以at24.c为例
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
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);
return i2c_add_driver(&at24_driver);
}
可以看出主要调用的是i2c_add_driver
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
最终调用如下:
i2c_register_driver
__process_new_driver
i2c_do_add_adapter
{
i2c_detect(adap, driver);
if (driver->attach_adapter)
}
i2c_detect
i2c_detect_address
err = driver->detect(temp_client, &info);
if (err) {
return err == -ENODEV ? 0 : err;
}
i2c_new_device(adapter, &info);
device_register
也是是用了,device_register注册驱动。
至于i2c_register_board_info,比较简单,只是将板级信息放到了链表中。
应用程序访问,驱动的接口最终都是调用i2c_transfer,
i2c_transfer
__i2c_transfer
ret = adap->algo->master_xfer(adap, msgs, num)
继续看一下适配器,算法是怎样组织的:
static const struct i2c_algorithm hi_i2c_algo = {
.master_xfer = hi_i2c_xfer,
.functionality = hi_i2c_func,
};
hi_i2c_probe
{
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
adap->algo = &hi_i2c_algo;
adap->nr = pdev->id;
errorcode = i2c_add_numbered_adapter(adap);
}
i2c_module_init
{
ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices));
ret = platform_driver_register(&hi_i2c_driver);
}