1. 程式人生 > >裝置樹學習之(七)I2C裝置的註冊過程分析

裝置樹學習之(七)I2C裝置的註冊過程分析

開發板: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 的名字。