Linux i2c子系统驱动probe

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weiqifa0/article/details/102423907

I2C 子系统

I2C 子系统使用的概率非常大,我之前有做过手机的经验, 手机跑的安卓系统,内核是Linux,手机的很多器件都是用I2C通信的,我经历过从板级设备到dts设备树的阶段,知道I2C在整个系统的举足轻重,正常的TP,Camera,sonser等等都是使用I2C进行控制的。

吹牛逼这么多,就是让大家知道理解I2C子系统的重要性,不过这篇文章就写一个小细节,I2C驱动的probe是如何被触发的,如果你不知道其中的原理,可能在写驱动的时候不能成功执行probe也是有可能的。

static const struct i2c_device_id goodix_ts_id[] = {	
    { GTP_I2C_NAME, 0 },	
    { }	
};	

	
static struct of_device_id goodix_ts_dt_ids[] = {	
    { .compatible = "goodix,gt9xx" },	
    { }	
};	

	
static struct i2c_driver goodix_ts_driver = {	
    .probe      = goodix_ts_probe,	
    .remove     = goodix_ts_remove,	
    .id_table   = goodix_ts_id,	
    .driver = {	
        .name     = GTP_I2C_NAME,	
        .owner    = THIS_MODULE,	
     .of_match_table = of_match_ptr(goodix_ts_dt_ids),	
    },	
};	

	
/*******************************************************    	
Function:	
    Driver Install function.	
Input:	
    None.	
Output:	
    Executive Outcomes. 0---succeed.	
********************************************************/	
static int goodix_ts_init(void)	
{	
    s32 ret;	
    /*	
    ......	
    */	
    ret = i2c_add_driver(&goodix_ts_driver);	
    return ret; 	
}

i2c_add_driver 驱动和设备匹配

i2c_add_driver()

i2c_register_driver 

driver_register 

driver_find 

bus_add_driver 

driver_attach 

bus_for_each_dev 

next_device

__driver_attach

driver_match_device

i2c_device_match 

acpi_driver_match_device

i2c_match_id 

of_driver_match_device 

of_match_device of_match_node

__of_match_node

__of_device_is_compatible

这个要说明的一个点是,我提出来一下,可能大家看代码的时候就不会那么困惑了,Linux 下的指针那么多,你每次如果调用都要追根溯源,那可能需要花费非常多的时间。

总线上的device和driver进行匹配的时候会调用 bus 对应的 match函数,对于i2c bus而言就是i2c_match,如果是platform_bus 那么就会回调到platform_match里面去执行。

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

	
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)	
        return i2c_match_id(driver->id_table, client) != NULL;	

	
    return 0;	
}

里面有三种 match 的函数,最后才会调用 i2c_match_id 这个函数,这个也是低版本内核还没有使用dts的时候使用的方式,也就是匹配dev和driver的name。

看下面新的 compatible 匹配方式

/**	
 * __of_device_is_compatible() - Check if the node matches given constraints	
 * @device: pointer to node	
 * @compat: required compatible string, NULL or "" for any match	
 * @type: required device_type value, NULL or "" for any match	
 * @name: required node name, NULL or "" for any match	
 *	
 * Checks if the given @compat, @type and @name strings match the	
 * properties of the given @device. A constraints can be skipped by	
 * passing NULL or an empty string as the constraint.	
 *	
 * Returns 0 for no match, and a positive integer on match. The return	
 * value is a relative score with larger values indicating better	
 * matches. The score is weighted for the most specific compatible value	
 * to get the highest score. Matching type is next, followed by matching	
 * name. Practically speaking, this results in the following priority	
 * order for matches:	
 *	
 * 1. specific compatible && type && name	
 * 2. specific compatible && type	
 * 3. specific compatible && name	
 * 4. specific compatible	
 * 5. general compatible && type && name	
 * 6. general compatible && type	
 * 7. general compatible && name	
 * 8. general compatible	
 * 9. type && name	
 * 10. type	
 * 11. name	
 */	
static int __of_device_is_compatible(const struct device_node *device,	
                     const char *compat, const char *type, const char *name)	
{	
    struct property *prop;	
    const char *cp;	
    int index = 0, score = 0;	

	
    /* Compatible match has highest priority */	
    if (compat && compat[0]) {	
        /*获取dts里面该节点的值*/	
        prop = __of_find_property(device, "compatible", NULL);	
        for (cp = of_prop_next_string(prop, NULL); cp;	
             cp = of_prop_next_string(prop, cp), index++) {	
            /*字符串比较*/	
            if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {	
                score = INT_MAX/2 - (index << 2);	
                break;	
            }	
        }	
        /*返回成功*/	
        if (!score)	
            return 0;	
    }	

	
    /* Matching type is better than matching name */	
    if (type && type[0]) {	
        if (!device->type || of_node_cmp(type, device->type))	
            return 0;	
        score += 2;	
    }	

	
    /* Matching name is a bit better than not */	
    if (name && name[0]) {	
        if (!device->name || of_node_cmp(name, device->name))	
            return 0;	
        score++;	
    }	

	
    return score;	
}

代码里面我们看到是同时比较 name ,type,compatible 这三个属性的,但是我们使用dts进行设置的时候,name和type的属性很多时候都是空的。

&i2c1 {	
        status = "okay";	

	
        /*	
        ......	
        */	
        gt9xx: gt9xx@14 {	
                compatible = "goodix,gt9xx";	
                reg = <0x14>;	
                touch-gpio = <&gpio1 0 IRQ_TYPE_EDGE_RISING>;	
                reset-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;	
                max-x = <800>;	
                max-y = <1280>;	
                tp-size = <89>;	
                configfile-num = <1>;	
                status = "okay";	
                tp-supply =  <&vcc3v0_tp>;	
        };	
};	

i2c probe被探测 执行的流程

i2c_add_driver()

i2c_register_driver 

driver_register driver_find 

kset_find_obj 

kobject_put 

to_driver

bus_add_driver

driver_attach 

bus_for_each_dev 

next_device

__driver_attach

driver_match_device 

driver_probe_device 

really_probe

i2c_device_probe

i2c_match_id

你以为上面设置就好了吗?我们看到

static struct i2c_driver goodix_ts_driver = {	
    .probe      = goodix_ts_probe,	
    .remove     = goodix_ts_remove,	
    .id_table   = goodix_ts_id,	
    .driver = {	
        .name     = GTP_I2C_NAME,	
        .owner    = THIS_MODULE,	
     .of_match_table = of_match_ptr(goodix_ts_dt_ids),	
    },	
};

里面有一个 id_tabel和一个 of_match_table 两个东西,既然probe探测只需要 of_match_tabel就可以了,是不是可以去掉id_tabel了呢?

这感觉是一个遗留问题,在i2c_probe函数里面有一个判断,不知道历史原因还是为何,不能做到完全兼容,看代码如下

static int i2c_device_probe(struct device *dev)	
{	
    /* ...... */	
    driver = to_i2c_driver(dev->driver);	
    /* 判断id_table为空就退出 */	
    if (!driver->probe || !driver->id_table)	
        return -ENODEV;	

	
    /* ...... */	
}

640?wx_fmt=jpeg

扫码或长按关注

回复「 加群 」进入技术群聊

猜你喜欢

转载自blog.csdn.net/weiqifa0/article/details/102423907
今日推荐