• (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);


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

  • 相关阅读:
    JFinal教程
    jvm总结
    函数初识【第十一篇】
    python基础【第十篇】
    python基础【第九篇】
    python基础【第八篇】
    python基础【第七篇】
    python基础【第六篇】
    python基础【第五篇】
    python基础【第四篇】
  • 原文地址:https://www.cnblogs.com/biglucky/p/4057488.html
Copyright © 2020-2023  润新知