设备树绑定声明对节点内容的要求,主要说明了内容了数据类型和键。Zephyr设备树绑定是自定义格式的YAML 文件。

介绍

设备树节点使用compatible属性与绑定匹配。
配置阶段,生成系统尝试将设备树中的每个节点与绑定文件匹配。如果此操作成功,生成系统将在验证节点的内容和为节点生成宏时使用绑定文件中的信息。

示例

设备树:

  1. /* Node in a DTS file */
  2. bar-device {
  3. compatible = "foo-company,bar-device";
  4. num-foos = <3>;
  5. };

绑定文件:

  1. # A YAML binding matching the node
  2. compatible: "foo-company,bar-device"
  3. properties:
  4. num-foos:
  5. type: int
  6. required: true

生成系统将bar-device节点的compatible与其YAML文件里的compatible匹配,节点的属性与绑定文件里的行匹配。

生成系统对绑定执行的操作

生成系统使用绑定文件来验证设备树节点,并将设备树的内容转换为devicetree_unfixed.h头文件。
例如,生成系统将使用绑定文件来检查节点中是否存在所需的属性,以及其值是否具有正确的类型。然后,生成系统将为节点的属性生成一个宏。此宏允许使用API在代码中获取属性的值。
对于另一个示例,以下节点将导致生成错误,因为它没有属性,并且num-foos属性在绑定中标记为必需:

  1. bad-node {
  2. compatible = "foo-company,bar-device";
  3. };

节点与绑定匹配的其他方式

如果节点的compatible属性中有多个字符串,则生成系统将按列出的顺序查找兼容的绑定,并使用第一个匹配项。
以下节点为例:

  1. baz-device {
  2. compatible = "foo-company,baz-device", "generic-baz-device";
  3. };

如果生成系统找不到带有compatible: "generic-baz-device"行的绑定文件,则该节点将与具有compatible: "foo-company,baz-device"行的绑定文件匹配。
没有compatible属性的节点可以与与其父节点关联的绑定进行匹配。这些称为子绑定。如果节点描述总线上的硬件(如I2CSPI),则在将节点与绑定匹配时也会考虑总线类型。
一些没有compatible属性的特殊节点与推断绑定匹配。对于这些节点,生成系统根据最终设备树中的属性生成宏。

绑定文件存放位置

生成系统在以下位置的子目录中查找dts/bindings绑定文件:

  • zephyr 存储库
  • 应用程序目录
  • 自定义板子目录
  • 指定的DTS_ROOT 变量中的任何目录
  • 在其构建设置中定义dts_root的任何模块

绑定文件是以.yml.yaml结尾的YAML文件。
绑定文件必须位于上述位置的dts/bindings的子目录中。

绑定文件语法

Zephyr的绑定文件是YAML类型的文件。在YAML文件由多个键值对组成。
以下是绑定文件所有的键:

  1. # 绑定所应用的设备的顶层描述:
  2. description: |
  3. This is the Vendomatic company's foo-device.
  4. Descriptions which span multiple lines (like this) are OK,
  5. and are encouraged for complex bindings.
  6. See https://yaml-multiline.info/ for formatting help.
  7. # 包含其他的绑定文件的定义
  8. include: other.yaml
  9. # 匹配节点到这个绑定文件上
  10. compatible: "manufacturer,foo-device"
  11. # 节点需要满足的属性
  12. properties:
  13. # Requirements for and descriptions of the properties that this
  14. # binding's nodes need to satisfy go here.
  15. # 使用这个键可以约束匹配此绑定的节点的子节点
  16. child-binding:
  17. # You can constrain the children of the nodes matching this binding
  18. # using this key.
  19. # 如果节点描述了总线硬件,比如SPI总线控制器,在SoC上使用'bus:'来表示是哪个
  20. bus: spi
  21. # 如果节点作为总线上的设备出现,比如外部设备SPI内存芯片,
  22. # 使用'on-bus:'来表示什么类型的总线。和compatible一样,这个键也会影响节点绑定匹配的方式
  23. on-bus: spi
  24. # 指定单元名称的域
  25. foo-cells:
  26. # "Specifier" cell names for the 'foo' domain go here; example 'foo'
  27. # values are 'gpio', 'pwm', and 'dma'. See below for more information.

description键

主要写的是节点硬件的描述。

compatible键

此键用于将节点与绑定文件进行匹配。它在绑定文件中应如下所示:

  1. # Note the comma-separated vendor prefix and device name
  2. compatible: "manufacturer,device"

此设备树节点将与上述绑定匹配:

  1. device {
  2. compatible = "manufacturer,device";
  3. };

如果找到多个兼容的绑定文件,则会引发错误。

properties键

该键描述与绑定匹配的节点可以包含的属性。
例如,UART外设的绑定可能如下所示:

  1. compatible: "manufacturer,serial"
  2. properties:
  3. reg:
  4. type: array
  5. description: UART peripheral MMIO register space
  6. required: true
  7. current-speed:
  8. type: int
  9. description: current baud rate
  10. required: true
  11. label:
  12. type: string
  13. description: human-readable name
  14. required: false

以下节点中的属性将通过上面的绑定文件的验证:

  1. my-serial@deadbeef {
  2. compatible = "manufacturer,serial";
  3. reg = <0xdeadbeef 0x1000>;
  4. current-speed = <115200>;
  5. label = "UART_0";
  6. };

除了某些特殊属性(如reg)之外,只有绑定文件中列出的属性才会生成宏。

属性的示例

  1. properties:
  2. # Describes a property like 'current-speed = <115200>;'. We pretend that
  3. # it's obligatory for the example node and set 'required: true'.
  4. current-speed:
  5. type: int
  6. required: true
  7. description: Initial baud rate for bar-device
  8. # Describes an optional property like 'keys = "foo", "bar";'
  9. keys:
  10. type: string-array
  11. required: false
  12. description: Keys for bar-device
  13. # Describes an optional property like 'maximum-speed = "full-speed";'
  14. # the enum specifies known values that the string property may take
  15. maximum-speed:
  16. type: string
  17. required: false
  18. description: Configures USB controllers to work up to a specific speed.
  19. enum:
  20. - "low-speed"
  21. - "full-speed"
  22. - "high-speed"
  23. - "super-speed"
  24. # Describes an optional property like 'resolution = <16>;'
  25. # the enum specifies known values that the int property may take
  26. resolution:
  27. type: int
  28. required: false
  29. enum:
  30. - 8
  31. - 16
  32. - 24
  33. - 32
  34. # Describes a required property '#address-cells = <1>'; the const
  35. # specifies that the value for the property is expected to be the value 1
  36. "#address-cells":
  37. type: int
  38. required: true
  39. const: 1
  40. int-with-default:
  41. type: int
  42. required: false
  43. default: 123
  44. description: Value for int register, default is power-up configuration.
  45. array-with-default:
  46. type: array
  47. required: false
  48. default: [1, 2, 3] # Same as 'array-with-default = <1 2 3>'
  49. string-with-default:
  50. type: string
  51. required: false
  52. default: "foo"
  53. string-array-with-default:
  54. type: string-array
  55. required: false
  56. default: ["foo", "bar"] # Same as 'string-array-with-default = "foo", "bar"'
  57. uint8-array-with-default:
  58. type: uint8-array
  59. required: false
  60. default: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'

属性的语法

  1. <property name>:
  2. required: <true | false>
  3. type: <string | int | boolean | array | uint8-array | string-array |
  4. phandle | phandles | phandle-array | path | compound>
  5. deprecated: <true | false>
  6. default: <default>
  7. description: <description of the property>
  8. enum:
  9. - <item1>
  10. - <item2>
  11. ...
  12. - <itemN>
  13. 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属性:

  1. pwmleds {
  2. compatible = "pwm-leds";
  3. red_pwm_led {
  4. pwms = <&pwm3 4 15625000>;
  5. };
  6. green_pwm_led {
  7. pwms = <&pwm3 0 15625000>;
  8. };
  9. /* ... */
  10. };

绑定文件如下:

  1. compatible: "pwm-leds"
  2. child-binding:
  3. description: LED that uses PWM
  4. properties:
  5. pwms:
  6. type: phandle-array
  7. required: true

child-binding也可以以递归方式工作。例如:

  1. compatible: foo
  2. child-binding:
  3. child-binding:
  4. properties:
  5. my-property:
  6. type: int
  7. required: true

将应用于设备树中的grandchild节点:

  1. parent {
  2. compatible = "foo";
  3. child {
  4. grandchild {
  5. my-property = <123>;
  6. };
  7. };
  8. };

bus键

如果节点是总线控制器,请在绑定中使用bus来说明总线的类型。例如,SoC上SPI控制器的绑定如下所示:

  1. compatible: "manufacturer,spi-peripheral"
  2. bus: spi

绑定文件中存在此键会通知生成系统与此绑定匹配的任何节点的子节点出现在此类型的总线上。

on-bus键

如果节点是总线上的设备,在绑定文件中使用on-bus说明总线的类型。
例如,外部SPI存储器芯片的绑定应包括以下行:

  1. on-bus: spi

查找节点的绑定时,生成系统会检查父节点的绑定是否包含bus: <bus type>。如果是这样,则仅考虑具有匹配bus: <bus type>的绑定文件。首先搜索具有显式on-bus: <bus type>的绑定,然后搜索没有显式on-bus的绑定。按顺序对节点属性中的每个项重复搜索。

此功能允许同一设备具有不同的绑定,具体取决于它出现在哪个总线上。例如,考虑一个兼容的传感器器件,可以通过I2CSPI使用。
因此,传感器节点可能在器件树中显示为SPII2C控制器的子节点,如下所示:

  1. spi-bus@0 {
  2. /* ... some compatible with 'bus: spi', etc. ... */
  3. sensor@0 {
  4. compatible = "manufacturer,sensor";
  5. reg = <0>;
  6. /* ... */
  7. };
  8. };
  9. i2c-bus@0 {
  10. /* ... some compatible with 'bus: i2c', etc. ... */
  11. sensor@79 {
  12. compatible = "manufacturer,sensor";
  13. reg = <79>;
  14. /* ... */
  15. };
  16. };

可以编写两个单独的绑定文件,这些文件与这些单独的传感器节点匹配,即使它们具有相同的兼容性:

  1. # manufacturer,sensor-spi.yaml, which matches sensor@0 on the SPI bus:
  2. compatible: "manufacturer,sensor"
  3. on-bus: spi
  1. # manufacturer,sensor-i2c.yaml, which matches sensor@79 on the I2C bus:
  2. compatible: "manufacturer,sensor"
  3. properties:
  4. uses-clock-stretching:
  5. type: boolean
  6. required: false
  7. on-bus: i2c

foo-cells键

说明符单元格通常与属性类型为phandle-array时一起使用。
假定某个节点具有以下类型属性:

  1. pwm0: pwm@0 {
  2. compatible = "foo,pwm";
  3. #pwm-cells = <2>;
  4. };
  5. pwm3: pwm@3 {
  6. compatible = "bar,pwm";
  7. #pwm-cells = <1>;
  8. };
  9. my-device {
  10. pwms = <&pwm0 1 2>, <&pwm3 4>;
  11. };
  1. # foo,pwm.yaml
  2. compatible: "foo,pwm"
  3. ...
  4. pwm-cells:
  5. - channel
  6. - period
  7. # bar,pwm.yaml
  8. compatible: "bar,pwm"
  9. ...
  10. pwm-cells:
  11. - period

可以看到pwm0#pwm-cells的属性为2,这就意味着说明符单元格的列表数量为2,因此<&pwm0 1 2>12的含义就由单元格说明符来定义,分别代表channelperiod

include键

绑定可以包含其他文件,这些文件可用于在绑定之间共享公共属性定义。

包含单个绑定文件

  1. include: foo.yaml

如果找到任何名为foo.yaml的文件,则该文件将包含在此绑定中。
如果键在绑定文件和包含绑定文件中以不同的值出现,则直接报错,但required是个例外。

基础绑定文件

base.yaml文件包含许多常见属性的定义。编写新绑定时,最好检查是否已定义了某些必需的属性,如果定义了,则将其包括在内。

  1. # Common fields for all devices
  2. properties:
  3. status:
  4. type: string
  5. required: false
  6. description: indicates the operational status of a device
  7. enum:
  8. - "ok" # Deprecated form
  9. - "okay"
  10. - "disabled"
  11. - "reserved"
  12. - "fail"
  13. - "fail-sss"
  14. compatible:
  15. type: string-array
  16. required: true
  17. description: compatible strings
  18. reg:
  19. type: array
  20. description: register space
  21. required: false
  22. reg-names:
  23. type: string-array
  24. description: name of each register space
  25. required: false
  26. interrupts:
  27. type: array
  28. required: false
  29. description: interrupts for device
  30. # Does not follow the 'type: phandle-array' scheme, but gets type-checked
  31. # by the code. Declare it here just so that other bindings can make it
  32. # 'required: true' easily if they want to.
  33. interrupts-extended:
  34. type: compound
  35. required: false
  36. description: extended interrupt specifier for device
  37. interrupt-names:
  38. type: string-array
  39. required: false
  40. description: name of each interrupt
  41. interrupt-parent:
  42. type: phandle
  43. required: false
  44. description: phandle to interrupt controller node
  45. label:
  46. type: string
  47. required: false
  48. description: Human readable string describing the device (used as device_get_binding() argument)
  49. clocks:
  50. type: phandle-array
  51. required: false
  52. description: Clock gate information
  53. clock-names:
  54. type: string-array
  55. required: false
  56. description: name of each clock
  57. "#address-cells":
  58. type: int
  59. required: false
  60. description: number of address cells in reg property
  61. "#size-cells":
  62. type: int
  63. required: false
  64. description: number of size cells in reg property
  65. dmas:
  66. required: false
  67. type: phandle-array
  68. description: DMA channels specifiers
  69. dma-names:
  70. required: false
  71. type: string-array
  72. description: Provided names of DMA channel specifiers
  73. io-channels:
  74. required: false
  75. type: phandle-array
  76. description: IO channels specifiers
  77. io-channel-names:
  78. required: false
  79. type: string-array
  80. description: Provided names of IO channel specifiers

包含多个绑定文件

  1. include:
  2. - foo.yaml
  3. - bar.yaml

包括多个文件时,所包含文件中属性上的任何重叠键都将放在一起。

过滤属性定义

在某些情况下,您可能希望包括文件中的某些属性定义,但不是全部。在这种情况下,应该是一个列表,您可以通过在列表中过滤掉所需的定义,如下所示:

  1. include:
  2. - name: foo.yaml
  3. property-allowlist:
  4. - i-want-this-one
  5. - and-this-one
  6. - name: bar.yaml
  7. property-blocklist:
  8. - do-not-include-this-one
  9. - or-this-one

混合包含

您可以在单个列表包含文件和属性定义:

  1. include:
  2. - foo.yaml
  3. - name: bar.yaml
  4. property-blocklist:
  5. - do-not-include-this-one
  6. - or-this-one

过滤子绑定的属性定义

  1. include:
  2. - name: bar.yaml
  3. child-binding:
  4. property-allowlist:
  5. - child-prop-to-allow

推断绑定

Zephyr的设备树脚本可以根据其/zephyr,user属性中观察到的值推断特殊节点的绑定。
此节点匹配绑定,该绑定由生成系统根据其在最终设备树中的属性值动态创建。它没有compatible属性。
例如:

  1. #include <dt-bindings/gpio/gpio.h>
  2. / {
  3. zephyr,user {
  4. boolean;
  5. bytes = [81 82 83];
  6. number = <23>;
  7. numbers = <1>, <2>, <3>;
  8. string = "text";
  9. strings = "a", "b", "c";
  10. handle = <&gpio0>;
  11. handles = <&gpio0>, <&gpio1>;
  12. signal-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
  13. };
  14. };

可以获取到如下的值:

  1. #define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
  2. DT_PROP(ZEPHYR_USER_NODE, boolean) // 1
  3. DT_PROP(ZEPHYR_USER_NODE, bytes) // {0x81, 0x82, 0x83}
  4. DT_PROP(ZEPHYR_USER_NODE, number) // 23
  5. DT_PROP(ZEPHYR_USER_NODE, numbers) // {1, 2, 3}
  6. DT_PROP(ZEPHYR_USER_NODE, string) // "text"
  7. DT_PROP(ZEPHYR_USER_NODE, strings) // {"a", "b", "c"}

可以通过以下方式获取gpio0节点:

  1. /*
  2. * Same thing as:
  3. *
  4. * ... my_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0));
  5. */
  6. const struct device *my_device =
  7. DEVICE_DT_GET(DT_PROP(ZEPHYR_USER_NODE, handle));
  8. #define PHANDLE_TO_DEVICE(node_id, prop, idx) \
  9. DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),
  10. /*
  11. * Same thing as:
  12. *
  13. * ... *my_devices[] = {
  14. * DEVICE_DT_GET(DT_NODELABEL(gpio0)),
  15. * DEVICE_DT_GET(DT_NODELABEL(gpio1)),
  16. * };
  17. */
  18. const struct device *my_devices[] = {
  19. DT_FOREACH_PROP_ELEM(ZEPHYR_USER_NODE, handles, PHANDLE_TO_DEVICE)
  20. };

可以通过以下方式获取signal-gpios中的引脚:

  1. #include <drivers/gpio.h>
  2. #define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
  3. const struct gpio_dt_spec signal =
  4. GPIO_DT_SPEC_GET(ZEPHYR_USER_NODE, signal_gpios);
  5. /* Configure the pin */
  6. gpio_pin_configure_dt(&signal, GPIO_OUTPUT_INACTIVE);
  7. /* Set the pin to its active level */
  8. gpio_pin_set_dt(&signal, 1);