I2C子系统驱动架构 - 具体实现

文章系列

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_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架构中也提供了函数,具体下面介绍

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模块有关系,需具体对待,整个流程就是这样

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值