]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
dm: doc: Add documentation for of-platdata-inst
authorSimon Glass <sjg@chromium.org>
Mon, 15 Mar 2021 04:25:43 +0000 (17:25 +1300)
committerSimon Glass <sjg@chromium.org>
Fri, 26 Mar 2021 04:03:09 +0000 (17:03 +1300)
Add a description of the new features, along with internal technical
documentation.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
doc/driver-model/of-plat.rst

index 3c9ba01b3020204c21820d4e86dfee4542737251..74f1932473ba2d3ae99afd31fcc24739abcf5bcc 100644 (file)
@@ -286,9 +286,394 @@ 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
@@ -298,6 +683,208 @@ prevents them being used inadvertently. All usage must be bracketed with
 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.