I2C子系统驱动架构 - 简介
I2C子系统驱动架构 - 驱动框架
I2C子系统驱动架构 - 具体实现
第一步是先进行i2c bus设备的注册流程,然后是i2c 从设备的注册流程,遵从这么几步:platform_bus-> i2c_bus->i2c设备
i2c-bus是挂载在platform总线上的,所以一个i2c-bus设备就是一个platform-device,所以就要通过函数platform_device_register来进行注册,而这一过程已经在linux设备树的解释介绍过了,这里不再多介绍。
接下来介绍platform_driver_register来进行驱动的注册过程,每个芯片的i2c模块的驱动代码都在drivers/i2c/busses目录下,platform-driver注册成功后就匹配成功,进入’probe’函数。
我们知道’probe’函数会进行一些初始化设置,然后进入函数i2c_add_numbered_adapter,在本文的系列文章2里,已经介绍过还有另外的函数来进行adapter的注册,可以参考,不过他们最终都会进入函数i2c_register_adapter,下面介绍此函数
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
}
rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;--设置总线类型
adap->dev.type = &i2c_adapter_type;--设置设备类型(对于i2c从设备来说是i2c_client_type类型,都是挂在i2c-bus上)
res = device_register(&adap->dev);--进行adapter注册
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
/* bus recovery specific initialization */
if (adap->bus_recovery_info) {
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
if (!bri->recover_bus) {
dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}
/* Generic GPIO recovery */
if (bri->recover_bus == i2c_generic_gpio_recovery) {
if (!gpio_is_valid(bri->scl_gpio)) {
dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}
if (gpio_is_valid(bri->sda_gpio))
bri->get_sda = get_sda_gpio_value;
else
bri->get_sda = NULL;
bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
} else if (!bri->set_scl || !bri->get_scl) {
/* Generic SCL recovery */
dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
adap->bus_recovery_info = NULL;
}
}
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap);--进行i2c从设备的device注册
acpi_i2c_register_devices(adap);
acpi_i2c_install_space_handler(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
完成i2c-bus的注册后,还会把挂载在总线上的设备通过函数of_i2c_register_devices进行注册,最终通过函数i2c_new_device来进行,下面介绍此方面的内容
i2c从设备的注册函数有多个,可以查看系列文章2,不过最终都会调用函数i2c_new_device
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type; --设置总线类型
client->dev.type = &i2c_client_type; --设备设备类型
client->dev.of_node = info->of_node;
ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion);
i2c_dev_set_name(adap, client);
status = device_register(&client->dev); --进行设备注册
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
完成device的注册后,此device就挂载到i2c-bus上,下面进行driver的注册,使用函数i2c_register_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* 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;
/* Drivers should switch to dev_pm_ops instead. */
if (driver->suspend)
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
driver->driver.name);
if (driver->resume)
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
driver->driver.name);
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_driver->probe,然后就是进行字符设备的注册,向用户空间提供操作设备的接口,操作i2c设备的函数在i2c架构中也提供了函数,具体下面介绍
在字符设备中进行读写最终会通过read和write函数来进行,然后会调用i2c架构中的i2c_master_recv和i2c_master_send函数,这两个函数最后都会调用函数i2c_transfer->__i2c_transfer,我们看看这个函数
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
* enabled. This is an efficient way of keeping the for-loop from
* being executed when not needed.
*/
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, &msgs[i], i);
else
trace_i2c_write(adap, &msgs[i], i);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
trace_i2c_result(adap, i, ret);
}
return ret;
}
通过函数可以看出,最终是要调用i2c_algorithm的函数,而这个函数和各个i2c模块有关系,需具体对待,整个流程就是这样