开发板:tiny4412SDK + S702 + 4GB Flash
要移植的内核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自带的 U-Boot 2010.12
busybox版本:busybox 1.25
目标:
设备树中普通的节点都被注册为平台设备驱动中的“设备”,也就是注册到 platform_bus_type 的,但是 i2c spi 设备等,它们都是注册到 i2c_bus_type spi_bus_type 的,那么内核在解析设备树的过程中是如何处理的呢?本文分析设备树解析过程中 i2c 设备的注册过程。掌握设备树中 i2c 设备的表示方式。
在内核中,i2c 控制器驱动内核已经支持的非常好了,我们做的只需要写设备驱动程序,经过粗略分析,在设备树中,内核是这样处理控制器驱动程序和设备驱动程序之间的关系的。控制器驱动为父节点,设备驱动为子节点,在注册控制器驱动时顺便会遍历子节点,将设备程序注册进去。下面分析代码。
i2c@138E0000 {
#address-cells = <0x1>;
#size-cells = <0x0>;
compatible = "samsung,s3c2440-hdmiphy-i2c";
reg = <0x138e0000 0x100>;
interrupts = <0x0 0x5d 0x0>;
clocks = <0x7 0x145>;
clock-names = "i2c";
status = "disabled";
hdmiphy@38 {
compatible = "exynos4210-hdmiphy";
reg = <0x38>;
linux,phandle = <0x30>;
phandle = <0x30>;
};
};
父节点为 i2c 控制器,子节点为挂载该控制器总线上的设备。
"samsung,s3c2440-hdmiphy-i2c" 对应的驱动程序中必然会注册控制器驱动
ret = i2c_add_numbered_adapter(&i2c->adap); //注册控制器驱动
if (adap->nr == -1) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);
return __i2c_add_numbered_adapter(adap);
i2c_register_adapter
of_i2c_register_devices(adap);
for_each_available_child_of_node(adap->dev.of_node, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
of_i2c_register_device(adap, node);
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr_be;
u32 addr;
int len;
of_modalias_node(node, info.type, sizeof(info.type))
addr_be = of_get_property(node, "reg", &len);
addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info.flags |= I2C_CLIENT_SLAVE;
}
i2c_check_addr_validity(addr, info.flags))
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
result = i2c_new_device(adap, &info);
return result;
}
// info.type == strlcpy(modalias, p ? p + 1 : compatible, len);//如果有“,”取“,”之后的内容,如果没有取全部
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
compatible = of_get_property(node, "compatible", &cplen);
if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
p = strchr(compatible, ',');
strlcpy(modalias, p ? p + 1 : compatible, len);//如果有“,”取“,”之后的内容,如果没有取全部
return 0;
}
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
strlcpy(client->name, info->type, sizeof(client->name));
match 函数:
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;
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;
}
i2c_bus_type 的匹配过程:
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;
}
结论:
hdmiphy@38 {
compatible = "exynos4210-hdmiphy";
reg = <0x38>;
linux,phandle = <0x30>;
phandle = <0x30>;
};
compatible = “exynos4210-hdmiphy”;如果有“,”取“,”之后的内容,如果没有取全部作为 i2c_client 的名字。