mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-18 21:21:37 +00:00
dm: Update the of-platdata README for the new features
Revise the content based on the v2 additions. This is kept as a separate patch to avoid confusing those who have already reviewed the v1 series. Signed-off-by: Simon Glass <sjg@chromium.org> Suggested-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
parent
b979d3d4c5
commit
1269625177
1 changed files with 76 additions and 34 deletions
|
@ -19,24 +19,28 @@ 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.
|
case the overhead of device tree access may be too great.
|
||||||
|
|
||||||
It is possible to create platform data manually by defining C structures
|
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
|
for it, and reference that data in a U_BOOT_DEVICE() declaration. This
|
||||||
bypasses the use of device tree completely, but is an available option for
|
bypasses the use of device tree completely, effectively creating a parallel
|
||||||
SPL.
|
configuration mechanism. But it is an available option for SPL.
|
||||||
|
|
||||||
As an alternative, a new 'of-platdata' feature is provided. This converts
|
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.
|
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
|
This saves the 3KB of code overhead and perhaps a few hundred more bytes due
|
||||||
to more efficient storage of the data.
|
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
|
Caveats
|
||||||
-------
|
-------
|
||||||
|
|
||||||
There are many problems with this features. It should only be used when
|
There are many problems with this features. It should only be used when
|
||||||
stricly necessary. Notable problems include:
|
strictly necessary. Notable problems include:
|
||||||
|
|
||||||
- Device tree does not describe data types but the C code must define a
|
- Device tree does not describe data types. But the C code must define a
|
||||||
type for each property. Thesee are guessed using heuristics which
|
type for each property. These are guessed using heuristics which
|
||||||
are wrong in several fairly common cases. For example an 8-byte value
|
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
|
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
|
boolean value that is not present means 'false', but cannot be
|
||||||
|
@ -45,14 +49,15 @@ stricly necessary. Notable problems include:
|
||||||
|
|
||||||
- Naming of nodes and properties is automatic. This means that they follow
|
- 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
|
the naming in the device tree, which may result in C identifiers that
|
||||||
look a bit strange
|
look a bit strange.
|
||||||
|
|
||||||
- It is not possible to find a value given a property name. Code must use
|
- 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 associated C member variable directly in the code. This makes
|
||||||
the code less robust in the face of device-tree changes. It also
|
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
|
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
|
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.
|
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
|
- 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
|
must use the same structure to access the data. Since a driver
|
||||||
|
@ -112,7 +117,6 @@ struct dtd_rockchip_rk3288_dw_mshc {
|
||||||
fdt32_t interrupts[3];
|
fdt32_t interrupts[3];
|
||||||
fdt32_t num_slots;
|
fdt32_t num_slots;
|
||||||
fdt32_t reg[2];
|
fdt32_t reg[2];
|
||||||
bool u_boot_dm_pre_reloc;
|
|
||||||
fdt32_t vmmc_supply;
|
fdt32_t vmmc_supply;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,7 +129,10 @@ static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
|
||||||
.clock_freq_min_max = {0x61a80, 0x8f0d180},
|
.clock_freq_min_max = {0x61a80, 0x8f0d180},
|
||||||
.vmmc_supply = 0xb,
|
.vmmc_supply = 0xb,
|
||||||
.num_slots = 0x1,
|
.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}},
|
.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,
|
.cap_mmc_highspeed = true,
|
||||||
.disable_wp = true,
|
.disable_wp = true,
|
||||||
.bus_width = 0x4,
|
.bus_width = 0x4,
|
||||||
|
@ -136,6 +143,7 @@ static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
|
||||||
U_BOOT_DEVICE(dwmmc_at_ff0c0000) = {
|
U_BOOT_DEVICE(dwmmc_at_ff0c0000) = {
|
||||||
.name = "rockchip_rk3288_dw_mshc",
|
.name = "rockchip_rk3288_dw_mshc",
|
||||||
.platdata = &dtv_dwmmc_at_ff0c0000,
|
.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
|
The device is then instantiated at run-time and the platform data can be
|
||||||
|
@ -149,15 +157,31 @@ platform data in the driver. The ofdata_to_platdata() method should
|
||||||
therefore do nothing in such a driver.
|
therefore do nothing in such a driver.
|
||||||
|
|
||||||
|
|
||||||
|
Converting of-platdata to a useful form
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
Of course it would be possible use the of-platdata directly in your driver
|
||||||
|
whenever configuration information is required. However this meands 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
|
How to structure your driver
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
Drivers should always support device tree as an option. The of-platdata
|
Drivers should always support device tree as an option. The of-platdata
|
||||||
feature is intended as a add-on to existing drivers.
|
feature is intended as a add-on to existing drivers.
|
||||||
|
|
||||||
Your driver should directly access the platdata struct in its probe()
|
Your driver should convert the platdata struct in its probe() method. The
|
||||||
method. The existing device tree decoding logic should be kept in the
|
existing device tree decoding logic should be kept in the
|
||||||
ofdata_to_platdata() and wrapped with #ifdef.
|
ofdata_to_platdata() method and wrapped with #if.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -165,13 +189,12 @@ For example:
|
||||||
|
|
||||||
struct mmc_platdata {
|
struct mmc_platdata {
|
||||||
#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
||||||
/* Put this first */
|
/* Put this first since driver model will copy the data here */
|
||||||
struct dtd_mmc dtplat;
|
struct dtd_mmc dtplat;
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* Other fields can go here, to be filled in by decoding from
|
* Other fields can go here, to be filled in by decoding from
|
||||||
* the device tree. They will point to random memory in the
|
* the device tree (or the C structures when of-platdata is used).
|
||||||
* of-plat case.
|
|
||||||
*/
|
*/
|
||||||
int fifo_depth;
|
int fifo_depth;
|
||||||
};
|
};
|
||||||
|
@ -179,6 +202,7 @@ For example:
|
||||||
static int mmc_ofdata_to_platdata(struct udevice *dev)
|
static int mmc_ofdata_to_platdata(struct udevice *dev)
|
||||||
{
|
{
|
||||||
#if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
#if !CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
||||||
|
/* Decode the device tree data */
|
||||||
struct mmc_platdata *plat = dev_get_platdata(dev);
|
struct mmc_platdata *plat = dev_get_platdata(dev);
|
||||||
const void *blob = gd->fdt_blob;
|
const void *blob = gd->fdt_blob;
|
||||||
int node = dev->of_offset;
|
int node = dev->of_offset;
|
||||||
|
@ -192,15 +216,15 @@ For example:
|
||||||
static int mmc_probe(struct udevice *dev)
|
static int mmc_probe(struct udevice *dev)
|
||||||
{
|
{
|
||||||
struct mmc_platdata *plat = dev_get_platdata(dev);
|
struct mmc_platdata *plat = dev_get_platdata(dev);
|
||||||
|
|
||||||
#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
#if CONFIG_IS_ENABLED(SPL_OF_PLATDATA)
|
||||||
|
/* Decode the of-platdata from the C structures */
|
||||||
struct dtd_mmc *dtplat = &plat->dtplat;
|
struct dtd_mmc *dtplat = &plat->dtplat;
|
||||||
|
|
||||||
/* Set up the device from the dtplat data */
|
plat->fifo_depth = dtplat->fifo_depth;
|
||||||
writel(dtplat->fifo_depth, ...)
|
#endif
|
||||||
#else
|
|
||||||
/* Set up the device from the plat data */
|
/* Set up the device from the plat data */
|
||||||
writel(plat->fifo_depth, ...)
|
writel(plat->fifo_depth, ...)
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct udevice_id mmc_ids[] = {
|
static const struct udevice_id mmc_ids[] = {
|
||||||
|
@ -220,15 +244,26 @@ For example:
|
||||||
|
|
||||||
|
|
||||||
In the case where SPL_OF_PLATDATA is enabled, platdata_auto_alloc_size is
|
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
|
still used to allocate space for the platform data. This is different from
|
||||||
where device tree is used, the platform data is allocated, and starts
|
the normal behaviour and is triggered by the use of of-platdata (strictly
|
||||||
zeroed. In this case the ofdata_to_platdata() method should set up the
|
speaking it is a non-zero platdata_size which triggers this).
|
||||||
platform data.
|
|
||||||
|
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.
|
||||||
|
|
||||||
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,
|
The device tree becomes in accessible when CONFIG_SPL_OF_PLATDATA is enabled,
|
||||||
since the device-tree access code is not compiled in.
|
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
|
Internals
|
||||||
---------
|
---------
|
||||||
|
@ -236,7 +271,8 @@ Internals
|
||||||
The dt-structs.h file includes the generated file
|
The dt-structs.h file includes the generated file
|
||||||
(include/generated//dt-structs.h) if CONFIG_SPL_OF_PLATDATA is enabled.
|
(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
|
Otherwise (such as in U-Boot proper) these structs are not available. This
|
||||||
prevents them being used inadvertently.
|
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
|
The dt-platdata.c file contains the device declarations and is is built in
|
||||||
spl/dt-platdata.c.
|
spl/dt-platdata.c.
|
||||||
|
@ -249,20 +285,26 @@ yet implemented, however.
|
||||||
The beginnings of a libfdt Python module are provided. So far this only
|
The beginnings of a libfdt Python module are provided. So far this only
|
||||||
implements a subset of the features.
|
implements a subset of the features.
|
||||||
|
|
||||||
The 'swig' tool is needed to build the libfdt Python module.
|
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 <trini@konsulko.com>.
|
||||||
|
|
||||||
|
|
||||||
Future work
|
Future work
|
||||||
-----------
|
-----------
|
||||||
- Add unit tests
|
|
||||||
- Add a sandbox_spl functional test
|
|
||||||
- Consider programmatically reading binding files instead of device tree
|
- Consider programmatically reading binding files instead of device tree
|
||||||
contents
|
contents
|
||||||
- Drop the device tree data from the SPL image
|
|
||||||
- Complete the phandle feature
|
- Complete the phandle feature
|
||||||
- Get this running on a Rockchip board
|
|
||||||
- Move to using a full Python libfdt module
|
- Move to using a full Python libfdt module
|
||||||
|
|
||||||
--
|
--
|
||||||
Simon Glass <sjg@chromium.org>
|
Simon Glass <sjg@chromium.org>
|
||||||
|
Google, Inc
|
||||||
6/6/16
|
6/6/16
|
||||||
|
Updated Independence Day 2016
|
||||||
|
|
Loading…
Add table
Reference in a new issue