zoukankan      html  css  js  c++  java
  • (DT系列四)驱动加载中, 如何取得device tree中的属性

    本文以At91rm9200平台为例,从源码实现的角度来分析驱动加载时,Device tree的属性是如何取得的。
    一:系统级初始化
    DT_MACHINE_START 主要是定义"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
    #define DT_MACHINE_START(_name, _namestr)
    static const struct machine_desc __mach_desc_##_name
     __used
     __attribute__((__section__(".arch.info.init"))) = {
    .nr = ~0,
    .name = _namestr,


    1,
    DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)")
    /* Maintainer: Atmel */
    .timer  = &at91sam926x_timer,
    .map_io  = at91_map_io,
    .init_early  = at91_dt_initialize,
    .init_irq  = at91_dt_init_irq,
    .init_machine  = at91_dt_device_init,
    .dt_compat  = at91_dt_board_compat,
    MACHINE_END


    2,
    void __init at91_map_io(void)
    {
    /* Map peripherals */
    iotable_init(&at91_io_desc, 1);


    at91_soc_initdata.type = AT91_SOC_NONE;
    at91_soc_initdata.subtype = AT91_SOC_SUBTYPE_NONE;


    soc_detect(AT91_BASE_DBGU0);
    if (!at91_soc_is_detected())
    soc_detect(AT91_BASE_DBGU1);


    if (!at91_soc_is_detected())
    panic("AT91: Impossible to detect the SOC type");


    pr_info("AT91: Detected soc type: %s ",
    at91_get_soc_type(&at91_soc_initdata));
    pr_info("AT91: Detected soc subtype: %s ",
    at91_get_soc_subtype(&at91_soc_initdata));


    if (!at91_soc_is_enabled())
    panic("AT91: Soc not enabled");


    if (at91_boot_soc.map_io)
    at91_boot_soc.map_io();
    }


    3,
    static void __init soc_detect(u32 dbgu_base)
    {
    u32 cidr, socid;


    cidr = __raw_readl(AT91_IO_P2V(dbgu_base) + AT91_DBGU_CIDR);
    socid = cidr & ~AT91_CIDR_VERSION;


    switch (socid) {
    case ARCH_ID_AT91RM9200:
    at91_soc_initdata.type = AT91_SOC_RM9200;
    at91_boot_soc = at91rm9200_soc;
    break;


    case ARCH_ID_AT91SAM9260:
    at91_soc_initdata.type = AT91_SOC_SAM9260;
    at91_boot_soc = at91sam9260_soc;
    break;
    }
    }


    4,
    static inline int at91_soc_is_enabled(void)
    {
    return at91_boot_soc.init != NULL;
    }


    5,
    Arch/arm/mach-at91/At91rm9200.c
    struct at91_init_soc __initdata at91rm9200_soc = {
    .map_io = at91rm9200_map_io,
    .default_irq_priority = at91rm9200_default_irq_priority,
    .ioremap_registers = at91rm9200_ioremap_registers,
    .register_clocks = at91rm9200_register_clocks,
    .init = at91rm9200_initialize,
    };


    二,硬件的实际但简单的初始化
    6,
    static void __init at91rm9200_initialize(void)
    {
    arm_pm_idle = at91rm9200_idle;
    arm_pm_restart = at91rm9200_restart;


    /* 初始化GPIO 子系统*/
    at91_gpio_init(at91rm9200_gpio,
    cpu_is_at91rm9200_bga() ? AT91RM9200_BGA : AT91RM9200_PQFP);
    }


    7,
    /*
     * 该函数被特定的处理器初始化时调用,用来使能GPIO 引脚的支持.
     */
    void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks)
    {
    unsigned i;
    struct at91_gpio_chip *at91_gpio, *last = NULL;


    BUG_ON(nr_banks > MAX_GPIO_BANKS);


    if (of_at91_gpio_init() < 0) {
    /* No GPIO controller found in device tree */
    for (i = 0; i < nr_banks; i++)
    at91_gpio_init_one(i, data[i].regbase, data[i].id);
    }


    for (i = 0; i < gpio_banks; i++) {
    at91_gpio = &gpio_chip[i];


    /*
    * GPIO controller are grouped on some SoC:
    * PIOC, PIOD and PIOE can share the same IRQ line
    */
    if (last && last->pioc_hwirq == at91_gpio->pioc_hwirq)
    last->next = at91_gpio;
    last = at91_gpio;


    gpiochip_add(&at91_gpio->chip);
    }
    }


    8,
    static int __init of_at91_gpio_init(void)
    {
    struct device_node *np = NULL;


    /*
    * This isn't ideal, but it gets things hooked up until this
    * driver is converted into a platform_device
    */
    /*1,对每个节点进行属性的查询操作
      2,钩子函数使用的场景:驱动加载时,device node生成相应的platform device。*/
    for_each_compatible_node(np, NULL, "atmel,at91rm9200-gpio")
    of_at91_gpio_init_one(np);


    return gpio_banks > 0 ? 0 : -EINVAL;
    }


    三,实际的属性查找过程
    9,
    #define for_each_compatible_node(dn, type, compatible)
    for (dn = of_find_compatible_node(NULL, type, compatible); dn;
        dn = of_find_compatible_node(dn, type, compatible))


    10,
    /**
     * of_find_compatible_node - Find a node based on type and one of the
     *                                tokens in its "compatible" property
     * @from:  The node to start searching from or NULL, the node
     * you pass will not be searched, only the next one
     * will; typically, you pass what the previous call
     * returned. of_node_put() will be called on it
     * @type:  The type string to match "device_type" or NULL to ignore
     * @compatible:The string to match to one of the tokens in the device
     * "compatible" list.
     *
     * Returns a node pointer with refcount incremented, use
     * of_node_put() on it when done.
     */
    //通过type参数找到相应类型的节点,并且节点的一个tokens在参数compatible属性中。
    struct device_node *of_find_compatible_node(struct device_node *from,
    const char *type, const char *compatible)
    {
    struct device_node *np;


    read_lock(&devtree_lock);
    np = from ? from->allnext : allnodes;
    for (; np; np = np->allnext) {
    //通过类型找相应节点
    if (type
       && !(np->type && (of_node_cmp(np->type, type) == 0)))
    continue;
    //通过属性找相应节点
    if (of_device_is_compatible(np, compatible) && of_node_get(np))
    break;
    }
    of_node_put(from);
    read_unlock(&devtree_lock);
    return np;
    }


    11,
    // 核查所给的"compat" 字符串能否匹配某个device node中的"compatible" 属性。
    int of_device_is_compatible(const struct device_node *device,
    const char *compat)
    {
    const char* cp;
    int cplen, l;


    // 通过所给的名字找到相应节点的属性
    cp = of_get_property(device, "compatible", &cplen);
    if (cp == NULL)
    return 0;
    while (cplen > 0) {


    //重新验证其compatible属性是否匹配
    if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
    return 1;
    l = strlen(cp) + 1;
    cp += l;
    cplen -= l;
    }


    return 0;
    }


    12,
    /*
     * 通过所给的名字找到相应节点的属性,并返回其数值。若没有找到,则返回NULL。
     */
    const void *of_get_property(const struct device_node *np, const char *name,
    int *lenp)
    {
    //此函数是真正去找device tree中对应的属性
    struct property *pp = of_find_property(np, name, lenp);


    return pp ? pp->value : NULL;
    }


    13, //从函数定义上看,比of_get_property()不同的是返回值变为property。
    struct property *of_find_property(const struct device_node *np,
     const char *name,
     int *lenp)
    {
    struct property *pp;


    if (!np)
    return NULL;


    read_lock(&devtree_lock);
    for (pp = np->properties; pp != 0; pp = pp->next) {
    //调用基本的字符串比较函数
    if (of_prop_cmp(pp->name, name) == 0) {
    if (lenp != 0)
    *lenp = pp->length;
    break;
    }
    }
    read_unlock(&devtree_lock);


    return pp;
    }


    14, //庐山真面目的property结构体
    struct property {
    char *name;
    int length;
    void *value;
    struct property *next;
    unsigned long _flags;
    unsigned int unique_id;
    };


    15, //多了一层封装,这应该是遵从了Linux kernel的编码规范,待确认。
    #define of_prop_cmp(s1, s2) strcasecmp((s1), (s2))


    16, //忽略字母大小写的字符串比较。
    int strcasecmp(const char *s1, const char *s2)
    {
    int c1, c2;


    do {
    c1 = tolower(*s1++);
    c2 = tolower(*s2++);
    } while (c1 == c2 && c1 != 0);
    return c1 - c2;
    }


    四,真正的通过属性来执行硬件初始化
    17,(从函数8转过来的)
    最终回到第八个函数的调用: for_each_compatible_node()
    之后执行此函数: of_at91_gpio_init_one()
    //找到相应的属性,并以此属性进行相应的初始化等操作......
    static void __init of_at91_gpio_init_one(struct device_node *np)
    {
    int alias_idx;
    struct at91_gpio_chip *at91_gpio;


    if (!np)
    return;


    alias_idx =  of_alias_get_id(np, "gpio");
    if (alias_idx >= MAX_GPIO_BANKS) {
    pr_err("at91_gpio, failed alias idx(%d) > MAX_GPIO_BANKS(%d), ignoring. ",
    alias_idx, MAX_GPIO_BANKS);
    return;
    }


    at91_gpio = &gpio_chip[alias_idx];
    at91_gpio->chip.base = alias_idx * at91_gpio->chip.ngpio;


    at91_gpio->regbase = of_iomap(np, 0);
    if (!at91_gpio->regbase) {
    pr_err("at91_gpio.%d, failed to map registers, ignoring. ",
    alias_idx);
    return;
    }


    /* 获得中断属性*/
    if (of_property_read_u32(np, "interrupts", &at91_gpio->pioc_hwirq)) {
    pr_err("at91_gpio.%d, failed to get interrupts property, ignoring. ",
    alias_idx);
    goto ioremap_err;
    }


    /* 从compatibility属性里获得相关“能力” */
    if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio"))
    at91_gpio_caps |= AT91_GPIO_CAP_PIO3;


    /* 设置clock */
    if (at91_gpio_setup_clk(alias_idx))
    goto ioremap_err;


    at91_gpio->chip.of_node = np;
    gpio_banks = max(gpio_banks, alias_idx + 1);
    at91_gpio->pioc_idx = alias_idx;
    return;


    ioremap_err:
    iounmap(at91_gpio->regbase);
    }


    五,具体任务及相关参考
    以上从四个部分,通过追踪代码,一一先后的叙述了device tree中的属性如何获得,并起到相应的作用。下面将从工作任务的角度来分析:
    任务:
    驱动加载中取得device tree中的属性,有哪些关键的函数,各个函数
    的用法是什么。函数的实现原理是什么


    相关参考:
    关键的函数:
    1,
    //对每个节点进行属性的查询操作
    for_each_compatible_node(np, NULL, "atmel,at91rm9200-gpio")
    2,
    //通过type参数找到相应类型的节点,并且节点的一个tokens在参数compatible属性中。
    of_find_compatible_node(NULL, type, compatible)
    3,
    // 核查所给的"compat" 字符串能否匹配某个device node中的"compatible" 属性。
    of_device_is_compatible(np, compatible) && of_node_get(np))
    4,
    // 通过所给的名字找到相应节点的属性
    of_get_property(device, "compatible", &cplen);
    5,
    //从函数定义上看,比of_get_property()不同的是返回值变为property。
    of_find_property(np, name, lenp);


    各个函数的用法相对简单,属于层层调用。最终的实现都是调用相应的字符串比较函数。这里写的相对简单,以后再丰富。

  • 相关阅读:
    JAVA基础——编程练习(二)
    JAVA基础——面向对象三大特性:封装、继承、多态
    JVM内存
    50. Pow(x, n) (JAVA)
    47. Permutations II (JAVA)
    46. Permutations (JAVA)
    45. Jump Game II (JAVA)
    43. Multiply Strings (JAVA)
    42. Trapping Rain Water (JAVA)
    41. First Missing Positive (JAVA)
  • 原文地址:https://www.cnblogs.com/biglucky/p/4057488.html
Copyright © 2011-2022 走看看