本文以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_END2,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\n",at91_get_soc_type(&at91_soc_initdata));pr_info("AT91: Detected soc subtype: %s\n",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.cstruct 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.\n",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.\n",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.\n",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);各个函数的用法相对简单,属于层层调用。最终的实现都是调用相应的字符串比较函数。这里写的相对简单,以后再丰富。