设备树学习之(七)I2C设备的注册过程分析

转载地址:https://blog.csdn.net/lizuobin2/article/details/54564974

开发板: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>;
        };
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

父节点为 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
// 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
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;  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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;  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结论:

        hdmiphy@38 {
            compatible = "exynos4210-hdmiphy";
            reg = <0x38>;
            linux,phandle = <0x30>;
            phandle = <0x30>;
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值