设备树绑定声明对节点内容的要求,主要说明了内容了数据类型和键。Zephyr设备树绑定是自定义格式的YAML
文件。
介绍
设备树节点使用compatible
属性与绑定匹配。
在配置
阶段,生成系统
尝试将设备树中的每个节点与绑定文件匹配。如果此操作成功,生成系统
将在验证节点的内容和为节点生成宏时使用绑定文件
中的信息。
示例
设备树:
/* Node in a DTS file */
bar-device {
compatible = "foo-company,bar-device";
num-foos = <3>;
};
绑定文件:
# A YAML binding matching the node
compatible: "foo-company,bar-device"
properties:
num-foos:
type: int
required: true
生成系统将bar-device
节点的compatible
与其YAML文件里的compatible
匹配,节点的属性与绑定文件里的行匹配。
生成系统对绑定执行的操作
生成系统
使用绑定文件
来验证设备树节点,并将设备树的内容转换为devicetree_unfixed.h
头文件。
例如,生成系统将使用绑定文件
来检查节点中是否存在所需的属性,以及其值是否具有正确的类型。然后,生成系统将为节点的属性生成一个宏。此宏允许使用API
在代码中获取属性的值。
对于另一个示例,以下节点将导致生成错误,因为它没有属性,并且num-foos
属性在绑定中标记为必需:
bad-node {
compatible = "foo-company,bar-device";
};
节点与绑定匹配的其他方式
如果节点的compatible
属性中有多个字符串,则生成系统将按列出的顺序查找兼容
的绑定,并使用第一个匹配项。
以下节点为例:
baz-device {
compatible = "foo-company,baz-device", "generic-baz-device";
};
如果生成系统找不到带有compatible: "generic-baz-device"
行的绑定文件,则该节点将与具有compatible: "foo-company,baz-device"
行的绑定文件匹配。
没有compatible
属性的节点可以与与其父节点关联的绑定进行匹配。这些称为子绑定
。如果节点描述总线上的硬件(如I2C
或SPI
),则在将节点与绑定匹配时也会考虑总线类型。
一些没有compatible
属性的特殊节点与推断绑定匹配。对于这些节点,生成系统
根据最终设备树中的属性生成宏。
绑定文件存放位置
生成系统在以下位置的子目录中查找dts/bindings
绑定文件:
- zephyr 存储库
- 应用程序目录
- 自定义板子目录
- 指定的
DTS_ROOT
变量中的任何目录 - 在其
构建设置
中定义dts_root
的任何模块
绑定文件是以.yml
或.yaml
结尾的YAML
文件。
绑定文件必须位于上述位置的dts/bindings
的子目录中。
绑定文件语法
Zephyr
的绑定文件是YAML
类型的文件。在YAML文件由多个键值对
组成。
以下是绑定文件所有的键:
# 绑定所应用的设备的顶层描述:
description: |
This is the Vendomatic company's foo-device.
Descriptions which span multiple lines (like this) are OK,
and are encouraged for complex bindings.
See https://yaml-multiline.info/ for formatting help.
# 包含其他的绑定文件的定义
include: other.yaml
# 匹配节点到这个绑定文件上
compatible: "manufacturer,foo-device"
# 节点需要满足的属性
properties:
# Requirements for and descriptions of the properties that this
# binding's nodes need to satisfy go here.
# 使用这个键可以约束匹配此绑定的节点的子节点
child-binding:
# You can constrain the children of the nodes matching this binding
# using this key.
# 如果节点描述了总线硬件,比如SPI总线控制器,在SoC上使用'bus:'来表示是哪个
bus: spi
# 如果节点作为总线上的设备出现,比如外部设备SPI内存芯片,
# 使用'on-bus:'来表示什么类型的总线。和compatible一样,这个键也会影响节点绑定匹配的方式
on-bus: spi
# 指定单元名称的域
foo-cells:
# "Specifier" cell names for the 'foo' domain go here; example 'foo'
# values are 'gpio', 'pwm', and 'dma'. See below for more information.
description键
主要写的是节点硬件的描述。
compatible键
此键用于将节点与绑定文件进行匹配。它在绑定文件中应如下所示:
# Note the comma-separated vendor prefix and device name
compatible: "manufacturer,device"
此设备树节点将与上述绑定匹配:
device {
compatible = "manufacturer,device";
};
如果找到多个兼容
的绑定文件,则会引发错误。
properties键
该键描述与绑定匹配的节点可以包含的属性。
例如,UART
外设的绑定可能如下所示:
compatible: "manufacturer,serial"
properties:
reg:
type: array
description: UART peripheral MMIO register space
required: true
current-speed:
type: int
description: current baud rate
required: true
label:
type: string
description: human-readable name
required: false
以下节点中的属性将通过上面的绑定文件
的验证:
my-serial@deadbeef {
compatible = "manufacturer,serial";
reg = <0xdeadbeef 0x1000>;
current-speed = <115200>;
label = "UART_0";
};
除了某些特殊属性(如reg
)之外,只有绑定文件中列出的属性才会生成宏。
属性的示例
properties:
# Describes a property like 'current-speed = <115200>;'. We pretend that
# it's obligatory for the example node and set 'required: true'.
current-speed:
type: int
required: true
description: Initial baud rate for bar-device
# Describes an optional property like 'keys = "foo", "bar";'
keys:
type: string-array
required: false
description: Keys for bar-device
# Describes an optional property like 'maximum-speed = "full-speed";'
# the enum specifies known values that the string property may take
maximum-speed:
type: string
required: false
description: Configures USB controllers to work up to a specific speed.
enum:
- "low-speed"
- "full-speed"
- "high-speed"
- "super-speed"
# Describes an optional property like 'resolution = <16>;'
# the enum specifies known values that the int property may take
resolution:
type: int
required: false
enum:
- 8
- 16
- 24
- 32
# Describes a required property '#address-cells = <1>'; the const
# specifies that the value for the property is expected to be the value 1
"#address-cells":
type: int
required: true
const: 1
int-with-default:
type: int
required: false
default: 123
description: Value for int register, default is power-up configuration.
array-with-default:
type: array
required: false
default: [1, 2, 3] # Same as 'array-with-default = <1 2 3>'
string-with-default:
type: string
required: false
default: "foo"
string-array-with-default:
type: string-array
required: false
default: ["foo", "bar"] # Same as 'string-array-with-default = "foo", "bar"'
uint8-array-with-default:
type: uint8-array
required: false
default: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'
属性的语法
<property name>:
required: <true | false>
type: <string | int | boolean | array | uint8-array | string-array |
phandle | phandles | phandle-array | path | compound>
deprecated: <true | false>
default: <default>
description: <description of the property>
enum:
- <item1>
- <item2>
...
- <itemN>
const: <string | int>
required
: 代表此节点属性是否是必须的type
: 代表节点属性的类型
属性的类型如下:
类型 | 描述 | 示例 |
---|---|---|
string | 字符串 | label = "UART_0"; |
int | 32位整数 | current-speed = <115200>; |
boolean | bool类型 | hw-flow-control; |
array | 32位整数数组 | offsets = <0x100 0x200 0x300>; |
uint8-array | 字节数组 | local-mac-address = [de ad be ef 12 34]; |
string-array | 字符串数组 | dma-names = "tx", "rx"; |
phandle | 节点 | interrupt-parent = <&gic>; |
phandles | 节点数组 | pinctrl-0 = <&usart2_tx_pd5 &usart2_rx_pd6>; |
phandle-array | 节点和32位整数的数组 | dmas = <&dma0 2>, <&dma0 3>; |
path | 节点路径引用或路径字符串 | zephyr,bt-c2h-uart = &uart0; 或foo = "/path/to/some/node"; |
compound | 更复杂的类型,一个包罗万象的类型,但不会生成任何宏 | foo = <&label>, [01 02]; |
deprecated
: 具有deprecated: true
的属性向用户和工具指示该属性将被逐步淘汰。default
: 可选的,提供一个值,如果设备树节点中缺少该属性,将使用该值。enum
: 该属性可能包含的值的列表。如果设备树中的属性值不在列表中,则会引发错误。const
: 这指定了属性必须采用的常量值。
child-binding键
当节点
具有所有相同属性
的子节点时,可以使用child-binding
键。每个子节点
都获取child-binding键
的内容作为其绑定,但如果子节点匹配到了绑定文件,则优先使用绑定文件。
一个PWM LED节点
的绑定如下所示,其中子节点
需要具有一个pwms
属性:
pwmleds {
compatible = "pwm-leds";
red_pwm_led {
pwms = <&pwm3 4 15625000>;
};
green_pwm_led {
pwms = <&pwm3 0 15625000>;
};
/* ... */
};
绑定文件如下:
compatible: "pwm-leds"
child-binding:
description: LED that uses PWM
properties:
pwms:
type: phandle-array
required: true
child-binding
也可以以递归方式工作。例如:
compatible: foo
child-binding:
child-binding:
properties:
my-property:
type: int
required: true
将应用于设备树中的grandchild
节点:
parent {
compatible = "foo";
child {
grandchild {
my-property = <123>;
};
};
};
bus键
如果节点是总线控制器
,请在绑定
中使用bus
来说明总线的类型。例如,SoC上SPI控制器
的绑定如下所示:
compatible: "manufacturer,spi-peripheral"
bus: spi
绑定文件中存在此键会通知生成系统与此绑定匹配的任何节点的子节点出现在此类型的总线上。
on-bus键
如果节点是总线上的设备
,在绑定文件中使用on-bus
说明总线的类型。
例如,外部SPI存储器芯片的绑定应包括以下行:
on-bus: spi
查找节点的绑定时,生成系统会检查父节点的绑定是否包含bus: <bus type>
。如果是这样,则仅考虑具有匹配bus: <bus type>
的绑定文件。首先搜索具有显式on-bus: <bus type>
的绑定,然后搜索没有显式on-bus
的绑定。按顺序对节点属性中的每个项重复搜索。
此功能允许同一设备具有不同的绑定
,具体取决于它出现在哪个总线上。例如,考虑一个兼容
的传感器器件,可以通过I2C
或SPI
使用。
因此,传感器节点可能在器件树中显示为SPI
或I2C
控制器的子节点,如下所示:
spi-bus@0 {
/* ... some compatible with 'bus: spi', etc. ... */
sensor@0 {
compatible = "manufacturer,sensor";
reg = <0>;
/* ... */
};
};
i2c-bus@0 {
/* ... some compatible with 'bus: i2c', etc. ... */
sensor@79 {
compatible = "manufacturer,sensor";
reg = <79>;
/* ... */
};
};
可以编写两个单独的绑定文件,这些文件与这些单独的传感器节点匹配,即使它们具有相同的兼容性:
# manufacturer,sensor-spi.yaml, which matches sensor@0 on the SPI bus:
compatible: "manufacturer,sensor"
on-bus: spi
# manufacturer,sensor-i2c.yaml, which matches sensor@79 on the I2C bus:
compatible: "manufacturer,sensor"
properties:
uses-clock-stretching:
type: boolean
required: false
on-bus: i2c
foo-cells键
说明符单元格
通常与属性类型为phandle-array
时一起使用。
假定某个节点具有以下类型属性:
pwm0: pwm@0 {
compatible = "foo,pwm";
#pwm-cells = <2>;
};
pwm3: pwm@3 {
compatible = "bar,pwm";
#pwm-cells = <1>;
};
my-device {
pwms = <&pwm0 1 2>, <&pwm3 4>;
};
# foo,pwm.yaml
compatible: "foo,pwm"
...
pwm-cells:
- channel
- period
# bar,pwm.yaml
compatible: "bar,pwm"
...
pwm-cells:
- period
可以看到pwm0
的#pwm-cells
的属性为2,这就意味着说明符单元格
的列表数量为2
,因此<&pwm0 1 2>
中1
和2
的含义就由单元格说明符
来定义,分别代表channel
和period
。
include键
绑定可以包含其他文件,这些文件可用于在绑定之间共享公共属性定义。
包含单个绑定文件
include: foo.yaml
如果找到任何名为foo.yaml
的文件,则该文件将包含在此绑定中。
如果键在绑定文件和包含绑定文件中以不同的值出现,则直接报错,但required
是个例外。
基础绑定文件
base.yaml
文件包含许多常见属性的定义。编写新绑定时,最好检查是否已定义了某些必需的属性,如果定义了,则将其包括在内。
# Common fields for all devices
properties:
status:
type: string
required: false
description: indicates the operational status of a device
enum:
- "ok" # Deprecated form
- "okay"
- "disabled"
- "reserved"
- "fail"
- "fail-sss"
compatible:
type: string-array
required: true
description: compatible strings
reg:
type: array
description: register space
required: false
reg-names:
type: string-array
description: name of each register space
required: false
interrupts:
type: array
required: false
description: interrupts for device
# Does not follow the 'type: phandle-array' scheme, but gets type-checked
# by the code. Declare it here just so that other bindings can make it
# 'required: true' easily if they want to.
interrupts-extended:
type: compound
required: false
description: extended interrupt specifier for device
interrupt-names:
type: string-array
required: false
description: name of each interrupt
interrupt-parent:
type: phandle
required: false
description: phandle to interrupt controller node
label:
type: string
required: false
description: Human readable string describing the device (used as device_get_binding() argument)
clocks:
type: phandle-array
required: false
description: Clock gate information
clock-names:
type: string-array
required: false
description: name of each clock
"#address-cells":
type: int
required: false
description: number of address cells in reg property
"#size-cells":
type: int
required: false
description: number of size cells in reg property
dmas:
required: false
type: phandle-array
description: DMA channels specifiers
dma-names:
required: false
type: string-array
description: Provided names of DMA channel specifiers
io-channels:
required: false
type: phandle-array
description: IO channels specifiers
io-channel-names:
required: false
type: string-array
description: Provided names of IO channel specifiers
包含多个绑定文件
include:
- foo.yaml
- bar.yaml
包括多个文件时,所包含文件中属性上的任何重叠键都将放在一起。
过滤属性定义
在某些情况下,您可能希望包括文件中的某些属性定义,但不是全部。在这种情况下,应该是一个列表,您可以通过在列表中过滤掉所需的定义,如下所示:
include:
- name: foo.yaml
property-allowlist:
- i-want-this-one
- and-this-one
- name: bar.yaml
property-blocklist:
- do-not-include-this-one
- or-this-one
混合包含
您可以在单个列表包含文件和属性定义:
include:
- foo.yaml
- name: bar.yaml
property-blocklist:
- do-not-include-this-one
- or-this-one
过滤子绑定的属性定义
include:
- name: bar.yaml
child-binding:
property-allowlist:
- child-prop-to-allow
推断绑定
Zephyr的设备树
脚本可以根据其/zephyr,user
属性中观察到的值推断
特殊节点的绑定。
此节点匹配绑定,该绑定由生成系统根据其在最终设备树中的属性值动态创建。它没有compatible
属性。
例如:
#include <dt-bindings/gpio/gpio.h>
/ {
zephyr,user {
boolean;
bytes = [81 82 83];
number = <23>;
numbers = <1>, <2>, <3>;
string = "text";
strings = "a", "b", "c";
handle = <&gpio0>;
handles = <&gpio0>, <&gpio1>;
signal-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
};
};
可以获取到如下的值:
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
DT_PROP(ZEPHYR_USER_NODE, boolean) // 1
DT_PROP(ZEPHYR_USER_NODE, bytes) // {0x81, 0x82, 0x83}
DT_PROP(ZEPHYR_USER_NODE, number) // 23
DT_PROP(ZEPHYR_USER_NODE, numbers) // {1, 2, 3}
DT_PROP(ZEPHYR_USER_NODE, string) // "text"
DT_PROP(ZEPHYR_USER_NODE, strings) // {"a", "b", "c"}
可以通过以下方式获取gpio0
节点:
/*
* Same thing as:
*
* ... my_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0));
*/
const struct device *my_device =
DEVICE_DT_GET(DT_PROP(ZEPHYR_USER_NODE, handle));
#define PHANDLE_TO_DEVICE(node_id, prop, idx) \
DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),
/*
* Same thing as:
*
* ... *my_devices[] = {
* DEVICE_DT_GET(DT_NODELABEL(gpio0)),
* DEVICE_DT_GET(DT_NODELABEL(gpio1)),
* };
*/
const struct device *my_devices[] = {
DT_FOREACH_PROP_ELEM(ZEPHYR_USER_NODE, handles, PHANDLE_TO_DEVICE)
};
可以通过以下方式获取signal-gpios
中的引脚:
#include <drivers/gpio.h>
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
const struct gpio_dt_spec signal =
GPIO_DT_SPEC_GET(ZEPHYR_USER_NODE, signal_gpios);
/* Configure the pin */
gpio_pin_configure_dt(&signal, GPIO_OUTPUT_INACTIVE);
/* Set the pin to its active level */
gpio_pin_set_dt(&signal, 1);