linux驱动 7.I2C驱动

简述

I2c是一个应用很广的总线。通讯协议简单,而且一条总线上可以挂载多个设备,在这里讲一下I2c在linux中的架构。首先借一下网络上的一张图:
在这里插入图片描述

I2c的框架主要分为以上这么几个部分。下面根据代码,相机讲述一下,各个部分的实现。

软件版本:linux 4.14.98
硬件型号:imx7

1. I2C-imx 控制器驱动

文件:drivers/i2c/busses/i2c-imx.c

static const struct dev_pm_ops i2c_imx_pm_ops = {
        SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
        SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
                           i2c_imx_runtime_resume, NULL)
};
#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
#else
#define I2C_IMX_PM_OPS NULL
#endif /* CONFIG_PM */

static struct platform_driver i2c_imx_driver = {
        .probe = i2c_imx_probe,
        .remove = i2c_imx_remove,
        .driver = {
                .name = DRIVER_NAME,
                .pm = I2C_IMX_PM_OPS,
                .of_match_table = i2c_imx_dt_ids,
        },
        .id_table = imx_i2c_devtype,
};

static int __init i2c_adap_imx_init(void)
{
        return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);

static void __exit i2c_adap_imx_exit(void)
{
        platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

其中i2c_imx_dt_ids定义如下:

static const struct of_device_id i2c_imx_dt_ids[] = {
        { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
        { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
        { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
        { .compatible = "fsl,imx7d-i2c", .data = &imx7d_i2c_hwdata, },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);

下面看下probe函数

static int i2c_imx_probe(struct platform_device *pdev)
{
        const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
                                                           &pdev->dev);
        struct imx_i2c_struct *i2c_imx;
        struct resource *res;
        struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
        void __iomem *base;
        int irq, ret;
        dma_addr_t phy_addr;

        dev_dbg(&pdev->dev, "<%s>\n", __func__);

        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "can't get irq number\n");
                return irq;
        }

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(base))
                return PTR_ERR(base);

        phy_addr = (dma_addr_t)res->start;
        i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
        if (!i2c_imx)
                return -ENOMEM;

        if (of_id)
                i2c_imx->hwdata = of_id->data;
        else
                i2c_imx->hwdata = (struct imx_i2c_hwdata *)
                                platform_get_device_id(pdev)->driver_data;

        /* Setup i2c_imx driver structure */
        strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
        i2c_imx->adapter.owner          = THIS_MODULE;
        i2c_imx->adapter.algo           = &i2c_imx_algo;
        i2c_imx->adapter.dev.parent     = &pdev->dev;
        i2c_imx->adapter.nr             = pdev->id;
        i2c_imx->adapter.dev.of_node    = pdev->dev.of_node;
        i2c_imx->base                   = base;

        /* Get I2C clock */
        i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(i2c_imx->clk)) {
                dev_err(&pdev->dev, "can't get I2C clock\n");
                return PTR_ERR(i2c_imx->clk);
        }

        ret = clk_prepare_enable(i2c_imx->clk);
        if (ret) {
                dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
                return ret;
        }

        /* Request IRQ */
        ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
                                IRQF_SHARED | IRQF_NO_SUSPEND,
                                pdev->name, i2c_imx);
        if (ret) {
                dev_err(&pdev->dev, "can't claim irq %d\n", irq);
                goto clk_disable;
        }

        /* Init queue */
        init_waitqueue_head(&i2c_imx->queue);

        /* Set up adapter data */
        i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

        /* Set up platform driver data */
        platform_set_drvdata(pdev, i2c_imx);

        pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);

        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0)
                goto rpm_disable;

        /* Set up clock divider */
        i2c_imx->bitrate = IMX_I2C_BIT_RATE;
        ret = of_property_read_u32(pdev->dev.of_node,
                                   "clock-frequency", &i2c_imx->bitrate);
        if (ret < 0 && pdata && pdata->bitrate)
                i2c_imx->bitrate = pdata->bitrate;

        /*
         * This limit caused by an i.MX7D hardware issue(e7805 in Errata).
         * If there is no limit, when the bitrate set up to 400KHz, it will
         * cause the SCK low level period less than 1.3us.
         */
        if (is_imx7d_i2c(i2c_imx) && i2c_imx->bitrate > IMX_I2C_MAX_E_BIT_RATE)
                i2c_imx->bitrate = IMX_I2C_MAX_E_BIT_RATE;

        /* Set up chip registers to defaults */
        imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
                        i2c_imx, IMX_I2C_I2CR);
        imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);

        /* Init optional bus recovery function */
        ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
        /* Give it another chance if pinctrl used is not ready yet */
        if (ret == -EPROBE_DEFER)
                goto rpm_disable;

        /* Add I2C adapter */
        ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
        if (ret < 0)
                goto rpm_disable;

        pm_runtime_mark_last_busy(&pdev->dev);
        pm_runtime_put_autosuspend(&pdev->dev);

        dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
        dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
        dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
                i2c_imx->adapter.name);
        dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

        /* Init DMA config if supported */
        i2c_imx_dma_request(i2c_imx, phy_addr);

        return 0;   /* Return OK */

rpm_disable:
        pm_runtime_put_noidle(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
        pm_runtime_dont_use_autosuspend(&pdev->dev);

clk_disable:
        clk_disable_unprepare(i2c_imx->clk);
        return ret;
}

在这个函数里面重点是 ret = i2c_add_numbered_adapter(&i2c_imx->adapter),注册I2C适配器和I2C设备都在里面实现。这个函数是在i2c core中实现。待会儿我们讲解。在i2c控制器驱动这边主要的是i2c_algorithm和irq的实现。i2c_algorithm定义如下

static const struct i2c_algorithm i2c_imx_algo = {
        .master_xfer    = i2c_imx_xfer,
        .functionality  = i2c_imx_func,
};
static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
                | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}
..............................................................
..............................................................
..............................................................
static int i2c_imx_xfer(struct i2c_adapter *adapter,
                                                struct i2c_msg *msgs, int num)
{
        unsigned int i, temp;
        int result;
        bool is_lastmsg = false;
        bool enable_runtime_pm = false;
        struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);

        dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);


        if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) {
                pm_runtime_enable(i2c_imx->adapter.dev.parent);
                enable_runtime_pm = true;
        }

        result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
        if (result < 0)
                goto out;

        /* Start I2C transfer */
        result = i2c_imx_start(i2c_imx);
        if (result) {
                if (i2c_imx->adapter.bus_recovery_info) {
                        i2c_recover_bus(&i2c_imx->adapter);
                        result = i2c_imx_start(i2c_imx);
                }
        }

        if (result)
                goto fail0;

        /* read/write data */
        for (i = 0; i < num; i++) {
                if (i == num - 1)
                        is_lastmsg = true;

                if (i) {
                        dev_dbg(&i2c_imx->adapter.dev,
                                "<%s> repeated start\n", __func__);
                        temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
                        temp |= I2CR_RSTA;
                        imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
                        result = i2c_imx_bus_busy(i2c_imx, 1);
                        if (result)
                                goto fail0;
                }
                dev_dbg(&i2c_imx->adapter.dev,
                        "<%s> transfer message: %d\n", __func__, i);
                /* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUS
                temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
                dev_dbg(&i2c_imx->adapter.dev,
                        "<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
                        __func__,
                        (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
                        (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
                        (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
                temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
                dev_dbg(&i2c_imx->adapter.dev,
                        "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
                        __func__,
                        (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
                        (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
                        (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
                        (temp & I2SR_RXAK ? 1 : 0));
#endif
                if (msgs[i].flags & I2C_M_RD)
                        result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
                else {
                        if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
                                result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
                        else
                                result = i2c_imx_write(i2c_imx, &msgs[i]);
                }
                if (result)
                        goto fail0;
        }

fail0:
        /* Stop I2C transfer */
        i2c_imx_stop(i2c_imx);

        pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
        pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);

out:
        if (enable_runtime_pm)
                pm_runtime_disable(i2c_imx->adapter.dev.parent);

        dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
                (result < 0) ? "error" : "success msg",
                        (result < 0) ? result : num);
        return (result < 0) ? result : num;
}

i2c中断申请如下。
devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
IRQF_SHARED | IRQF_NO_SUSPEND,
pdev->name, i2c_imx);
devm_request_irq()函数如下:

devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
                 unsigned long irqflags, const char *devname, void *dev_id)
{
        return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
                                         devname, dev_id);
}
.........................................
..........................................
/**
 *      devm_request_threaded_irq - allocate an interrupt line for a managed device
 *      @dev: device to request interrupt for
 *      @irq: Interrupt line to allocate
 *      @handler: Function to be called when the IRQ occurs
 *      @thread_fn: function to be called in a threaded interrupt context. NULL
 *                  for devices which handle everything in @handler
 *      @irqflags: Interrupt type flags
 *      @devname: An ascii name for the claiming device, dev_name(dev) if NULL
 *      @dev_id: A cookie passed back to the handler function
 *
 *      Except for the extra @dev argument, this function takes the
 *      same arguments and performs the same function as
 *      request_threaded_irq().  IRQs requested with this function will be
 *      automatically freed on driver detach.
 *
 *      If an IRQ allocated with this function needs to be freed
 *      separately, devm_free_irq() must be used.
 */
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
                              irq_handler_t handler, irq_handler_t thread_fn,
                              unsigned long irqflags, const char *devname,
                              void *dev_id)
{
        struct irq_devres *dr;
        int rc;

        dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),
                          GFP_KERNEL);
        if (!dr)
                return -ENOMEM;

        if (!devname)
                devname = dev_name(dev);

        rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
                                  dev_id);
        if (rc) {
                devres_free(dr);
                return rc;
        }

        dr->irq = irq;
        dr->dev_id = dev_id;
        devres_add(dev, dr);

        return 0;
}
EXPORT_SYMBOL(devm_request_threaded_irq);

接下来我们到i2c core 层来看看。

2. I2C core

文件位置:drivers/i2c/i2c-core-base.c
首先来看i2c_init()函数。

static int __init i2c_init(void)
{
        int retval;

        retval = of_alias_get_highest_id("i2c");

        down_write(&__i2c_board_lock);
        if (retval >= __i2c_first_dynamic_bus_num)
                __i2c_first_dynamic_bus_num = retval + 1;
        up_write(&__i2c_board_lock);

        retval = bus_register(&i2c_bus_type);
        if (retval)
                return retval;

        is_registered = true;

#ifdef CONFIG_I2C_COMPAT
        i2c_adapter_compat_class = class_compat_register("i2c-adapter");
        if (!i2c_adapter_compat_class) {
                retval = -ENOMEM;
                goto bus_err;
        }
#endif
        retval = i2c_add_driver(&dummy_driver);
        if (retval)
                goto class_err;

        if (IS_ENABLED(CONFIG_OF_DYNAMIC))
                WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
        if (IS_ENABLED(CONFIG_ACPI))
                WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));

        return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
        class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
        is_registered = false;
        bus_unregister(&i2c_bus_type);
        return retval;
}

在该函数中利用 bus_register(&i2c_bus_type)向系统注册I2c总线。
其中i2c_bus_type定义如下:

struct bus_type i2c_bus_type = {
        .name           = "i2c",
        .match          = i2c_device_match,
        .probe          = i2c_device_probe,
        .remove         = i2c_device_remove,
        .shutdown       = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

在这里我们看一下i2c_device_probe()函数:

static int i2c_device_probe(struct device *dev)
{
        struct i2c_client       *client = i2c_verify_client(dev);
        struct i2c_driver       *driver;
        int status;

        if (!client)
                return 0;

        driver = to_i2c_driver(dev->driver);

        if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
                int irq = -ENOENT;

                if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
                        dev_dbg(dev, "Using Host Notify IRQ\n");
                        irq = i2c_smbus_host_notify_to_irq(client);
                } else if (dev->of_node) {
                        irq = of_irq_get_byname(dev->of_node, "irq");
                        if (irq == -EINVAL || irq == -ENODATA)
                                irq = of_irq_get(dev->of_node, 0);
                } else if (ACPI_COMPANION(dev)) {
                        irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
                }
                if (irq == -EPROBE_DEFER)
                        return irq;

                if (irq < 0)
                        irq = 0;

                client->irq = irq;
        }

        /*
         * An I2C ID table is not mandatory, if and only if, a suitable OF
         * or ACPI ID table is supplied for the probing device.
         */
        if (!driver->id_table &&
            !i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&
            !i2c_of_match_device(dev->driver->of_match_table, client))
                return -ENODEV;

        if (client->flags & I2C_CLIENT_WAKE) {
                int wakeirq = -ENOENT;

                if (dev->of_node) {
                        wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
                        if (wakeirq == -EPROBE_DEFER)
                                return wakeirq;
                }

                device_init_wakeup(&client->dev, true);

                if (wakeirq > 0 && wakeirq != client->irq)
                        status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
                else if (client->irq > 0)
                        status = dev_pm_set_wake_irq(dev, client->irq);
                else
                        status = 0;

                if (status)
                        dev_warn(&client->dev, "failed to set up wakeup irq\n");
        }

        dev_dbg(dev, "probe\n");

        status = of_clk_set_defaults(dev->of_node, false);
        if (status < 0)
                goto err_clear_wakeup_irq;

        status = dev_pm_domain_attach(&client->dev, true);
        if (status == -EPROBE_DEFER)
                goto err_clear_wakeup_irq;

        /*
         * When there are no more users of probe(),
         * rename probe_new to probe.
         */
        if (driver->probe_new)
                status = driver->probe_new(client);
        else if (driver->probe)
                status = driver->probe(client,
                                       i2c_match_id(driver->id_table, client));
        else
                status = -EINVAL;

        if (status)
                goto err_detach_pm_domain;

        return 0;

err_detach_pm_domain:
        dev_pm_domain_detach(&client->dev, true);
err_clear_wakeup_irq:
        dev_pm_clear_wake_irq(&client->dev);
        device_init_wakeup(&client->dev, false);
        return status;
}

如果在driver中初始化了probe,那么就使用driver中定义的probe.,就是上一节中的probe函数。

下面我们看一下上一节中提到的 i2c_add_numbered_adapter(&i2c_imx->adapter),这个函数实现:

/**
 * i2c_add_numbered_adapter - declare i2c adapter, use static bus number
 * @adap: the adapter to register (with adap->nr initialized)
 * Context: can sleep
 *
 * This routine is used to declare an I2C adapter when its bus number
 * matters.  For example, use it for I2C adapters from system-on-chip CPUs,
 * or otherwise built in to the system's mainboard, and where i2c_board_info
 * is used to properly configure I2C devices.
 *
 * If the requested bus number is set to -1, then this function will behave
 * identically to i2c_add_adapter, and will dynamically assign a bus number.
 *
 * If no devices have pre-been declared for this bus, then be sure to
 * register the adapter before any dynamically allocated ones.  Otherwise
 * the required bus ID may not be available.
 *
 * When this returns zero, the specified adapter became available for
 * clients using the bus number provided in adap->nr.  Also, the table
 * of I2C devices pre-declared using i2c_register_board_info() is scanned,
 * and the appropriate driver model device nodes are created.  Otherwise, a
 * negative errno value is returned.
 */
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
        if (adap->nr == -1) /* -1 means dynamically assign bus id */
                return i2c_add_adapter(adap);

        return __i2c_add_numbered_adapter(adap);
}
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);

......................................
/**
 * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1
 * @adap: the adapter to register (with adap->nr initialized)
 * Context: can sleep
 *
 * See i2c_add_numbered_adapter() for details.
 */
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
        int id;

        mutex_lock(&core_lock);
        id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
        mutex_unlock(&core_lock);
        if (WARN(id < 0, "couldn't get idr"))
                return id == -ENOSPC ? -EBUSY : id;

        return i2c_register_adapter(adap);
}
..............................................
/**
 * i2c_add_adapter - declare i2c adapter, use dynamic bus number
 * @adapter: the adapter to add
 * Context: can sleep
 *
 * This routine is used to declare an I2C adapter when its bus number
 * doesn't matter or when its bus number is specified by an dt alias.
 * Examples of bases when the bus number doesn't matter: I2C adapters
 * dynamically added by USB links or PCI plugin cards.
 *
 * When this returns zero, a new bus number was allocated and stored
 * in adap->nr, and the specified adapter became available for clients.
 * Otherwise, a negative errno value is returned.
 */
int i2c_add_adapter(struct i2c_adapter *adapter)
{
        struct device *dev = &adapter->dev;
        int id;

        if (dev->of_node) {
                id = of_alias_get_id(dev->of_node, "i2c");
                if (id >= 0) {
                        adapter->nr = id;
                        return __i2c_add_numbered_adapter(adapter);
                }
        }

        mutex_lock(&core_lock);
        id = idr_alloc(&i2c_adapter_idr, adapter,
                       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
        mutex_unlock(&core_lock);
        if (WARN(id < 0, "couldn't get idr"))
                return id;

        adapter->nr = id;

        return i2c_register_adapter(adapter);
}
EXPORT_SYMBOL(i2c_add_adapter);

重点是i2c_register_adapter这个函数:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
        int res = -EINVAL;

        /* Can't register until after driver model init */
        if (WARN_ON(!is_registered)) {
                res = -EAGAIN;
                goto out_list;
        }

        /* Sanity checks */
        if (WARN(!adap->name[0], "i2c adapter has no name"))
                goto out_list;

        if (!adap->algo) {
                pr_err("adapter '%s': no algo supplied!\n", adap->name);
                goto out_list;
        }

        if (!adap->lock_ops)
                adap->lock_ops = &i2c_adapter_lock_ops;

        rt_mutex_init(&adap->bus_lock);
        rt_mutex_init(&adap->mux_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;

        /* register soft irqs for Host Notify */
        res = i2c_setup_host_notify_irq_domain(adap);
        if (res) {
                pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
                       adap->name, res);
                goto out_list;
        }

        dev_set_name(&adap->dev, "i2c-%d", adap->nr);
        adap->dev.bus = &i2c_bus_type;
        adap->dev.type = &i2c_adapter_type;
        res = device_register(&adap->dev);
        if (res) {
                pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
                goto out_list;
        }

        dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

        pm_runtime_no_callbacks(&adap->dev);
        pm_suspend_ignore_children(&adap->dev, true);
        pm_runtime_enable(&adap->dev);

#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

        i2c_init_recovery(adap);

        /* create pre-declared device nodes */
        of_i2c_register_devices(adap);
        i2c_acpi_register_devices(adap);
        i2c_acpi_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_scan_static_board_info(adap)函数,这个函数中会注册所有设备树中定义的设备。看代码:

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);
}
..............................
/**
 * i2c_new_device - instantiate an i2c device
 * @adap: the adapter managing the device
 * @info: describes one I2C device; bus_num is ignored
 * Context: can sleep
 *
 * Create an i2c device. Binding is handled through driver model
 * probe()/remove() methods.  A driver may be bound to this device when we
 * return from this function, or any later moment (e.g. maybe hotplugging will
 * load the driver module).  This call is not appropriate for use by mainboard
 * initialization logic, which usually runs during an arch_initcall() long
 * before any i2c_adapter could exist.
 *
 * This returns the new i2c client, which may be saved for later use with
 * i2c_unregister_device(); or NULL to indicate an error.
 */
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;
        if (!client->irq)
                client->irq = i2c_dev_irq_from_resources(info->resources,
                                                         info->num_resources);

        strlcpy(client->name, info->type, sizeof(client->name));

        status = i2c_check_addr_validity(client->addr, client->flags);
        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, i2c_encode_flags_to_addr(client));
        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;
        client->dev.fwnode = info->fwnode;

        i2c_dev_set_name(adap, client);

        if (info->properties) {
                status = device_add_properties(&client->dev, info->properties);
                if (status) {
                        dev_err(&adap->dev,
                                "Failed to add properties to client %s: %d\n",
                                client->name, status);
                        goto out_err;
                }
        }

        status = device_register(&client->dev);
        if (status)
                goto out_free_props;

        dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
                client->name, dev_name(&client->dev));

        return client;

out_free_props:
        if (info->properties)
                device_remove_properties(&client->dev);
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;
}
EXPORT_SYMBOL_GPL(i2c_new_device);

在i2c_register_adapter函数中,bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)函数会遍历所有注册了的驱动。利用__process_new_adapter()函数进行处理。


static int i2c_do_add_adapter(struct i2c_driver *driver,
                              struct i2c_adapter *adap)
{
        /* Detect supported devices on that bus, and instantiate them */
        i2c_detect(adap, driver);

        /* Let legacy drivers scan this bus for matching devices */
        if (driver->attach_adapter) {
                dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
                         driver->driver.name);
                dev_warn(&adap->dev,
                         "Please use another way to instantiate your i2c_client\n");
                /* We ignore the return code; if it fails, too bad */
                driver->attach_adapter(adap);
        }
        return 0;
}

static int __process_new_adapter(struct device_driver *d, void *data)
{
        return i2c_do_add_adapter(to_i2c_driver(d), data);
}

该函数会检查驱动与适配器上所在总线上的设备是否匹配。

3.I2C设备驱动

最后我们来看一下设备驱动。说一下一个i2c通用的驱动,其位于文件drivers/i2c/i2c-dev.c,首先来看
i2c_dev_init(void)函数的定义:

static int __init i2c_dev_init(void)
{
        int res;

        printk(KERN_INFO "i2c /dev entries driver\n");

        res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
        if (res)
                goto out;

        i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
        if (IS_ERR(i2c_dev_class)) {
                res = PTR_ERR(i2c_dev_class);
                goto out_unreg_chrdev;
        }
        i2c_dev_class->dev_groups = i2c_groups;

        /* Keep track of adapters which will be added or removed later */
        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;

out_unreg_class:
        class_destroy(i2c_dev_class);
out_unreg_chrdev:
        unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
        printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
        return res;
}

static void __exit i2c_dev_exit(void)
{
        bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);
        i2c_for_each_dev(NULL, i2cdev_detach_adapter);
        class_destroy(i2c_dev_class);
        unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
}

其中 class_create(THIS_MODULE, “i2c-dev”),是创建设备节点。我们看i2c_for_each_dev(NULL, i2cdev_attach_adapter)函数,
i2cdev_attach_adapter()定义如下:

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
        struct i2c_adapter *adap;
        struct i2c_dev *i2c_dev;
        int res;

        if (dev->type != &i2c_adapter_type)
                return 0;
        adap = to_i2c_adapter(dev);

        i2c_dev = get_free_i2c_dev(adap);
        if (IS_ERR(i2c_dev))
                return PTR_ERR(i2c_dev);

        cdev_init(&i2c_dev->cdev, &i2cdev_fops);
        i2c_dev->cdev.owner = THIS_MODULE;
        res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
        if (res)
                goto error_cdev;

        /* 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);
        if (IS_ERR(i2c_dev->dev)) {
                res = PTR_ERR(i2c_dev->dev);
                goto error;
        }

        pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
                 adap->name, adap->nr);
        return 0;
error:
        cdev_del(&i2c_dev->cdev);
error_cdev:
        put_i2c_dev(i2c_dev);
        return res;
}

查看get_free_i2c_dev()函数。


static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
        struct i2c_dev *i2c_dev;

        if (adap->nr >= I2C_MINORS) {
                printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
                       adap->nr);
                return ERR_PTR(-ENODEV);
        }

        i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
        if (!i2c_dev)
                return ERR_PTR(-ENOMEM);
        i2c_dev->adap = adap;

        spin_lock(&i2c_dev_list_lock);
        list_add_tail(&i2c_dev->list, &i2c_dev_list);
        spin_unlock(&i2c_dev_list_lock);
        return i2c_dev;
}

这个函数其实就是为i2c对象申请内存,并且加入到i2c_dev_list链表中。
参考文章:
https://www.cnblogs.com/lknlfy/p/3265078.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动开发中的I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器、传感器、存储器等外设。下面是关于Linux驱动开发中的I2C的一些重要概念和步骤: 1. I2C总线:I2C总线是一种多主从设备的串行通信总线,可以同时连接多个设备。在Linux中,I2C总线由I2C控制器和I2C适配器组成。 2. I2C控制器:I2C控制器是硬件模块,负责控制I2C总线的通信。在Linux中,每个I2C控制器都有一个对应的驱动程序。 3. I2C适配器:I2C适配器是与I2C总线连接的硬件接口,可以是主机上的硬件接口或者通过GPIO模拟的软件接口。在Linux中,每个I2C适配器都有一个对应的驱动程序。 4. I2C设备驱动:每个连接到I2C总线上的设备都需要一个对应的设备驱动程序来进行控制和通信。在Linux中,每个I2C设备都有一个对应的驱动程序。 在Linux驱动开发中,使用I2C的步骤如下: 1. 注册I2C适配器:首先需要在Linux内核中注册I2C适配器,以便系统能够识别和管理I2C总线上的设备。 2. 创建I2C设备驱动:为每个连接到I2C总线上的设备编写对应的设备驱动程序,包括设备的初始化、读写操作等。 3. 注册I2C设备驱动:将设备驱动程序注册到Linux内核中,以便系统能够正确地加载和使用该驱动程序。 4. 使用I2C设备:在应用程序中通过打开设备文件、调用相应的读写接口来使用I2C设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值