kernel - device tree

一、ubuntu设备树编译方法

  1. ubuntu安装dtc工具
sudo apt-get install device-tree-compiler
  1. 使用dtc工具编译设备树源文件
dtc -I dts -O dtb -o test.dtb test.dts
  1. 使用dtc工具反编译设备树二进制文件
dtc -I dtb -O dts -o test.dts test.dtb

二、解析设备树并注册平台设备of_platform_populate

// 文件arch/arm/kernel/setup.c
static int __init customize_machine(void)
{
    of_iommu_init();
    if (machine_desc->init_machine)
        machine_desc->init_machine();
#ifdef CONFIG_OF
    else
        of_platform_populate(NULL, of_default_bus_match_table,
                    NULL, NULL);
#endif
    return 0;
}
const struct of_device_id of_default_bus_match_table[] = {
    { .compatible = "simple-bus", },
    { .compatible = "simple-mfd", },
#ifdef CONFIG_ARM_AMBA
    { .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
    {} /* Empty terminated list */
};  
  • 该函数为arm平台板级初始化入口,调用init_machine板级初始化
  • 调用of_platform_populate函数解析设备树创建注册平台设备,此时传入参数of_default_bus_match_table
// 文件drivers/of/platform.c
int of_platform_populate(struct device_node *root,
            const struct of_device_id *matches,
            const struct of_dev_auxdata *lookup,
            struct device *parent)
{
    struct device_node *child;
    int rc = 0;

    root = root ? of_node_get(root) : of_find_node_by_path("/");
    if (!root)
        return -EINVAL;

    for_each_child_of_node(root, child) {
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc)
            break;
    }
    of_node_set_flag(root, OF_POPULATED_BUS);
                    
    of_node_put(root);
    return rc;
}
  • 遍历设备树root的子结点,调用of_platform_bus_create创建注册Platform device
// 文件drivers/of/platform.c
static int of_platform_bus_create(struct device_node *bus,
                  const struct of_device_id *matches,
                  const struct of_dev_auxdata *lookup,
                  struct device *parent, bool strict)
{
   ......

    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    if (!dev || !of_match_node(matches, bus))
        return 0;

    for_each_child_of_node(bus, child) {
        pr_debug("   create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
            of_node_put(child);
            break;
        }
    }
    ......
}
  • of_platform_bus_create调用of_platform_device_create_pdata创建平台设备
  • 根据结点bus创建platform device
  • 如果dev创建失败或者node id与of_default_bus_match_table匹配,则递归搜索子结点
static struct platform_device *of_platform_device_create_pdata(
                    struct device_node *np,                  
                    const char *bus_id,
                    void *platform_data,
                    struct device *parent)                   
{
    ......

    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        goto err_clear_flag;
    ......

}
  • 调用of_device_alloc分配platform_device内存空间,并解析平台资源(内存和中断)

小结,在板级初始化时,of_platform_populate会去解析设备树为platform device有两种情况:

  • 根结点下的子结点
  • 如果子节点的compiltale属性与of_default_bus_match_table匹配,则递归解析该结点下的子结点

三、I2C设备树的解析

// I2C设备树实例
&i2c2 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c2>;
    status = "okay";

    codec: wm8960@1a {
            compatible = "wlf,wm8960";
            reg = <0x1a>;
            clocks = <&clks IMX6QDL_CLK_CKO>;
            //clocks = <&clks 201>;
            clock-names = "mclk";
            DCVDD-supply = <&reg_audio>;
            DBVDD-supply = <&reg_audio>;
            AVDD-supply = <&reg_audio>;
            CPVDD-supply = <&reg_audio>;
            MICVDD-supply = <&reg_audio>;
            PLLVDD-supply = <&reg_audio>;
            SPKVDD1-supply = <&reg_audio>;
            SPKVDD2-supply = <&reg_audio>;
            wlf,shared-lrclk;
            /* capless; */
           // amic-mono;
};
  • 父结点将作为I2C控制器设备,通过i2c_add_numbered_adapter函数注册到系统中
  • 子结点将被解析为I2C从设备,注册到I2C总线上去,进而与对应的I2C驱动相匹配
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
    if (adap->nr == -1) /* -1 means dynamically assign bus id */
        return i2c_add_adapter(adap);

    return __i2c_add_numbered_adapter(adap);
}
EXPORT_SYMBOL_GPL(i3c_add_numbered_adapter);
  • i2c_add_numbered_adapter调用__i2c_add_numbered_adapter,然后__i2c_add_numbered_adapter在调用i2c_register_adapter注册I2C控制器
static int i2c_register_adapter(struct i2c_adapter *adap)
{
    ......
    
    exit_recovery:
    /* create pre-declared device nodes */
    of_i2c_register_devices(adap);
    acpi_i2c_register_devices(adap);
    acpi_i2c_install_space_handler(adap);

    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);

    /* Notify drivers */
    mutex_lock(&core_lock);
    bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    mutex_unlock(&core_lock);

    return 0;

out_list:
    mutex_lock(&core_lock);
    idr_remove(&i2c_adapter_idr, adap->nr);
    mutex_unlock(&core_lock);
    return res;
}
  • i2c_register_adapter调用of_i2c_register_devices解析设备树创建i2c_client,并注册到I2C总线
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
    struct device_node *node;

    /* Only register child devices if the adapter has a node pointer set */
    if (!adap->dev.of_node)
        return;

    dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

    for_each_available_child_of_node(adap->dev.of_node, node)
        of_i2c_register_device(adap, node);
}
  • 遍历设备树子节点,调用of_i2c_register_device创建i2c_client
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;
    int len;

    dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

    if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
        dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }

    addr = of_get_property(node, "reg", &len);
    if (!addr || (len < sizeof(int))) {
        dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
            node->full_name);
        return ERR_PTR(-EINVAL);
    }

    info.addr = be32_to_cpup(addr);
    if (info.addr > (1 << 10) - 1) {
        dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
            info.addr, node->full_name);
        return ERR_PTR(-EINVAL);
    }

    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);
    if (result == NULL) {
        dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
            node->full_name);
        of_node_put(node);
        return ERR_PTR(-EINVAL);
    }
    return result;
}
  • 解析设备树填充i2c_board_info结构体
  • 调用i2c_new_device创建注册i2c_client
posted @ 2019-06-06 10:11  qzhang1535  阅读(472)  评论(0编辑  收藏  举报