Linux i2c子系统驱动probe

Posted mb60ffdbe016b5d

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;


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

扫码或长按关注

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



以上是关于Linux i2c子系统驱动probe的主要内容,如果未能解决你的问题,请参考以下文章

linux驱动:i2c设备总线驱动

驱动程序实现i2c通讯-39

linux驱动probe函数的实现框架思考

gslx680触摸屏驱动源码码分析(gslX680.c)

[国嵌攻略][156][I2C自编设备驱动设计]

Linux驱动中,probe函数何时被调用