From 45dbb4dd23a0bcbeee66540e5baa85d549ac95fb Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 18 Jul 2019 00:33:55 -0700 Subject: doc: driver-model: Convert of-plat.txt to reST Convert plain text documentation to reStructuredText format and add it to Sphinx TOC tree. No essential content change. Signed-off-by: Bin Meng --- doc/driver-model/index.rst | 1 + doc/driver-model/of-plat.rst | 341 +++++++++++++++++++++++++++++++++++++++++++ doc/driver-model/of-plat.txt | 324 ---------------------------------------- 3 files changed, 342 insertions(+), 324 deletions(-) create mode 100644 doc/driver-model/of-plat.rst delete mode 100644 doc/driver-model/of-plat.txt diff --git a/doc/driver-model/index.rst b/doc/driver-model/index.rst index ff9b18350c7..d1c19a41036 100644 --- a/doc/driver-model/index.rst +++ b/doc/driver-model/index.rst @@ -12,3 +12,4 @@ Driver Model i2c-howto livetree migration + of-plat diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst new file mode 100644 index 00000000000..0d3cd8c01e2 --- /dev/null +++ b/doc/driver-model/of-plat.rst @@ -0,0 +1,341 @@ +.. 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 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 reference that data in a U_BOOT_DEVICE() declaration. This +bypasses the use of device tree completely, effectively creating a parallel +configuration mechanism. But it is an available option for SPL. + +As an alternative, a new 'of-platdata' feature is provided. This converts the +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. + +Note: Quite a bit of thought has gone into the design of this feature. +However it still has many rough edges and comments and suggestions are +strongly encouraged! Quite possibly there is a much better approach. + + +Caveats +------- + +There are many problems with this features. It should only be used when +strictly necessary. Notable problems 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 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 name, and a likely a different 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. + + - Correct relations between nodes are not implemented. This means that + parent/child relations (like bus device iteration) do not work yet. + Some phandles (those that are recognised as such) are converted into + a pointer 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. + + +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 new tool called 'dtoc' converts a device tree file either into a set of +struct declarations, one for each compatible node, and a set of +U_BOOT_DEVICE() 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 = ; + 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 declaration: + +.. code-block:: c + + 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, + .platdata_size = sizeof(dtv_dwmmc_at_ff0c0000), + }; + +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_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. + +Note that for the platform data to be matched with a driver, the 'name' +property of the U_BOOT_DEVICE() 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. + +Where a node has multiple compatible strings, a #define is used 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 device tree, since the of-platdata +structure is not available when device tree is used. It would make no sense +to use this structure if device tree were available, since the structure has +all the limitations metioned in caveats above. + +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 +ofdata_to_platdata() method since this is not called when platform data is +already present. + + +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 convert the platdata struct in its probe() method. The +existing device tree decoding logic should be kept in the +ofdata_to_platdata() method and wrapped with #if. + +For example: + +.. code-block:: c + + #include + + struct mmc_platdata { + #if CONFIG_IS_ENABLED(SPL_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 device tree (or the C structures when of-platdata is used). + */ + int fifo_depth; + }; + + static int mmc_ofdata_to_platdata(struct udevice *dev) + { + #if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA) + /* Decode the device tree data */ + struct mmc_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(dev); + + #if CONFIG_IS_ENABLED(SPL_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", + .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 +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 platdata_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 device tree is used, +the platform data is allocated, and starts zeroed. In this case the +ofdata_to_platdata() method should still set up the platform data (and the +of-platdata struct will not be present). + +SPL must use either of-platdata or device tree. Drivers cannot use both at +the same time, but they must support device tree. Supporting of-platdata is +optional. + +The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled, +since the device-tree 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. + +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. All usage must be bracketed with +#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA). + +The dt-platdata.c file contains the device declarations and is is built in +spl/dt-platdata.c. + +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. If this is not +found then the Python model is not used and a fallback is used instead, which +makes use of fdtget. + + +Credits +------- + +This is an implementation of an idea by Tom Rini . + + +Future work +----------- +- Consider programmatically reading binding files instead of device tree + contents +- Complete the phandle feature +- Move to using a full Python libfdt module + + +.. Simon Glass +.. Google, Inc +.. 6/6/16 +.. Updated Independence Day 2016 diff --git a/doc/driver-model/of-plat.txt b/doc/driver-model/of-plat.txt deleted file mode 100644 index 0109ec56c35..00000000000 --- a/doc/driver-model/of-plat.txt +++ /dev/null @@ -1,324 +0,0 @@ -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 reference that data in a U_BOOT_DEVICE() declaration. This -bypasses the use of device tree completely, effectively creating a parallel -configuration mechanism. But it is an available option for SPL. - -As an alternative, a new 'of-platdata' feature is provided. This converts the -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. - -Note: Quite a bit of thought has gone into the design of this feature. -However it still has many rough edges and comments and suggestions are -strongly encouraged! Quite possibly there is a much better approach. - - -Caveats -------- - -There are many problems with this features. It should only be used when -strictly necessary. Notable problems 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 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 name, and a likely a different 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. - - - Correct relations between nodes are not implemented. This means that - parent/child relations (like bus device iteration) do not work yet. - Some phandles (those that are recognised as such) are converted into - a pointer 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. - - -How it works ------------- - -The feature is enabled by CONFIG OF_PLATDATA. This is only available in -SPL/TPL and should be tested with: - - #if CONFIG_IS_ENABLED(OF_PLATDATA) - -A new tool called 'dtoc' converts a device tree file either into a set of -struct declarations, one for each compatible node, and 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 = ; - 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_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 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, - .platdata_size = sizeof(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. - -Note that for the platform data to be matched with a driver, the 'name' -property of the U_BOOT_DEVICE() 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. - -Where a node has multiple compatible strings, a #define is used to make them -equivalent, e.g.: - -#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 device tree, since the of-platdata -structure is not available when device tree is used. It would make no sense -to use this structure if device tree were available, since the structure has -all the limitations metioned in caveats above. - -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 -ofdata_to_platdata() method since this is not called when platform data is -already present. - - -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 convert the platdata struct in its probe() method. The -existing device tree decoding logic should be kept in the -ofdata_to_platdata() method and wrapped with #if. - -For example: - - #include - - struct mmc_platdata { - #if CONFIG_IS_ENABLED(SPL_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 device tree (or the C structures when of-platdata is used). - */ - int fifo_depth; - }; - - static int mmc_ofdata_to_platdata(struct udevice *dev) - { - #if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA) - /* Decode the device tree data */ - struct mmc_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(dev); - - #if CONFIG_IS_ENABLED(SPL_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", - .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 -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 platdata_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 device tree is used, -the platform data is allocated, and starts zeroed. In this case the -ofdata_to_platdata() method should still set up the platform data (and the -of-platdata struct will not be present). - -SPL must use either of-platdata or device tree. Drivers cannot use both at -the same time, but they must support device tree. Supporting of-platdata is -optional. - -The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled, -since the device-tree 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. - -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. All usage must be bracketed with -#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA). - -The dt-platdata.c file contains the device declarations and is is built in -spl/dt-platdata.c. - -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. If this is not -found then the Python model is not used and a fallback is used instead, which -makes use of fdtget. - - -Credits -------- - -This is an implementation of an idea by Tom Rini . - - -Future work ------------ -- Consider programmatically reading binding files instead of device tree - contents -- Complete the phandle feature -- Move to using a full Python libfdt module - --- -Simon Glass -Google, Inc -6/6/16 -Updated Independence Day 2016 -- cgit v1.2.3