整个设备数牵涉买你比较广,既增加了新的用于描述设备硬件信息的文件格式,又增加了编译这个文本的工具,同时bootloader也需要支持将编译后的设备数传递给Linux内核
/ {
nodel {
a-string-property = "A string";
a-string-list-property = "first string", "second string"
a-byte-data-property = {0x01 0x23 0x34 0x56};
child-node {
first-child-property;
second-child-property = <1>
a-string-property = "Hello, world"
};
child-node2 {
};
};
node2 {
an-enpty-property;
a-cell-property = <1 2 3 4>; /* each munber (cell) is a uint32 */
child-node1 {
};
};
};
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
vexpress-v2p-ca15_a7.dtb \
xenvm-4.2.dtb
UBoot > fdt addr 0x71000000
//例1
//板子arch/arm/boot/dts/vexpress-v2p-ca9.dts兼容于"arm,vexpress,v2p-ca9"和“arm,vexpress”
compatible = "arm,vexpress,v2p-ca9", "arm, vexpress"
//板子arch/arm/boot/dts/vexpress-v2p-ca5s.dts兼容性如下:
compatible = "arm,vexpress,v2p-ca5s", "arm, vexpress"
//板子arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts兼容性如下:
compatible = "arm,vexpress,v2p-ca15_a7", "arm, vexpress"
//例2
//arch/arm/boot/dts/exymos4210-origen.dts的兼容性:
compatible = "insignal,origen", "samsung,exynos4210", "samsung,exynos4"
//arch/arm/boot/dts/exymos4210-universal_c210.dts的兼容性:
compatible = "insignal,universal_c210", "samsung,exynos4210", "samsung,exynos4"
static const char * const v2m_dt_match[] __initconst = {
"arm,vexpress",
"xen,xenvm",
NULL,
};
DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
.dt_compat = v2m_dt_match,
.smp = smp_ops(vexpress_smp_ops),
.map_io = v2m_dt_map_io,
.init_early = v2m_dt_init_early,
.init_irq = v2m_dt_init_irq,
.timer = &v2m_dt_timer,
.init_machine = v2m_dt_init,
.handle_irq = gic_handle_irq,
.restart = vexpress_restart,
MACHINE_END
int of_machine_is_compatible(const char *compat)
of_machine_is_compatible("samsung,universal_c210")
of_machine_is_compatible("samsung,exynos4210")
of_machine_is_compatible("samsung,exynos4")
flash@0,00000000 {
compatible = "arm,vexpress-flash","cfi-flash";
reg = <0 0x00000000 0x04000000>
<1 0x00000000 0x04000000>;
bank-width = <4>
};
//platform设备驱动中的of_match_table
static const struct of_device_id a1234_i2c_of_match[] = {
{ .compatible = "acme,a1234-i2c-bus ", },
{},
};
MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);
static struct platform_driver i2c_a1234_driver = {
.driver = {
.name = "a1234-i2c-bus ",
.owner = THIS_MODULE,
.of_match_table = a1234_i2c_of_match,
},
.probe = i2c_a1234_probe,
.remove = i2c_a1234_remove,
};
module_platform_driver(i2c_a1234_driver);
//i2c和spi设备驱动中的of_match_table
static const struct of_device_id wm8753_of_match[] = {
{ .compatible = "wlf,wm8753", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8753_of_match);
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
.owner = THIS_MODULE,
.of_match_table = wm8753_of_match,
},
.probe = wm8753_spi_probe,
.remove = wm8753_spi_remove,
};
static struct i2c_driver wm8753_i2c_driver = {
.driver = {
.name = "wm8753",
.owner = THIS_MODULE,
.of_match_table = wm8753_of_match,
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
.id_table = wm8753_i2c_id,
};
//spi的别名匹配
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
int of_device_is_compatible(const struct device_node *device, const char *compat);
root结点”/”的cpus子节点下面又包含2个cpu子结点,描述了此machine上的2个CPU,且它们的compatible 属性为”arm,cortex-a9”。
可以给一个设备节点添加label,之后可以通过&label的形式访问这个label,这种引用是通过phandle(pointer handle)进行的
//代码1
reg
#address-cells
#size-cells
//代码2
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts.
The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high level-sensitive
8 = active low level-sensitive
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of
the 8 possible cpus attached to the GIC. A bit set to '1' indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts.
有了Device Tree后,大量的板级信息都不再需要,譬如过去经常在arch/arm/plat-xxx和arch/arm/mach-xxx实施的如下事情:
//代码1
90 static struct resource xxx_resources[] = {
91 [0] = {
92 .start = …,
93 .end = …,
94 .flags = IORESOURCE_MEM,
95 },
96 [1] = {
97 .start = …,
98 .end = …,
99 .flags = IORESOURCE_IRQ,
100 },
101 };
102
103 static struct platform_device xxx_device = {
104 .name = "xxx",
105 .id = -1,
106 .dev = {
107 .platform_data = &xxx_data,
108 },
109 .resource = xxx_resources,
110 .num_resources = ARRAY_SIZE(xxx_resources),
111 };
//代码2
18 static struct of_device_id xxx_of_bus_ids[] __initdata = {
19 { .compatible = "simple-bus", },
20 {},
21 };
22
23 void __init xxx_mach_init(void)
24 {
25 of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);
26 }
32
33 #ifdef CONFIG_ARCH_XXX
38
39 DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)")
41 …
45 .init_machine = xxx_mach_init,
46 …
49 MACHINE_END
50 #endif
//代码1
145 static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {
146 {
147 I2C_BOARD_INFO("tlv320aic23", 0x1a),
148 }, {
149 I2C_BOARD_INFO("fm3130", 0x68),
150 }, {
151 I2C_BOARD_INFO("24c64", 0x50),
152 },
153 };
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
…
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
79 static struct spi_board_info afeb9260_spi_devices[] = {
80 { /* DataFlash chip */
81 .modalias = "mtd_dataflash",
82 .chip_select = 1,
83 .max_speed_hz = 15 * 1000 * 1000,
84 .bus_num = 0,
85 },
86 };
在Linux的BSP和驱动代码中,还经常会使用到Linux中一组Device Tree的API,这些API通常被冠以of_前缀,它们的实现代码位于内核的drivers/of目录。
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz);
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz);
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz);
int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);
534 of_property_read_u32_array(np, "arm,data-latency",
535 data, ARRAY_SIZE(data));
137 L2: cache-controller@1e00a000 {
138 compatible = "arm,pl310-cache";
139 reg = <0x1e00a000 0x1000>;
140 interrupts = <0 43 4>;
141 cache-level = <2>;
142 arm,data-latency = <1 1 1>;
143 arm,tag-latency = <1 1 1>;
144 }
有些情况下,整形属性的长度可能为1,于是内核为了方便调用者,又在上述API的基础上封装出了更加简单的读单一整形属性的API,它们为int of_property_read_u8()、of_property_read_u16()等,实现于include/linux/of.h:
513 static inline int of_property_read_u8(const struct device_node *np,
514 const char *propname,
515 u8 *out_value)
516 {
517 return of_property_read_u8_array(np, propname, out_value, 1);
518 }
519
520 static inline int of_property_read_u16(const struct device_node *np,
521 const char *propname,
522 u16 *out_value)
523 {
524 return of_property_read_u16_array(np, propname, out_value, 1);
525 }
526
527 static inline int of_property_read_u32(const struct device_node *np,
528 const char *propname,
529 u32 *out_value)
530 {
531 return of_property_read_u32_array(np, propname, out_value, 1);
532 }
处整形属性外,字符串属性也比较常用,其对应的API包括:
int of_property_read_string(struct device_node *np, const char
*propname, const char **out_string);
int of_property_read_string_index(struct device_node *np, const char
*propname, int index, const char **output);
前者读取字符串属性,后者读取字符串数组属性中的第index个字符串。如drivers/clk/clk.c中的of_clk_get_parent_name()透过of_property_read_string_index()遍历clkspec结点的所有”clock-output-names”字符串数组属性。
1759 const char *of_clk_get_parent_name(struct device_node *np, int index)
1760 {
1761 struct of_phandle_args clkspec;
1762 const char *clk_name;
1763 int rc;
1764
1765 if (index < 0)
1766 return NULL;
1767
1768 rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
1769 &clkspec);
1770 if (rc)
1771 return NULL;
1772
1773 if (of_property_read_string_index(clkspec.np, "clock-output-names",
1774 clkspec.args_count ? clkspec.args[0] : 0,
1775 &clk_name) < 0)
1776 clk_name = clkspec.np->name;
1777
1778 of_node_put(clkspec.np);
1779 return clk_name;
1780 }
1781 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
处整形外、字符串以外的最常用属性类就是布尔类型,其对应的API如下
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。
void __iomem *of_iomap(struct device_node *node, int index);
通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap。
unsigned int irq_of_parse_and_map(struct device_node *dev, int index);
透过Device Tree或者设备的中断号,实际上是从.dts中的interrupts属性解析出中断号。若设备使用了多个中断,index指定中断的索引号。
还有一些OF API,这里不一一列举,具体可参考include/linux/of.h头文件。