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;
/* ...... */
}
扫码或长按关注
回复「 加群 」进入技术群聊
转载:https://blog.csdn.net/weiqifa0/article/details/102423907