Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0+ |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 2 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 3 | Compiled-in Device Tree / Platform Data |
| 4 | ======================================= |
| 5 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 6 | |
| 7 | Introduction |
| 8 | ------------ |
| 9 | |
| 10 | Device tree is the standard configuration method in U-Boot. It is used to |
| 11 | define what devices are in the system and provide configuration information |
| 12 | to these devices. |
| 13 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 14 | The overhead of adding devicetree access to U-Boot is fairly modest, |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 15 | approximately 3KB on Thumb 2 (plus the size of the DT itself). This means |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 16 | that in most cases it is best to use devicetree for configuration. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 17 | |
| 18 | However there are some very constrained environments where U-Boot needs to |
| 19 | work. These include SPL with severe memory limitations. For example, some |
| 20 | SoCs require a 16KB SPL image which must include a full MMC stack. In this |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 21 | case the overhead of devicetree access may be too great. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 22 | |
| 23 | It is possible to create platform data manually by defining C structures |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 24 | for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This |
| 25 | bypasses the use of devicetree completely, effectively creating a parallel |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 26 | configuration mechanism. But it is an available option for SPL. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 27 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 28 | As an alternative, the 'of-platdata' feature is provided. This converts the |
| 29 | devicetree contents into C code which can be compiled into the SPL binary. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 30 | This saves the 3KB of code overhead and perhaps a few hundred more bytes due |
| 31 | to more efficient storage of the data. |
| 32 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 33 | |
| 34 | How it works |
| 35 | ------------ |
| 36 | |
Simon Goldschmidt | 49ce8ca | 2019-01-16 20:40:18 +0100 | [diff] [blame] | 37 | The feature is enabled by CONFIG OF_PLATDATA. This is only available in |
| 38 | SPL/TPL and should be tested with: |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 39 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 40 | .. code-block:: c |
| 41 | |
| 42 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 43 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 44 | A tool called 'dtoc' converts a devicetree file either into a set of |
Simon Goldschmidt | 49ce8ca | 2019-01-16 20:40:18 +0100 | [diff] [blame] | 45 | struct declarations, one for each compatible node, and a set of |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 46 | `U_BOOT_DRVINFO()` declarations along with the actual platform data for each |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 47 | device. As an example, consider this MMC node: |
| 48 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 49 | .. code-block:: none |
| 50 | |
| 51 | sdmmc: dwmmc@ff0c0000 { |
| 52 | compatible = "rockchip,rk3288-dw-mshc"; |
| 53 | clock-freq-min-max = <400000 150000000>; |
| 54 | clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, |
| 55 | <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; |
| 56 | clock-names = "biu", "ciu", "ciu_drv", "ciu_sample"; |
| 57 | fifo-depth = <0x100>; |
| 58 | interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; |
| 59 | reg = <0xff0c0000 0x4000>; |
| 60 | bus-width = <4>; |
| 61 | cap-mmc-highspeed; |
| 62 | cap-sd-highspeed; |
| 63 | card-detect-delay = <200>; |
| 64 | disable-wp; |
| 65 | num-slots = <1>; |
| 66 | pinctrl-names = "default"; |
| 67 | pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>; |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 68 | vmmc-supply = <&vcc_sd>; |
| 69 | status = "okay"; |
| 70 | u-boot,dm-pre-reloc; |
| 71 | }; |
| 72 | |
| 73 | |
| 74 | Some of these properties are dropped by U-Boot under control of the |
| 75 | CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce |
| 76 | the following C struct declaration: |
| 77 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 78 | .. code-block:: c |
| 79 | |
| 80 | struct dtd_rockchip_rk3288_dw_mshc { |
| 81 | fdt32_t bus_width; |
| 82 | bool cap_mmc_highspeed; |
| 83 | bool cap_sd_highspeed; |
| 84 | fdt32_t card_detect_delay; |
| 85 | fdt32_t clock_freq_min_max[2]; |
| 86 | struct phandle_1_arg clocks[4]; |
| 87 | bool disable_wp; |
| 88 | fdt32_t fifo_depth; |
| 89 | fdt32_t interrupts[3]; |
| 90 | fdt32_t num_slots; |
| 91 | fdt32_t reg[2]; |
| 92 | fdt32_t vmmc_supply; |
| 93 | }; |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 94 | |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 95 | and the following device declarations: |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 96 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 97 | .. code-block:: c |
| 98 | |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 99 | /* Node /clock-controller@ff760000 index 0 */ |
| 100 | ... |
| 101 | |
| 102 | /* Node /dwmmc@ff0c0000 index 2 */ |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 103 | static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = { |
| 104 | .fifo_depth = 0x100, |
| 105 | .cap_sd_highspeed = true, |
| 106 | .interrupts = {0x0, 0x20, 0x4}, |
| 107 | .clock_freq_min_max = {0x61a80, 0x8f0d180}, |
| 108 | .vmmc_supply = 0xb, |
| 109 | .num_slots = 0x1, |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 110 | .clocks = {{0, 456}, |
| 111 | {0, 68}, |
| 112 | {0, 114}, |
| 113 | {0, 118}}, |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 114 | .cap_mmc_highspeed = true, |
| 115 | .disable_wp = true, |
| 116 | .bus_width = 0x4, |
| 117 | .u_boot_dm_pre_reloc = true, |
| 118 | .reg = {0xff0c0000, 0x4000}, |
| 119 | .card_detect_delay = 0xc8, |
| 120 | }; |
| 121 | |
Simon Glass | 1d8364a | 2020-12-28 20:34:54 -0700 | [diff] [blame] | 122 | U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = { |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 123 | .name = "rockchip_rk3288_dw_mshc", |
Simon Glass | 71fa5b4 | 2020-12-03 16:55:18 -0700 | [diff] [blame] | 124 | .plat = &dtv_dwmmc_at_ff0c0000, |
| 125 | .plat_size = sizeof(dtv_dwmmc_at_ff0c0000), |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 126 | .parent_idx = -1, |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 127 | }; |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 128 | |
| 129 | The device is then instantiated at run-time and the platform data can be |
| 130 | accessed using: |
| 131 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 132 | .. code-block:: c |
| 133 | |
| 134 | struct udevice *dev; |
Simon Glass | fa20e93 | 2020-12-03 16:55:20 -0700 | [diff] [blame] | 135 | struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev); |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 136 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 137 | This avoids the code overhead of converting the devicetree data to |
| 138 | platform data in the driver. The `of_to_plat()` method should |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 139 | therefore do nothing in such a driver. |
| 140 | |
Simon Goldschmidt | 49ce8ca | 2019-01-16 20:40:18 +0100 | [diff] [blame] | 141 | Note that for the platform data to be matched with a driver, the 'name' |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 142 | property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared |
| 143 | via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a |
Simon Goldschmidt | 49ce8ca | 2019-01-16 20:40:18 +0100 | [diff] [blame] | 144 | 'name' corresponding to the devicetree 'compatible' string (after converting |
| 145 | it to a valid name for C) is needed, so a dedicated driver is required for |
| 146 | each 'compatible' string. |
| 147 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 148 | In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be |
Walter Lozano | c38b491 | 2020-06-25 01:10:09 -0300 | [diff] [blame] | 149 | used to declare an alias for a driver name, typically a 'compatible' string. |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 150 | This macro produces no code, but is used by dtoc tool. It must be located in the |
Simon Glass | 2f4455f | 2021-02-03 06:01:05 -0700 | [diff] [blame] | 151 | same file as its associated driver, ideally just after it. |
Walter Lozano | c38b491 | 2020-06-25 01:10:09 -0300 | [diff] [blame] | 152 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 153 | The parent_idx is the index of the parent `driver_info` structure within its |
| 154 | linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to |
| 155 | support `dev_get_parent()`. |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 156 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 157 | During the build process dtoc parses both `U_BOOT_DRIVER()` and |
| 158 | `DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases. |
| 159 | If the 'compatible' string used for a device does not not match a valid driver |
| 160 | name, it will be checked against the list of driver aliases in order to get the |
| 161 | right driver name to use. If in this step there is no match found a warning is |
| 162 | issued to avoid run-time failures. |
Walter Lozano | c38b491 | 2020-06-25 01:10:09 -0300 | [diff] [blame] | 163 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 164 | Where a node has multiple compatible strings, dtoc generates a `#define` to |
| 165 | make them equivalent, e.g.: |
Simon Glass | 934be0b | 2017-06-13 21:10:06 -0600 | [diff] [blame] | 166 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 167 | .. code-block:: c |
Simon Glass | 934be0b | 2017-06-13 21:10:06 -0600 | [diff] [blame] | 168 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 169 | #define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc |
| 170 | |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 171 | |
| 172 | Converting of-platdata to a useful form |
| 173 | --------------------------------------- |
| 174 | |
Simon Goldschmidt | 49ce8ca | 2019-01-16 20:40:18 +0100 | [diff] [blame] | 175 | Of course it would be possible to use the of-platdata directly in your driver |
| 176 | whenever configuration information is required. However this means that the |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 177 | driver will not be able to support devicetree, since the of-platdata |
| 178 | structure is not available when devicetree is used. It would make no sense |
| 179 | to use this structure if devicetree were available, since the structure has |
| 180 | all the limitations metioned in caveats below. |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 181 | |
| 182 | Therefore it is recommended that the of-platdata structure should be used |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 183 | only in the `probe()` method of your driver. It cannot be used in the |
| 184 | `of_to_plat()` method since this is not called when platform data is |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 185 | already present. |
| 186 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 187 | |
| 188 | How to structure your driver |
| 189 | ---------------------------- |
| 190 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 191 | Drivers should always support devicetree as an option. The of-platdata |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 192 | feature is intended as a add-on to existing drivers. |
| 193 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 194 | Your driver should convert the plat struct in its `probe()` method. The |
| 195 | existing devicetree decoding logic should be kept in the |
| 196 | `of_to_plat()` method and wrapped with `#if`. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 197 | |
| 198 | For example: |
| 199 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 200 | .. code-block:: c |
| 201 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 202 | #include <dt-structs.h> |
| 203 | |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 204 | struct mmc_plat { |
Lukasz Majewski | fd54ab2 | 2019-09-03 15:43:19 +0200 | [diff] [blame] | 205 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 206 | /* Put this first since driver model will copy the data here */ |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 207 | struct dtd_mmc dtplat; |
| 208 | #endif |
| 209 | /* |
| 210 | * Other fields can go here, to be filled in by decoding from |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 211 | * the devicetree (or the C structures when of-platdata is used). |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 212 | */ |
| 213 | int fifo_depth; |
| 214 | }; |
| 215 | |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 216 | static int mmc_of_to_plat(struct udevice *dev) |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 217 | { |
Simon Glass | 6d70ba0 | 2021-08-07 07:24:06 -0600 | [diff] [blame] | 218 | if (CONFIG_IS_ENABLED(OF_REAL)) { |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 219 | /* Decode the devicetree data */ |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 220 | struct mmc_plat *plat = dev_get_plat(dev); |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 221 | const void *blob = gd->fdt_blob; |
Simon Glass | dd79d6e | 2017-01-17 16:52:55 -0700 | [diff] [blame] | 222 | int node = dev_of_offset(dev); |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 223 | |
| 224 | plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0); |
Simon Glass | 6d70ba0 | 2021-08-07 07:24:06 -0600 | [diff] [blame] | 225 | } |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 226 | |
Simon Glass | 6d70ba0 | 2021-08-07 07:24:06 -0600 | [diff] [blame] | 227 | return 0; |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | static int mmc_probe(struct udevice *dev) |
| 231 | { |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 232 | struct mmc_plat *plat = dev_get_plat(dev); |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 233 | |
Lukasz Majewski | fd54ab2 | 2019-09-03 15:43:19 +0200 | [diff] [blame] | 234 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 235 | /* Decode the of-platdata from the C structures */ |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 236 | struct dtd_mmc *dtplat = &plat->dtplat; |
| 237 | |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 238 | plat->fifo_depth = dtplat->fifo_depth; |
| 239 | #endif |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 240 | /* Set up the device from the plat data */ |
| 241 | writel(plat->fifo_depth, ...) |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | static const struct udevice_id mmc_ids[] = { |
| 245 | { .compatible = "vendor,mmc" }, |
| 246 | { } |
| 247 | }; |
| 248 | |
| 249 | U_BOOT_DRIVER(mmc_drv) = { |
Walter Lozano | c38b491 | 2020-06-25 01:10:09 -0300 | [diff] [blame] | 250 | .name = "mmc_drv", |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 251 | .id = UCLASS_MMC, |
| 252 | .of_match = mmc_ids, |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 253 | .of_to_plat = mmc_of_to_plat, |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 254 | .probe = mmc_probe, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 255 | .priv_auto = sizeof(struct mmc_priv), |
Simon Glass | b75b15b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 256 | .plat_auto = sizeof(struct mmc_plat), |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 257 | }; |
| 258 | |
Simon Glass | df65db8 | 2020-12-28 20:34:57 -0700 | [diff] [blame] | 259 | DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */ |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 260 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 261 | Note that `struct mmc_plat` is defined in the C file, not in a header. This |
Simon Glass | 63c19ed | 2019-12-06 21:42:43 -0700 | [diff] [blame] | 262 | is to avoid needing to include dt-structs.h in a header file. The idea is to |
| 263 | keep the use of each of-platdata struct to the smallest possible code area. |
| 264 | There is just one driver C file for each struct, that can convert from the |
| 265 | of-platdata struct to the standard one used by the driver. |
| 266 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 267 | In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 268 | still used to allocate space for the platform data. This is different from |
| 269 | the normal behaviour and is triggered by the use of of-platdata (strictly |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 270 | speaking it is a non-zero `plat_size` which triggers this). |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 271 | |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 272 | The of-platdata struct contents is copied from the C structure data to the |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 273 | start of the newly allocated area. In the case where devicetree is used, |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 274 | the platform data is allocated, and starts zeroed. In this case the |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 275 | `of_to_plat()` method should still set up the platform data (and the |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 276 | of-platdata struct will not be present). |
| 277 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 278 | SPL must use either of-platdata or devicetree. Drivers cannot use both at |
| 279 | the same time, but they must support devicetree. Supporting of-platdata is |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 280 | optional. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 281 | |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 282 | The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled, |
| 283 | since the devicetree access code is not compiled in. A corollary is that |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 284 | a board can only move to using of-platdata if all the drivers it uses support |
| 285 | it. There would be little point in having some drivers require the device |
| 286 | tree data, since then libfdt would still be needed for those drivers and |
| 287 | there would be no code-size benefit. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 288 | |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 289 | |
| 290 | Build-time instantiation |
| 291 | ------------------------ |
| 292 | |
| 293 | Even with of-platdata there is a fair amount of code required in driver model. |
| 294 | It is possible to have U-Boot handle the instantiation of devices at build-time, |
| 295 | so avoiding the need for the `device_bind()` code and some parts of |
| 296 | `device_probe()`. |
| 297 | |
| 298 | The feature is enabled by CONFIG_OF_PLATDATA_INST. |
| 299 | |
| 300 | Here is an example device, as generated by dtoc:: |
| 301 | |
| 302 | /* |
| 303 | * Node /serial index 6 |
| 304 | * driver sandbox_serial parent root_driver |
| 305 | */ |
| 306 | |
| 307 | #include <asm/serial.h> |
| 308 | struct sandbox_serial_plat __attribute__ ((section (".priv_data"))) |
| 309 | _sandbox_serial_plat_serial = { |
| 310 | .dtplat = { |
| 311 | .sandbox_text_colour = "cyan", |
| 312 | }, |
| 313 | }; |
| 314 | #include <asm/serial.h> |
| 315 | u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)] |
| 316 | __attribute__ ((section (".priv_data"))); |
| 317 | #include <serial.h> |
| 318 | u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)] |
| 319 | __attribute__ ((section (".priv_data"))); |
| 320 | |
| 321 | DM_DEVICE_INST(serial) = { |
| 322 | .driver = DM_DRIVER_REF(sandbox_serial), |
| 323 | .name = "sandbox_serial", |
| 324 | .plat_ = &_sandbox_serial_plat_serial, |
| 325 | .priv_ = _sandbox_serial_priv_serial, |
| 326 | .uclass = DM_UCLASS_REF(serial), |
| 327 | .uclass_priv_ = _sandbox_serial_uc_priv_serial, |
| 328 | .uclass_node = { |
| 329 | .prev = &DM_UCLASS_REF(serial)->dev_head, |
| 330 | .next = &DM_UCLASS_REF(serial)->dev_head, |
| 331 | }, |
| 332 | .child_head = { |
| 333 | .prev = &DM_DEVICE_REF(serial)->child_head, |
| 334 | .next = &DM_DEVICE_REF(serial)->child_head, |
| 335 | }, |
| 336 | .sibling_node = { |
| 337 | .prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node, |
| 338 | .next = &DM_DEVICE_REF(spl_test)->sibling_node, |
| 339 | }, |
| 340 | .seq_ = 0, |
| 341 | }; |
| 342 | |
| 343 | Here is part of the driver, for reference:: |
| 344 | |
| 345 | static const struct udevice_id sandbox_serial_ids[] = { |
| 346 | { .compatible = "sandbox,serial" }, |
| 347 | { } |
| 348 | }; |
| 349 | |
| 350 | U_BOOT_DRIVER(sandbox_serial) = { |
| 351 | .name = "sandbox_serial", |
| 352 | .id = UCLASS_SERIAL, |
| 353 | .of_match = sandbox_serial_ids, |
| 354 | .of_to_plat = sandbox_serial_of_to_plat, |
| 355 | .plat_auto = sizeof(struct sandbox_serial_plat), |
| 356 | .priv_auto = sizeof(struct sandbox_serial_priv), |
| 357 | .probe = sandbox_serial_probe, |
| 358 | .remove = sandbox_serial_remove, |
| 359 | .ops = &sandbox_serial_ops, |
| 360 | .flags = DM_FLAG_PRE_RELOC, |
| 361 | }; |
| 362 | |
| 363 | |
| 364 | The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the |
| 365 | members are from that struct. The private data is declared immediately above, |
| 366 | as `_sandbox_serial_priv_serial`, so there is no need for run-time memory |
| 367 | allocation. The #include lines are generated as well, since dtoc searches the |
| 368 | U-Boot source code for the definition of `struct sandbox_serial_priv` and adds |
| 369 | the relevant header so that the code will compile without errors. |
| 370 | |
| 371 | The `plat_` member is set to the dtv data which is declared immediately above |
| 372 | the device. This is similar to how it would look without of-platdata-inst, but |
| 373 | node that the `dtplat` member inside is part of the wider |
| 374 | `_sandbox_serial_plat_serial` struct. This is because the driver declares its |
| 375 | own platform data, and the part generated by dtoc can only be a portion of it. |
| 376 | The `dtplat` part is always first in the struct. If the device has no |
| 377 | `.plat_auto` field, then a simple dtv struct can be used as with this example:: |
| 378 | |
| 379 | static struct dtd_sandbox_clk dtv_clk_sbox = { |
| 380 | .assigned_clock_rates = 0x141, |
| 381 | .assigned_clocks = {0x7, 0x3}, |
| 382 | }; |
| 383 | |
| 384 | #include <asm/clk.h> |
| 385 | u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)] |
| 386 | __attribute__ ((section (".priv_data"))); |
| 387 | |
| 388 | DM_DEVICE_INST(clk_sbox) = { |
| 389 | .driver = DM_DRIVER_REF(sandbox_clk), |
| 390 | .name = "sandbox_clk", |
| 391 | .plat_ = &dtv_clk_sbox, |
| 392 | |
| 393 | Here is part of the driver, for reference:: |
| 394 | |
| 395 | static const struct udevice_id sandbox_clk_ids[] = { |
| 396 | { .compatible = "sandbox,clk" }, |
| 397 | { } |
| 398 | }; |
| 399 | |
| 400 | U_BOOT_DRIVER(sandbox_clk) = { |
| 401 | .name = "sandbox_clk", |
| 402 | .id = UCLASS_CLK, |
| 403 | .of_match = sandbox_clk_ids, |
| 404 | .ops = &sandbox_clk_ops, |
| 405 | .probe = sandbox_clk_probe, |
| 406 | .priv_auto = sizeof(struct sandbox_clk_priv), |
| 407 | }; |
| 408 | |
| 409 | |
| 410 | You can see that `dtv_clk_sbox` just has the devicetree contents and there is |
| 411 | no need for the `dtplat` separation, since the driver has no platform data of |
| 412 | its own, besides that provided by the devicetree (i.e. no `.plat_auto` field). |
| 413 | |
| 414 | The doubly linked lists are handled by explicitly declaring the value of each |
| 415 | node, as you can see with the `.prev` and `.next` values in the example above. |
| 416 | Since dtoc knows the order of devices it can link them into the appropriate |
| 417 | lists correctly. |
| 418 | |
| 419 | One of the features of driver model is the ability for a uclass to have a |
| 420 | small amount of private data for each device in that uclass. This is used to |
| 421 | provide a generic data structure that the uclass can use for all devices, thus |
| 422 | allowing generic features to be implemented in common code. An example is I2C, |
| 423 | which stores the bus speed there. |
| 424 | |
| 425 | Similarly, parent devices can have data associated with each of their children. |
| 426 | This is used to provide information common to all children of a particular bus. |
| 427 | For an I2C bus, this is used to store the I2C address of each child on the bus. |
| 428 | |
| 429 | This is all handled automatically by dtoc:: |
| 430 | |
| 431 | #include <asm/i2c.h> |
| 432 | u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)] |
| 433 | __attribute__ ((section (".priv_data"))); |
| 434 | #include <i2c.h> |
| 435 | u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)] |
| 436 | __attribute__ ((section (".priv_data"))); |
| 437 | |
| 438 | DM_DEVICE_INST(i2c_at_0) = { |
| 439 | .driver = DM_DRIVER_REF(sandbox_i2c), |
| 440 | .name = "sandbox_i2c", |
| 441 | .plat_ = &dtv_i2c_at_0, |
| 442 | .priv_ = _sandbox_i2c_priv_i2c_at_0, |
| 443 | .uclass = DM_UCLASS_REF(i2c), |
| 444 | .uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0, |
| 445 | ... |
| 446 | |
| 447 | Part of driver, for reference:: |
| 448 | |
| 449 | static const struct udevice_id sandbox_i2c_ids[] = { |
| 450 | { .compatible = "sandbox,i2c" }, |
| 451 | { } |
| 452 | }; |
| 453 | |
| 454 | U_BOOT_DRIVER(sandbox_i2c) = { |
| 455 | .name = "sandbox_i2c", |
| 456 | .id = UCLASS_I2C, |
| 457 | .of_match = sandbox_i2c_ids, |
| 458 | .ops = &sandbox_i2c_ops, |
| 459 | .priv_auto = sizeof(struct sandbox_i2c_priv), |
| 460 | }; |
| 461 | |
| 462 | Part of I2C uclass, for reference:: |
| 463 | |
| 464 | UCLASS_DRIVER(i2c) = { |
| 465 | .id = UCLASS_I2C, |
| 466 | .name = "i2c", |
| 467 | .flags = DM_UC_FLAG_SEQ_ALIAS, |
| 468 | .post_bind = i2c_post_bind, |
| 469 | .pre_probe = i2c_pre_probe, |
| 470 | .post_probe = i2c_post_probe, |
| 471 | .per_device_auto = sizeof(struct dm_i2c_bus), |
| 472 | .per_child_plat_auto = sizeof(struct dm_i2c_chip), |
| 473 | .child_post_bind = i2c_child_post_bind, |
| 474 | }; |
| 475 | |
| 476 | Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared |
| 477 | in the device, as required by driver model. The required header file is included |
| 478 | so that the code will compile without errors. A similar mechanism is used for |
| 479 | child devices, but is not shown by this example. |
| 480 | |
| 481 | It would not be that useful to avoid binding devices but still need to allocate |
| 482 | uclasses at runtime. So dtoc generates uclass instances as well:: |
| 483 | |
| 484 | struct list_head uclass_head = { |
| 485 | .prev = &DM_UCLASS_REF(serial)->sibling_node, |
| 486 | .next = &DM_UCLASS_REF(clk)->sibling_node, |
| 487 | }; |
| 488 | |
| 489 | DM_UCLASS_INST(clk) = { |
| 490 | .uc_drv = DM_UCLASS_DRIVER_REF(clk), |
| 491 | .sibling_node = { |
| 492 | .prev = &uclass_head, |
| 493 | .next = &DM_UCLASS_REF(i2c)->sibling_node, |
| 494 | }, |
| 495 | .dev_head = { |
| 496 | .prev = &DM_DEVICE_REF(clk_sbox)->uclass_node, |
| 497 | .next = &DM_DEVICE_REF(clk_fixed)->uclass_node, |
| 498 | }, |
| 499 | }; |
| 500 | |
| 501 | At the top is the list head. Driver model uses this on start-up, instead of |
| 502 | creating its own. |
| 503 | |
| 504 | Below that are a set of `DM_UCLASS_INST()` macros, each declaring a |
| 505 | `struct uclass`. The doubly linked lists work as for devices. |
| 506 | |
| 507 | All private data is placed into a `.priv_data` section so that it is contiguous |
| 508 | in the resulting output binary. |
| 509 | |
| 510 | |
| 511 | Indexes |
| 512 | ------- |
| 513 | |
| 514 | U-Boot stores drivers, devices and many other things in linker_list structures. |
| 515 | These are sorted by name, so dtoc knows the order that they will appear when |
| 516 | the linker runs. Each driver_info / udevice is referenced by its index in the |
| 517 | linker_list array, called 'idx' in the code. |
| 518 | |
| 519 | When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it |
| 520 | is the driver_info index. In either case, indexes are used to reference devices |
| 521 | using device_get_by_ofplat_idx(). This allows phandles to work as expected. |
| 522 | |
| 523 | |
| 524 | Phases |
| 525 | ------ |
| 526 | |
| 527 | U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. |
| 528 | The latter does not use dtoc. |
| 529 | |
| 530 | In some rare cases different drivers are used for two phases. For example, |
| 531 | in TPL it may not be necessary to use the full PCI subsystem, so a simple |
| 532 | driver can be used instead. |
| 533 | |
| 534 | This works in the build system simply by compiling in one driver or the |
| 535 | other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has |
| 536 | no way of knowing which code is compiled in for which phase, since it does |
| 537 | not inspect Makefiles or dependency graphs. |
| 538 | |
| 539 | So to make this work for dtoc, we need to be able to explicitly mark |
| 540 | drivers with their phase. This is done by adding a macro to the driver:: |
| 541 | |
| 542 | /* code in tpl.c only compiled into TPL */ |
| 543 | U_BOOT_DRIVER(pci_x86) = { |
| 544 | .name = "pci_x86", |
| 545 | .id = UCLASS_SIMPLE_BUS, |
| 546 | .of_match = of_match_ptr(tpl_fake_pci_ids), |
| 547 | DM_PHASE(tpl) |
| 548 | }; |
| 549 | |
| 550 | |
| 551 | /* code in pci_x86.c compiled into SPL and U-Boot proper */ |
| 552 | U_BOOT_DRIVER(pci_x86) = { |
| 553 | .name = "pci_x86", |
| 554 | .id = UCLASS_PCI, |
| 555 | .of_match = pci_x86_ids, |
| 556 | .ops = &pci_x86_ops, |
| 557 | }; |
| 558 | |
| 559 | |
| 560 | Notice that the second driver has the same name but no DM_PHASE(), so it will be |
| 561 | used for SPL and U-Boot. |
| 562 | |
| 563 | Note also that this only affects the code generated by dtoc. You still need to |
| 564 | make sure that only the required driver is build into each phase. |
| 565 | |
| 566 | |
| 567 | Header files |
| 568 | ------------ |
| 569 | |
| 570 | With OF_PLATDATA_INST, dtoc must include the correct header file in the |
| 571 | generated code for any structs that are used, so that the code will compile. |
| 572 | For example, if `struct ns16550_plat` is used, the code must include the |
| 573 | `ns16550.h` header file. |
| 574 | |
| 575 | Typically dtoc can detect the header file needed for a driver by looking |
| 576 | for the structs that it uses. For example, if a driver as a `.priv_auto` |
| 577 | that uses `struct ns16550_plat`, then dtoc can search header files for the |
| 578 | definition of that struct and use the file. |
| 579 | |
| 580 | In some cases, enums are used in drivers, typically with the `.data` field |
| 581 | of `struct udevice_id`. Since dtoc does not support searching for these, |
| 582 | you must use the `DM_HDR()` macro to tell dtoc which header to use. This works |
| 583 | as a macro included in the driver definition:: |
| 584 | |
| 585 | static const struct udevice_id apl_syscon_ids[] = { |
| 586 | { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT }, |
| 587 | { } |
| 588 | }; |
| 589 | |
| 590 | U_BOOT_DRIVER(intel_apl_punit) = { |
| 591 | .name = "intel_apl_punit", |
| 592 | .id = UCLASS_SYSCON, |
| 593 | .of_match = apl_syscon_ids, |
| 594 | .probe = apl_punit_probe, |
| 595 | DM_HEADER(<asm/cpu.h>) /* for X86_SYSCON_PUNIT */ |
| 596 | }; |
| 597 | |
| 598 | |
| 599 | |
Simon Glass | ee3837c | 2021-07-04 12:19:50 -0600 | [diff] [blame] | 600 | Problems |
| 601 | -------- |
| 602 | |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 603 | This section shows some common problems and how to fix them. |
| 604 | |
| 605 | Driver not found |
| 606 | ~~~~~~~~~~~~~~~~ |
| 607 | |
Simon Glass | ee3837c | 2021-07-04 12:19:50 -0600 | [diff] [blame] | 608 | In some cases you will you see something like this:: |
| 609 | |
| 610 | WARNING: the driver rockchip_rk3188_grf was not found in the driver list |
| 611 | |
| 612 | The driver list is a list of drivers, each with a name. The name is in the |
| 613 | U_BOOT_DRIVER() declaration, repeated twice, one in brackets and once as the |
| 614 | .name member. For example, in the following declaration the driver name is |
| 615 | `rockchip_rk3188_grf`:: |
| 616 | |
| 617 | U_BOOT_DRIVER(rockchip_rk3188_grf) = { |
| 618 | .name = "rockchip_rk3188_grf", |
| 619 | .id = UCLASS_SYSCON, |
| 620 | .of_match = rk3188_syscon_ids + 1, |
| 621 | .bind = rk3188_syscon_bind_of_plat, |
| 622 | }; |
| 623 | |
| 624 | The first name U_BOOT_DRIVER(xx) is used to create a linker symbol so that the |
| 625 | driver can be accessed at build-time without any overhead. The second one |
| 626 | (.name = "xx") is used at runtime when something wants to print out the driver |
| 627 | name. |
| 628 | |
| 629 | The dtoc tool expects to be able to find a driver for each compatible string in |
| 630 | the devicetree. For example, if the devicetree has:: |
| 631 | |
| 632 | grf: grf@20008000 { |
| 633 | compatible = "rockchip,rk3188-grf", "syscon"; |
| 634 | reg = <0x20008000 0x200>; |
| 635 | u-boot,dm-spl; |
| 636 | }; |
| 637 | |
| 638 | then dtoc looks at the first compatible string ("rockchip,rk3188-grf"), |
| 639 | converts that to a C identifier (rockchip_rk3188_grf) and then looks for that. |
| 640 | |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 641 | Missing .compatible or Missing .id |
| 642 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 643 | |
Simon Glass | ee3837c | 2021-07-04 12:19:50 -0600 | [diff] [blame] | 644 | Various things can cause dtoc to fail to find the driver and it tries to |
Simon Glass | 155e2a4 | 2021-08-07 07:24:10 -0600 | [diff] [blame^] | 645 | warn about these. For example:: |
Simon Glass | ee3837c | 2021-07-04 12:19:50 -0600 | [diff] [blame] | 646 | |
| 647 | rockchip_rk3188_uart: Missing .compatible in drivers/serial/serial_rockchip.c |
| 648 | : WARNING: the driver rockchip_rk3188_uart was not found in the driver list |
| 649 | |
| 650 | Without a compatible string a driver cannot be used by dtoc, even if the |
| 651 | compatible string is not actually needed at runtime. |
| 652 | |
| 653 | If the problem is simply that there are multiple compatible strings, the |
| 654 | DM_DRIVER_ALIAS() macro can be used to tell dtoc about this and avoid a problem. |
| 655 | |
| 656 | Checks are also made to confirm that the referenced driver has a .compatible |
| 657 | member and a .id member. The first provides the array of compatible strings and |
| 658 | the second provides the uclass ID. |
| 659 | |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 660 | Missing parent |
| 661 | ~~~~~~~~~~~~~~ |
| 662 | |
| 663 | When a device is used, its parent must be present as well. If you see an error |
| 664 | like:: |
| 665 | |
| 666 | Node '/i2c@0/emul/emul0' requires parent node '/i2c@0/emul' but it is not in |
| 667 | the valid list |
| 668 | |
| 669 | it indicates that you are using a node whose parent is not present in the |
| 670 | devicetree. In this example, if you look at the device tree output |
| 671 | (e.g. fdtdump tpl/u-boot-tpl.dtb in your build directory), you may see something |
| 672 | like this:: |
| 673 | |
| 674 | emul { |
| 675 | emul0 { |
| 676 | compatible = "sandbox,i2c-rtc-emul"; |
| 677 | #emul-cells = <0x00000000>; |
| 678 | phandle = <0x00000003>; |
| 679 | }; |
| 680 | }; |
| 681 | |
| 682 | In this example, 'emul0' exists but its parent 'emul' has no properties. These |
| 683 | have been dropped by fdtgrep in an effort to reduce the devicetree size. This |
| 684 | indicates that the two nodes have different phase settings. Looking at the |
| 685 | source .dts:: |
| 686 | |
| 687 | i2c_emul: emul { |
| 688 | u-boot,dm-spl; |
| 689 | reg = <0xff>; |
| 690 | compatible = "sandbox,i2c-emul-parent"; |
| 691 | emul0: emul0 { |
| 692 | u-boot,dm-pre-reloc; |
| 693 | compatible = "sandbox,i2c-rtc-emul"; |
| 694 | #emul-cells = <0>; |
| 695 | }; |
| 696 | }; |
| 697 | |
| 698 | you can see that the child node 'emul0' usees 'u-boot,dm-pre-reloc', indicating |
| 699 | that the node is present in all SPL builds, but its parent uses 'u-boot,dm-spl' |
| 700 | indicating it is only present in SPL, not TPL. For a TPL build, this will fail |
| 701 | with the above message. The fix is to change 'emul0' to use the same |
| 702 | 'u-boot,dm-spl' condition, so that it is not present in TPL, like its parent. |
| 703 | |
| 704 | Link errors / undefined reference |
| 705 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 706 | |
| 707 | Sometimes dtoc does not find the problem for you, but something is wrong and |
| 708 | you get a link error, e.g.:: |
| 709 | |
| 710 | :(.u_boot_list_2_udevice_2_spl_test5+0x0): undefined reference to |
| 711 | `_u_boot_list_2_driver_2_sandbox_spl_test' |
| 712 | /usr/bin/ld: dts/dt-uclass.o:(.u_boot_list_2_uclass_2_misc+0x8): |
| 713 | undefined reference to `_u_boot_list_2_uclass_driver_2_misc' |
| 714 | |
| 715 | The first one indicates that the device cannot find its driver. This means that |
| 716 | there is a driver 'sandbox_spl_test' but it is not compiled into the build. |
| 717 | Check your Kconfig settings to make sure it is. If you don't want that in the |
| 718 | build, adjust your phase settings, e.g. by using 'u-boot,dm-spl' in the node |
| 719 | to exclude it from the TPL build:: |
| 720 | |
| 721 | spl-test5 { |
| 722 | u-boot,dm-tpl; |
| 723 | compatible = "sandbox,spl-test"; |
| 724 | stringarray = "tpl"; |
| 725 | }; |
| 726 | |
| 727 | We can drop the 'u-boot,dm-tpl' line so this node won't appear in the TPL |
| 728 | devicetree and thus the driver won't be needed. |
| 729 | |
| 730 | The second error above indicates that the MISC uclass is needed by the driver |
| 731 | (since it is in the MISC uclass) but that uclass is not compiled in the build. |
| 732 | The fix above would fix this error too. But if you do want this uclass in the |
| 733 | build, check your Kconfig settings to make sure the uclass is being built |
| 734 | (CONFIG_MISC in this case). |
| 735 | |
Simon Glass | 155e2a4 | 2021-08-07 07:24:10 -0600 | [diff] [blame^] | 736 | Another error that can crop up is something like:: |
| 737 | |
| 738 | spl/dts/dt-device.c:257:38: error: invalid application of ‘sizeof’ to |
| 739 | incomplete type ‘struct sandbox_irq_priv’ |
| 740 | 257 | u8 _sandbox_irq_priv_irq_sbox[sizeof(struct sandbox_irq_priv)] |
| 741 | | ^~~~~~ |
| 742 | |
| 743 | This indicates that `struct sandbox_irq_priv` is not defined anywhere. The |
| 744 | solution is to add a DM_HEADER() line, as below, so this is included in the |
| 745 | dt-device.c file:: |
| 746 | |
| 747 | U_BOOT_DRIVER(sandbox_irq) = { |
| 748 | .name = "sandbox_irq", |
| 749 | .id = UCLASS_IRQ, |
| 750 | .of_match = sandbox_irq_ids, |
| 751 | .ops = &sandbox_irq_ops, |
| 752 | .priv_auto = sizeof(struct sandbox_irq_priv), |
| 753 | DM_HEADER(<asm/irq.h>) |
| 754 | }; |
| 755 | |
| 756 | Note that there is no dependency checking on the above, so U-Boot will not |
| 757 | regenerate the dt-device.c file when you update the source file (here, |
| 758 | `irq_sandbox.c`). You need to run `make mrproper` first to get a fresh build. |
| 759 | |
| 760 | Another error that can crop up is something like:: |
| 761 | |
| 762 | spl/dts/dt-device.c:257:38: error: invalid application of ‘sizeof’ to |
| 763 | incomplete type ‘struct sandbox_irq_priv’ |
| 764 | 257 | u8 _sandbox_irq_priv_irq_sbox[sizeof(struct sandbox_irq_priv)] |
| 765 | | ^~~~~~ |
| 766 | |
| 767 | This indicates that `struct sandbox_irq_priv` is not defined anywhere. The |
| 768 | solution is to add a DM_HEADER() line, as below, so this is included in the |
| 769 | dt-device.c file:: |
| 770 | |
| 771 | U_BOOT_DRIVER(sandbox_irq) = { |
| 772 | .name = "sandbox_irq", |
| 773 | .id = UCLASS_IRQ, |
| 774 | .of_match = sandbox_irq_ids, |
| 775 | .ops = &sandbox_irq_ops, |
| 776 | .priv_auto = sizeof(struct sandbox_irq_priv), |
| 777 | DM_HEADER(<asm/irq.h>) |
| 778 | }; |
| 779 | |
| 780 | Note that there is no dependency checking on the above, so U-Boot will not |
| 781 | regenerate the dt-device.c file when you update the source file (here, |
| 782 | `irq_sandbox.c`). You need to run `make mrproper` first to get a fresh build. |
| 783 | |
Simon Glass | ee3837c | 2021-07-04 12:19:50 -0600 | [diff] [blame] | 784 | |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 785 | Caveats |
| 786 | ------- |
| 787 | |
| 788 | There are various complications with this feature which mean it should only |
| 789 | be used when strictly necessary, i.e. in SPL with limited memory. Notable |
| 790 | caveats include: |
| 791 | |
| 792 | - Device tree does not describe data types. But the C code must define a |
| 793 | type for each property. These are guessed using heuristics which |
| 794 | are wrong in several fairly common cases. For example an 8-byte value |
| 795 | is considered to be a 2-item integer array, and is byte-swapped. A |
| 796 | boolean value that is not present means 'false', but cannot be |
| 797 | included in the structures since there is generally no mention of it |
| 798 | in the devicetree file. |
| 799 | |
| 800 | - Naming of nodes and properties is automatic. This means that they follow |
| 801 | the naming in the devicetree, which may result in C identifiers that |
| 802 | look a bit strange. |
| 803 | |
| 804 | - It is not possible to find a value given a property name. Code must use |
| 805 | the associated C member variable directly in the code. This makes |
| 806 | the code less robust in the face of devicetree changes. To avoid having |
| 807 | a second struct with similar members and names you need to explicitly |
| 808 | declare it as an alias with `DM_DRIVER_ALIAS()`. |
| 809 | |
| 810 | - The platform data is provided to drivers as a C structure. The driver |
| 811 | must use the same structure to access the data. Since a driver |
| 812 | normally also supports devicetree it must use `#ifdef` to separate |
| 813 | out this code, since the structures are only available in SPL. This could |
| 814 | be fixed fairly easily by making the structs available outside SPL, so |
| 815 | that `IS_ENABLED()` could be used. |
| 816 | |
| 817 | - With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning |
| 818 | that (by default) it is not possible to call `device_bind()` from C code. |
| 819 | This means that all devices must have an associated devicetree node and |
| 820 | compatible string. For example if a GPIO device currently creates child |
| 821 | devices in its `bind()` method, it will not work with |
| 822 | CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the |
| 823 | devicetree binding should be updated to declare compatible strings for |
| 824 | the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this |
| 825 | is not recommended since it increases code size. |
| 826 | |
| 827 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 828 | Internals |
| 829 | --------- |
| 830 | |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 831 | Generated files |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 832 | ~~~~~~~~~~~~~~~ |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 833 | |
| 834 | When enabled, dtoc generates the following five files: |
| 835 | |
| 836 | include/generated/dt-decl.h (OF_PLATDATA_INST only) |
| 837 | Contains declarations for all drivers, devices and uclasses. This allows |
| 838 | any `struct udevice`, `struct driver` or `struct uclass` to be located by its |
| 839 | name |
| 840 | |
| 841 | include/generated/dt-structs-gen.h |
| 842 | Contains the struct definitions for the devicetree nodes that are used. This |
| 843 | is the same as without OF_PLATDATA_INST |
| 844 | |
| 845 | spl/dts/dt-plat.c (only with !OF_PLATDATA_INST) |
| 846 | Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices |
| 847 | at start-up. See above for an example |
| 848 | |
| 849 | spl/dts/dt-device.c (only with OF_PLATDATA_INST) |
| 850 | Contains `DM_DEVICE_INST()` declarations for each device that can be used at |
| 851 | run-time. These are declared in the file along with any private/platform data |
| 852 | that they use. Every device has an idx, as above. Since each device must be |
| 853 | part of a double-linked list, the nodes are declared in the code as well. |
| 854 | |
| 855 | spl/dts/dt-uclass.c (only with OF_PLATDATA_INST) |
| 856 | Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at |
| 857 | run-time. These are declared in the file along with any private data |
| 858 | associated with the uclass itself (the `.priv_auto` member). Since each |
| 859 | uclass must be part of a double-linked list, the nodes are declared in the |
| 860 | code as well. |
| 861 | |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 862 | The dt-structs.h file includes the generated file |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 863 | `(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 864 | Otherwise (such as in U-Boot proper) these structs are not available. This |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 865 | prevents them being used inadvertently. All usage must be bracketed with |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 866 | `#if CONFIG_IS_ENABLED(OF_PLATDATA)`. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 867 | |
Simon Glass | 71fa5b4 | 2020-12-03 16:55:18 -0700 | [diff] [blame] | 868 | The dt-plat.c file contains the device declarations and is is built in |
Simon Glass | 2500de2 | 2020-12-28 20:35:05 -0700 | [diff] [blame] | 869 | spl/dt-plat.c. |
| 870 | |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 871 | |
| 872 | CONFIG options |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 873 | ~~~~~~~~~~~~~~ |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 874 | |
| 875 | Several CONFIG options are used to control the behaviour of of-platdata, all |
| 876 | available for both SPL and TPL: |
| 877 | |
| 878 | OF_PLATDATA |
| 879 | This is the main option which enables the of-platdata feature |
| 880 | |
| 881 | OF_PLATDATA_PARENT |
| 882 | This allows `device_get_parent()` to work. Without this, all devices exist as |
| 883 | direct children of the root node. This option is highly desirable (if not |
| 884 | always absolutely essential) for buses such as I2C. |
| 885 | |
| 886 | OF_PLATDATA_INST |
| 887 | This controls the instantiation of devices at build time. With it disabled, |
| 888 | only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding |
| 889 | in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and |
| 890 | `DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at |
| 891 | runtime. |
| 892 | |
| 893 | OF_PLATDATA_NO_BIND |
| 894 | This controls whether `device_bind()` is supported. It is enabled by default |
| 895 | with OF_PLATDATA_INST since code-size reduction is really the main point of |
| 896 | the feature. It can be disabled if needed but is not likely to be supported |
| 897 | in the long term. |
| 898 | |
| 899 | OF_PLATDATA_DRIVER_RT |
| 900 | This controls whether the `struct driver_rt` records are used by U-Boot. |
| 901 | Normally when a device is bound, U-Boot stores the device pointer in one of |
| 902 | these records. There is one for every `struct driver_info` in the system, |
| 903 | i.e. one for every device that is bound from those records. It provides a |
| 904 | way to locate a device in the code and is used by |
| 905 | `device_get_by_ofplat_idx()`. This option is always enabled with of-platdata, |
| 906 | provided OF_PLATDATA_INST is not. In that case the records are useless since |
| 907 | we don't have any `struct driver_info` records. |
| 908 | |
| 909 | OF_PLATDATA_RT |
| 910 | This controls whether the `struct udevice_rt` records are used by U-Boot. |
| 911 | It moves the updatable fields from `struct udevice` (currently only `flags`) |
| 912 | into a separate structure, allowing the records to be kept in read-only |
| 913 | memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option |
| 914 | also controls whether the private data is used in situ, or first copied into |
| 915 | an allocated region. Again this is to allow the private data declared by |
| 916 | dtoc-generated code to be in read-only memory. Note that access to private |
| 917 | data must be done via accessor functions, such as `dev_get_priv()`, so that |
| 918 | the relocation is handled. |
| 919 | |
| 920 | READ_ONLY |
| 921 | This indicates that the data generated by dtoc should not be modified. Only |
| 922 | a few fields actually do get changed in U-Boot, such as device flags. This |
| 923 | option causes those to move into an allocated space (see OF_PLATDATA_RT). |
| 924 | Also, since updating doubly linked lists is generally impossible when some of |
| 925 | the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled. |
| 926 | |
| 927 | Data structures |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 928 | ~~~~~~~~~~~~~~~ |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 929 | |
| 930 | A few extra data structures are used with of-platdata: |
| 931 | |
| 932 | `struct udevice_rt` |
| 933 | Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds |
| 934 | the flags for each device, so that `struct udevice` can remain unchanged by |
| 935 | U-Boot, and potentially reside in read-only memory. Access to flags is then |
| 936 | via functions like `dev_get_flags()` and `dev_or_flags()`. This data |
| 937 | structure is allocated on start-up, where the private data is also copied. |
| 938 | All flags values start at 0 and any changes are handled by `dev_or_flags()` |
| 939 | and `dev_bic_flags()`. It would be more correct for the flags to be set to |
| 940 | `DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since |
| 941 | there is no code to bind/unbind devices and no code to allocate/free |
| 942 | private data / platform data, it doesn't matter. |
| 943 | |
| 944 | `struct driver_rt` |
| 945 | Run-time information for `struct driver_info` records. When |
| 946 | OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device |
| 947 | created by each record. This is needed so that is it possible to locate a |
| 948 | device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to |
| 949 | get a reference to a particular `struct driver_info`, with `name` being the |
| 950 | name of the devicetree node. This is very convenient. It is also fast, since |
| 951 | no searching or string comparison is needed. This data structure is |
| 952 | allocated on start-up, filled out by `device_bind()` and used by |
| 953 | `device_get_by_ofplat_idx()`. |
| 954 | |
| 955 | Other changes |
Simon Glass | 8ad73c7 | 2021-08-18 21:40:23 -0600 | [diff] [blame] | 956 | ~~~~~~~~~~~~~ |
Simon Glass | e2ec0ea | 2021-03-15 17:25:43 +1300 | [diff] [blame] | 957 | |
| 958 | Some other changes are made with of-platdata: |
| 959 | |
| 960 | Accessor functions |
| 961 | Accessing private / platform data via functions such as `dev_get_priv()` has |
| 962 | always been encouraged. With OF_PLATDATA_RT this is essential, since the |
| 963 | `priv_` and `plat_` (etc.) values point to the data generated by dtoc, not |
| 964 | the read-write copy that is sometimes made on start-up. Changing the |
| 965 | private / platform data pointers has always been discouraged (the API is |
| 966 | marked internal) but with OF_PLATDATA_RT this is not currently supported in |
| 967 | general, since it assumes that all such pointers point to the relocated data. |
| 968 | Note also that the renaming of struct members to have a trailing underscore |
| 969 | was partly done to make people aware that they should not be accessed |
| 970 | directly. |
| 971 | |
| 972 | `gd->uclass_root_s` |
| 973 | Normally U-Boot sets up the head of the uclass list here and makes |
| 974 | `gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a |
| 975 | declaration of `uclass_head` in `dt-uclass.c` since it needs to link the |
| 976 | head node into the list. In that case, `gd->uclass_root_s` is not used and |
| 977 | U-Boot just makes `gd->uclass_root` point to `uclass_head`. |
| 978 | |
| 979 | `gd->dm_driver_rt` |
| 980 | This holds a pointer to a list of `struct driver_rt` records, one for each |
| 981 | `struct driver_info`. The list is in alphabetical order by the name used |
| 982 | in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having |
| 983 | an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is |
| 984 | accessed via macros so that it can be used inside IS_ENABLED(), rather than |
| 985 | requiring #ifdefs in the C code when it is not present. |
| 986 | |
| 987 | `gd->dm_udevice_rt` |
| 988 | This holds a pointer to a list of `struct udevice_rt` records, one for each |
| 989 | `struct udevice`. The list is in alphabetical order by the name used |
| 990 | in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by |
| 991 | idx, with the first record having an index of 0. It is only used if |
| 992 | OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be |
| 993 | used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when |
| 994 | it is not present. |
| 995 | |
| 996 | `gd->dm_priv_base` |
| 997 | When OF_PLATDATA_RT is enabled, the private/platform data for each device is |
| 998 | copied into an allocated region by U-Boot on start-up. This points to that |
| 999 | region. All calls to accessor functions (e.g. `dev_get_priv()`) then |
| 1000 | translate from the pointer provided by the caller (assumed to lie between |
| 1001 | `__priv_data_start` and `__priv_data_end`) to the new allocated region. This |
| 1002 | member is accessed via macros so that it can be used inside IS_ENABLED(), |
| 1003 | rather than required #ifdefs in the C code when it is not present. |
| 1004 | |
| 1005 | `struct udevice->flags_` |
| 1006 | When OF_PLATDATA_RT is enabled, device flags are no-longer part of |
| 1007 | `struct udevice`, but are instead kept in `struct udevice_rt`, as described |
| 1008 | above. Flags are accessed via functions, such as `dev_get_flags()` and |
| 1009 | `dev_or_flags()`. |
| 1010 | |
| 1011 | `struct udevice->node_` |
| 1012 | When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need |
| 1013 | for this field. It is removed, just to save space. |
| 1014 | |
| 1015 | `DM_PHASE` |
| 1016 | This macro is used to indicate which phase of U-Boot a driver is intended |
| 1017 | for. See above for details. |
| 1018 | |
| 1019 | `DM_HDR` |
| 1020 | This macro is used to indicate which header file dtoc should use to allow |
| 1021 | a driver declaration to compile correctly. See above for details. |
| 1022 | |
| 1023 | `device_get_by_ofplat_idx()` |
| 1024 | There used to be a function called `device_get_by_driver_info()` which |
| 1025 | looked up a `struct driver_info` pointer and returned the `struct udevice` |
| 1026 | that was created from it. It was only available for use with of-platdata. |
| 1027 | This has been removed in favour of `device_get_by_ofplat_idx()` which uses |
| 1028 | `idx`, the index of the `struct driver_info` or `struct udevice` in the |
| 1029 | linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been |
| 1030 | updated to use this index instead of a pointer to `struct driver_info`. |
| 1031 | |
| 1032 | `DM_DRVINFO_GET` |
| 1033 | This has been removed since we now use indexes to obtain a driver from |
| 1034 | `struct phandle_0_arg` and the like. |
| 1035 | |
| 1036 | Two-pass binding |
| 1037 | The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated |
| 1038 | files so as to have parents declared ahead of children. This was convenient |
| 1039 | as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does |
| 1040 | not work as the idx value relies on using alphabetical order for everything, |
| 1041 | so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are |
| 1042 | then bound in order of idx, having no regard to parent/child relationships. |
| 1043 | For this reason, device binding now hapens in multiple passes, with parents |
| 1044 | being bound before their children. This is important so that children can |
| 1045 | find their parents in the bind() method if needed. |
| 1046 | |
| 1047 | Root device |
| 1048 | The root device is generally bound by U-Boot but with OF_PLATDATA_INST it |
| 1049 | cannot be, since binding needs to be done at build time. So in this case |
| 1050 | dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and |
| 1051 | U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot |
| 1052 | generally ignores the root node and does not create a `U_BOOT_DRVINFO()` |
| 1053 | record for it. This means that the idx numbers used by `struct driver_info` |
| 1054 | (when OF_PLATDATA_INST is disabled) and the idx numbers used by |
| 1055 | `struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a |
| 1056 | root node and the other does not. This does not actually matter, since only |
| 1057 | one of them is actually used for any particular build, but it is worth |
| 1058 | keeping in mind if comparing index values and switching OF_PLATDATA_INST on |
| 1059 | and off. |
| 1060 | |
| 1061 | `__priv_data_start` and `__priv_data_end` |
| 1062 | The private/platform data declared by dtoc is all collected together in |
| 1063 | a linker section and these symbols mark the start and end of it. This allows |
| 1064 | U-Boot to relocate the area to a new location if needed (with |
| 1065 | OF_PLATDATA_RT) |
| 1066 | |
| 1067 | `dm_priv_to_rw()` |
| 1068 | This function converts a private- or platform-data pointer value generated by |
| 1069 | dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT |
| 1070 | is enabled, in which case it translates the address to the relocated |
| 1071 | region. See above for more information. |
| 1072 | |
Simon Glass | 2500de2 | 2020-12-28 20:35:05 -0700 | [diff] [blame] | 1073 | The dm_populate_phandle_data() function that was previous needed has now been |
| 1074 | removed, since dtoc can address the drivers directly from dt-plat.c and does |
| 1075 | not need to fix up things at runtime. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 1076 | |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 1077 | The pylibfdt Python module is used to access the devicetree. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 1078 | |
Simon Glass | 946c723 | 2016-07-04 11:58:42 -0600 | [diff] [blame] | 1079 | |
| 1080 | Credits |
| 1081 | ------- |
| 1082 | |
| 1083 | This is an implementation of an idea by Tom Rini <trini@konsulko.com>. |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 1084 | |
| 1085 | |
| 1086 | Future work |
| 1087 | ----------- |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 1088 | - Consider programmatically reading binding files instead of devicetree |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 1089 | contents |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 1090 | - Allow IS_ENABLED() to be used in the C code instead of #if |
Simon Glass | 261ad28 | 2016-07-04 11:58:07 -0600 | [diff] [blame] | 1091 | |
Bin Meng | ae25869 | 2019-07-18 00:33:55 -0700 | [diff] [blame] | 1092 | |
| 1093 | .. Simon Glass <sjg@chromium.org> |
| 1094 | .. Google, Inc |
| 1095 | .. 6/6/16 |
| 1096 | .. Updated Independence Day 2016 |
Simon Glass | a772946 | 2020-10-03 11:31:42 -0600 | [diff] [blame] | 1097 | .. Updated 1st October 2020 |
Simon Glass | 1730e46 | 2021-03-15 17:25:42 +1300 | [diff] [blame] | 1098 | .. Updated 5th February 2021 |