一、前言

因为自己在研发过程中经常要涉及到tp驱动程序,而tp驱动就涉及到i2c驱动。经常可以看到在驱动程序中会定义一个struct i2c_driver的数据结构,并实现里面的某些成员,比如probe、resume、suspend、remove、id_table,之后会在module_init()中调用i2c_add_driver()添加这个驱动。比如说像下面这个样子:

static const struct i2c_device_id xxx_ts_id[] = {
    {xxx, 0},
    {xxx_1,1}
};
static struct i2c_driver xxx_ts_driver = {
    .driver = {
        .name = XXX_I2C_NAME,
        .owner = THIS_MODULE,
    },
    .probe      = xxx_ts_probe,
    .remove     = xxx_ts_remove,
    .id_table   = xxx_ts_id,
};

static int __init xxx_ts_init(void)
{
    int ret;
    printk("==xxx_ts_init==\n");
    ret = i2c_add_driver(&xxx_ts_driver);
    printk("ret=%d\n",ret);
    return ret;
}

module_init(xxx_ts_init);

按照仅有的一点知识就知道接下来会发生什么。当调用i2c_add_driver(&xxx_ts_driver);,如果设备与驱动匹配,就会执行xxx_ts_probe()的内容,后面就可以通过i2c读写接口操作该i2c设备了。这样看起来确实很简单,但对于我来说并不够,我想知道linux系统里面都做了一些什么事,于是我就看书查资料想要弄清楚linux i2c子系统的工作原理,因此就有了这一篇文章。本文章是根据整理的资料以及自身的理解来完成了,如有不足之处请指正。
文中对代码的验证是在Rockchip3126平台验证的。

二、i2c设备与驱动的匹配过程

调用i2c_add_driver()实际上的调用i2c_register_driver()。这个函数的详细内容如下:

/*
 * An i2c_driver is used with one or more i2c_client (device) nodes to access
 * i2c slave chips, on a bus instance associated with some i2c_adapter.
 */

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驱动。并且注释已经说明了:

i2c_driver通过一个或多个i2c_client的设备节点去访问i2c的从芯片。与一些i2c_adapter相联系的总线上。

这里面出现了三个名词i2c_clienti2c_adapter还有总线。此处暂且不讨论这三者之间的联系。但是这几个非常之重要。

1、简要流程

/* 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;
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,
};

往驱动核心中的i2c drivers链表中添加i2c驱动,并且该驱动程序的总线类型为i2c_bus_type。当调用driver_register()成功的时候,依次会调用i2c_bus_type中的i2c_device_match()判断设备与驱动的匹配,当设备与驱动匹配成功后调用i2c_device_probe()进而最终会调用xxx_ts_probe()

至于其详细的调用流程,可以参照我对linux理解之driver_register 。这里只列出一个大概的流程:
driver_register -> bus_add_driver -> driver_attach -> bus_for_each_dev -> __driver_attach -> driver_match_device -> i2c_device_match -> driver_probe_device -> really_probe -> i2c_device_probe

注:这几个函数的定义分别在 drivers\base 下的driver.c 和 dd.c 中。i2c相关的内容在 drivers\i2c\i2c-core.c中。

static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
{
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

really_probe()有下面一段代码就会调用i2c_device_probe()

if (dev->bus->probe) {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }

2、i2c_device_id的匹配

i2c_device_match()会根据情况调用不同的函数来判断驱动与设备是否匹配

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;

    printk("[I2C-TRACE] %s start, client->name = %s\n", __func__, client->name);
    if (!client)
        return 0;

    /* Attempt an OF style match */
    if (of_driver_match_device(dev, drv))
        return 1;

    /* Then ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    driver = to_i2c_driver(drv);
    printk("[I2C-TRACE] %s: driver->name = %s\n", __func__, driver->driver.name);
    /* match on an id table if there is one */
    if (driver->id_table)
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}

首先会尝试通过of_driver_match_device的接口来判断dts中配置的device与驱动是否匹配。 其实最后就是拿 device tree 中的 compatible 属性跟 driver 中的 of_match_table->compatible 做字符串的比較, 如果字符串相同, 那就匹配成功。

最后也通过调用i2c_match_id()判断设备与驱动是否匹配。

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}

通过i2c_device_id中的name与client->name是否一致来判断设备与驱动是否匹配。

在前面的i2c_driver中有一个成员i2c_device_id是用来标记此i2c驱动支持哪些设备。比如说同一个触控ic的厂商,他有多个产品型号xxx/xxx_1/xxx_2,他的xxx驱动程序可以同时支持这几个型号。

3、i2c_device_probe

接下来就很明显了,是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 (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");

    status = driver->probe(client, i2c_match_id(driver->id_table, client));
    if (status) {
        client->driver = NULL;
        i2c_set_clientdata(client, NULL);
    }
    return status;
}

4、小结

这里面的内容只是根据代码的流程解释了i2c设备与驱动的匹配过程。但是并没有涉及到linux i2c子系统的核心内容。在前面被忽略的i2c_clinei2c_adaptori2c_algorithm、i2c总线、i2c读写操作等内容将会在下面进行逐一的解释以及说明。

三、理论重点

1、概述

为了能够更好的理解i2c子系统,建议先看看宋宝华老师的《Linux设备驱动开发详解(第2版)》中关于i2c的详细描述。
此处摘抄文中的总结部分:

linux i2c驱动体系结构主要由3部分组成,即i2c核心、i2c总线驱动和i2c设备驱动。i2c核心是i2c总线驱动和i2c设备驱动的中间枢纽,它以通用的、与平台无关的接口实现了i2c中设备与适配器的沟通。i2c总线驱动填充i2c_adapteri2c_algorithm结构体,i2c设备驱动填充i2c_driver结构体并实现其本身所对应设备类型的驱动。

2、软件拓扑

此处的linux i2c子系统的软件拓扑图摘自Linux I2C framework(1)_概述
软件拓扑

1)platform bus(/sys/bus/platform)是驱动工程师常见的bus,用于挂载和CPU通过系统总线连接的各类外设。在I2C framework中,I2C控制器直接从属于platform bus,我们在linux kernel中常说的I2C driver,都是指I2C controller driver,都是以platform driver的形式存在,当然,对应的控制器是platform device。
2)与此同时,kernel抽象出I2C bus(/sys/bus/i2c),用于挂载和I2C controller通过I2C总线连接的各个I2C slave device。
3)比较特殊的地方是,I2C core使用一个虚拟实体—-I2C adapter,抽象I2C controller有关的功能(主要是数据的收发),I2C adapter也挂载在I2C bus上。
4)I2C adapter和I2C slave device都挂载在I2C bus上,就可以方便的进行Master(I2C adapter)和Slave之间的匹配操作,并通过I2C core提供的统一接口,访问I2C salve device,进行数据的收发。
5)以上各实体在sysfs中的位置,已经在“图片2”中通过红色字体标注,大家可自行理解。

(1)、platform bus上挂载挂载和CPU通过系统总线连接的各类外设。这里的platform driver实际上是对应于代码中driver/i2c/bus/i2c-rockchip.c 文件中的

static struct platform_driver rockchip_i2c_driver = {
    .probe      = rockchip_i2c_probe,
    .remove     = rockchip_i2c_remove,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "rockchip_i2c",
        .pm = ROCKCHIP_I2C_PM_OPS,
        .of_match_table = of_match_ptr(rockchip_i2c_of_match),
    },
};

(2)、 i2c controller实际上就是CPU访问i2c总线的硬件接口,在我的理解中就是某几个寄存器。i2c controller与i2c adapter是一一对应的关系。i2c adapter是软件抽象出来的i2c控制器接口,用于标识物理的i2c总线(physical i2c bus),且该总线需要有一套用于访问slave设备的算法(access algorithm)。

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {
    struct module *owner;
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* the algorithm to access the bus */
    void *algo_data;

    /* data fields that are valid for all devices   */
    struct rt_mutex bus_lock;

    int timeout;            /* in jiffies */
    int retries;
    struct device dev;      /* the adapter device */

    int nr;
    char name[48];
    struct completion dev_released;

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;

    struct i2c_bus_recovery_info *bus_recovery_info;
};

下面的内容摘自Linux I2C framework(2)_I2C provideri2c_adapter数据结构的解释:

1)由它的注释可知,struct i2c_adapter是用于标识物理的I2C总线(physical i2c bus),且该总线需要有一套用于访问slave设备的算法(access algorithm)。
2)所谓的access algorithm,就是通过I2C总线发送和接收数据的方法,它保存在algo指针(struct i2c_algorithm,具体可参考后续2.2小节的描述)中。
3)基于I2C传输的特性,不一定每一次总线访问(发送或者接收数据)都会成功,在传输失败的时候,可以选择重试。重试的逻辑由I2C core自行完成,但I2C controller driver需要设定重试的次数,这就是retries字段的意义。另外,有些consumer对结果的返回是有时间要求的,因此不能无节制的重试,timeout字段(单位为jiffies)在retries基础上,增加了时间限制,超过这个时间,就不能重试了。
4)nr,该I2C bus的ID,会体现在sysfs中(/sys/bus/i2c/devices/i2c-n中的‘n’),可由I2C controller driver在注册adapter时指定,或者通过DTS解析(后面会介绍),或者自动分配。
5)class,该I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体的类型包括(可参考include/linux/i2c.h中的定义和注释):
I2C_CLASS_HWMON,硬件监控类,如lm_sensors等;
I2C_CLASS_DDC,DDC是数字显示通道(Digital Display Channel)的意思, 通常用于显示设备信息的获取;
I2C_CLASS_SPD,存储类的模组;
I2C_CLASS_DEPRECATED,不再使用的class。
6)userspace_clients,一个i2c controller可以挂接多个client,所以这个成员就是用来标记挂接在该adapter上的设备链表头。

一个i2c controller(以下统统称之为i2c adapter)中上可以挂接多个i2c client,所以其成员有一个链表;i2c adapter要与i2c client通信,需要有对应的i2c algorithm,所以有一个const struct i2c_algorithm *algo的成员。

3、驱动框架

此处的linux i2c子系统的驱动架构摘自【驱动】linux下I2C驱动架构全面分析
驱动架构

第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层

第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层

第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层

第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层

第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。

在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的(与特定硬件相关的代码)已由芯片提供商编写完成,例如三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c
第三第四层与特定device相干的就需要驱动工程师来实现了。

第一层

前面说过,在 driver/i2c/bus/i2c-rockchip.c 文件中会添加一个rockchip_i2c_driver的平台驱动(i2c 总线驱动),当驱动成功之后进入probe,就会进行i2c_adapter相关内容的填充以及添加。第一层所有的内容都在driver/i2c/bus/i2c-rockchip.c文件中实现。

多数的i2c总线驱动中会定义一个 xxx_i2c的结构体,此处为rockchip_i2c,通过对其相关成员的访问来控制对应的功能。

rockchip_i2c

struct rockchip_i2c {
    spinlock_t      lock;
    wait_queue_head_t   wait;
    unsigned int        suspended;

    struct i2c_msg      *msg;
    unsigned int        is_busy;
    int         error;
    unsigned int        msg_ptr;

    unsigned int        irq;

    enum rockchip_i2c_state state;
    unsigned int        complete_what;
    unsigned long       clkrate;

    void __iomem        *regs;
    struct clk      *clk;
    struct device       *dev;
    struct resource     *ioarea;
    struct i2c_adapter  adap;

    unsigned long       scl_rate;
    unsigned long       i2c_rate;
    unsigned int        addr;
    unsigned char       addr_1st, addr_2nd;
    unsigned int        mode;
    unsigned int        count;

    unsigned int        check_idle;
    int         sda_gpio, scl_gpio;
    struct pinctrl_state    *gpio_state;
};

从其成员可以看出,主要用于提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号)。

rockchip_i2c_probe()
在这个函数中,可以看出它都做了以下几件事:
1> 初始化i2c_adapter中对应的内容以及添加i2c_adapter

i2c->dev = &pdev->dev;
i2c->adap.owner = THIS_MODULE;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->adap.retries = 2;
i2c->adap.timeout = msecs_to_jiffies(100);
i2c->adap.algo = &rockchip_i2c_algorithm;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = np;

....

/* setup info block for the i2c core */
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
    dev_err(&pdev->dev, "failed to add adapter\n");
    return ret;
}

2> 申请 i2c controller需要用到的io、中断,以及对其硬件的初始化。

i2c->check_idle = true;
of_property_read_u32(np, "rockchip,check-idle", &i2c->check_idle);
if (i2c->check_idle) {
    i2c->sda_gpio = of_get_gpio(np, 0);
    if (!gpio_is_valid(i2c->sda_gpio)) {
        dev_err(&pdev->dev, "sda gpio is invalid\n");
        return -EINVAL;
    }
    ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
    if (ret) {
        dev_err(&pdev->dev, "failed to request sda gpio\n");
        return ret;
    }
    i2c->scl_gpio = of_get_gpio(np, 1);
    if (!gpio_is_valid(i2c->scl_gpio)) {
        dev_err(&pdev->dev, "scl gpio is invalid\n");
        return -EINVAL;
    }
    ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
    if (ret) {
        dev_err(&pdev->dev, "failed to request scl gpio\n");
        return ret;
    }
    i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
    if (IS_ERR(i2c->gpio_state)) {
        dev_err(&pdev->dev, "no gpio pinctrl state\n");
        return PTR_ERR(i2c->gpio_state);
    }
    pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
    gpio_direction_input(i2c->sda_gpio);
    gpio_direction_input(i2c->scl_gpio);
    pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);
}

.....

ret = devm_request_irq(&pdev->dev, i2c->irq, rockchip_i2c_irq, 0,
            dev_name(&i2c->adap.dev), i2c);
if (ret) {
    dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
    return ret;
}

ret = clk_prepare(i2c->clk);
if (ret < 0) {
    dev_err(&pdev->dev, "Could not prepare clock\n");
    return ret;
}

i2c->i2c_rate = clk_get_rate(i2c->clk);

rockchip_i2c_init_hw(i2c, 100 * 1000);
dev_info(&pdev->dev, "%s: Rockchip I2C adapter\n", dev_name(&i2c->adap.dev));

从上面可以知道,这个i2c controller配置默认的i2c clock是100KHz而不是400KHz。

3> 驱动soc控制的i2c adapter在硬件上产生信号
在 i2c-rockchip.c 的函数列表就可以看到它所要做的哪些功能了。
函数列表

4> 最后是注册i2c 设备

of_i2c_register_devices(&i2c->adap);

第二层

实现i2c_adapter中的i2c_algorithm对应的成员:
i2c-rockchip.c

static const struct i2c_algorithm rockchip_i2c_algorithm = {
    .master_xfer        = rockchip_i2c_xfer,
    .functionality      = rockchip_i2c_func,
};
i2c->adap.algo = &rockchip_i2c_algorithm;

i2c_algorithm结构体的定义在include/linux/i2c.h

/*
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 */
struct i2c_algorithm {
    /* If an adapter algorithm can't do I2C-level access, set master_xfer
       to NULL. If an adapter algorithm can do SMBus access, set
       smbus_xfer. If set to NULL, the SMBus protocol is simulated
       using common I2C messages */
    /* master_xfer should return the number of messages successfully
       processed, or a negative value on error */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);

    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);
};

1)functionality,通过一个bitmap,告诉调用者该I2C adapter支持的功能
master_xfer,I2C协议有关的数据传输接口,输入参数是`struct i2c_msg类型的数组(大小由num指定)。返回值是成功传输的msg的个数,如有错误返回负值。
3)smbus_xfer,SMBUS有关的数据传输接口,如果为NULL,I2C core会尝试使用master_xfer模拟。

其中i2c_msg是i2c通信的基本传输单位,i2c的读写操作都是要事先填充该数据结构的成员。i2c_msg的详细定义如下:
include/uapi/linux/i2c.h

struct i2c_msg {
    __u16 addr;     /* slave address                        */
    __u16 flags;
    __u16 len;              /* msg length                           */
    __u8 *buf;              /* pointer to msg data                  */
};

第三层

该内容在文章的最前面已实现。

第四层

i2c-dev.c文件完全可以被看做一个i2c设备驱动,不过,它实现的一个i2c_clinet是虚拟、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的client链表中。应用层可通过它的接口访问挂接在i2c controller上的设备。

四、设备的注册

每一个i2c 设备都对应一个i2c_client,该定义如下:
include/linux/i2c.h

/**
 * struct i2c_client - represent an I2C slave device
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *  I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *  generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @driver: device's driver, hence pointer to access routines
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *  userspace_devices list
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 */
struct i2c_client {
    unsigned short flags;       /* div., see below      */
    unsigned short addr;        /* chip address - NOTE: 7bit    */
                    /* addresses are stored in the  */
                    /* _LOWER_ 7 bits       */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    struct i2c_driver *driver;  /* and our access routines  */
    struct device dev;      /* the device structure     */
    int irq;            /* irq issued by device     */
    struct list_head detected;
};

一个i2c设备需要知道其地址信息,所以有addr的成员,一个i2c设备是挂接在i2c controller上的,所以有adapter成员,i2c设备有对应的驱动程序,所以有driver的成员。这样的数据结构是完全与驱动框架的图完全相匹配的。

1、i2c_client信息的定义

i2c_client信息的定义有两种方法,一种是在dts中直接配置,另外一种是在BSP的板文件中通过i2c_board_info填充。
dts中配置的格式如下:

ts@40 {
        compatible = "xxx";
        reg = <0x40>;
        wake-gpio = <&gpio0 GPIO_D3 GPIO_ACTIVE_LOW>;
        irp-gpio = <&gpio0 GPIO_A2 IRQ_TYPE_LEVEL_HIGH>;
        revert_x = <0>;
        revert_y = <0>;
    };

dts的配置最终会转换为i2c_board_info的形式。具体是在rockchip_i2c_probe()的最后调用of_i2c_register_devices()注册i2c设备。

BSP的配置如下:

static struct i2c_board_info __initdata xxx_i2c_board_info[] = {
    {
        I2C_BOARD_INFO("xxx", 0x40),
        .irq = IRQ_PF5,
    },
    ......
}

2、i2c_client的注册

无论是采用dts还是BSP的形式,都要最终转换为i2c_board_info,最后都是通过i2c-core.c中的i2c_new_device()注册一个新的i2c设备。

/**
 * 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)

五、总结

在上述的内容中,都离不开i2c_adapteri2c_algorithmi2c_clienti2c_driver这四个数据结构以及i2c-core.c这个重要的文件。
1、i2c_adapter对应于i2c controller,这个控制器上可以挂接多个i2c设备。该主要控制产生i2c时序,如果要产生i2c时序,肯定需要sda、scl有可能还需要irq,这些资源的申请以及中断的注册都在 driver/i2c/bus/i2c-rockchip.c的文件中实现;
2、i2c_algorithm是i2c的通信方法,每个i2c controller都对应于一个与硬件相关的通信方法;
3、i2c_clienti2c_driver就是我们所理解的设备驱动了。
4、i2c-core.c提供了一组不依赖于硬件平台的接口函数,是作为i2c总线驱动和i2c设备驱动之间的纽带。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐