本文以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>