1099 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			1099 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
| .. SPDX-License-Identifier: GPL-2.0+
 | ||
| 
 | ||
| Compiled-in Device Tree / Platform Data
 | ||
| =======================================
 | ||
| 
 | ||
| 
 | ||
| Introduction
 | ||
| ------------
 | ||
| 
 | ||
| Device tree is the standard configuration method in U-Boot. It is used to
 | ||
| define what devices are in the system and provide configuration information
 | ||
| to these devices.
 | ||
| 
 | ||
| The overhead of adding devicetree access to U-Boot is fairly modest,
 | ||
| approximately 3KB on Thumb 2 (plus the size of the DT itself). This means
 | ||
| that in most cases it is best to use devicetree for configuration.
 | ||
| 
 | ||
| However there are some very constrained environments where U-Boot needs to
 | ||
| work. These include SPL with severe memory limitations. For example, some
 | ||
| SoCs require a 16KB SPL image which must include a full MMC stack. In this
 | ||
| case the overhead of devicetree access may be too great.
 | ||
| 
 | ||
| It is possible to create platform data manually by defining C structures
 | ||
| for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This
 | ||
| bypasses the use of devicetree completely, effectively creating a parallel
 | ||
| configuration mechanism. But it is an available option for SPL.
 | ||
| 
 | ||
| As an alternative, the 'of-platdata' feature is provided. This converts the
 | ||
| devicetree contents into C code which can be compiled into the SPL binary.
 | ||
| This saves the 3KB of code overhead and perhaps a few hundred more bytes due
 | ||
| to more efficient storage of the data.
 | ||
| 
 | ||
| 
 | ||
| How it works
 | ||
| ------------
 | ||
| 
 | ||
| The feature is enabled by CONFIG OF_PLATDATA. This is only available in
 | ||
| SPL/TPL and should be tested with:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     #if CONFIG_IS_ENABLED(OF_PLATDATA)
 | ||
| 
 | ||
| A tool called 'dtoc' converts a devicetree file either into a set of
 | ||
| struct declarations, one for each compatible node, and a set of
 | ||
| `U_BOOT_DRVINFO()` declarations along with the actual platform data for each
 | ||
| device. As an example, consider this MMC node:
 | ||
| 
 | ||
| .. code-block:: none
 | ||
| 
 | ||
|     sdmmc: dwmmc@ff0c0000 {
 | ||
|             compatible = "rockchip,rk3288-dw-mshc";
 | ||
|             clock-freq-min-max = <400000 150000000>;
 | ||
|             clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
 | ||
|                      <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
 | ||
|             clock-names = "biu", "ciu", "ciu_drv", "ciu_sample";
 | ||
|             fifo-depth = <0x100>;
 | ||
|             interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
 | ||
|             reg = <0xff0c0000 0x4000>;
 | ||
|             bus-width = <4>;
 | ||
|             cap-mmc-highspeed;
 | ||
|             cap-sd-highspeed;
 | ||
|             card-detect-delay = <200>;
 | ||
|             disable-wp;
 | ||
|             num-slots = <1>;
 | ||
|             pinctrl-names = "default";
 | ||
|             pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
 | ||
|                 vmmc-supply = <&vcc_sd>;
 | ||
|                 status = "okay";
 | ||
|                 u-boot,dm-pre-reloc;
 | ||
|         };
 | ||
| 
 | ||
| 
 | ||
| Some of these properties are dropped by U-Boot under control of the
 | ||
| CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce
 | ||
| the following C struct declaration:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     struct dtd_rockchip_rk3288_dw_mshc {
 | ||
|             fdt32_t         bus_width;
 | ||
|             bool            cap_mmc_highspeed;
 | ||
|             bool            cap_sd_highspeed;
 | ||
|             fdt32_t         card_detect_delay;
 | ||
|             fdt32_t         clock_freq_min_max[2];
 | ||
|             struct phandle_1_arg clocks[4];
 | ||
|             bool            disable_wp;
 | ||
|             fdt32_t         fifo_depth;
 | ||
|             fdt32_t         interrupts[3];
 | ||
|             fdt32_t         num_slots;
 | ||
|             fdt32_t         reg[2];
 | ||
|             fdt32_t         vmmc_supply;
 | ||
|     };
 | ||
| 
 | ||
| and the following device declarations:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     /* Node /clock-controller@ff760000 index 0 */
 | ||
|     ...
 | ||
| 
 | ||
|     /* Node /dwmmc@ff0c0000 index 2 */
 | ||
|     static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
 | ||
|             .fifo_depth             = 0x100,
 | ||
|             .cap_sd_highspeed       = true,
 | ||
|             .interrupts             = {0x0, 0x20, 0x4},
 | ||
|             .clock_freq_min_max     = {0x61a80, 0x8f0d180},
 | ||
|             .vmmc_supply            = 0xb,
 | ||
|             .num_slots              = 0x1,
 | ||
|             .clocks                 = {{0, 456},
 | ||
|                                        {0, 68},
 | ||
|                                        {0, 114},
 | ||
|                                        {0, 118}},
 | ||
|             .cap_mmc_highspeed      = true,
 | ||
|             .disable_wp             = true,
 | ||
|             .bus_width              = 0x4,
 | ||
|             .u_boot_dm_pre_reloc    = true,
 | ||
|             .reg                    = {0xff0c0000, 0x4000},
 | ||
|             .card_detect_delay      = 0xc8,
 | ||
|     };
 | ||
| 
 | ||
|     U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = {
 | ||
|             .name           = "rockchip_rk3288_dw_mshc",
 | ||
|             .plat       = &dtv_dwmmc_at_ff0c0000,
 | ||
|             .plat_size  = sizeof(dtv_dwmmc_at_ff0c0000),
 | ||
|             .parent_idx     = -1,
 | ||
|     };
 | ||
| 
 | ||
| The device is then instantiated at run-time and the platform data can be
 | ||
| accessed using:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     struct udevice *dev;
 | ||
|     struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev);
 | ||
| 
 | ||
| This avoids the code overhead of converting the devicetree data to
 | ||
| platform data in the driver. The `of_to_plat()` method should
 | ||
| therefore do nothing in such a driver.
 | ||
| 
 | ||
| Note that for the platform data to be matched with a driver, the 'name'
 | ||
| property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared
 | ||
| via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a
 | ||
| 'name' corresponding to the devicetree 'compatible' string (after converting
 | ||
| it to a valid name for C) is needed, so a dedicated driver is required for
 | ||
| each 'compatible' string.
 | ||
| 
 | ||
| In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be
 | ||
| used to declare an alias for a driver name, typically a 'compatible' string.
 | ||
| This macro produces no code, but is used by dtoc tool. It must be located in the
 | ||
| same file as its associated driver, ideally just after it.
 | ||
| 
 | ||
| The parent_idx is the index of the parent `driver_info` structure within its
 | ||
| linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to
 | ||
| support `dev_get_parent()`.
 | ||
| 
 | ||
| During the build process dtoc parses both `U_BOOT_DRIVER()` and
 | ||
| `DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases.
 | ||
| If the 'compatible' string used for a device does not not match a valid driver
 | ||
| name, it will be checked against the list of driver aliases in order to get the
 | ||
| right driver name to use. If in this step there is no match found a warning is
 | ||
| issued to avoid run-time failures.
 | ||
| 
 | ||
| Where a node has multiple compatible strings, dtoc generates a `#define` to
 | ||
| make them equivalent, e.g.:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     #define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc
 | ||
| 
 | ||
| 
 | ||
| Converting of-platdata to a useful form
 | ||
| ---------------------------------------
 | ||
| 
 | ||
| Of course it would be possible to use the of-platdata directly in your driver
 | ||
| whenever configuration information is required. However this means that the
 | ||
| driver will not be able to support devicetree, since the of-platdata
 | ||
| structure is not available when devicetree is used. It would make no sense
 | ||
| to use this structure if devicetree were available, since the structure has
 | ||
| all the limitations metioned in caveats below.
 | ||
| 
 | ||
| Therefore it is recommended that the of-platdata structure should be used
 | ||
| only in the `probe()` method of your driver. It cannot be used in the
 | ||
| `of_to_plat()` method since this is not called when platform data is
 | ||
| already present.
 | ||
| 
 | ||
| 
 | ||
| How to structure your driver
 | ||
| ----------------------------
 | ||
| 
 | ||
| Drivers should always support devicetree as an option. The of-platdata
 | ||
| feature is intended as a add-on to existing drivers.
 | ||
| 
 | ||
| Your driver should convert the plat struct in its `probe()` method. The
 | ||
| existing devicetree decoding logic should be kept in the
 | ||
| `of_to_plat()` method and wrapped with `#if`.
 | ||
| 
 | ||
| For example:
 | ||
| 
 | ||
| .. code-block:: c
 | ||
| 
 | ||
|     #include <dt-structs.h>
 | ||
| 
 | ||
|     struct mmc_plat {
 | ||
|     #if CONFIG_IS_ENABLED(OF_PLATDATA)
 | ||
|             /* Put this first since driver model will copy the data here */
 | ||
|             struct dtd_mmc dtplat;
 | ||
|     #endif
 | ||
|             /*
 | ||
|              * Other fields can go here, to be filled in by decoding from
 | ||
|              * the devicetree (or the C structures when of-platdata is used).
 | ||
|              */
 | ||
|             int fifo_depth;
 | ||
|     };
 | ||
| 
 | ||
|     static int mmc_of_to_plat(struct udevice *dev)
 | ||
|     {
 | ||
|         if (CONFIG_IS_ENABLED(OF_REAL)) {
 | ||
|             /* Decode the devicetree data */
 | ||
|             struct mmc_plat *plat = dev_get_plat(dev);
 | ||
|             const void *blob = gd->fdt_blob;
 | ||
|             int node = dev_of_offset(dev);
 | ||
| 
 | ||
|             plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0);
 | ||
|         }
 | ||
| 
 | ||
|         return 0;
 | ||
|     }
 | ||
| 
 | ||
|     static int mmc_probe(struct udevice *dev)
 | ||
|     {
 | ||
|             struct mmc_plat *plat = dev_get_plat(dev);
 | ||
| 
 | ||
|     #if CONFIG_IS_ENABLED(OF_PLATDATA)
 | ||
|             /* Decode the of-platdata from the C structures */
 | ||
|             struct dtd_mmc *dtplat = &plat->dtplat;
 | ||
| 
 | ||
|             plat->fifo_depth = dtplat->fifo_depth;
 | ||
|     #endif
 | ||
|             /* Set up the device from the plat data */
 | ||
|             writel(plat->fifo_depth, ...)
 | ||
|     }
 | ||
| 
 | ||
|     static const struct udevice_id mmc_ids[] = {
 | ||
|             { .compatible = "vendor,mmc" },
 | ||
|             { }
 | ||
|     };
 | ||
| 
 | ||
|     U_BOOT_DRIVER(mmc_drv) = {
 | ||
|             .name           = "mmc_drv",
 | ||
|             .id             = UCLASS_MMC,
 | ||
|             .of_match       = mmc_ids,
 | ||
|             .of_to_plat = mmc_of_to_plat,
 | ||
|             .probe          = mmc_probe,
 | ||
|             .priv_auto = sizeof(struct mmc_priv),
 | ||
|             .plat_auto = sizeof(struct mmc_plat),
 | ||
|     };
 | ||
| 
 | ||
|     DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */
 | ||
| 
 | ||
| Note that `struct mmc_plat` is defined in the C file, not in a header. This
 | ||
| is to avoid needing to include dt-structs.h in a header file. The idea is to
 | ||
| keep the use of each of-platdata struct to the smallest possible code area.
 | ||
| There is just one driver C file for each struct, that can convert from the
 | ||
| of-platdata struct to the standard one used by the driver.
 | ||
| 
 | ||
| In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is
 | ||
| still used to allocate space for the platform data. This is different from
 | ||
| the normal behaviour and is triggered by the use of of-platdata (strictly
 | ||
| speaking it is a non-zero `plat_size` which triggers this).
 | ||
| 
 | ||
| The of-platdata struct contents is copied from the C structure data to the
 | ||
| start of the newly allocated area. In the case where devicetree is used,
 | ||
| the platform data is allocated, and starts zeroed. In this case the
 | ||
| `of_to_plat()` method should still set up the platform data (and the
 | ||
| of-platdata struct will not be present).
 | ||
| 
 | ||
| SPL must use either of-platdata or devicetree. Drivers cannot use both at
 | ||
| the same time, but they must support devicetree. Supporting of-platdata is
 | ||
| optional.
 | ||
| 
 | ||
| The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled,
 | ||
| since the devicetree access code is not compiled in. A corollary is that
 | ||
| a board can only move to using of-platdata if all the drivers it uses support
 | ||
| it. There would be little point in having some drivers require the device
 | ||
| tree data, since then libfdt would still be needed for those drivers and
 | ||
| there would be no code-size benefit.
 | ||
| 
 | ||
| 
 | ||
| Build-time instantiation
 | ||
| ------------------------
 | ||
| 
 | ||
| Even with of-platdata there is a fair amount of code required in driver model.
 | ||
| It is possible to have U-Boot handle the instantiation of devices at build-time,
 | ||
| so avoiding the need for the `device_bind()` code and some parts of
 | ||
| `device_probe()`.
 | ||
| 
 | ||
| The feature is enabled by CONFIG_OF_PLATDATA_INST.
 | ||
| 
 | ||
| Here is an example device, as generated by dtoc::
 | ||
| 
 | ||
|    /*
 | ||
|     * Node /serial index 6
 | ||
|     * driver sandbox_serial parent root_driver
 | ||
|    */
 | ||
| 
 | ||
|    #include <asm/serial.h>
 | ||
|    struct sandbox_serial_plat __attribute__ ((section (".priv_data")))
 | ||
|       _sandbox_serial_plat_serial = {
 | ||
|       .dtplat = {
 | ||
|          .sandbox_text_colour   = "cyan",
 | ||
|       },
 | ||
|    };
 | ||
|    #include <asm/serial.h>
 | ||
|    u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)]
 | ||
|       __attribute__ ((section (".priv_data")));
 | ||
|    #include <serial.h>
 | ||
|    u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)]
 | ||
|       __attribute__ ((section (".priv_data")));
 | ||
| 
 | ||
|    DM_DEVICE_INST(serial) = {
 | ||
|       .driver     = DM_DRIVER_REF(sandbox_serial),
 | ||
|       .name       = "sandbox_serial",
 | ||
|       .plat_      = &_sandbox_serial_plat_serial,
 | ||
|       .priv_      = _sandbox_serial_priv_serial,
 | ||
|       .uclass     = DM_UCLASS_REF(serial),
 | ||
|       .uclass_priv_ = _sandbox_serial_uc_priv_serial,
 | ||
|       .uclass_node   = {
 | ||
|          .prev = &DM_UCLASS_REF(serial)->dev_head,
 | ||
|          .next = &DM_UCLASS_REF(serial)->dev_head,
 | ||
|       },
 | ||
|       .child_head   = {
 | ||
|          .prev = &DM_DEVICE_REF(serial)->child_head,
 | ||
|          .next = &DM_DEVICE_REF(serial)->child_head,
 | ||
|       },
 | ||
|       .sibling_node   = {
 | ||
|          .prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node,
 | ||
|          .next = &DM_DEVICE_REF(spl_test)->sibling_node,
 | ||
|       },
 | ||
|       .seq_ = 0,
 | ||
|    };
 | ||
| 
 | ||
| Here is part of the driver, for reference::
 | ||
| 
 | ||
|    static const struct udevice_id sandbox_serial_ids[] = {
 | ||
|       { .compatible = "sandbox,serial" },
 | ||
|       { }
 | ||
|    };
 | ||
| 
 | ||
|    U_BOOT_DRIVER(sandbox_serial) = {
 | ||
|       .name   = "sandbox_serial",
 | ||
|       .id   = UCLASS_SERIAL,
 | ||
|       .of_match    = sandbox_serial_ids,
 | ||
|       .of_to_plat  = sandbox_serial_of_to_plat,
 | ||
|       .plat_auto   = sizeof(struct sandbox_serial_plat),
 | ||
|       .priv_auto   = sizeof(struct sandbox_serial_priv),
 | ||
|       .probe = sandbox_serial_probe,
 | ||
|       .remove = sandbox_serial_remove,
 | ||
|       .ops   = &sandbox_serial_ops,
 | ||
|       .flags = DM_FLAG_PRE_RELOC,
 | ||
|    };
 | ||
| 
 | ||
| 
 | ||
| The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the
 | ||
| members are from that struct. The private data is declared immediately above,
 | ||
| as `_sandbox_serial_priv_serial`, so there is no need for run-time memory
 | ||
| allocation. The #include lines are generated as well, since dtoc searches the
 | ||
| U-Boot source code for the definition of `struct sandbox_serial_priv` and adds
 | ||
| the relevant header so that the code will compile without errors.
 | ||
| 
 | ||
| The `plat_` member is set to the dtv data which is declared immediately above
 | ||
| the device. This is similar to how it would look without of-platdata-inst, but
 | ||
| node that the `dtplat` member inside is part of the wider
 | ||
| `_sandbox_serial_plat_serial` struct. This is because the driver declares its
 | ||
| own platform data, and the part generated by dtoc can only be a portion of it.
 | ||
| The `dtplat` part is always first in the struct. If the device has no
 | ||
| `.plat_auto` field, then a simple dtv struct can be used as with this example::
 | ||
| 
 | ||
|    static struct dtd_sandbox_clk dtv_clk_sbox = {
 | ||
|       .assigned_clock_rates   = 0x141,
 | ||
|       .assigned_clocks   = {0x7, 0x3},
 | ||
|    };
 | ||
| 
 | ||
|    #include <asm/clk.h>
 | ||
|    u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)]
 | ||
|       __attribute__ ((section (".priv_data")));
 | ||
| 
 | ||
|    DM_DEVICE_INST(clk_sbox) = {
 | ||
|       .driver    = DM_DRIVER_REF(sandbox_clk),
 | ||
|       .name      = "sandbox_clk",
 | ||
|       .plat_     = &dtv_clk_sbox,
 | ||
| 
 | ||
| Here is part of the driver, for reference::
 | ||
| 
 | ||
|    static const struct udevice_id sandbox_clk_ids[] = {
 | ||
|       { .compatible = "sandbox,clk" },
 | ||
|       { }
 | ||
|    };
 | ||
| 
 | ||
|    U_BOOT_DRIVER(sandbox_clk) = {
 | ||
|       .name       = "sandbox_clk",
 | ||
|       .id         = UCLASS_CLK,
 | ||
|       .of_match   = sandbox_clk_ids,
 | ||
|       .ops        = &sandbox_clk_ops,
 | ||
|       .probe      = sandbox_clk_probe,
 | ||
|       .priv_auto  = sizeof(struct sandbox_clk_priv),
 | ||
|    };
 | ||
| 
 | ||
| 
 | ||
| You can see that `dtv_clk_sbox` just has the devicetree contents and there is
 | ||
| no need for the `dtplat` separation, since the driver has no platform data of
 | ||
| its own, besides that provided by the devicetree (i.e. no `.plat_auto` field).
 | ||
| 
 | ||
| The doubly linked lists are handled by explicitly declaring the value of each
 | ||
| node, as you can see with the `.prev` and `.next` values in the example above.
 | ||
| Since dtoc knows the order of devices it can link them into the appropriate
 | ||
| lists correctly.
 | ||
| 
 | ||
| One of the features of driver model is the ability for a uclass to have a
 | ||
| small amount of private data for each device in that uclass. This is used to
 | ||
| provide a generic data structure that the uclass can use for all devices, thus
 | ||
| allowing generic features to be implemented in common code. An example is I2C,
 | ||
| which stores the bus speed there.
 | ||
| 
 | ||
| Similarly, parent devices can have data associated with each of their children.
 | ||
| This is used to provide information common to all children of a particular bus.
 | ||
| For an I2C bus, this is used to store the I2C address of each child on the bus.
 | ||
| 
 | ||
| This is all handled automatically by dtoc::
 | ||
| 
 | ||
|    #include <asm/i2c.h>
 | ||
|    u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)]
 | ||
|       __attribute__ ((section (".priv_data")));
 | ||
|    #include <i2c.h>
 | ||
|    u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)]
 | ||
|       __attribute__ ((section (".priv_data")));
 | ||
| 
 | ||
|    DM_DEVICE_INST(i2c_at_0) = {
 | ||
|       .driver      = DM_DRIVER_REF(sandbox_i2c),
 | ||
|       .name      = "sandbox_i2c",
 | ||
|       .plat_   = &dtv_i2c_at_0,
 | ||
|       .priv_      = _sandbox_i2c_priv_i2c_at_0,
 | ||
|       .uclass   = DM_UCLASS_REF(i2c),
 | ||
|       .uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0,
 | ||
|      ...
 | ||
| 
 | ||
| Part of driver, for reference::
 | ||
| 
 | ||
|    static const struct udevice_id sandbox_i2c_ids[] = {
 | ||
|       { .compatible = "sandbox,i2c" },
 | ||
|       { }
 | ||
|    };
 | ||
| 
 | ||
|    U_BOOT_DRIVER(sandbox_i2c) = {
 | ||
|       .name   = "sandbox_i2c",
 | ||
|       .id   = UCLASS_I2C,
 | ||
|       .of_match = sandbox_i2c_ids,
 | ||
|       .ops   = &sandbox_i2c_ops,
 | ||
|       .priv_auto   = sizeof(struct sandbox_i2c_priv),
 | ||
|    };
 | ||
| 
 | ||
| Part of I2C uclass, for reference::
 | ||
| 
 | ||
|    UCLASS_DRIVER(i2c) = {
 | ||
|       .id         = UCLASS_I2C,
 | ||
|       .name       = "i2c",
 | ||
|       .flags      = DM_UC_FLAG_SEQ_ALIAS,
 | ||
|       .post_bind  = i2c_post_bind,
 | ||
|       .pre_probe  = i2c_pre_probe,
 | ||
|       .post_probe = i2c_post_probe,
 | ||
|       .per_device_auto   = sizeof(struct dm_i2c_bus),
 | ||
|       .per_child_plat_auto   = sizeof(struct dm_i2c_chip),
 | ||
|       .child_post_bind = i2c_child_post_bind,
 | ||
|    };
 | ||
| 
 | ||
| Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared
 | ||
| in the device, as required by driver model. The required header file is included
 | ||
| so that the code will compile without errors. A similar mechanism is used for
 | ||
| child devices, but is not shown by this example.
 | ||
| 
 | ||
| It would not be that useful to avoid binding devices but still need to allocate
 | ||
| uclasses at runtime. So dtoc generates uclass instances as well::
 | ||
| 
 | ||
|    struct list_head uclass_head = {
 | ||
|       .prev = &DM_UCLASS_REF(serial)->sibling_node,
 | ||
|       .next = &DM_UCLASS_REF(clk)->sibling_node,
 | ||
|    };
 | ||
| 
 | ||
|    DM_UCLASS_INST(clk) = {
 | ||
|       .uc_drv      = DM_UCLASS_DRIVER_REF(clk),
 | ||
|       .sibling_node   = {
 | ||
|          .prev = &uclass_head,
 | ||
|          .next = &DM_UCLASS_REF(i2c)->sibling_node,
 | ||
|       },
 | ||
|       .dev_head   = {
 | ||
|          .prev = &DM_DEVICE_REF(clk_sbox)->uclass_node,
 | ||
|          .next = &DM_DEVICE_REF(clk_fixed)->uclass_node,
 | ||
|       },
 | ||
|    };
 | ||
| 
 | ||
| At the top is the list head. Driver model uses this on start-up, instead of
 | ||
| creating its own.
 | ||
| 
 | ||
| Below that are a set of `DM_UCLASS_INST()` macros, each declaring a
 | ||
| `struct uclass`. The doubly linked lists work as for devices.
 | ||
| 
 | ||
| All private data is placed into a `.priv_data` section so that it is contiguous
 | ||
| in the resulting output binary.
 | ||
| 
 | ||
| 
 | ||
| Indexes
 | ||
| -------
 | ||
| 
 | ||
| U-Boot stores drivers, devices and many other things in linker_list structures.
 | ||
| These are sorted by name, so dtoc knows the order that they will appear when
 | ||
| the linker runs. Each driver_info / udevice is referenced by its index in the
 | ||
| linker_list array, called 'idx' in the code.
 | ||
| 
 | ||
| When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it
 | ||
| is the driver_info index. In either case, indexes are used to reference devices
 | ||
| using device_get_by_ofplat_idx(). This allows phandles to work as expected.
 | ||
| 
 | ||
| 
 | ||
| Phases
 | ||
| ------
 | ||
| 
 | ||
| U-Boot operates in several phases, typically TPL, SPL and U-Boot proper.
 | ||
| The latter does not use dtoc.
 | ||
| 
 | ||
| In some rare cases different drivers are used for two phases. For example,
 | ||
| in TPL it may not be necessary to use the full PCI subsystem, so a simple
 | ||
| driver can be used instead.
 | ||
| 
 | ||
| This works in the build system simply by compiling in one driver or the
 | ||
| other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has
 | ||
| no way of knowing which code is compiled in for which phase, since it does
 | ||
| not inspect Makefiles or dependency graphs.
 | ||
| 
 | ||
| So to make this work for dtoc, we need to be able to explicitly mark
 | ||
| drivers with their phase. This is done by adding a macro to the driver::
 | ||
| 
 | ||
|    /* code in tpl.c only compiled into TPL */
 | ||
|    U_BOOT_DRIVER(pci_x86) = {
 | ||
|       .name   = "pci_x86",
 | ||
|       .id   = UCLASS_SIMPLE_BUS,
 | ||
|       .of_match = of_match_ptr(tpl_fake_pci_ids),
 | ||
|       DM_PHASE(tpl)
 | ||
|    };
 | ||
| 
 | ||
| 
 | ||
|    /* code in pci_x86.c compiled into SPL and U-Boot proper */
 | ||
|    U_BOOT_DRIVER(pci_x86) = {
 | ||
|       .name   = "pci_x86",
 | ||
|       .id   = UCLASS_PCI,
 | ||
|       .of_match = pci_x86_ids,
 | ||
|       .ops   = &pci_x86_ops,
 | ||
|    };
 | ||
| 
 | ||
| 
 | ||
| Notice that the second driver has the same name but no DM_PHASE(), so it will be
 | ||
| used for SPL and U-Boot.
 | ||
| 
 | ||
| Note also that this only affects the code generated by dtoc. You still need to
 | ||
| make sure that only the required driver is build into each phase.
 | ||
| 
 | ||
| 
 | ||
| Header files
 | ||
| ------------
 | ||
| 
 | ||
| With OF_PLATDATA_INST, dtoc must include the correct header file in the
 | ||
| generated code for any structs that are used, so that the code will compile.
 | ||
| For example, if `struct ns16550_plat` is used, the code must include the
 | ||
| `ns16550.h` header file.
 | ||
| 
 | ||
| Typically dtoc can detect the header file needed for a driver by looking
 | ||
| for the structs that it uses. For example, if a driver as a `.priv_auto`
 | ||
| that uses `struct ns16550_plat`, then dtoc can search header files for the
 | ||
| definition of that struct and use the file.
 | ||
| 
 | ||
| In some cases, enums are used in drivers, typically with the `.data` field
 | ||
| of `struct udevice_id`. Since dtoc does not support searching for these,
 | ||
| you must use the `DM_HDR()` macro to tell dtoc which header to use. This works
 | ||
| as a macro included in the driver definition::
 | ||
| 
 | ||
|    static const struct udevice_id apl_syscon_ids[] = {
 | ||
|       { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT },
 | ||
|       { }
 | ||
|    };
 | ||
| 
 | ||
|    U_BOOT_DRIVER(intel_apl_punit) = {
 | ||
|       .name       = "intel_apl_punit",
 | ||
|       .id         = UCLASS_SYSCON,
 | ||
|       .of_match   = apl_syscon_ids,
 | ||
|       .probe      = apl_punit_probe,
 | ||
|       DM_HEADER(<asm/cpu.h>)    /* for X86_SYSCON_PUNIT */
 | ||
|    };
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| Problems
 | ||
| --------
 | ||
| 
 | ||
| This section shows some common problems and how to fix them.
 | ||
| 
 | ||
| Driver not found
 | ||
| ~~~~~~~~~~~~~~~~
 | ||
| 
 | ||
| In some cases you will you see something like this::
 | ||
| 
 | ||
|    WARNING: the driver rockchip_rk3188_grf was not found in the driver list
 | ||
| 
 | ||
| The driver list is a list of drivers, each with a name. The name is in the
 | ||
| U_BOOT_DRIVER() declaration, repeated twice, one in brackets and once as the
 | ||
| .name member. For example, in the following declaration the driver name is
 | ||
| `rockchip_rk3188_grf`::
 | ||
| 
 | ||
|   U_BOOT_DRIVER(rockchip_rk3188_grf) = {
 | ||
|        .name = "rockchip_rk3188_grf",
 | ||
|        .id = UCLASS_SYSCON,
 | ||
|        .of_match = rk3188_syscon_ids + 1,
 | ||
|        .bind = rk3188_syscon_bind_of_plat,
 | ||
|   };
 | ||
| 
 | ||
| The first name U_BOOT_DRIVER(xx) is used to create a linker symbol so that the
 | ||
| driver can be accessed at build-time without any overhead. The second one
 | ||
| (.name = "xx") is used at runtime when something wants to print out the driver
 | ||
| name.
 | ||
| 
 | ||
| The dtoc tool expects to be able to find a driver for each compatible string in
 | ||
| the devicetree. For example, if the devicetree has::
 | ||
| 
 | ||
|    grf: grf@20008000 {
 | ||
|       compatible = "rockchip,rk3188-grf", "syscon";
 | ||
|       reg = <0x20008000 0x200>;
 | ||
|       u-boot,dm-spl;
 | ||
|    };
 | ||
| 
 | ||
| then dtoc looks at the first compatible string ("rockchip,rk3188-grf"),
 | ||
| converts that to a C identifier (rockchip_rk3188_grf) and then looks for that.
 | ||
| 
 | ||
| Missing .compatible or Missing .id
 | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||
| 
 | ||
| Various things can cause dtoc to fail to find the driver and it tries to
 | ||
| warn about these. For example::
 | ||
| 
 | ||
|    rockchip_rk3188_uart: Missing .compatible in drivers/serial/serial_rockchip.c
 | ||
|                     : WARNING: the driver rockchip_rk3188_uart was not found in the driver list
 | ||
| 
 | ||
| Without a compatible string a driver cannot be used by dtoc, even if the
 | ||
| compatible string is not actually needed at runtime.
 | ||
| 
 | ||
| If the problem is simply that there are multiple compatible strings, the
 | ||
| DM_DRIVER_ALIAS() macro can be used to tell dtoc about this and avoid a problem.
 | ||
| 
 | ||
| Checks are also made to confirm that the referenced driver has a .compatible
 | ||
| member and a .id member. The first provides the array of compatible strings and
 | ||
| the second provides the uclass ID.
 | ||
| 
 | ||
| Missing parent
 | ||
| ~~~~~~~~~~~~~~
 | ||
| 
 | ||
| When a device is used, its parent must be present as well. If you see an error
 | ||
| like::
 | ||
| 
 | ||
|    Node '/i2c@0/emul/emul0' requires parent node '/i2c@0/emul' but it is not in
 | ||
|       the valid list
 | ||
| 
 | ||
| it indicates that you are using a node whose parent is not present in the
 | ||
| devicetree. In this example, if you look at the device tree output
 | ||
| (e.g. fdtdump tpl/u-boot-tpl.dtb in your build directory), you may see something
 | ||
| like this::
 | ||
| 
 | ||
|    emul {
 | ||
|        emul0 {
 | ||
|            compatible = "sandbox,i2c-rtc-emul";
 | ||
|            #emul-cells = <0x00000000>;
 | ||
|            phandle = <0x00000003>;
 | ||
|        };
 | ||
|    };
 | ||
| 
 | ||
| In this example, 'emul0' exists but its parent 'emul' has no properties. These
 | ||
| have been dropped by fdtgrep in an effort to reduce the devicetree size. This
 | ||
| indicates that the two nodes have different phase settings. Looking at the
 | ||
| source .dts::
 | ||
| 
 | ||
|    i2c_emul: emul {
 | ||
|       u-boot,dm-spl;
 | ||
|       reg = <0xff>;
 | ||
|       compatible = "sandbox,i2c-emul-parent";
 | ||
|       emul0: emul0 {
 | ||
|          u-boot,dm-pre-reloc;
 | ||
|          compatible = "sandbox,i2c-rtc-emul";
 | ||
|          #emul-cells = <0>;
 | ||
|       };
 | ||
|    };
 | ||
| 
 | ||
| you can see that the child node 'emul0' usees 'u-boot,dm-pre-reloc', indicating
 | ||
| that the node is present in all SPL builds, but its parent uses 'u-boot,dm-spl'
 | ||
| indicating it is only present in SPL, not TPL. For a TPL build, this will fail
 | ||
| with the above message. The fix is to change 'emul0' to use the same
 | ||
| 'u-boot,dm-spl' condition, so that it is not present in TPL, like its parent.
 | ||
| 
 | ||
| Link errors / undefined reference
 | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||
| 
 | ||
| Sometimes dtoc does not find the problem for you, but something is wrong and
 | ||
| you get a link error, e.g.::
 | ||
| 
 | ||
|    :(__u_boot_list_2_udevice_2_spl_test5+0x0): undefined reference to
 | ||
|       `_u_boot_list_2_driver_2_sandbox_spl_test'
 | ||
|    /usr/bin/ld: dts/dt-uclass.o:(__u_boot_list_2_uclass_2_misc+0x8):
 | ||
|         undefined reference to `_u_boot_list_2_uclass_driver_2_misc'
 | ||
| 
 | ||
| The first one indicates that the device cannot find its driver. This means that
 | ||
| there is a driver 'sandbox_spl_test' but it is not compiled into the build.
 | ||
| Check your Kconfig settings to make sure it is. If you don't want that in the
 | ||
| build, adjust your phase settings, e.g. by using 'u-boot,dm-spl' in the node
 | ||
| to exclude it from the TPL build::
 | ||
| 
 | ||
| 	spl-test5 {
 | ||
| 		u-boot,dm-tpl;
 | ||
| 		compatible = "sandbox,spl-test";
 | ||
| 		stringarray = "tpl";
 | ||
| 	};
 | ||
| 
 | ||
| We can drop the 'u-boot,dm-tpl' line so this node won't appear in the TPL
 | ||
| devicetree and thus the driver won't be needed.
 | ||
| 
 | ||
| The second error above indicates that the MISC uclass is needed by the driver
 | ||
| (since it is in the MISC uclass) but that uclass is not compiled in the build.
 | ||
| The fix above would fix this error too. But if you do want this uclass in the
 | ||
| build, check your Kconfig settings to make sure the uclass is being built
 | ||
| (CONFIG_MISC in this case).
 | ||
| 
 | ||
| Another error that can crop up is something like::
 | ||
| 
 | ||
|    spl/dts/dt-device.c:257:38: error: invalid application of ‘sizeof’ to
 | ||
|          incomplete type ‘struct sandbox_irq_priv’
 | ||
|       257 | u8 _sandbox_irq_priv_irq_sbox[sizeof(struct sandbox_irq_priv)]
 | ||
|           |                                      ^~~~~~
 | ||
| 
 | ||
| This indicates that `struct sandbox_irq_priv` is not defined anywhere. The
 | ||
| solution is to add a DM_HEADER() line, as below, so this is included in the
 | ||
| dt-device.c file::
 | ||
| 
 | ||
|    U_BOOT_DRIVER(sandbox_irq) = {
 | ||
|       .name		= "sandbox_irq",
 | ||
|       .id		= UCLASS_IRQ,
 | ||
|       .of_match	= sandbox_irq_ids,
 | ||
|       .ops		= &sandbox_irq_ops,
 | ||
|       .priv_auto	= sizeof(struct sandbox_irq_priv),
 | ||
|       DM_HEADER(<asm/irq.h>)
 | ||
|    };
 | ||
| 
 | ||
| Note that there is no dependency checking on the above, so U-Boot will not
 | ||
| regenerate the dt-device.c file when you update the source file (here,
 | ||
| `irq_sandbox.c`). You need to run `make mrproper` first to get a fresh build.
 | ||
| 
 | ||
| Another error that can crop up is something like::
 | ||
| 
 | ||
|    spl/dts/dt-device.c:257:38: error: invalid application of ‘sizeof’ to
 | ||
|          incomplete type ‘struct sandbox_irq_priv’
 | ||
|       257 | u8 _sandbox_irq_priv_irq_sbox[sizeof(struct sandbox_irq_priv)]
 | ||
|           |                                      ^~~~~~
 | ||
| 
 | ||
| This indicates that `struct sandbox_irq_priv` is not defined anywhere. The
 | ||
| solution is to add a DM_HEADER() line, as below, so this is included in the
 | ||
| dt-device.c file::
 | ||
| 
 | ||
|    U_BOOT_DRIVER(sandbox_irq) = {
 | ||
|       .name		= "sandbox_irq",
 | ||
|       .id		= UCLASS_IRQ,
 | ||
|       .of_match	= sandbox_irq_ids,
 | ||
|       .ops		= &sandbox_irq_ops,
 | ||
|       .priv_auto	= sizeof(struct sandbox_irq_priv),
 | ||
|       DM_HEADER(<asm/irq.h>)
 | ||
|    };
 | ||
| 
 | ||
| Note that there is no dependency checking on the above, so U-Boot will not
 | ||
| regenerate the dt-device.c file when you update the source file (here,
 | ||
| `irq_sandbox.c`). You need to run `make mrproper` first to get a fresh build.
 | ||
| 
 | ||
| 
 | ||
| Caveats
 | ||
| -------
 | ||
| 
 | ||
| There are various complications with this feature which mean it should only
 | ||
| be used when strictly necessary, i.e. in SPL with limited memory. Notable
 | ||
| caveats include:
 | ||
| 
 | ||
|    - Device tree does not describe data types. But the C code must define a
 | ||
|      type for each property. These are guessed using heuristics which
 | ||
|      are wrong in several fairly common cases. For example an 8-byte value
 | ||
|      is considered to be a 2-item integer array, and is byte-swapped. A
 | ||
|      boolean value that is not present means 'false', but cannot be
 | ||
|      included in the structures since there is generally no mention of it
 | ||
|      in the devicetree file.
 | ||
| 
 | ||
|    - Naming of nodes and properties is automatic. This means that they follow
 | ||
|      the naming in the devicetree, which may result in C identifiers that
 | ||
|      look a bit strange.
 | ||
| 
 | ||
|    - It is not possible to find a value given a property name. Code must use
 | ||
|      the associated C member variable directly in the code. This makes
 | ||
|      the code less robust in the face of devicetree changes. To avoid having
 | ||
|      a second struct with similar members and names you need to explicitly
 | ||
|      declare it as an alias with `DM_DRIVER_ALIAS()`.
 | ||
| 
 | ||
|    - The platform data is provided to drivers as a C structure. The driver
 | ||
|      must use the same structure to access the data. Since a driver
 | ||
|      normally also supports devicetree it must use `#ifdef` to separate
 | ||
|      out this code, since the structures are only available in SPL. This could
 | ||
|      be fixed fairly easily by making the structs available outside SPL, so
 | ||
|      that `IS_ENABLED()` could be used.
 | ||
| 
 | ||
|    - With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning
 | ||
|      that (by default) it is not possible to call `device_bind()` from C code.
 | ||
|      This means that all devices must have an associated devicetree node and
 | ||
|      compatible string. For example if a GPIO device currently creates child
 | ||
|      devices in its `bind()` method, it will not work with
 | ||
|      CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the
 | ||
|      devicetree binding should be updated to declare compatible strings for
 | ||
|      the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this
 | ||
|      is not recommended since it increases code size.
 | ||
| 
 | ||
| 
 | ||
| Internals
 | ||
| ---------
 | ||
| 
 | ||
| Generated files
 | ||
| ~~~~~~~~~~~~~~~
 | ||
| 
 | ||
| When enabled, dtoc generates the following five files:
 | ||
| 
 | ||
| include/generated/dt-decl.h (OF_PLATDATA_INST only)
 | ||
|    Contains declarations for all drivers, devices and uclasses. This allows
 | ||
|    any `struct udevice`, `struct driver` or `struct uclass` to be located by its
 | ||
|    name
 | ||
| 
 | ||
| include/generated/dt-structs-gen.h
 | ||
|    Contains the struct definitions for the devicetree nodes that are used. This
 | ||
|    is the same as without OF_PLATDATA_INST
 | ||
| 
 | ||
| spl/dts/dt-plat.c (only with !OF_PLATDATA_INST)
 | ||
|    Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices
 | ||
|    at start-up. See above for an example
 | ||
| 
 | ||
| spl/dts/dt-device.c (only with OF_PLATDATA_INST)
 | ||
|    Contains `DM_DEVICE_INST()` declarations for each device that can be used at
 | ||
|    run-time. These are declared in the file along with any private/platform data
 | ||
|    that they use. Every device has an idx, as above. Since each device must be
 | ||
|    part of a double-linked list, the nodes are declared in the code as well.
 | ||
| 
 | ||
| spl/dts/dt-uclass.c (only with OF_PLATDATA_INST)
 | ||
|    Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at
 | ||
|    run-time. These are declared in the file along with any private data
 | ||
|    associated with the uclass itself (the `.priv_auto` member). Since each
 | ||
|    uclass must be part of a double-linked list, the nodes are declared in the
 | ||
|    code as well.
 | ||
| 
 | ||
| The dt-structs.h file includes the generated file
 | ||
| `(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled.
 | ||
| Otherwise (such as in U-Boot proper) these structs are not available. This
 | ||
| prevents them being used inadvertently. All usage must be bracketed with
 | ||
| `#if CONFIG_IS_ENABLED(OF_PLATDATA)`.
 | ||
| 
 | ||
| The dt-plat.c file contains the device declarations and is is built in
 | ||
| spl/dt-plat.c.
 | ||
| 
 | ||
| 
 | ||
| CONFIG options
 | ||
| ~~~~~~~~~~~~~~
 | ||
| 
 | ||
| Several CONFIG options are used to control the behaviour of of-platdata, all
 | ||
| available for both SPL and TPL:
 | ||
| 
 | ||
| OF_PLATDATA
 | ||
|    This is the main option which enables the of-platdata feature
 | ||
| 
 | ||
| OF_PLATDATA_PARENT
 | ||
|    This allows `device_get_parent()` to work. Without this, all devices exist as
 | ||
|    direct children of the root node. This option is highly desirable (if not
 | ||
|    always absolutely essential) for buses such as I2C.
 | ||
| 
 | ||
| OF_PLATDATA_INST
 | ||
|    This controls the instantiation of devices at build time. With it disabled,
 | ||
|    only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding
 | ||
|    in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and
 | ||
|    `DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at
 | ||
|    runtime.
 | ||
| 
 | ||
| OF_PLATDATA_NO_BIND
 | ||
|    This controls whether `device_bind()` is supported. It is enabled by default
 | ||
|    with OF_PLATDATA_INST since code-size reduction is really the main point of
 | ||
|    the feature. It can be disabled if needed but is not likely to be supported
 | ||
|    in the long term.
 | ||
| 
 | ||
| OF_PLATDATA_DRIVER_RT
 | ||
|    This controls whether the `struct driver_rt` records are used by U-Boot.
 | ||
|    Normally when a device is bound, U-Boot stores the device pointer in one of
 | ||
|    these records. There is one for every `struct driver_info` in the system,
 | ||
|    i.e. one for every device that is bound from those records. It provides a
 | ||
|    way to locate a device in the code and is used by
 | ||
|    `device_get_by_ofplat_idx()`. This option is always enabled with of-platdata,
 | ||
|    provided OF_PLATDATA_INST is not. In that case the records are useless since
 | ||
|    we don't have any `struct driver_info` records.
 | ||
| 
 | ||
| OF_PLATDATA_RT
 | ||
|    This controls whether the `struct udevice_rt` records are used by U-Boot.
 | ||
|    It moves the updatable fields from `struct udevice` (currently only `flags`)
 | ||
|    into a separate structure, allowing the records to be kept in read-only
 | ||
|    memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option
 | ||
|    also controls whether the private data is used in situ, or first copied into
 | ||
|    an allocated region. Again this is to allow the private data declared by
 | ||
|    dtoc-generated code to be in read-only memory. Note that access to private
 | ||
|    data must be done via accessor functions, such as `dev_get_priv()`, so that
 | ||
|    the relocation is handled.
 | ||
| 
 | ||
| READ_ONLY
 | ||
|    This indicates that the data generated by dtoc should not be modified. Only
 | ||
|    a few fields actually do get changed in U-Boot, such as device flags. This
 | ||
|    option causes those to move into an allocated space (see OF_PLATDATA_RT).
 | ||
|    Also, since updating doubly linked lists is generally impossible when some of
 | ||
|    the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled.
 | ||
| 
 | ||
| Data structures
 | ||
| ~~~~~~~~~~~~~~~
 | ||
| 
 | ||
| A few extra data structures are used with of-platdata:
 | ||
| 
 | ||
| `struct udevice_rt`
 | ||
|    Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds
 | ||
|    the flags for each device, so that `struct udevice` can remain unchanged by
 | ||
|    U-Boot, and potentially reside in read-only memory. Access to flags is then
 | ||
|    via functions like `dev_get_flags()` and `dev_or_flags()`. This data
 | ||
|    structure is allocated on start-up, where the private data is also copied.
 | ||
|    All flags values start at 0 and any changes are handled by `dev_or_flags()`
 | ||
|    and `dev_bic_flags()`. It would be more correct for the flags to be set to
 | ||
|    `DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since
 | ||
|    there is no code to bind/unbind devices and no code to allocate/free
 | ||
|    private data / platform data, it doesn't matter.
 | ||
| 
 | ||
| `struct driver_rt`
 | ||
|    Run-time information for `struct driver_info` records. When
 | ||
|    OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device
 | ||
|    created by each record. This is needed so that is it possible to locate a
 | ||
|    device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to
 | ||
|    get a reference to a particular `struct driver_info`, with `name` being the
 | ||
|    name of the devicetree node. This is very convenient. It is also fast, since
 | ||
|    no    searching or string comparison is needed. This data structure is
 | ||
|    allocated    on start-up, filled out by `device_bind()` and used by
 | ||
|    `device_get_by_ofplat_idx()`.
 | ||
| 
 | ||
| Other changes
 | ||
| ~~~~~~~~~~~~~
 | ||
| 
 | ||
| Some other changes are made with of-platdata:
 | ||
| 
 | ||
| Accessor functions
 | ||
|    Accessing private / platform data via functions such as `dev_get_priv()` has
 | ||
|    always been encouraged. With OF_PLATDATA_RT this is essential, since the
 | ||
|    `priv_` and `plat_`  (etc.) values point to the data generated by dtoc, not
 | ||
|    the read-write copy that is sometimes made on start-up. Changing the
 | ||
|    private / platform data  pointers has always been discouraged (the API is
 | ||
|    marked internal) but with OF_PLATDATA_RT this is not currently supported in
 | ||
|    general, since it assumes that all such pointers point to the relocated data.
 | ||
|    Note also that the renaming of struct members to have a trailing underscore
 | ||
|    was partly done to make people aware that they should not be accessed
 | ||
|    directly.
 | ||
| 
 | ||
| `gd->uclass_root_s`
 | ||
|    Normally U-Boot sets up the head of the uclass list here and makes
 | ||
|    `gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a
 | ||
|    declaration of `uclass_head` in `dt-uclass.c` since it needs to link the
 | ||
|    head node into the list. In that case, `gd->uclass_root_s` is not used and
 | ||
|    U-Boot just makes `gd->uclass_root` point to `uclass_head`.
 | ||
| 
 | ||
| `gd->dm_driver_rt`
 | ||
|    This holds a pointer to a list of `struct driver_rt` records, one for each
 | ||
|    `struct driver_info`. The list is in alphabetical order by the name used
 | ||
|    in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having
 | ||
|    an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is
 | ||
|    accessed via macros so that it can be used inside IS_ENABLED(), rather than
 | ||
|    requiring #ifdefs in the C code when it is not present.
 | ||
| 
 | ||
| `gd->dm_udevice_rt`
 | ||
|    This holds a pointer to a list of `struct udevice_rt` records, one for each
 | ||
|    `struct udevice`. The list is in alphabetical order by the name used
 | ||
|    in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by
 | ||
|    idx, with the first record having an index of 0. It is only used if
 | ||
|    OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be
 | ||
|    used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when
 | ||
|    it is not present.
 | ||
| 
 | ||
| `gd->dm_priv_base`
 | ||
|    When OF_PLATDATA_RT is enabled, the private/platform data for each device is
 | ||
|    copied into an allocated region by U-Boot on start-up. This points to that
 | ||
|    region. All calls to accessor functions (e.g. `dev_get_priv()`) then
 | ||
|    translate from the pointer provided by the caller (assumed to lie between
 | ||
|    `__priv_data_start` and `__priv_data_end`) to the new allocated region. This
 | ||
|    member is accessed via macros so that it can be used inside IS_ENABLED(),
 | ||
|    rather than required #ifdefs in the C code when it is not present.
 | ||
| 
 | ||
| `struct udevice->flags_`
 | ||
|    When OF_PLATDATA_RT is enabled, device flags are no-longer part of
 | ||
|    `struct udevice`, but are instead kept in `struct udevice_rt`, as described
 | ||
|    above. Flags are accessed via functions, such as `dev_get_flags()` and
 | ||
|    `dev_or_flags()`.
 | ||
| 
 | ||
| `struct udevice->node_`
 | ||
|    When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need
 | ||
|    for this field. It is removed, just to save space.
 | ||
| 
 | ||
| `DM_PHASE`
 | ||
|    This macro is used to indicate which phase of U-Boot a driver is intended
 | ||
|    for. See above for details.
 | ||
| 
 | ||
| `DM_HDR`
 | ||
|    This macro is used to indicate which header file dtoc should use to allow
 | ||
|    a driver declaration to compile correctly. See above for details.
 | ||
| 
 | ||
| `device_get_by_ofplat_idx()`
 | ||
|    There used to be a function called `device_get_by_driver_info()` which
 | ||
|    looked up a `struct driver_info` pointer and returned the `struct udevice`
 | ||
|    that was created from it. It was only available for use with of-platdata.
 | ||
|    This has been removed in favour of `device_get_by_ofplat_idx()` which uses
 | ||
|    `idx`, the index of the `struct driver_info` or `struct udevice` in the
 | ||
|    linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been
 | ||
|    updated to use this index instead of a pointer to `struct driver_info`.
 | ||
| 
 | ||
| `DM_DRVINFO_GET`
 | ||
|    This has been removed since we now use indexes to obtain a driver from
 | ||
|    `struct phandle_0_arg` and the like.
 | ||
| 
 | ||
| Two-pass binding
 | ||
|    The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated
 | ||
|    files so as to have parents declared ahead of children. This was convenient
 | ||
|    as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does
 | ||
|    not work as the idx value relies on using alphabetical order for everything,
 | ||
|    so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are
 | ||
|    then bound in order of idx, having no regard to parent/child relationships.
 | ||
|    For this reason, device binding now hapens in multiple passes, with parents
 | ||
|    being bound before their children. This is important so that children can
 | ||
|    find their parents in the bind() method if needed.
 | ||
| 
 | ||
| Root device
 | ||
|    The root device is generally bound by U-Boot but with OF_PLATDATA_INST it
 | ||
|    cannot be, since binding needs to be done at build time. So in this case
 | ||
|    dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and
 | ||
|    U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot
 | ||
|    generally ignores the root node and does not create a `U_BOOT_DRVINFO()`
 | ||
|    record for it. This means that the idx numbers used by `struct driver_info`
 | ||
|    (when OF_PLATDATA_INST is disabled) and the idx numbers used by
 | ||
|    `struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a
 | ||
|    root node and the other does not. This does not actually matter, since only
 | ||
|    one of them is actually used for any particular build, but it is worth
 | ||
|    keeping in mind if comparing index values and switching OF_PLATDATA_INST on
 | ||
|    and off.
 | ||
| 
 | ||
| `__priv_data_start` and `__priv_data_end`
 | ||
|    The private/platform data declared by dtoc is all collected together in
 | ||
|    a linker section and these symbols mark the start and end of it. This allows
 | ||
|    U-Boot to relocate the area to a new location if needed (with
 | ||
|    OF_PLATDATA_RT)
 | ||
| 
 | ||
| `dm_priv_to_rw()`
 | ||
|    This function converts a private- or platform-data pointer value generated by
 | ||
|    dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT
 | ||
|    is enabled, in which case it translates the address to the relocated
 | ||
|    region. See above for more information.
 | ||
| 
 | ||
| The dm_populate_phandle_data() function that was previous needed has now been
 | ||
| removed, since dtoc can address the drivers directly from dt-plat.c and does
 | ||
| not need to fix up things at runtime.
 | ||
| 
 | ||
| The pylibfdt Python module is used to access the devicetree.
 | ||
| 
 | ||
| 
 | ||
| Credits
 | ||
| -------
 | ||
| 
 | ||
| This is an implementation of an idea by Tom Rini <trini@konsulko.com>.
 | ||
| 
 | ||
| 
 | ||
| Future work
 | ||
| -----------
 | ||
| - Consider programmatically reading binding files instead of devicetree
 | ||
|   contents
 | ||
| - Allow IS_ENABLED() to be used in the C code instead of #if
 | ||
| 
 | ||
| 
 | ||
| .. Simon Glass <sjg@chromium.org>
 | ||
| .. Google, Inc
 | ||
| .. 6/6/16
 | ||
| .. Updated Independence Day 2016
 | ||
| .. Updated 1st October 2020
 | ||
| .. Updated 5th February 2021
 |