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; /* ...... */
}
扫码或长按关注
回复「 加群 」进入技术群聊