Linux i2c子系统驱动probe

宝剑锋从磨砺出,梅花香自苦寒来。这篇文章主要讲述Linux i2c子系统驱动probe相关的知识,希望能为你提供帮助。


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

【Linux i2c子系统驱动probe】

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


/* ...... */



Linux i2c子系统驱动probe

文章图片

扫码或长按关注
回复「 ?加群? 」进入技术群聊





    推荐阅读