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

原文网址:http://www.cnblogs.com/biglucky/p/4057488.html

本文以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\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.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.\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);

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

时间: 2024-10-09 21:16:03

【转】(DT系列四)驱动加载中, 如何取得device tree中的属性的相关文章

老调重弹:JDBC系列 之 &lt;驱动加载原理全面解析&gt;

前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读者一个参考---以下是本文的组织结构,读者可以点击上面的目录查看: 概述 一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用.基本结构图如下: 驱动加载入内存的过程 这里所谓的驱动,其实就是实现了java.sql.Driver

老调重弹:JDBC系列之&lt;驱动加载原理全面解析) ----转

  最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读者一个参考--- 概述 一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用.基本结构图如下: 驱动加载入内存的过程 这里所谓的驱动,其实就是实现了java.sql.Driver接口的类.如oracle的驱动类是 oracle.j

RX系列四 | RxAndroid | 加载图片 | 提交表单

RX系列四 | RxAndroid | 加载图片 | 提交表单 说实话,学RxJava就是为了我们在Android中运用的更加顺手一点,也就是RxAndroid,我们还是先一步步来,学会怎么去用的比较好,之前的三篇算是铺垫,让你有一点认识,那Rx在Android中有什么好处呢?我们先模拟一些原始功能和他对比下 一.加载图片 很多人说Rx出来之后,是编程思想的一种进阶,实际上我学习了这种思想之后,确实是觉得有了很大的改变,不过,需要一点学习成本再加上,需要对原先的思想有些改观,使得我依旧有点不适应

(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_

Android中Activity四种加载模式

Activity四种加载模式 我们知道在配置Activity的时候可以指定android:lauchMode属性,该属性用于配置该Activity的加载模式,概述行支持以下四种: 1.standard: 标准模式,这是默认的加载模式. 2.singleTop: Task顶单例模式. 3.singleTask: Task内单例模式. 4.singleInstance: 全局单例模式. 那么Activity为什么需要制定加载模式呢? 由于在Android上启动一个应用后,系统会自动的创建一个属于该应

【ESXI6.0】 ESXI6.0安装时无法安装网卡驱动的解决方法及将网卡驱动加载进ISO

若安装时提示如下图所示 之后安装无法完成,会提示没有检测到网络适配器,如下图. 这时候需要将网卡驱动加载进ISO中才能在安装时候识别网卡驱动. 网卡驱动从这里下载: https://vibsdepot.v-front.de/wiki/index.php/List_of_currently_available_ESXi_packages 找到对应的型号.点击进入下一页下载. 需要使用如下工具: ESXi-Customizer-v2.7.2 http://pan.baidu.com/s/1eQ2f8

Activity四种加载模式

android activity四种加载模式,有 >>>standard: 标准模式,这是默认的加载方式 >>>singleTop : task顶单例模式 >>>singleTask : Task内单列模式 >>>singleInstance :全局单列模式 <1>standard: 标准模式,这是默认的加载方式 public class StandardTest extends Activity { @Override

多功能PCIE交换机之八:窗口扩展和驱动加载的常见问题

结合本人在PCIE NTB/DMA最近的实际工作,总结了地址转换窗口扩展和驱动加载过程中碰到的主要问题和解决办法. 0.系统启动后看不到NTB设备 需要检查BIOS,在PCIE设置里面NTB芯片是否使能.这是因为针对不同的应用场景和客户需要,BIOS里面通常添加了Enable/Disable NTB的选项. 1.如何扩展地址转换窗口 a.确定系统要求的地址转换窗口的范围和大小: b.确保系统要求的地址转换窗口的范围和大能够被BIOS支持 c.从可用的BAR2/3和BAR4/5中选择未使用的或者可

【光速使用开源框架系列】图片加载框架ImageLoader

[关于本系列] 最近看了不少开源框架,网上的资料也非常多,但是我认为了解一个框架最好的方法就是实际使用.本系列博文就是带领大家快速的上手一些常用的开源框架,体会到其作用. 由于作者水平有限,本系列只会描述如何快速的使用框架的基本功能,更多的详细设置大家可以在这些项目的页面上找到. [介绍]: ImageLoader作为一款开源的异步图片加载框架,在Android开发中经常被使用到,该控件可以加载和显示图片.并且对其进行缓存,提供了很多方便的选项帮助开发者们快速达到开发需求. [GitHub页面]