linux 驱动-i2c

本文详细介绍了Linux内核中的i2c驱动框架,包括数据结构如i2c_client、i2c_adapter、i2c_board_info、i2c_driver等,以及内核如何通过dts、总线号、动态添加等方式注册i2c设备,最后讨论了如何获取i2c_client结构并进行通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    本文借助mtk平台分析linux的i2c框架,mtk平台mt6765,内核版本kernel-4.9,本文只分析linux的i2c框架,不讨论i2c总线协议相关内容.

作者: baron

一、数据结构

1、i2c_client

具体的i2次设备的软件抽象,它拥有所有i2c次设备的信息:

  1. 包括i2c总线(物理总线)的控制器的硬件描述信息,这些信息通过结构 adapter 来进行描述
  2. 以及具体设备的addr信息,中断,以及name,这些信息通过结构 i2c_board_info 来进行描述

i2c_client这个结构被注册时候就被注册到 i2c bus 上了,因此我们可以利用 i2c bus 获取到这个结构

说明:i2c有两个总线的概念,一个是设备模型中虚拟的i2c总线,另一个则是实际的物理上的i2c总线,在不说明的情况下默认设备模型中的虚拟总线。

struct i2c_client {
    unsigned short flags;      //i2c通信标志位
    unsigned short addr;       //次设备,i2c地址7位地址位,flags+addr组成8位地址

    char name[I2C_NAME_SIZE];	  //设备名称,i2c_board_info 的 type 成员,driver 匹配时会用到该名称
    struct i2c_adapter *adapter;  //指向挂接的总线上的控制器
    struct device dev;      //设备模型dev结构
    int irq;                //用到的中断号
    struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
    i2c_slave_cb_t slave_cb; /* callback for slave mode  */
#endif
};

2、i2c_adapter

i2c适配器,adapter翻译过来就是适配器的意思,这个结构描述了硬件上的i2c总线的控制器信息,注意不是我们理解的设备模型上虚拟的i2c总线,而是实实在在的接在cpu上面的硬件i2c总线的控制器信息

其中 algo 链接着该总线的i2c通信相关结构 i2c_algorithm 。 按理来讲通信方式也是总线控制器的一部分为什么要将其分离出来,这样做是为了代码的复用性,如果系统有6个i2c总线则需要写6个通信接口,而区别往往只是一些和控制器某些寄存器的值不同而已,为了提高代码的效率,于是将 i2c_algorithm 独立出来作为一个共有的模块。

很明显作为总线控制器的描述,i2c_adapter 不会描述具体的设备信息,具体的设备信息由中间结构 i2c_board_info 描述,并将其存入 i2c_client 结构。

从上面的描述可以知道 i2c 适配器的数量其实是固定的,硬件有多少个 i2c 总线,就会有多少个 i2c 适配器,例如 mt6765 有6个物理的i2c总线i2c0-i2c6,因此mt6765就有6个i2c适配器。

struct i2c_adapter {
    struct module *owner;
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; //i2c通信接口
    void *algo_data;

    /* data fields that are valid for all devices   */
    const struct i2c_lock_operations *lock_ops;
    struct rt_mutex bus_lock;
    struct rt_mutex mux_lock;

    /*
     i2c总线访问(发送或者接收数据)在传输失败的时候,可以选择重试。retries表示从试的次数。
     另外,有些设备对结果的返回是有时间要求的,因此不能无节制的重试,
     timeout字段(单位为jiffies)在retries基础上,增加了时间限制,超过这个时间,就不能重试了
    */
    int timeout;	//超时时间
    int retries;    //重复次数
    struct device dev;  //设备模型dev,用于在sys/生成目录结构

    int nr;	//i2c总线号,例如,0代表使用i2c0,这个不是我们所说的设备模型里面的i2c总线,是cpu中的i2c物理总线号
    char name[48]; //adapter的名称
    struct completion dev_released;

    struct mutex userspace_clients_lock;
    struct list_head userspace_clients; //用于管理用户空间创建的i2c设备

    struct i2c_bus_recovery_info *bus_recovery_info;
    const struct i2c_adapter_quirks *quirks;
};


//通信方式
struct i2c_algorithm {

	/* 作为主设备时的通信函数 *
    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);

    u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
    int (*reg_slave)(struct i2c_client *client);
    int (*unreg_slave)(struct i2c_client *client);
#endif
};

3、i2c_board_info

这个结构用于描述具体的i2c设备的硬件信息,设备地址,通信方式,中断号等。

  1. 当使用dts创建 i2c_client 时,它作为中间结构,当他把硬件信息传递给真正的设备结构 i2c_client,它的使命也就完成了,dts中的设备信息先在内核中转换为 i2c_board_info 这个结构,然后通过这个结构传给 i2c_client 。
  2. 当使用老式的使用总线号创建 i2c_client时,所有的由 i2c_register_board_info 动态创建的 i2c_board_info 描述的设备信息将被链接到 __i2c_board_list 链表进行维护,在加载内核时查询 __i2c_board_list 将 i2c_board_info 描述的信息传给 i2c_client
struct i2c_board_info {
    char        type[I2C_NAME_SIZE]; //i2c设备设名称,将被设置到client->name
    unsigned short  flags;
    unsigned short  addr; //设备地址
    void        *platform_data;
    struct dev_archdata *archdata;
    struct device_node *of_node; //设备节点
    struct fwnode_handle *fwnode;
    int     irq; //中断号
};

4、i2c_driver

该结构用来描述虚拟 i2c bus 总线上的 driver ,通过向 i2c bus 注册该结构进行匹配,获取对应的 i2c_client

struct i2c_driver {
    unsigned int class;

    /* Notifies the driver that a new bus has appeared. You should avoid
     * using this, it will be removed in a near future.
     */
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;  //这个方法已经被过时,不应该被使用。

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *); //当匹配到i2c_client时调用这个函数
    int (*remove)(struct i2c_client *); //和probe相反

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     * For the SMBus Host Notify protocol, the data corresponds to the
     * 16-bit payload data reported by the slave device acting as master.
     */
    void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
              unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver; //设备模型中的device_driver结构
    const struct i2c_device_id *id_table; //指向用来和i2c总线上的i2c_client进行匹配的一组 name 

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *); //动态扫描的方式注册i2c_client, linux并不推荐这种方式
    const unsigned short *address_list; //动态扫描时使用的一组地址
    struct list_head clients;  //用于链接detect动态扫描生成的设备。
};

5、i2c_device_id

struct i2c_device_id {
    char name[I2C_NAME_SIZE]; //用于匹配的name,这个name将去匹配 i2c_client->name
    kernel_ulong_t driver_data; /* 用于保存驱动的私有数据 */
};

6、i2c_msg

linux的i2c通信是以 i2c_msg 作为操作单位

struct i2c_msg {
    __u16 addr; 	//设备地址
    __u16 flags;	//传输标志位
#define I2C_M_RD        0x0001  //设置了这个表示表示本次通信为接收数据,否则为发送数据。
                    /* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN       0x0010  //设置了这个表示从设备地址有10bit
#define I2C_M_RECV_LEN      0x0400  
#define I2C_M_NO_RD_ACK     0x0800  //在读操作中不需要ack
#define I2C_M_IGNORE_NAK    0x1000  //当前i2cmsg忽略i2c器件的ack和nack信号
#define I2C_M_REV_DIR_ADDR  0x2000  //读写标志位反过来
#define I2C_M_NOSTART       0x4000  //当前i2c_msg不发送start信号
#define I2C_M_STOP      0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
    __u16 len;      //数据长度,单位为byte
    __u8 *buf;      //数据缓冲区指针
};

三、内核i2c驱动框架

li2c总线的硬件结构一般如下图所示

cpu 作为主设备挂接在所有总线上,而对应的设备根据硬件设计挂接在相应的总线上。图所示gsensor挂接在i2c0上,camera挂接在i2c1上,tp挂接在i2c2上。硬件结构非常简单,而linux的实现相对而言复杂度还是蛮高的,向上面的i2c设备 gsensor、camera、tp 等对于总线来讲只是地址上的不同而已,本质上都是i2c设备没有什么不同。 linux-4.9提供6种添加i2c设备的方法,参考 Documentation/i2c/instantiating-devices,下面逐一讨论。

1、使用dts添加i2c设备

只需在dts文件中添加i2c设备相关节点,内核就会自动创建对应的 i2c_client 结构。 例如:添加两个设备 24c256、pca9532以以100kHz的速度连接到i2c1总线。

    i2c1: i2c@400a0000 {
        /* ... master properties skipped ... */
        clock-frequency = <100000>; //100KHZ

        flash@50 {
            compatible = "atmel,24c256";
            reg = <0x50>;
        };

        pca9532: gpio@60 {
            compatible = "nxp,pca9532";
            gpio-controller;
            #gpio-cells = <2>;
            reg = <0x60>;
        };
    };

这样内核在加载的时候就会自动创建出 name 为 24c256 和 pca9532 的i2c_client结构并将其注册进入i2c bus。它的流程如下所示:

上图展示了整个注册流程,对于驱动工程师来讲非常简单,只需在dts中填充对应的信息就行了。而内核却帮我我们完成了一系类注册流程。而作为驱动工程师不仅仅要做到知其然,还要知其所以然,下面以mtk平台为例分析整个注册流程。

源码路径drivers/i2c/busses/i2c-mtk.c,ic原厂提供i2c函数都被放在drivers/i2c/busses/这个目录

static s32 __init mt_i2c_init(void)
{
#ifdef CONFIG_MTK_I2C_ARBITRATION
    int ret;

    ret = enable_arbitration();
    if (ret) {
        pr_info("Cannot enalbe arbitration.\n");
        return ret;
    }
#endif

    if (!map_dma_regs())
        pr_info("Mapp dma regs successfully.\n");
    if (!mt_i2c_parse_comp_data())
        pr_info("Get compatible data from dts successfully.\n");

    pr_info(" mt_i2c_init driver as platform device\n");
    return platform_driver_register(&mt_i2c_driver); //向内核注册 mt_i2c_driver 
}

上面代码向内核注册了一个名叫 “mt-i2c” 的 platform 驱动

#define I2C_DRV_NAME           "mt-i2c"

static const struct of_device_id mtk_i2c_of_match[] = {
    { .compatible = "mediatek,i2c", .data = &i2c_common_compat},
    {},
};

static struct platform_driver mt_i2c_driver = {
    .probe = mt_i2c_probe,
    .remove = mt_i2c_remove,
    .driver = {
        .name = I2C_DRV_NAME,
        .owner = THIS_MODULE,
        .pm = &mt_i2c_dev_pm_ops,
        .of_match_table = of_match_ptr(mtk_i2c_of_match),
    },
};

可以看到它 match 的设备的 compatible 字段是 “mediatek,i2c” ,于是在dts中找对应的设备节点。

    i2c0: i2c0@11007000 {
        compatible = "mediatek,i2c";
        id = <0>;
        ...
    };

    i2c1: i2c1@11008000 {
        compatible = "mediatek,i2c";
        id = <1>;
        ...
    };

    i2c2: i2c2@11009000 {
        compatible = "mediatek,i2c";
        id = <2>;
        ...
    };

    i2c3: i2c3@1100f000 {
        compatible = "mediatek,i2c";
        id = <3>;
        ...
    };

    i2c4: i2c4@11011000 {
        compatible = "mediatek,i2c";
        id = <4>;
        ...
    };

    i2c5: i2c5@11016000 {
        compatible = "mediatek,i2c";
        id = <5>;
        ...
    };

    i2c6: i2c6@1100d000 {
        compatible = "mediatek,i2c";
        id = <6>;
        ...
    };

凡是在dts中具有compatible字段的节点,都会被内核注册为platform设备,例如上面的 i2c0,将会被转换为名字为"11007000.i2c0"的platform设备,再看i2c0到i2c6的节点都有相同的compatible字段"mediatek,i2c",因此当驱动mt_i2c_driver注册的时候,会分别与之匹配调用对应的 mt_i2c_probe 函数,也就是说会调用6次mt_i2c_probe函数分别注册i2c0到i2c6, 以i2c0为例进行分析。

static int mt_i2c_probe(struct platform_device *pdev)
{
    int ret = 0;
    //int cnt = 0;
    struct mt_i2c *i2c; //这个结构中包含了 i2c_adapter 结构
    unsigned int clk_src_in_hz;
    struct resource *res;
    const struct of_device_id *of_id;

    i2c = devm_kzalloc(&pdev->dev, sizeof(struct mt_i2c), GFP_KERNEL); 
    if (i2c == NULL)
        return -ENOMEM;

    //获取dts中的相关数据其中包括 i2c->id 
    ret = mt_i2c_parse_dt(pdev->dev.of_node, i2c); 
    if (ret)
        return -EINVAL;
	
    /***省略***/

    i2c->dev_comp = of_id->data;
    i2c->adap.dev.of_node = pdev->dev.of_node; //设置设备节点为 i2c0 根节点
    i2c->dev = &i2c->adap.dev; 
    i2c->adap.dev.parent = &pdev->dev;	//设置父设备为传入的platform设备"11007000.i2c0",有此可知所有的i2c_adapter设备都将是platform设备的子设备,都将位于 /sys/devices/110xx000.i2cx/ 之下
    i2c->adap.owner = THIS_MODULE;
    i2c->adap.algo = &mt_i2c_algorithm; //这里始化了i2c的通信操作函数,这个函数有mtk原厂实现,这个是平台相关的寄存器操作
    i2c->adap.algo_data = NULL;
    i2c->adap.timeout = 2 * HZ; //超时时间2s,从这里可知mtk的i2c超时时间为2s
    i2c->adap.retries = 1;	//mtk平台设置,重复次数1次
    i2c->adap.nr = i2c->id; //表示使用的i2c总线号,0表示i2c0
    spin_lock_init(&i2c->cg_lock);

    /***省略***/
	
    strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); //初始化adap的名字为mt-i2c

    /***省略***/

    /* ret = i2c_add_adapter(&i2c->adap); */
    ret = i2c_add_numbered_adapter(&i2c->adap); //将adap注册进内核
	
    /***省略***/

    return 0;
}

上述函数,从dts获取相关的平台台相关的硬件信息,通信方式(mt_i2c_algorithm)、硬件总线号(adap.nr)等,以及平台相关的信息,通信超时时间、通信重复次数等,利用这些信息创建并初始化一个 i2c_adapter(包含在mt_i2c中),并将其注册进内核,来看看它的注册过程,它的代码在 drivers/i2c/i2c-core.c

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);

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_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)) {	//检查adap是否已经注册,防止存重复注册
        res = -EAGAIN;
        goto out_list;
    }

    /* Sanity checks */
    if (WARN(!adap->name[0], "i2c adapter has no name")) //检查 i2c_adapter 的名字,没有名字直接返回
        goto out_list;

    if (!adap->algo) {	// i2c_adapter检查是否有发送接收函数,没有直接返回
        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; //设置如果没设置超时时间则,默认超时时间为1s

    dev_set_name(&adap->dev, "i2c-%d", adap->nr); // 设置 adap->dev 的名字i2c-0
    adap->dev.bus = &i2c_bus_type; //设置总线
    adap->dev.type = &i2c_adapter_type;	//设置 adapter 的设备类型
    res = device_register(&adap->dev); // 向 i2c_bus_type 总线注册adapter设备 ,将生成 /sys/devices/11007000.i2c0/i2c-0 节点
    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); //遍历设备节点注册对应的 client
    i2c_acpi_register_devices(adap); //百度了一下说acpi是电源管理相关的东西,那就先不管
    i2c_acpi_install_space_handler(adap);

    if (adap->nr < __i2c_first_dynamic_bus_num) 
        i2c_scan_static_board_info(adap); //老的方式注册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;
}

这个函数对adapte进行了一些错误检查,并进一步对它进行初始化,并将其注册进i2c总线,该函数还会调用 of_i2c_register_devices 创建 dts中对应的 i2c 设备相关的 i2c_client,看看它怎么做的。

static void of_i2c_register_devices(struct i2c_adapter *adap)
{
    struct device_node *bus, *node;
    struct i2c_client *client;

    /* Only register child devices if the adapter has a node pointer set */
    if (!adap->dev.of_node) //检测设备节点是否存在
        return;

    dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

    bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); //mtk平台这个i2c-bus设备节点并不存在。
    if (!bus)
        bus = of_node_get(adap->dev.of_node); //直接返回i2c0设备节点,在前面被初始化

    for_each_available_child_of_node(bus, node) { //遍历 i2c0的设备节点下的子节点
        if (of_node_test_and_set_flag(node, OF_POPULATED))
            continue;

        client = of_i2c_register_device(adap, node); //对于子节点执行这个函数
        if (IS_ERR(client)) {
            dev_warn(&adap->dev,
                 "Failed to create I2C device for %s\n",
                 node->full_name);
            of_node_clear_flag(node, OF_POPULATED);
        }
    }

    of_node_put(bus);
}

这个函数就是检测adap的硬件总线控制器设备节点是否存在,如果存在则对该总线节点的每一个子节点执行 of_i2c_register_device 函数,来看看它干了什么。

//i2c0的子节点
&i2c0 {
    cap_touch@24 {
        compatible = "mediatek,cap_touch";
        reg = <0x24>;
        interrupt-parent = <&pio>;
        interrupts = <0 IRQ_TYPE_EDGE_FALLING 0 0>;
        int-gpio = <&pio 0 0>;
        rst-gpio = <&pio 174 0>;
        status = "okay";
    };
};

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
                         struct device_node *node)
{
    struct i2c_client *result;
    struct i2c_board_info info = {};
    struct dev_archdata dev_ad = {};
    const __be32 *addr_be;
    u32 addr;
    int len;

    dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
	
    //获取节点下的 compatible 字段逗号后面的字符串,如果没有逗号则获取 compatible 字段的字符串,
    这里将返回 cap_touch 并将其存入 info.type
    if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
        dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }
	
    //获取reg描述"0x24",即设备地址
    addr_be = of_get_property(node, "reg", &len);
    if (!addr_be || (len < sizeof(*addr_be))) {
        dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }

    addr = be32_to_cpup(addr_be); //将其转化为16进制的0x24
    if (addr & I2C_TEN_BIT_ADDRESS) {
        addr &= ~I2C_TEN_BIT_ADDRESS;
        info.flags |= I2C_CLIENT_TEN;
    }

    if (addr & I2C_OWN_SLAVE_ADDRESS) { //地址检测
        addr &= ~I2C_OWN_SLAVE_ADDRESS;
        info.flags |= I2C_CLIENT_SLAVE;
    }

    if (i2c_check_addr_validity(addr, info.flags)) { //地址检测
        dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
            addr, node->full_name);
        return ERR_PTR(-EINVAL);
    }

    info.addr = addr; //初始化 i2c_board_info 结构地址
    info.of_node = of_node_get(node); //初始化设备节点为i2c0
    info.archdata = &dev_ad; 

    if (of_get_property(node, "wakeup-source", NULL))
        info.flags |= I2C_CLIENT_WAKE;

    result = i2c_new_device(adap, &info); //创建并注册一个i2c_client,即注册一个新的i2c设备。
    if (result == NULL) {
        dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
            node->full_name);
        of_node_put(node);
        return ERR_PTR(-EINVAL);
    }
    return result;
}

这个函数创建了一个局部的i2c_board_info结构,从dts中获取到i2c子设备相关信息并将其初始化之后,将作为参数用于创建i2c_client,看看i2c_new_device怎么创建一个新的i2c设备。

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); //动态创建一个client结构
    if (!client)
        return NULL;

    client->adapter = adap; //初始化adapter成员

    client->dev.platform_data = info->platform_data; // 初始化platform_data成员

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags; //初始化flag成员
    client->addr = info->addr;	 //初始化设备地址
    client->irq = info->irq;	 //初始化中断引脚,这里没有初始化,也会在i2c_device_probe中重新,获取相关的中断号。

    strlcpy(client->name, info->type, sizeof(client->name)); //设置name为info->type,即设置为前面获取的cap_touch,这个名字非常重要,因为驱动匹配将使用这个名字。

    status = i2c_check_addr_validity(client->addr, client->flags);	//检测i2c地址
    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; //设置总线为i2c总线
    client->dev.type = &i2c_client_type; //设置设备类型
    client->dev.of_node = info->of_node; //设置设备节点为i2c0
    client->dev.fwnode = info->fwnode; 

    /*
     * dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), i2c_encode_flags_to_addr(client));
     * 设置设备名字为 0-0024 
     */
    i2c_dev_set_name(adap, client); 
    status = device_register(&client->dev); //将设备注册进入bus总线,同时会生成节点 /sys/devices/11007000.i2c0/i2c-0/0-0024
    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;
}

到此为止就完成了i2c0节点下的cap_touch的设备注册,of_i2c_register_devices函数会遍历i2c0节点下的子节点并依次进行注册。

最后在来对整个注册流程进行一个梳理,对应前面的框图,对于mtk平台对每一个i2c总线节点都会注册一个adapter,在adapter注册的时候后会遍历子节点下的每个i2c设备并创建对应的i2c设备结构client。而在创建过程中会用到一个中间结构 i2c_board_info,它用于描述i2c设备的硬件信息,在注册完成之后就被释放掉。

2、通过总线号添加i2c设备

这种方法适用于许多I2C总线是系统总线的嵌入式系统。 在这样的系统上,每个I2C总线都有一个事先已知的编号。 因此可以预先声明该总线上的I2C设备。 这是通过调用i2c_register_board_info()注册的struct i2c_board_info数组完成的,在没有dts之前使用的就是这种方式

#define I2C_BOARD_INFO(dev_type, dev_addr) \
    .type = dev_type, .addr = (dev_addr)

static struct i2c_board_info h4_i2c_board_info[] __initdata = {
    {
        I2C_BOARD_INFO("isp1301_omap", 0x2d),
        .irq        = OMAP_GPIO_IRQ(125),
    },
    {   /* EEPROM on mainboard */
        I2C_BOARD_INFO("24c01", 0x52),
        .platform_data  = &m24c01,
    },
    {   /* EEPROM on cpu card */
        I2C_BOARD_INFO("24c01", 0x57),
        .platform_data  = &m24c01,
    },
};

static void __init omap_h4_init(void)
{
    (...)
    i2c_register_board_info(1, h4_i2c_board_info,
            ARRAY_SIZE(h4_i2c_board_info));
    (...)
}

上面的代码在I2C总线1上声明了3个设备,包括它们各自的地址和其驱动程序所需的自定义数据。 注册I2C总线查询后,I2C内核将自动实例化I2C设备。以s3c2410为例进行分析,代码路径:drivers/i2c/busses/i2c-s3c2410.c

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c;
    struct s3c2410_platform_i2c *pdata = NULL;
    struct resource *res;
    int ret;

    /*** 省略部分 ***/

    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner = THIS_MODULE;
    i2c->adap.algo = &s3c24xx_i2c_algorithm; 
    i2c->adap.retries = 2; //初始化重复次数
    i2c->adap.class = I2C_CLASS_DEPRECATED; //不支持自动检测
    i2c->tx_setup = 50;

    /*** 省略部分 ***/

    i2c->adap.algo_data = i2c;
    i2c->adap.dev.parent = &pdev->dev; //初始化父节点

    /*** 省略部分 ***/

    i2c->adap.nr = i2c->pdata->bus_num;
    i2c->adap.dev.of_node = pdev->dev.of_node;

    /*** 省略部分 ***/

    ret = i2c_add_numbered_adapter(&i2c->adap); //注册adapter
    if (ret < 0) {
        pm_runtime_disable(&pdev->dev);
        s3c24xx_i2c_deregister_cpufreq(i2c);
        clk_unprepare(i2c->clk);
        return ret;
    }
	

同样是初始化adapter相关参数并向内核注册adapter

i2c_add_numbered_adapter->
    __i2c_add_numbered_adapter->
        i2c_register_adapter

最后还是调用 i2c_register_adapter 这个函数来看看这种方式调用的是什么函数

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

    /***省略部分***/

    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
	
    /***省略部分***/
}

由于前面已经分析过这个函数,就直接给出关键代码,在这句代码中发现一个参数 __i2c_first_dynamic_bus_num 这是一个内核创建的全来表示当前局整型变量用的i2c总线的数量,这个机制的一个特点就是i2c总线的数量是由设备注册的时候添加的。它是什么时候添加的呢?先往下看

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_board_list 链表,找到其中挂接在 adapter->nr 上的设备, 然后使用它的设备信息调用 i2c_new_device 创建一个 i2c_client 设备。 设备信息是通过 i2c_register_board_info 注册的,来看一下这个函数。

int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
    int status;

    down_write(&__i2c_board_lock);

    printk("i2c_register_board_info busnum = %d\n",busnum);

    /* dynamic bus numbers will be assigned after the last static one */
    if (busnum >= __i2c_first_dynamic_bus_num)
        __i2c_first_dynamic_bus_num = busnum + 1;

    for (status = 0; len; len--, info++) {
        struct i2c_devinfo  *devinfo;

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }

        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);
    }

    up_write(&__i2c_board_lock);

    return status;
}

我们看见这个函数的功能很简单,当前 busnum 大于 __i2c_first_dynamic_bus_num 则更新 __i2c_first_dynamic_bus_num ,总线数量其实是在添加设备信息的时候添加的。当添加了设备信息之后将该设备信息链接到 __i2c_board_list 全局链表,__i2c_board_list 维护着由 i2c_register_board_info 注册的设备信息。

整个过程分为两步:

  1. 首先 arch/arm/mach-xxx 文件调用 i2c_register_board_info 函数 先向 __i2c_board_list 链表添加板子上的设备信息
  2. 然后在调用 drivers/i2c/busses/i2c-xxx 文件向内核注册创建并初始化adapter结构并注册进内核,在内核中遍历 __i2c_board_list 使用挂接在对应adapter上的设备信息 i2c_devinfo->info 创建出对应的i2c_client,调用流程如下:
i2c_add_numbered_adapter->
    __i2c_add_numbered_adapter --->
        i2c_register_adapter --->
            if (adap->nr < __i2c_first_dynamic_bus_num)
                i2c_scan_static_board_info(adap); --->
                    if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))

3、动态添加i2c设备

前面的两种方式都是静态的方式添加的,就是在内核加载之前先提供i2c设备信息,在加载的时候创建,而内核也提供了动态加载的方法。一个个典型的例子是电视适配器。它们通常是通过I2C总线连接到主芯片的调谐器,视频解码器,音频解码器等。 由于不会事先知道I2C总线的编号,因此无法使用上述方法1和2。

static struct i2c_board_info sfe4001_hwmon_info = {
    I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
    /***省略部分***/
    efx->board_info.hwmon_client =
        i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

    /***省略部分***/
}

使用这个方式自己创建出一个 i2c_board_info 结构,并直接调用函数 i2c_new_device 创建一个i2c_client.上面的代码实例化了所讨论的网络适配器上I2C总线上的1个I2C设备,这个方式是动态的添加一个 i2c_client 的方式,使用这个方式的原因是我们事先不知道,我们的设备会挂在哪个i2c总线上。这种情况下还有一种可能就是我们不知道,总线上是否存在这样一个设备,我们想在创建前先检测这个总线是否存在这个设备于是可以使用下面的方式

static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

static int usb_hcd_nxp_probe(struct platform_device *pdev)
{
    /***省略部分***/
    struct i2c_adapter *i2c_adap;
    struct i2c_board_info i2c_info;

    /***省略部分***/
    i2c_adap = i2c_get_adapter(2);
    memset(&i2c_info, 0, sizeof(struct i2c_board_info));
    strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
    isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, normal_i2c, NULL);
    i2c_put_adapter(i2c_adap);
    /***省略部分***/
}

上面的代码在所讨论的OHCI适配器上的I2C总线上最多实例化1个I2C设备。 它首先尝试在地址0x2c处进行尝试,如果在该位置未找到任何内容,则尝试在地址0x2d中进行处理,如果仍然找不到任何内容,则放弃。这里面有个关键函数 i2c_new_probed_device 来看看这个函数的实现原理。

struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
              struct i2c_board_info *info,
              unsigned short const *addr_list,
              int (*probe)(struct i2c_adapter *, unsigned short addr))
{
    int i;

    if (!probe)
        probe = i2c_default_probe; //如果没有probe则使用内核默认的i2c检测函数,这个函数检测当前总线上的设备是否存在

    for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
        /* Check address validity */
        if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) {
            dev_warn(&adap->dev, "Invalid 7-bit address 0x%02x\n",
                 addr_list[i]);
            continue;
        }

        /* Check address availability (7 bit, no need to encode flags) */
        if (i2c_check_addr_busy(adap, addr_list[i])) {
            dev_dbg(&adap->dev,
                "Address 0x%02x already in use, not probing\n",
                addr_list[i]);
            continue;
        }

        /* Test address responsiveness */
        if (probe(adap, addr_list[i])) //检测当前总线上的设i2c备是否存在。
            break;
    }

    if (addr_list[i] == I2C_CLIENT_END) {
        dev_dbg(&adap->dev, "Probing failed, no device found\n");
        return NULL;
    }

    info->addr = addr_list[i];
    return i2c_new_device(adap, info); //创建i2c_client
}

/* 这个函数的功能就是向当前总线上的i2c设备发送一个i2c信息,看看设备是否应答,用来判断是否存在i2c设备 */
static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
{
    int err;
    union i2c_smbus_data dummy;

#ifdef CONFIG_X86
    if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON)
     && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA))
        err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
                     I2C_SMBUS_BYTE_DATA, &dummy);
    else
#endif
    if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50)
     && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
        err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
                     I2C_SMBUS_QUICK, NULL);
    else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE))
        err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
                     I2C_SMBUS_BYTE, &dummy);
    else {
        dev_warn(&adap->dev, "No suitable probing method supported for address 0x%02X\n",
             addr);
        err = -EOPNOTSUPP;
    }

    return err >= 0;
}

4、动态探测I2C总线添加i2c设备

有时没有足够的有关I2C设备的信息,甚至无法调用i2c_new_probed_device()。典型的情况是PC主板上的硬件监视芯片。有几十种模型,它们可以存在于25个不同的地址。鉴于那里有大量的主板,几乎不可能建立一个完整的硬件监控芯片清单。幸运的是,这些芯片中的大多数都有制造商和设备ID寄存器,因此可以通过探测来识别它们。也就是我们的detect内核中的机制了。在这种情况下,既不声明也不显式实例化I2C设备。取而代之的是,i2c-core会在加载驱动程序后立即探测此类设备,如果找到了驱动程序,则会自动实例化I2C设备这也就是 adapter 中 class 成员变量的作用了,内核在加载驱动时会使用驱动提供的地址链表 i2c_driver->address_list,自动去具有同类 class 的 adapter 上探测这个链表上的地址,如果探测到了则实例化驱动

为了防止此机制的任何不当行为,适用以下限制:

  1. I2C设备驱动程序必须实现detect()方法,该方法通过从任意寄存器读取来识别支持的设备。
  2. 仅对可能具有受支持设备并同意进行探测的总线进行探测。例如,这避免了在电视适配器上探测硬盘监控芯片的麻烦。
    例:请参阅drivers/hwmon/lm90.c中的lm90_driver和lm90_detect()

那些熟悉2.4内核和早期2.6内核的i2c子系统的人会发现,此方法本质上与此处所做的相似。两个重要区别是:

  1. 探测只是现在实例化I2C设备的一种方法,而那是那时的唯一方法。在可能的情况下,应首选方法1、2、3。方法4仅应在没有其他方法的情况下使用,因为它可能会产生不良的副作用。
  2. I2C总线必须明确地说明哪些I2C驱动程序类可以对其进行探测(通过类位域),而所有I2C总线默认情值为空类,这意味着不进行探测。类位域的目的是限制上述不希望的副作用。

再一次声明,应尽可能避免使用方法4。显式设备实例化(方法1、2、3)是更可取的,因为它更安全,更快捷。

由于内核不推荐使用这种方式就不详细分析了给出调用流程就行了,感兴趣跟着调用流程看看

i2c_add_numbered_adapter->
    __i2c_add_numbered_adapter --->
        i2c_register_adapter --->
            bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); --->
                i2c_do_add_adapter --->
                    i2c_detect --->
                        i2c_detect_address --->
                            i2c_default_probe //判断设备是否在这个总线上
                            driver->detect(temp_client, &info); //调用这个函数,在这个函数和设备通信,获取到设备信息,完善info中的信息。
                            i2c_new_device //创建i2c_client
                            list_add_tail(&client->detected, &driver->clients); // 将设备链接到 clients

这种方式适用于不知道设备被挂在哪个总线上的情况,通过遍历所有的总线,去判断设备是否在总线上,如果存在则调用 driver->detect 进一步获取设备信息,用于完善info结构,如果设备信息完善则创建 i2c_client。

5、从用户空间添加i2c设备

通常,内核应该知道连接了哪些I2C设备以及它们位于什么地址。但是,在某些情况下却没有,因此添加了sysfs接口以让用户提供信息。该接口由2个属性文件组成,new_device和delete_device。这两个文件都是只写的,并且必须为它们写正确的参数,以便正确地创建或删除I2C设备。

创建一个i2c设备
文件 new_device 用于创建一个i2c设备,具有2个参数:I2C设备的名称(字符串)和I2C设备的地址(一个数字,通常以十六进制表示,以0x开头,但也可以以十进制表示。)例如:

echo eeprom 0x50>/sys/bus/i2c/devices/i2c-3/new_device

删除一个i2c设备
文件delete_device,用于删除一个i2c设备,具有一个参数:I2C设备的地址。由于在给定的I2C网段上没有两个设备可以住在同一地址,因此该地址足以唯一地标识要删除的设备。例:

echo 0x50>/sys/bus/i2c/devices/i2c-3/delete_device

来看看内核的实现

static ssize_t
i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
             const char *buf, size_t count)
{
    struct i2c_adapter *adap = to_i2c_adapter(dev);
    struct i2c_board_info info;
    struct i2c_client *client;
    char *blank, end;
    int res;

    memset(&info, 0, sizeof(struct i2c_board_info));

    blank = strchr(buf, ' ');
    if (!blank) {
        dev_err(dev, "%s: Missing parameters\n", "new_device");
        return -EINVAL;
    }
    if (blank - buf > I2C_NAME_SIZE - 1) {
        dev_err(dev, "%s: Invalid device name\n", "new_device");
        return -EINVAL;
    }
    memcpy(info.type, buf, blank - buf);

    /* Parse remaining parameters, reject extra parameters */
    res = sscanf(++blank, "%hi%c", &info.addr, &end);
    if (res < 1) {
        dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
        return -EINVAL;
    }
    if (res > 1  && end != '\n') {
        dev_err(dev, "%s: Extra parameters\n", "new_device");
        return -EINVAL;
    }

    if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) {
        info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT;
        info.flags |= I2C_CLIENT_TEN;
    }

    if (info.addr & I2C_ADDR_OFFSET_SLAVE) {
        info.addr &= ~I2C_ADDR_OFFSET_SLAVE;
        info.flags |= I2C_CLIENT_SLAVE;
    }

    client = i2c_new_device(adap, &info);
    if (!client)
        return -EINVAL;

    /* Keep track of the added device */
    mutex_lock(&adap->userspace_clients_lock);
    list_add_tail(&client->detected, &adap->userspace_clients);
    mutex_unlock(&adap->userspace_clients_lock);
    dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
         info.type, info.addr);

    return count;
}
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);

static ssize_t
i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
            const char *buf, size_t count)
{
    struct i2c_adapter *adap = to_i2c_adapter(dev);
    struct i2c_client *client, *next;
    unsigned short addr;
    char end;
    int res;

    /* Parse parameters, reject extra parameters */
    res = sscanf(buf, "%hi%c", &addr, &end);
    if (res < 1) {
        dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
        return -EINVAL;
    }
    if (res > 1  && end != '\n') {
        dev_err(dev, "%s: Extra parameters\n", "delete_device");
        return -EINVAL;
    }

    /* Make sure the device was added through sysfs */
    res = -ENOENT;
    mutex_lock_nested(&adap->userspace_clients_lock,
              i2c_adapter_depth(adap));
    list_for_each_entry_safe(client, next, &adap->userspace_clients,
                 detected) {
        if (i2c_encode_flags_to_addr(client) == addr) {
            dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
                 "delete_device", client->name, client->addr);

            list_del(&client->detected);
            i2c_unregister_device(client);
            res = count;
            break;
        }
     }
    mutex_unlock(&adap->userspace_clients_lock);

    if (res < 0)
        dev_err(dev, "%s: Can't find device in list\n",
            "delete_device");
    return res;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
                   i2c_sysfs_delete_device);

static struct attribute *i2c_adapter_attrs[] = {
    &dev_attr_name.attr,
    &dev_attr_new_device.attr,
    &dev_attr_delete_device.attr,
    NULL
};
ATTRIBUTE_GROUPS(i2c_adapter);

可以看见其实很简单,就是获取初入的name和addr动态初始化一个info,然后利用 new_device 函数创建一个i2c_client ,这种方式和方式2是一样的,只是将方式2开放给用户空间一个接口。

6、acpi方式添加

参考: kernel-4.9/Documentation/acpi/enumeration.txt.

三、获取 i2c_client 结构

有了具体的设备结构 i2c_client, 我们要怎么用呢,比如我想向i2c0上的 cap_touch 发送数据,怎么获取到对应的 i2c_client 结构呢?很简单因为 i2c_client 被挂接到了 i2c 总线上,因此我们只需要向 i2c 总线上注册一个 i2c_driver ,并且使用要用到的 i2c_client 的name作为 i2c_driver 的 i2c_device_id->name。那么总线就会去匹配和 i2c_device_id->name 相同 name 的 i2c_client(这是i2c总线的match函数匹配规则),匹配成功则调用driver的probe成员函数并且将i2c_client作为参数传入,这样我们就获取到了我们想要的具体的 i2c_client 设备结构。 于是想要获取到 cap_touch 的 i2c_client 的示例代码如下(代码并不完整这只是一个伪代码):

int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk("my_drv_probe\n"); 
    return 0;
}

int my_i2c_remove(struct i2c_client * client)
{
    printk("my_drv_remove\n");
    return 0;
}

//遍历这里数组中的 i2c_device_id.name 一个一个匹配
static const struct i2c_device_id my_id_table[] = {
    { "cap_touch", 0 }, //通过这个名字获取到我们需要的 i2c_client 设备结构 
    { NULL, 0},
};

static struct i2c_driver my_i2c_driver = {
    .probe  = my_i2c_probe,
    .remove = my_i2c_remove,
    .driver = {
           .name = "cap_touch", //驱动的name将出现在 /sys/bus/i2c/ 下
           },
	.id_table = my_id_table, //匹配用到的结构
};

static int my_i2c_init(void)
{
    i2c_add_driver(&my_i2c_driver);
    return 0;
}

static void my_i2c_exit(void)
{
	
}

module_init(my_i2c_init);
module_exit(my_i2c_exit);

i2c_add_driver 的内部工作原理到底是怎么样的呢,看看源码。

#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)


int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;

    /* Can't register until after driver model init */
    if (WARN_ON(!is_registered))
        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; //初始化总线
    INIT_LIST_HEAD(&driver->clients);

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    //将驱动注册进入bus总线
    res = driver_register(&driver->driver);
    if (res)
        return res;

    pr_debug("driver [%s] registered\n", driver->driver.name);

    /* Walk the adapters that are already present */
    //对应前面提到的 动态探测I2C总线添加i2c设备 内核不推荐使用这种方式。
    i2c_for_each_dev(driver, __process_new_driver);

    return 0;
}

调用driver_register这个函数将驱动注册进入内核时,会使用设备模型的匹配机制,和总线上的设备进行匹配。简单描述一下调用流程:

bus_add_driver---->
	driver_attach----> 无论如何最终都会调用这个函数
		bus_for_each_dev---->
			__driver_attach---->
				driver_match_device---->
					drv->bus->match(dev, drv) //如果匹配成功则调用 really_probe
					really_probe---->
						dev->bus->probe(dev) //默认点调用这个
						drv->probe(dev) //如果没有设置 dev->bus->probe 函数,则调用这个

最终调用总线上的match函数 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;

    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);
    /* match on an id table if there is one */
    if (driver->id_table) // id_table 进行匹配
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}

从该函数知道,i2c总线提供了3中匹配的方式,无论是什么方式都是获取我们需要的 i2c_client 结构:

  1. 使用设备树 compatible 字段进行匹配
  2. acpi 方式匹配(这个用了再来补充)
  3. 使用 id_table方式进行匹配

其中设备树方式在设备模型中已讨论,这种匹配方式只能匹配到设备树中生成的设备,而我们根本不会将 i2c_client 写到设备树中,因此这个匹配方式是匹配不到我们需要的 i2c_client 设备的,我也不知道为什么会写入这种规则,有大佬知道可以告知一下,这里讨论 id_table 方式

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_client->name 和 i2c_device_id->name 是否相同,如果相同则匹配成功,匹配成功后调用总线对应的 probe 函数 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;

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

        if (dev->of_node) { //更新irq
            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;
    }

    driver = to_i2c_driver(dev->driver); //获取对应的 i2c_driver 结构
    if (!driver->probe || !driver->id_table)
        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;

    status = driver->probe(client, i2c_match_id(driver->id_table, client)); //最终调用i2c_driver下的probe函数,并使用 client 匹配成功的driver->id_table作为参数
    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;
}

i2c_device_probe 函数又根据更新了了一次 irq,并最终调用了注册的 i2c_driver 结构的 probe 成员函数。于是在 probe 函数中我们就获取到了我们需要的 i2c_client 结构。内核也提供了一个demo,参考kernel-4.9,Documentation/i2c/upgrading-clients

struct example_state {
    struct i2c_client   *client;
    ....
};

static int example_probe(struct i2c_client *client,
                 const struct i2c_device_id *id)
{
    struct example_state *state;
    struct device *dev = &client->dev;

    state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
    if (state == NULL) {
        dev_err(dev, "failed to create our state\n");
        return -ENOMEM;
    }

    state->client = client;
    i2c_set_clientdata(client, state);

    /* rest of the initialisation goes here. */

    dev_info(dev, "example client created\n");

    return 0;
}

static int example_remove(struct i2c_client *client)
{
    struct example_state *state = i2c_get_clientdata(client);

    kfree(state);
    return 0;
}

static struct i2c_device_id example_idtable[] = {
    { "example", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, example_idtable);

static struct i2c_driver example_driver = {
    .driver     = {
        .owner      = THIS_MODULE,
        .name       = "example",
        .pm     = &example_pm_ops,
    },
    .id_table   = example_idtable,
    .probe      = example_probe,
    .remove     = example_remove,
};

四、使用 i2c_client 通信

获取到 i2c_client 结构之后,就可以使用它来进行i2c通信了,内核使用 i2c_msg 结构装了传输的数据,每次通讯都是以 i2c_msg 为单位的

struct i2c_msg {
    __u16 addr; 	//设备地址
    __u16 flags;	//传输标志位
#define I2C_M_RD        0x0001  //设置了这个表示表示本次通信为接收数据,否则为发送数据。
                    /* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN       0x0010  //设置了这个表示从设备地址有10bit
#define I2C_M_RECV_LEN      0x0400  
#define I2C_M_NO_RD_ACK     0x0800  //在读操作中不需要ack
#define I2C_M_IGNORE_NAK    0x1000  //当前i2cmsg忽略i2c器件的ack和nack信号
#define I2C_M_REV_DIR_ADDR  0x2000  //读写标志位反过来
#define I2C_M_NOSTART       0x4000  //当前i2c_msg不发送start信号
#define I2C_M_STOP      0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
    __u16 len;      //数据长度,单位为byte
    __u8 *buf;      //数据缓冲区指针
};

1、i2c_master_send

该函数用于向 i2c_client 发送buf指向的数据,数据长度为count字节

int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
    int ret;
    struct i2c_adapter *adap = client->adapter;
    struct i2c_msg msg;

    msg.addr = client->addr;
    msg.flags = client->flags & I2C_M_TEN;
    msg.len = count;
    msg.buf = (char *)buf;

    ret = i2c_transfer(adap, &msg, 1);

    /*
     * If everything went ok (i.e. 1 msg transmitted), return #bytes
     * transmitted, else error code.
     */
    return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_send);

2、i2c_master_recv

该函数用于向 i2c_client 接收数据长度为count字节的数据,数据被存在buf中。

int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
    struct i2c_adapter *adap = client->adapter;
    struct i2c_msg msg;
    int ret;

    msg.addr = client->addr;
    msg.flags = client->flags & I2C_M_TEN;
    msg.flags |= I2C_M_RD;
    msg.len = count;
    msg.buf = buf;

    ret = i2c_transfer(adap, &msg, 1);

    /*
     * If everything went ok (i.e. 1 msg received), return #bytes received,
     * else error code.
     */
    return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_recv);

3、i2c_transfer

该函数向 i2c_adapter 所表示的总线上传输 num 个 msgs。

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    int ret;

    if (adap->algo->master_xfer) {
#ifdef DEBUG
        for (ret = 0; ret < num; ret++) {
            dev_dbg(&adap->dev,
                "master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",
                ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W',
                msgs[ret].addr, msgs[ret].len,
                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
        }
#endif

        if (in_atomic() || irqs_disabled()) {
            ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
            if (!ret)
                /* I2C activity is ongoing. */
                return -EAGAIN;
        } else {
            i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
        }

        ret = __i2c_transfer(adap, msgs, num);
        i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);

        return ret;
    } else {
        dev_dbg(&adap->dev, "I2C level transfers not supported\n");
        return -EOPNOTSUPP;
    }
}
EXPORT_SYMBOL(i2c_transfer);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值