天天看點

裝置樹學習之(七)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] {
        #address-cells = <>;
        #size-cells = <>;
        compatible = "samsung,s3c2440-hdmiphy-i2c";
        reg = < >;
        interrupts = <  >;
        clocks = < >;
        clock-names = "i2c";
        status = "disabled";

        [email protected] {
            compatible = "exynos4210-hdmiphy";
            reg = <>;
            linux,phandle = <>;
            phandle = <>;
        };
    };
           

父節點為 i2c 控制器,子節點為挂載該控制器總線上的裝置。

"samsung,s3c2440-hdmiphy-i2c" 對應的驅動程式中必然會注冊控制器驅動
    ret = i2c_add_numbered_adapter(&i2c->adap); //注冊控制器驅動
    if (adap->nr == -) /* -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 +  : compatible, len);//如果有“,”取“,”之後的内容,如果沒有取全部
    return ;
}
           
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 ;  

    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 ;  
}  
           

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[]) {  
        if (strcmp(client->name, id->name) == )  
            return id;  
        id++;  
    }  
    return NULL;  
} 
           

結論:

[email protected] {
            compatible = "exynos4210-hdmiphy";
            reg = <0x38>;
            linux,phandle = <0x30>;
            phandle = <0x30>;
        };
           

compatible = “exynos4210-hdmiphy”;如果有“,”取“,”之後的内容,如果沒有取全部作為 i2c_client 的名字。