|
| 1 | +# DTC (Devicetree Compiler) |
| 2 | + |
| 3 | +## Introduction to the DTC |
| 4 | + |
| 5 | +Device Tree Compiler, dtc, takes as input a device-tree in a given format and outputs a device-tree in another format for booting kernels on embedded systems. |
| 6 | +Typically, the input format is "dts" (device-tree source), a human readable source format, and creates a "dtb" (device-tree binary), or binary format as output. |
| 7 | + |
| 8 | +> If you not install dtc tools in your host system, the dtc module will tell you how to do. |
| 9 | +
|
| 10 | +### Generate DTS |
| 11 | + |
| 12 | +When you get a DTB or FDT file from firmware or the other runtime system, you may want to convert it to dts for reading easier. Just do in your Python or SConscript, dummpy.dtb, eg.: |
| 13 | + |
| 14 | +```python |
| 15 | +import os, sys |
| 16 | + |
| 17 | +RTT_ROOT = os.getenv('RTT_ROOT') |
| 18 | +sys.path.append(RTT_ROOT + '/tools') |
| 19 | + |
| 20 | +from building import * |
| 21 | +import dtc |
| 22 | + |
| 23 | +dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb") |
| 24 | +``` |
| 25 | + |
| 26 | +Then you will get a `dummpy.dts` in current directory, dts file will replace if exists before, so you can try another name like this: |
| 27 | + |
| 28 | +```python |
| 29 | +[...] |
| 30 | + |
| 31 | +dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", "dummpy-tmp.dts") |
| 32 | +# or |
| 33 | +dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", dts_name = "dummpy-tmp.dts") |
| 34 | +``` |
| 35 | + |
| 36 | +### Generate DTB |
| 37 | + |
| 38 | +First, you may want to know some knowledge about dts: [DeviceTree Specification](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html) |
| 39 | + |
| 40 | +#### Include and Macros |
| 41 | + |
| 42 | +Gog, dtc is not supports preprocess like C preprocessor (cpp), but you can use cpp to do it in the dts, don't worry, we have appended the step in dtc module. |
| 43 | + |
| 44 | +When you have some `dt-bindings` include files or macros, like this: |
| 45 | + |
| 46 | +```c |
| 47 | +/* |
| 48 | + * Used "#include" if header file need preprocessor, |
| 49 | + * `components/drivers/include` and current directory path is default. |
| 50 | + */ |
| 51 | +#include <dt-bindings/size.h> |
| 52 | +#include "dummy.dtsi" |
| 53 | +/* Well, if dtsi is simple, you can use "/include/", it is supported by dtc */ |
| 54 | +/include/ "chosen.dtsi" |
| 55 | + |
| 56 | +#define MMIO_BASE 0x10000 |
| 57 | +#define MMIO_SIZE SIZE_GB |
| 58 | +#define MEM_BASE (MMIO_BASE + MMIO_SIZE) |
| 59 | + |
| 60 | +#ifndef CPU_HARDID |
| 61 | +#define CPU_HARDID 0 |
| 62 | +#endif |
| 63 | + |
| 64 | +#ifndef SOC_INTC |
| 65 | +#define SOC_INTC intc_a |
| 66 | +#endif |
| 67 | + |
| 68 | +/ { |
| 69 | + #address-cells = <2>; |
| 70 | + #size-cells = <2>; |
| 71 | + /* |
| 72 | + * Macros after "&" will be replaced, |
| 73 | + * there will affect the interrupt controller in this SoC. |
| 74 | + */ |
| 75 | + interrupt-parent = <&SOC_INTC>; |
| 76 | + |
| 77 | + [...] |
| 78 | + |
| 79 | + memory { |
| 80 | + /* When there is a calculation, please use "()" to include them */ |
| 81 | + reg = <0x0 MEM_BASE 0x0 (3 * SIZE_GB)>; |
| 82 | + device_type = "memory"; |
| 83 | + }; |
| 84 | + |
| 85 | + cpus { |
| 86 | + #size-cells = <0>; |
| 87 | + #address-cells = <1>; |
| 88 | + |
| 89 | + /* Macros after "@" will be replaced */ |
| 90 | + cpu0: cpu@CPU_HARDID { |
| 91 | + reg = <CPU_HARDID>; |
| 92 | + device_type = "cpu"; |
| 93 | + }; |
| 94 | + }; |
| 95 | + |
| 96 | + /* Macros replace support phandle name, too */ |
| 97 | + intc_a: intc-a { |
| 98 | + interrupt-controller; |
| 99 | + }; |
| 100 | + |
| 101 | + intc_b: intc-b { |
| 102 | + interrupt-controller; |
| 103 | + }; |
| 104 | + |
| 105 | + [...] |
| 106 | +}; |
| 107 | +``` |
| 108 | + |
| 109 | +Generate DTS, now: |
| 110 | + |
| 111 | +```python |
| 112 | +import os, sys |
| 113 | + |
| 114 | +RTT_ROOT = os.getenv('RTT_ROOT') |
| 115 | +sys.path.append(RTT_ROOT + '/tools') |
| 116 | + |
| 117 | +from building import * |
| 118 | +import dtc |
| 119 | + |
| 120 | +dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"] |
| 121 | +``` |
| 122 | + |
| 123 | +Append more include path, like SoC DM directory: |
| 124 | + |
| 125 | +```python |
| 126 | +[...] |
| 127 | + |
| 128 | +dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], include_paths = ['dm/include', 'firmware']) |
| 129 | +``` |
| 130 | + |
| 131 | +Maybe you have other ideas? |
| 132 | + |
| 133 | +#### Multiple DTB |
| 134 | + |
| 135 | +SoC have different SBC, eg. `dummy.dtsi`: |
| 136 | +```c |
| 137 | +/* SoC dummy */ |
| 138 | +/ { |
| 139 | + #address-cells = <2>; |
| 140 | + #size-cells = <2>; |
| 141 | + model = "Dummy SoC Board"; |
| 142 | + |
| 143 | + [...] |
| 144 | + |
| 145 | + chosen { |
| 146 | + bootargs = "cma=8M coherent_pool=2M"; |
| 147 | + }; |
| 148 | + |
| 149 | + reserved-memory { |
| 150 | + #address-cells = <2>; |
| 151 | + #size-cells = <2>; |
| 152 | + |
| 153 | + isp_shm@100000 { |
| 154 | + reg = <0x0 0x100000 0x0 0x100000>; |
| 155 | + }; |
| 156 | + |
| 157 | + dsp_shm@200000 { |
| 158 | + reg = <0x0 0x200000 0x0 0x100000>; |
| 159 | + }; |
| 160 | + }; |
| 161 | + |
| 162 | + dsp { |
| 163 | + status = "okay"; |
| 164 | + }; |
| 165 | + |
| 166 | + buddy { |
| 167 | + isp = <&{/reserved-memory/isp_shm@100000}>; |
| 168 | + dsp = <&{/reserved-memory/dsp_shm@200000}>; |
| 169 | + }; |
| 170 | + |
| 171 | + uart0: uart { |
| 172 | + status = "disabled"; |
| 173 | + }; |
| 174 | + |
| 175 | + i2c0: i2c { |
| 176 | + status = "okay"; |
| 177 | + }; |
| 178 | + |
| 179 | + [...] |
| 180 | +}; |
| 181 | +``` |
| 182 | + |
| 183 | +If you want to enable or disable a device in vendorA: |
| 184 | + |
| 185 | +```c |
| 186 | +/* vendorA dummy */ |
| 187 | +#include "dummy.dtsi" |
| 188 | + |
| 189 | +/ { |
| 190 | + /* No phandle name can modify in place */ |
| 191 | + chosen { |
| 192 | + bootargs = "console=uart0 cma=8M coherent_pool=2M"; |
| 193 | + }; |
| 194 | +}; |
| 195 | + |
| 196 | +/* Reference and modify direct if has phandle name */ |
| 197 | +&uart0 { |
| 198 | + status = "okay"; |
| 199 | + pinctrl-0 = <&uart0_m1>; |
| 200 | +}; |
| 201 | + |
| 202 | +&i2c0 { |
| 203 | + status = "disabled"; |
| 204 | +}; |
| 205 | +``` |
| 206 | + |
| 207 | +Some nodes will parse by kernel without "status", disable them by this way: |
| 208 | + |
| 209 | +```c |
| 210 | +/* vendorB dummy */ |
| 211 | +#include "dummy.dtsi" |
| 212 | + |
| 213 | +/delete-node/ &dsp_shm; |
| 214 | + |
| 215 | +/ { |
| 216 | + /* Delete in place if no phandle name */ |
| 217 | + /delete-node/ dsp; |
| 218 | + |
| 219 | + /* Delete property */ |
| 220 | + buddy { |
| 221 | + /delete-property/ dsp; |
| 222 | + }; |
| 223 | +}; |
| 224 | +``` |
| 225 | + |
| 226 | +New device on bus? |
| 227 | + |
| 228 | +```c |
| 229 | +/* vendorC dummy */ |
| 230 | +#include "dummy.dtsi" |
| 231 | + |
| 232 | +&i2c0 { |
| 233 | + rtc@0 { |
| 234 | + clock-frequency = <32768>; |
| 235 | + }; |
| 236 | +}; |
| 237 | +``` |
| 238 | + |
| 239 | +Build dtb together: |
| 240 | + |
| 241 | +```python |
| 242 | +[...] |
| 243 | + |
| 244 | +dtc.dts_to_dtb(RTT_ROOT, ["dummpy-vendorA.dts", "dummpy-vendorB.dts", "dummpy-vendorC.dts"]) |
| 245 | +``` |
| 246 | + |
| 247 | +Will get `dummpy-vendorA.dtb`, `dummpy-vendorB.dtb`, `dummpy-vendorC.dtb` |
| 248 | + |
| 249 | +### Warnings |
| 250 | + |
| 251 | +Irrelevant warnings are annoying, but dtc do it, you can remove them like this: |
| 252 | + |
| 253 | +```python |
| 254 | +[...] |
| 255 | + |
| 256 | +dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"]) |
| 257 | +``` |
| 258 | + |
| 259 | +**Confirm your dts is right, please :)** |
| 260 | + |
| 261 | +### Raw options |
| 262 | + |
| 263 | +DTC have more raw options than generate dts show by `dtc --help`, like to append them like this: |
| 264 | + |
| 265 | +```python |
| 266 | +[...] |
| 267 | + |
| 268 | +dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", options = "--quiet") |
| 269 | +dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], options = "--quiet ") |
| 270 | +``` |
0 commit comments