(DT系列四)驱动加载中, 如何取得device tree中的属性

本文以At91rm9200平台为例,从源码实现的角度来分析驱动加载时,Devicetree的属性是如何取得的。

 

一:系统级初始化

DT_MACHINE_START主要是定义"struct machine_desc"的类型,放在section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。

#defineDT_MACHINE_START(_name, _namestr)\

static const structmachine_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\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(u32dbgu_base)

{

u32 cidr, socid;

 

 

cidr =__raw_readl(AT91_IO_P2V(dbgu_base) + AT91_DBGU_CIDR);

socid = cidr &~AT91_CIDR_VERSION;

 

 

switch (socid) {

caseARCH_ID_AT91RM9200:

at91_soc_initdata.type= AT91_SOC_RM9200;

at91_boot_soc =at91rm9200_soc;

break;

 

 

caseARCH_ID_AT91SAM9260:

at91_soc_initdata.type= AT91_SOC_SAM9260;

at91_boot_soc =at91sam9260_soc;

break;

}

}

 

 

4,

static inline int at91_soc_is_enabled(void)

{

returnat91_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 __initat91rm9200_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 __initat91_gpio_init(struct at91_gpio_bank *data, int nr_banks)

{

unsigned i;

structat91_gpio_chip *at91_gpio, *last = NULL;

 

 

BUG_ON(nr_banks >MAX_GPIO_BANKS);

 

 

if(of_at91_gpio_init() < 0) {

/* No GPIOcontroller 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 controllerare grouped on some SoC:

* PIOC, PIOD andPIOE 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 __initof_at91_gpio_init(void)

{

struct device_node*np = NULL;

 

 

/*

* This isn't ideal,but it gets things hooked up until this

* driver isconverted 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,

#definefor_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 typeand one of the

 *                              tokens in its "compatible" property

 *@from: Thenode 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: Thetype string to match "device_type" or NULL to ignore

 *@compatible:The string to match to one of thetokens 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" 属性。

intof_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)

{

//此函数是真正去找devicetree中对应的属性

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;

intlength;

void*value;

struct property*next;

unsigned long_flags;

unsigned intunique_id;

};

 

 

15,//多了一层封装,这应该是遵从了Linux kernel的编码规范,待确认。

#defineof_prop_cmp(s1, s2)strcasecmp((s1),(s2))

 

 

16,//忽略字母大小写的字符串比较。

int strcasecmp(constchar *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 __initof_at91_gpio_init_one(struct device_node *np)

{

int alias_idx;

structat91_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);

}

 

 

五,具体任务及相关参考

以上从四个部分,通过追踪代码,一一先后的叙述了devicetree中的属性如何获得,并起到相应的作用。下面将从工作任务的角度来分析:

任务:

驱动加载中取得devicetree中的属性,有哪些关键的函数,各个函数

的用法是什么。函数的实现原理是什么

 

 

相关参考:

关键的函数:

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

 

 

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

 

源文档 <http://blog.csdn.net/lichengtongxiazai/article/details/38941933

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值