aboutsummaryrefslogtreecommitdiff
path: root/doc/driver-model/of-plat.rst
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2021-03-18 20:25:12 +1300
committerSimon Glass <sjg@chromium.org>2021-03-26 17:03:10 +1300
commit5ce319133b2364e3283c3cde7a269681ff8431af (patch)
tree2badbcc08d4e19d9c1319deac066c54559e919c8 /doc/driver-model/of-plat.rst
parentd1ceeeff6c2ee1e55b7140654c8d6de44b60dab6 (diff)
downloadu-boot-5ce319133b2364e3283c3cde7a269681ff8431af.zip
u-boot-5ce319133b2364e3283c3cde7a269681ff8431af.tar.gz
u-boot-5ce319133b2364e3283c3cde7a269681ff8431af.tar.bz2
doc: Move driver model docs under develop/
These docs are useful for developers, not users. Move them under that section. Suggested-by: Heinrich Schuchardt <xypron.glpk@gmx.de> Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'doc/driver-model/of-plat.rst')
-rw-r--r--doc/driver-model/of-plat.rst913
1 files changed, 0 insertions, 913 deletions
diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst
deleted file mode 100644
index 74f1932..0000000
--- a/doc/driver-model/of-plat.rst
+++ /dev/null
@@ -1,913 +0,0 @@
-.. 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_PLATDATA)
- /* 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);
- #endif
-
- 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 */
- };
-
-
-
-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