I2C子系统驱动架构 - 具体实现
2016-07-04 11:36
537 查看
文章系列
I2C子系统驱动架构 - 简介I2C子系统驱动架构 - 驱动框架
I2C子系统驱动架构 - 具体实现
I2C设备注册流程
第一步是先进行i2c bus设备的注册流程,然后是i2c 从设备的注册流程,遵从这么几步:platform_bus-> i2c_bus->i2c设备i2c bus设备的注册流程
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 从设备的注册流程
i2c从设备的注册函数有多个,可以查看系列文章2,不过最终都会调用函数i2c_new_devicestruct 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架构中也提供了函数,具体下面介绍
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模块有关系,需具体对待,整个流程就是这样
相关文章推荐
- SNS网站成功原因剖析_完结
- java的三层架构03_持久层(数据处理)
- java的三层架构02_业务层(逻辑处理)
- java的三层架构01_展示层
- 30个酷毙的交互式网站(HTML5+CSS3)
- 点点点系列最少的程序架构
- 小数据:理论和架构
- 查看Linux系统架构类型的5条常用命令
- 经常访问的网站
- 一款不错的html5网站模板案例代码下载,用于开发个人网站,兼容手机与PC
- haproxy高可用以及双主模式(二)
- 滴滴passport设计之道:帐号体系高可用的7条经验
- 网站搜索排名失真 陷阱重重小心你的钱!
- 企业级应用架构解析
- 网站运营怎么制作“诱饵”把目标用户“钓”上来
- 网站运营怎么制作“诱饵”把目标用户“钓”上来
- Machine Learning总结性网站
- 关于游戏架构设计的一些整理吧
- 网站的架构演进
- 架构中的设计原则总结