裝置樹學習之(七)I2C裝置的註冊過程分析
阿新 • • 發佈:2019-01-01
開發板: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 控制器驅動核心已經支援的非常好了,我們做的只需要寫裝置驅動程式,經過粗略分析,在裝置樹中,核心是這樣處理控制器驅動程式和裝置驅動程式之間的關係的。控制器驅動為父節點,裝置驅動為子節點,在註冊控制器驅動時順便會遍歷子節點,將裝置程式註冊進去。下面分析程式碼。
[email protected]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";
[email protected]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;
}
結論:
[email protected] {
compatible = "exynos4210-hdmiphy";
reg = <0x38>;
linux,phandle = <0x30>;
phandle = <0x30>;
};
compatible = “exynos4210-hdmiphy”;如果有“,”取“,”之後的內容,如果沒有取全部作為 i2c_client 的名字。