arm linux启动流程带dts

  • Post author:
  • Post category:linux


初始化

start_kernel()

init/main.c

asmlinkage void __init start_kernel(void)

{



setup_arch(&command_line);



rest_init();

}

setup_arch()

arch/arm64/kernel/setup.c

void __init setup_arch(char **cmdline_p)

{



setup_machine_fdt(__fdt_pointer);



parse_early_param();



unflatten_device_tree();

ifdef CONFIG_SMP

smp_init_cpus();

endif

...

}

rest_init()

init/main.c

static noinline void __init_refok rest_init(void)

{



kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);



}

static int __ref kernel_init(void *unused)

{

int ret;

kernel_init_freeable();

}

static noinline void __init kernel_init_freeable(void)

{

smp_prepare_cpus(setup_max_cpus);

}

void __init smp_prepare_cpus(unsigned int max_cpus)

{

int err;

unsigned int cpu, ncores = num_possible_cpus();

init_cpu_topology();

setup_machine_fdt()

arch/arm64/kernel/setup.c

static void __init setup_machine_fdt(phys_addr_t dt_phys)

{

if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {

while (true)

cpu_relax();

}

machine_name = of_flat_dt_get_machine_name();

}

setup_machine_fdt()调用了两个函数:

1.early_init_dt_scan()

2.of_flat_dt_get_machine_name()

of_flat_dt_get_machine_name用于获取如下属性:

model

compatible

如果model没有设置,就用compatible来设置machine_name.

early_init_dt_scan()

drivers/of/fdt.c

bool __init early_init_dt_scan(void *params)

{

if (!params)

return false;

/* Setup flat device-tree pointer */
initial_boot_params = params;

/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);

/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);

return true;

}

early_init_dt_scan()做了三件事,分别解析如下相关属性:

1、early_init_dt_scan_chosen()

chosen

bootargs

2、early_init_dt_scan_root()

size-cells

address-cells

3、early_init_dt_scan_memory()

device_type

linux,usable-memory

drivers/of/fdt.c

early_init_dt_scan_memory()参阅后面关于memory节点的说明。

unflatten_device_tree

start_kernel->setup_arch->unflatten_device_tree->

drivers/of/fdt.c

void __init unflatten_device_tree(void)

{

__unflatten_device_tree(initial_boot_params, &of_allnodes,

early_init_dt_alloc_memory_arch);

/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);

}

arm64_device_init

arch/arm64/kernel/setup.c

static int __init arm64_device_init(void)

{

of_clk_init(NULL);

of_platform_populate(NULL[参数为NULL,则用根节点], of_default_bus_match_table, NULL, NULL);

return 0;

}

arch_initcall_sync

启动加载级别

;

drivers/of/platform.c

const struct of_device_id of_default_bus_match_table[] = {

{ .compatible = “simple-bus”, },

ifdef CONFIG_ARM_AMBA

{ .compatible = "arm,amba-bus", },

endif /* CONFIG_ARM_AMBA */

{} /* Empty terminated list */

};

of_platform_populate

drivers/of/platform.c

int of_platform_populate(struct device_node *root,

const struct of_device_id *matches,

const struct of_dev_auxdata *lookup,

struct device *parent)

{

struct device_node *child;

int rc = 0;

root = root ? of_node_get(root) : of_find_node_by_path("/");[如果root参数为NULL,则用根节点]
if (!root)
    return -EINVAL;

for_each_child_of_node(root, child) {
    rc = of_platform_bus_create(child, matches, lookup, parent, true);
    if (rc)
        break;
}

of_node_put(root);
return rc;

}

实际调用:

int of_platform_populate(struct device_node *root,

const struct of_device_id *matches,

const struct of_dev_auxdata *lookup,

struct device *parent)

{

for_each_child_of_node(root, child) {

rc = of_platform_bus_create(child, of_default_bus_match_table, NULL, NULL, true);

}

}

of_platform_bus_create

of_platform_populate实际调用:

of_platform_bus_create(child, of_default_bus_match_table, NULL, NULL, true);

static int of_platform_bus_create(struct device_node *bus,

const struct of_device_id *matches,

const struct of_dev_auxdata *lookup,

struct device *parent, bool strict)

{

if (strict && (!of_get_property(bus, “compatible”, NULL))) {

pr_err(“%s() – skipping %s, no compatible prop\n”,


func

, bus->full_name);

return 0;

}

dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);

if (!dev || !of_match_node(matches, bus))

return [到这里就直接返回了。]0;

for_each_child_of_node(bus, child) {
    rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
    if (rc) {
        of_node_put(child);
        break;
    }
}
return rc;[这一段代码没有执行。]

}

关于参数true:

drivers/of/platform.c

static int of_platform_bus_create(struct device_node *bus,

const struct of_device_id *matches,

const struct of_dev_auxdata *lookup,

struct device *parent, bool strict)

{



/* Make sure it has a compatible property */

if (strict && (!of_get_property(bus, “compatible”, NULL))) {

pr_debug(“%s() – skipping %s, no compatible prop\n”,


func

, bus->full_name);

return 0;

}



}

也就是说dts配置的节点必须有compatible属性。

节点

定义

[label:] node-name[@unit-address] {

[properties definitions]

[child nodes]

}

1、[]可选;

2、lable用于在dts中引用;

3、@后面是地址;

4、如果没有@unit-address,则node-name必须唯一;

5、如果有@unit-address,则node-name可以相同;

例1

gic: interrupt-controller@2c001000 {

compatible = “arm,cortex-a15-gic”, “arm,cortex-a9-gic”;

#interrupt-cells = <3>;

#address-cells = <0>;

interrupt-controller;

reg = <0x0 0xc4301000 0 0x1000>,

<0x0 0xc4302000 0 0x0100>;

interrupts = ;

};

编译后的dtb是:

interrupt-controller@2c001000 {

compatible = “arm,cortex-a15-gic”, “arm,cortex-a9-gic”;

#interrupt-cells = <0x00000003>;

#address-cells = <0x00000000>;

interrupt-controller;

reg = <0x00000000 0xc4301000 0x00000000 0x00001000 0x00000000 0xc4302000 0x00000000 0x00000100>;

interrupts = <0x00000001 0x00000009 0x00000f04>;

linux,phandle = <0x00000001>;

phandle = <0x00000001>;

};

例2:

gpio: banks@c11080b0 {

reg = <0x0 0xc88344b0 0x0 0x2c>,

<0x0 0xc88344e8 0x0 0x14>,

<0x0 0xc8834520 0x0 0x14>,

<0x0 0xc8834430 0x0 0x3c>;

reg-names = “mux”, “pull”, “pull-enable”, “gpio”;

gpio-controller;

#gpio-cells = <2>;

};

memory

例1:

memory@00000000 {

device_type = “memory”;

linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;

};

这里linux,usable-memory和reg属性一样。建议统一使用reg。

前面两个数是64位的起始地址,后面两个数是64位的大小。

该节点是由early_init_dt_scan_memory()来解析。

drivers/of/fdt.c

int __init early_init_dt_scan_memory(unsigned long node, const char *uname,

int depth, void *data)

{

const char *type = of_get_flat_dt_prop(node, “device_type”, NULL);

const __be32 *reg, *endp;

int l;

/* We are scanning "memory" nodes only */
if (type == NULL) {
    /*
     * The longtrail doesn't have a device_type on the
     * /memory node, so look for the node called /memory@0.
     */
    if (depth != 1 || strcmp(uname, "memory@0") != 0)
        return 0;
} else if (strcmp(type, "memory") != 0)
    return 0;

reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
    reg = of_get_flat_dt_prop(node, "reg", &l);
if (reg == NULL)
    return 0;

}

解释:

1、如果没有有device_type属性,则必须在根节点(depth=1)下这么写dts:

memory@0 {

linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;

};

2、如果device_type属性的值不是memory,则该函数不继续解析。

3、优先使用linux,usable-memory,如果没有,则使用reg来解析memory。

cpus

例1:

cpus:cpus {

#address-cells = <2>;

#size-cells = <0>;

#cooling-cells = <2>; /* min followed by max */

cpu@0 {
    device_type = "cpu";
    compatible = "arm,cortex-a53","arm,armv8";
    reg = <0x0 0x0>;
    enable-method = "psci";
};

cpu@1 {
    device_type = "cpu";
    compatible = "arm,cortex-a53","arm,armv8";
    reg = <0x0 0x1>;
    enable-method = "psci";
};
cpu@2 {
    device_type = "cpu";
    compatible = "arm,cortex-a53","arm,armv8";
    reg = <0x0 0x2>;
    enable-method = "psci";
};

cpu@3 {
    device_type = "cpu";
    compatible = "arm,cortex-a53","arm,armv8";
    reg = <0x0 0x3>;
    enable-method = "psci";
};

};

arch/arm64/kernel/setup.c

void __init setup_arch(char **cmdline_p)

{



setup_machine_fdt(__fdt_pointer);



parse_early_param();



unflatten_device_tree();

ifdef CONFIG_SMP

smp_init_cpus();

endif

...

}

解析过程:

第一阶段:

start_kernel()->setup_arch()->smp_init_cpus()

第二阶段:

start_kernel()->rest_init()

->kernel_init(kernel_thread)->…->smp_prepare_cpus()

smp_init_cpus()

start_kernel()->setup_arch()->smp_init_cpus()

arch/arm64/kernel/smp.c

void __init smp_init_cpus(void)

{

struct device_node *dn = NULL;

unsigned int i, cpu = 1;

bool bootcpu_valid = false;

while ((dn = of_find_node_by_type(dn, "cpu"))) {
    cell = of_get_property(dn, "reg", NULL);
    hwid = of_read_number(cell, of_n_addr_cells(dn));

    if (cpu_read_ops(dn, cpu) != 0)
        goto next;

    if (cpu_ops[cpu]->cpu_init(dn, cpu))
        goto next;

    cpu_logical_map(cpu) = hwid;

    cpu++;
}

for (i = 0; i < NR_CPUS; i++)
    if (cpu_logical_map(i) != INVALID_HWID)
        set_cpu_possible(i, true);

}

smp_prepare_cpus

start_kernel()->rest_init()

->kernel_init(kernel_thread)->kernel_init_freeable()->

smp_prepare_cpus()

do_pre_smp_initcalls()

smp_init()

sched_init_smp()

init/main.c

static noinline void __init kernel_init_freeable(void)

{

cad_pid = task_pid(current);

smp_prepare_cpus(setup_max_cpus);

do_pre_smp_initcalls();
lockup_detector_init();

smp_init();
sched_init_smp();

do_basic_setup();

}

smp_prepare_cpus()->init_cpu_topology()->

parse_dt_topology()

parse_dt_cpu_power

arch/arm64/kernel/topology.c

void __init init_cpu_topology(void)

{

reset_cpu_topology();

/*
 * Discard anything that was parsed if we hit an error so we
 * don't use partial information.
 */
if (parse_dt_topology())
    reset_cpu_topology();

reset_cpu_power();
parse_dt_cpu_power();

}

static int __init parse_dt_topology(void)

{

cn = of_find_node_by_path(“/cpus”);

map = of_get_child_by_name(cn, "cpu-map");
if (!map)
    goto out;

out:

of_node_put(cn);

return ret;

}

static void __init parse_dt_cpu_power(void)

{

for_each_possible_cpu(cpu) {
    cn = of_get_cpu_node(cpu, NULL);

    for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
        if (of_device_is_compatible(cn, cpu_eff->compatible))
            break;
    rate = of_get_property(cn, "clock-frequency", &len);
    if (!rate || len != 4) {
        pr_err("%s: Missing clock-frequency property\n",
            cn->full_name);
        continue;
    }

    capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
    cpu_capacity(cpu) = capacity;

}

}

enable-method

cpu@0 {

device_type = “cpu”;

compatible = “arm,cortex-a53”,”arm,armv8”;

reg = <0x0 0x0>;

enable-method = “psci”;

};

arch/arm64/kernel/cpu_ops.c

int __init cpu_read_ops(struct device_node *dn, int cpu)

{

const char *enable_method = of_get_property(dn, “enable-method”, NULL);

cpu_ops[cpu] = cpu_get_ops(enable_method);

return 0;

}

static const struct cpu_operations * __init cpu_get_ops(const char *name)

{

const struct cpu_operations **ops = supported_cpu_ops;

while (*ops) {
    if (!strcmp(name, (*ops)->name))
        return *ops;

    ops++;
}

return NULL;

}

static const struct cpu_operations *supported_cpu_ops[] __initconst = {

ifdef CONFIG_SMP

&smp_spin_table_ops,
&cpu_psci_ops,

endif

NULL,

};

const struct cpu_operations cpu_psci_ops = {

.name = “psci”,

.cpu_init = cpu_psci_cpu_init,

.cpu_prepare = cpu_psci_cpu_prepare,

.cpu_boot = cpu_psci_cpu_boot,

.cpu_suspend = cpu_psci_cpu_suspend,

ifdef CONFIG_HOTPLUG_CPU

.cpu_disable    = cpu_psci_cpu_disable,
.cpu_die    = cpu_psci_cpu_die,

endif

};

reserved-memory

exmaple

reserved-memory {

#address-cells = <2>;

#size-cells = <2>;

ranges;

/* global autoconfigured region for contiguous allocations */

secmon_reserved:linux,secmon {

compatible = “amlogic, aml_secmon_memory”;

reg = <0x0 0x10000000 0x0 0x200000>;

no-map;

};

pstore:aml_pstore {

compatible = “amlogic, pstore”;

reg = <0x0 0x07300000 0x0 0x100000>;

no-map;

};

di_reserved:linux,di {

compatible = “amlogic, di-mem”;

size = <0x0 0x3300000>; //51M for support nr10bit

multi-use;

};



};

sequence

start_kernel()->setup_arch()->arm64_memblock_init()->

early_init_fdt_scan_reserved_mem()->

of_scan_flat_dt(__fdt_scan_reserved_mem)

fdt_init_reserved_mem()

early_init_fdt_scan_reserved_mem()

arch/arm64/kernel/setup.c

void __init setup_arch(char **cmdline_p)

{



setup_machine_fdt(__fdt_pointer);



parse_early_param();

arm64_memblock_init();



unflatten_device_tree();

ifdef CONFIG_SMP

smp_init_cpus();

endif

...

}

arch/arm64/mm/init.c

void __init arm64_memblock_init(void)

{



early_init_fdt_scan_reserved_mem();



}

drivers/of/fdt.c

void __init early_init_fdt_scan_reserved_mem(void)

{

if (!initial_boot_params)

return;

of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
fdt_init_reserved_mem();

}

__fdt_scan_reserved_mem()

drivers/of/fdt.c

static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,

int depth, void *data)

{

if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
    if (__reserved_mem_check_root(node) != 0) {
        pr_err("Reserved memory: unsupported node format, ignoring\n");
        /* break scan */
        return 1;
    }
    found = 1;
    /* scan next node */
    return 0;
} else if (!found) {
    /* scan next node */
    return 0;
} else if (found && depth < 2) {
    /* scanning of /reserved-memory has been finished */
    return 1;
}

status = of_get_flat_dt_prop(node, "status[如果没有status,则默认status =okay]", NULL);
if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
    return 0;

err = __reserved_mem_reserve_reg(node, uname);
if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
    fdt_reserved_mem_save_node(node, uname, 0, 0);

/* scan next node */
return 0;

}

__reserved_mem_check_root()

static int __init __reserved_mem_check_root(unsigned long node)

{

const __be32 *prop;

prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
    return -EINVAL;

prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
    return -EINVAL;

prop = of_get_flat_dt_prop(node, "ranges", NULL);
if (!prop)
    return -EINVAL;
return 0;

}

必须提供以下三个属性:

size-cells

address-cells

ranges

而且#size-cells和#address-cells的值必须和根节点的值(由dt_root_size_cells和dt_root_addr_cells保存)相同。

reserved_mem[]

drivers/of/of_reserved_mem.c

define MAX_RESERVED_REGIONS 16

static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];

从上面代码看,reserved memory最多只能有16个。

属性

property[=value]

1、单个字符串

2、字符串列表

3、单个无符号整型数

4、无符号整型数列表

5、无参数值属性

address-cells

drivers/of/base.c

int of_n_addr_cells(struct device_node *np)

{

const __be32 *ip;

do {
    if (np->parent)
        np = np->parent;
    ip = of_get_property(np, "#address-cells", NULL);
    if (ip)
        return be32_to_cpup(ip);
} while (np->parent);
/* No #address-cells property for the root node */
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;

}

EXPORT_SYMBOL(of_n_addr_cells);

int of_n_size_cells(struct device_node *np)

{

const __be32 *ip;

do {
    if (np->parent)
        np = np->parent;
    ip = of_get_property(np, "#size-cells", NULL);
    if (ip)
        return be32_to_cpup(ip);
} while (np->parent);
/* No #size-cells property for the root node */
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;

}

EXPORT_SYMBOL(of_n_size_cells);

size-cells

描述子节点的reg属性。#address-cells表示地址域占用几个cell,#size-cells表示地址长度占用几个cell。

针对reg格式:

reg =

address-cells 表示address1/2 包含几个cell。

size-cells表示length2/2包含几个cell

例1:

/ {

#address-cells = <2>;

#size-cells = <2>;

sd {

reg = <0x0 0xd0072000 0x0 0x2000>;

}

}

例1的sd节点的reg属性,address是0x0 0xd0072000,length是0x0 0x2000。这个例子的地址实际都是64bit地址。

例2:

/ {

#address-cells = <2>;

#size-cells = <2>;

i2c {

address-cells = <1>;

size-cells = <0>;

at24 {

reg = <0x50>;

}

}

}

reg

格式:

reg =

address-cells

size-cells

也就是说节点的reg属性,需要使用父节点的#address-cells、#size-cells的值来解析。

例子

reserved-memory {

#address-cells = <2>;

#size-cells = <2>;

ranges;

/* global autoconfigured region for contiguous allocations */

secmon_reserved:linux,secmon {

compatible = “amlogic, aml_secmon_memory”;

reg = <0x0 0x10000000 0x0 0x200000>;

no-map;

};

model

const char * __init of_flat_dt_get_machine_name(void)

{

const char *name;

unsigned long dt_root = of_get_flat_dt_root();

name = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!name)
    name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
return name;

}

从代码看,如果定义了model,就用model来设置machine_name,否则就是用compatible.

有两处使用:

of_flat_dt_match_machine()

pr_info(“Machine model: %s\n”, of_flat_dt_get_machine_name());

start_kernel->setup_arch->setup_machine_fdt->

machine_name = of_flat_dt_get_machine_name();

compatible

格式:

[manufacturer], [model]

例1:

compatible = “amlogic, gxtvbb”;

interrupt-parent

例子

interrupt-parent = <&gic>;

gic: interrupt-controller@2c001000 {

compatible = “arm,cortex-a15-gic”, “arm,cortex-a9-gic”;

#interrupt-cells = <3>;

#address-cells = <0>;

interrupt-controller;

reg = <0x0 0xc4301000 0 0x1000>,

<0x0 0xc4302000 0 0x0100>;

interrupts = ;

};

1、定义interrupt-parent属性是定义在root节点,这样其他有中断的节点就不需要再定义了,直接继承。

系统在启动时,按照如下过程,分解irq dts:

of_irq_find_parent+0x68/0x6c

of_irq_init+0xb0/0x2c4

irqchip_init+0x10/0x1c

init_IRQ+0x8/0x2c arch/arm64/kernel/irq.c:80: irqchip_init();

start_kernel+0x1ec/0x354

分解

drivers/of/irq.c

device_name

drivers/of/platform.c

void of_device_make_bus_id(struct device *dev)

{

name = of_get_property(node, "device_name", NULL);
if (name) {
    dev_set_name(dev, "%s", name);
    return;
}
reg = of_get_property(node, "reg", NULL);
if (reg) {
    if (of_can_translate_address(node)) {
        addr = of_translate_address(node, reg);
    } else {
        addrp = of_get_address(node, 0, NULL, NULL);
        if (addrp)
            addr = of_read_number(addrp, 1);
        else
            addr = OF_BAD_ADDR;
    }
    if (addr != OF_BAD_ADDR) {
        dev_set_name(dev, "%llx.%s",
                 (unsigned long long)addr, node->name);
        return;
    }
}

/*
 * No BusID, use the node name and add a globally incremented
 * counter (and pray...)
 */
magic = atomic_add_return(1, &bus_no_reg_magic);
dev_set_name(dev, "%s.%d", node->name, magic - 1);

}

优先使用device_name属性作为设备名。

例1:

aml_sensor0: aml-sensor@0 {

compatible = “amlogic, aml-thermal”;

device_name = “thermal”;

#thermal-sensor-cells = <1>;

cpu_dyn_coeff = <140>;

/* cpu_freq gpu_freq cpu_core gpu_core */

min_state = <1000000 400 1 1>;

};

platform->dev->name:thermal

node->full_name:/aml-sensor@0

如果没有device_name,则使用reg参数作为设备名。

例2:

gpu_clk:gpu_clk@c883c00 {

compatible = “meson, gpu-clkgen-1.00.a”;

#clock-cells = <0>;

reg = <0 0xc883c000 0 0x00100>;

platform->dev->name:c883c000.gpu_clk

node->full_name: /gpu_clk@c883c00

如果没有reg,则动态分配(atomic)一个整数。

例3:

timer {

compatible = “arm,armv8-timer”;

interrupts = ,

,

,

;

};

platform->dev->name:/timer

node->full_name: timer.0

static atomic_t bus_no_reg_magic;

magic = atomic_add_return(1, &bus_no_reg_magic);

dev_set_name(dev, “%s.%d”, node->name, magic – 1);

第一个返回的值是0,依次是1, 2, 3, 4, …

格式是:节点的短名.编号

timer.0 timer.1 timer2 tmier.3

结构体

of_device_id

函数

of_get_flat_dt_root()

例1:

unsigned long root = of_get_flat_dt_root();

drivers/of/fdt.c

unsigned long __init of_get_flat_dt_root(void)

{

return 0;

}

of_flat_dt_is_compatible()

drivers/of/fdt.c

int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)

{

return of_fdt_is_compatible(initial_boot_params, node, compat);

}

of_scan_flat_dt()

drivers/of/fdt.c

int __init of_scan_flat_dt(int (*it)(unsigned long node,

const char *uname, int depth,

void *data),

void *data)

{

const void *blob = initial_boot_params;

const char *pathp;

int offset, rc = 0, depth = -1;

    for (offset = fdt_next_node(blob, -1, &depth);
         offset >= 0 && depth >= 0 && !rc;
         offset = fdt_next_node(blob, offset, &depth)) {

    pathp = fdt_get_name(blob, offset, NULL);
    if (*pathp == '/')
        pathp = kbasename(pathp);
    rc = it(offset, pathp, depth, data);
}
return rc;

}

每一个节点,包括其子节点,都会扫描一遍。

如果一旦回调函数返回非零值,则不再继续。

有如下几处调用:

drivers/of/fdt.c

void __init early_init_fdt_scan_reserved_mem(void)

{

if (!initial_boot_params)

return;

of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
fdt_init_reserved_mem();

}

bool __init early_init_dt_scan(void *params)

{

if (!params)

return false;

/* Setup flat device-tree pointer */
initial_boot_params = params;

/* check device tree validity */
if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
    initial_boot_params = NULL;
    return false;
}

/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);

/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);

return true;

}

of_flat_dt_is_compatible()

drivers/of/of_reserved_mem.c

static int __init __reserved_mem_init_node(struct reserved_mem *rmem)

{

extern const struct of_device_id __reservedmem_of_table[];

const struct of_device_id *i;

int ret = 0;

for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
    reservedmem_of_init_fn initfn = i->data;
    const char *compat = i->compatible;

    if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
        continue;

    /*
     * scan whole table to set up all initfn, if one memory region
     * is used by multi-users.
     */
    if (initfn(rmem) == 0) {
        pr_debug("Reserved memory: initialized node %s, compatible id %s\n",
            rmem->name, compat);
    } else
        ret--;
}
return ret;

}

of_find_node_by_path()

例1:

struct device_node *root = of_find_node_by_path(“/”)

例2:

struct device_node *memory;

memory = of_find_node_by_path(“/memory”);

of_find_node_by_name()

如果第一个参数为NULL,则是在全局中查找结点。

例1:

np = of_find_node_by_name(NULL, “thermal-zones”)

of_device_is_compatible

of_get_property

of_property_read_u32_index

of_property_read_u32_array

of_property_read_u64

of_property_read_string

of_property_read_string_index

of_property_count_strings

of_iomap

of_irq_get

irq_of_parse_and_map

of_irq_to_resource

of_irq_count

gpiolib-of

drivers/gpio/gpiolib-of.c