dm: Add a README for of-platdata
authorSimon Glass <sjg@chromium.org>
Mon, 4 Jul 2016 17:58:07 +0000 (11:58 -0600)
committerSimon Glass <sjg@chromium.org>
Fri, 15 Jul 2016 02:40:24 +0000 (20:40 -0600)
Add documentation on how this works, including the benefits and drawbacks.

Signed-off-by: Simon Glass <sjg@chromium.org>
doc/driver-model/of-plat.txt [new file with mode: 0644]

diff --git a/doc/driver-model/of-plat.txt b/doc/driver-model/of-plat.txt
new file mode 100644 (file)
index 0000000..96b9b46
--- /dev/null
@@ -0,0 +1,268 @@
+Driver Model 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 device tree 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 device tree 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 device tree access may be too great.
+
+It is possible to create platform data manually by defining C structures
+for it, and referencing that data in a U_BOOT_DEVICE() declaration. This
+bypasses the use of device tree completely, but is an available option for
+SPL.
+
+As an alternative, a new 'of-platdata' feature is provided. This converts
+device tree 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.
+
+
+Caveats
+-------
+
+There are many problems with this features. It should only be used when
+stricly necessary. Notable problems include:
+
+   - Device tree does not describe data types but the C code must define a
+        type for each property. Thesee 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 device tree file.
+
+   - Naming of nodes and properties is automatic. This means that they follow
+        the naming in the device tree, 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 device-tree changes. It also
+        makes it very unlikely that your driver code will be useful for more
+        than one SoC. Even if the code is common, each SoC will end up with
+        a different C struct and format for the platform data.
+
+   - 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 device tree it must use #ifdef to separate
+        out this code, since the structures are only available in SPL.
+
+
+How it works
+------------
+
+The feature is enabled by CONFIG SPL_OF_PLATDATA. This is only available
+in SPL and should be tested with:
+
+        #if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
+
+A new tool called 'dtoc' converts a device tree file either into a set of
+struct declarations, one for each compatible node, or a set of
+U_BOOT_DEVICE() declarations along with the actual platform data for each
+device. As an example, consider this MMC node:
+
+        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:
+
+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_2_cell clocks[4];
+        bool            disable_wp;
+        fdt32_t         fifo_depth;
+        fdt32_t         interrupts[3];
+        fdt32_t         num_slots;
+        fdt32_t         reg[2];
+        bool            u_boot_dm_pre_reloc;
+        fdt32_t         vmmc_supply;
+};
+
+and the following device declaration:
+
+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                 = {{&dtv_clock_controller_at_ff760000, 456}, {&dtv_clock_controller_at_ff760000, 68}, {&dtv_clock_controller_at_ff760000, 114}, {&dtv_clock_controller_at_ff760000, 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_DEVICE(dwmmc_at_ff0c0000) = {
+        .name           = "rockchip_rk3288_dw_mshc",
+        .platdata       = &dtv_dwmmc_at_ff0c0000,
+};
+
+The device is then instantiated at run-time and the platform data can be
+accessed using:
+
+        struct udevice *dev;
+        struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_platdata(dev);
+
+This avoids the code overhead of converting the device tree data to
+platform data in the driver. The ofdata_to_platdata() method should
+therefore do nothing in such a driver.
+
+
+How to structure your driver
+----------------------------
+
+Drivers should always support device tree as an option. The of-platdata
+feature is intended as a add-on to existing drivers.
+
+Your driver should directly access the platdata struct in its probe()
+method. The existing device tree decoding logic should be kept in the
+ofdata_to_platdata() and wrapped with #ifdef.
+
+For example:
+
+    #include <dt-structs.h>
+
+    struct mmc_platdata {
+    #if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
+            /* Put this first */
+            struct dtd_mmc dtplat;
+    #endif
+            /*
+             * Other fields can go here, to be filled in by decoding from
+             * the device tree. They will point to random memory in the
+             * of-plat case.
+             */
+            int fifo_depth;
+    };
+
+    static int mmc_ofdata_to_platdata(struct udevice *dev)
+    {
+    #if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
+            struct mmc_platdata *plat = dev_get_platdata(dev);
+            const void *blob = gd->fdt_blob;
+            int node = dev->of_offset;
+
+            plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0);
+    #endif
+
+            return 0;
+    }
+
+    static int mmc_probe(struct udevice *dev)
+    {
+            struct mmc_platdata *plat = dev_get_platdata(dev);
+    #if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
+            struct dtd_mmc *dtplat = &plat->dtplat;
+
+            /* Set up the device from the dtplat data */
+            writel(dtplat->fifo_depth, ...)
+    #else
+            /* Set up the device from the plat data */
+            writel(plat->fifo_depth, ...)
+    #endif
+    }
+
+    static const struct udevice_id mmc_ids[] = {
+            { .compatible = "vendor,mmc" },
+            { }
+    };
+
+    U_BOOT_DRIVER(mmc_drv) = {
+            .name           = "mmc",
+            .id             = UCLASS_MMC,
+            .of_match       = mmc_ids,
+            .ofdata_to_platdata = mmc_ofdata_to_platdata,
+            .probe          = mmc_probe,
+            .priv_auto_alloc_size = sizeof(struct mmc_priv),
+            .platdata_auto_alloc_size = sizeof(struct mmc_platdata),
+    };
+
+
+In the case where SPL_OF_PLATDATA is enabled, platdata_auto_alloc_size is
+ignored, and the platform data points to the C structure data. In the case
+where device tree is used, the platform data is allocated, and starts
+zeroed. In this case the ofdata_to_platdata() method should set up the
+platform data.
+
+SPL must use either of-platdata or device tree. Drivers cannot use both.
+The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled,
+since the device-tree access code is not compiled in.
+
+
+Internals
+---------
+
+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.
+
+The dt-platdata.c file contains the device declarations and is is built in
+spl/dt-platdata.c.
+
+Some phandles (thsoe that are recognised as such) are converted into
+points to platform data. This pointer can potentially be used to access the
+referenced device (by searching for the pointer value). This feature is not
+yet implemented, however.
+
+The beginnings of a libfdt Python module are provided. So far this only
+implements a subset of the features.
+
+The 'swig' tool is needed to build the libfdt Python module.
+
+
+Future work
+-----------
+- Add unit tests
+- Add a sandbox_spl functional test
+- Consider programmatically reading binding files instead of device tree
+     contents
+- Drop the device tree data from the SPL image
+- Complete the phandle feature
+- Get this running on a Rockchip board
+- Move to using a full Python libfdt module
+
+--
+Simon Glass <sjg@chromium.org>
+6/6/16