mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-26 12:27:12 +00:00
fdt: add new fdt address parsing functions
fdtdec_get_addr_size() hard-codes the number of cells used to represent an address or size in DT. This is incorrect in many cases depending on the DT binding for a particular node or property (e.g. it is incorrect for the "reg" property). In most cases, DT parsing code must use the properties #address-cells and #size-cells to parse addres properties. This change splits up the implementation of fdtdec_get_addr_size() so that the core logic can be used for both hard-coded and non-hard-coded cases. Various wrapper functions are implemented that support cases where hard-coded cell counts should or should not be used, and where the client does and doesn't know the parent node ID that contains the properties #address-cells and #size-cells. dev_get_addr() is updated to use the new functions. Core functionality in fdtdec_get_addr_size_fixed() is widely tested via fdtdec_get_addr_size(). I tested fdtdec_get_addr_size_auto_noparent() and dev_get_addr() by manually modifying the Tegra I2C driver to invoke them. Much of the core implementation of fdtdec_get_addr_size_fixed(), fdtdec_get_addr_size_auto_parent(), and fdtdec_get_addr_size_auto_noparent() comes from Thierry Reding's previous commit "fdt: Fix fdtdec_get_addr_size() for 64-bit". Based-on-work-by: Thierry Reding <treding@nvidia.com> Cc: Thierry Reding <treding@nvidia.com> Cc: Simon Glass <sjg@chromium.org> Cc: Michal Suchanek <hramrach@gmail.com> Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Simon Glass <sjg@chromium.org> Dropped #define DEBUG at the top of fdtdec.c: Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
850f788709
commit
02464e386b
3 changed files with 202 additions and 32 deletions
|
@ -581,7 +581,10 @@ fdt_addr_t dev_get_addr(struct udevice *dev)
|
||||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||||
fdt_addr_t addr;
|
fdt_addr_t addr;
|
||||||
|
|
||||||
addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
|
addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
|
||||||
|
dev->parent->of_offset,
|
||||||
|
dev->of_offset, "reg",
|
||||||
|
0, NULL);
|
||||||
if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
|
if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
|
||||||
if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS)
|
if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS)
|
||||||
addr = simple_bus_translate(dev->parent, addr);
|
addr = simple_bus_translate(dev->parent, addr);
|
||||||
|
|
111
include/fdtdec.h
111
include/fdtdec.h
|
@ -295,10 +295,90 @@ int fdtdec_next_compatible(const void *blob, int node,
|
||||||
int fdtdec_next_compatible_subnode(const void *blob, int node,
|
int fdtdec_next_compatible_subnode(const void *blob, int node,
|
||||||
enum fdt_compat_id id, int *depthp);
|
enum fdt_compat_id id, int *depthp);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Look up an address property in a node and return it as an address.
|
* Look up an address property in a node and return the parsed address, and
|
||||||
* The property must hold either one address with no trailing data or
|
* optionally the parsed size.
|
||||||
* one address with a length. This is only tested on 32-bit machines.
|
*
|
||||||
|
* This variant assumes a known and fixed number of cells are used to
|
||||||
|
* represent the address and size.
|
||||||
|
*
|
||||||
|
* You probably don't want to use this function directly except to parse
|
||||||
|
* non-standard properties, and never to parse the "reg" property. Instead,
|
||||||
|
* use one of the "auto" variants below, which automatically honor the
|
||||||
|
* #address-cells and #size-cells properties in the parent node.
|
||||||
|
*
|
||||||
|
* @param blob FDT blob
|
||||||
|
* @param node node to examine
|
||||||
|
* @param prop_name name of property to find
|
||||||
|
* @param index which address to retrieve from a list of addresses. Often 0.
|
||||||
|
* @param na the number of cells used to represent an address
|
||||||
|
* @param ns the number of cells used to represent a size
|
||||||
|
* @param sizep a pointer to store the size into. Use NULL if not required
|
||||||
|
* @return address, if found, or FDT_ADDR_T_NONE if not
|
||||||
|
*/
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node,
|
||||||
|
const char *prop_name, int index, int na, int ns,
|
||||||
|
fdt_size_t *sizep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up an address property in a node and return the parsed address, and
|
||||||
|
* optionally the parsed size.
|
||||||
|
*
|
||||||
|
* This variant automatically determines the number of cells used to represent
|
||||||
|
* the address and size by parsing the provided parent node's #address-cells
|
||||||
|
* and #size-cells properties.
|
||||||
|
*
|
||||||
|
* @param blob FDT blob
|
||||||
|
* @param parent parent node of @node
|
||||||
|
* @param node node to examine
|
||||||
|
* @param prop_name name of property to find
|
||||||
|
* @param index which address to retrieve from a list of addresses. Often 0.
|
||||||
|
* @param sizep a pointer to store the size into. Use NULL if not required
|
||||||
|
* @return address, if found, or FDT_ADDR_T_NONE if not
|
||||||
|
*/
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent,
|
||||||
|
int node, const char *prop_name, int index, fdt_size_t *sizep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up an address property in a node and return the parsed address, and
|
||||||
|
* optionally the parsed size.
|
||||||
|
*
|
||||||
|
* This variant automatically determines the number of cells used to represent
|
||||||
|
* the address and size by parsing the parent node's #address-cells
|
||||||
|
* and #size-cells properties. The parent node is automatically found.
|
||||||
|
*
|
||||||
|
* The automatic parent lookup implemented by this function is slow.
|
||||||
|
* Consequently, fdtdec_get_addr_size_auto_parent() should be used where
|
||||||
|
* possible.
|
||||||
|
*
|
||||||
|
* @param blob FDT blob
|
||||||
|
* @param parent parent node of @node
|
||||||
|
* @param node node to examine
|
||||||
|
* @param prop_name name of property to find
|
||||||
|
* @param index which address to retrieve from a list of addresses. Often 0.
|
||||||
|
* @param sizep a pointer to store the size into. Use NULL if not required
|
||||||
|
* @return address, if found, or FDT_ADDR_T_NONE if not
|
||||||
|
*/
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node,
|
||||||
|
const char *prop_name, int index, fdt_size_t *sizep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up an address property in a node and return the parsed address.
|
||||||
|
*
|
||||||
|
* This variant hard-codes the number of cells used to represent the address
|
||||||
|
* and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also
|
||||||
|
* always returns the first address value in the property (index 0).
|
||||||
|
*
|
||||||
|
* Use of this function is not recommended due to the hard-coding of cell
|
||||||
|
* counts. There is no programmatic validation that these hard-coded values
|
||||||
|
* actually match the device tree content in any way at all. This assumption
|
||||||
|
* can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately
|
||||||
|
* set in the U-Boot build and exercising strict control over DT content to
|
||||||
|
* ensure use of matching #address-cells/#size-cells properties. However, this
|
||||||
|
* approach is error-prone; those familiar with DT will not expect the
|
||||||
|
* assumption to exist, and could easily invalidate it. If the assumption is
|
||||||
|
* invalidated, this function will not report the issue, and debugging will
|
||||||
|
* be required. Instead, use fdtdec_get_addr_size_auto_parent().
|
||||||
*
|
*
|
||||||
* @param blob FDT blob
|
* @param blob FDT blob
|
||||||
* @param node node to examine
|
* @param node node to examine
|
||||||
|
@ -308,14 +388,29 @@ int fdtdec_next_compatible_subnode(const void *blob, int node,
|
||||||
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
||||||
const char *prop_name);
|
const char *prop_name);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Look up an address property in a node and return it as an address.
|
* Look up an address property in a node and return the parsed address, and
|
||||||
* The property must hold one address with a length. This is only tested
|
* optionally the parsed size.
|
||||||
* on 32-bit machines.
|
*
|
||||||
|
* This variant hard-codes the number of cells used to represent the address
|
||||||
|
* and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also
|
||||||
|
* always returns the first address value in the property (index 0).
|
||||||
|
*
|
||||||
|
* Use of this function is not recommended due to the hard-coding of cell
|
||||||
|
* counts. There is no programmatic validation that these hard-coded values
|
||||||
|
* actually match the device tree content in any way at all. This assumption
|
||||||
|
* can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately
|
||||||
|
* set in the U-Boot build and exercising strict control over DT content to
|
||||||
|
* ensure use of matching #address-cells/#size-cells properties. However, this
|
||||||
|
* approach is error-prone; those familiar with DT will not expect the
|
||||||
|
* assumption to exist, and could easily invalidate it. If the assumption is
|
||||||
|
* invalidated, this function will not report the issue, and debugging will
|
||||||
|
* be required. Instead, use fdtdec_get_addr_size_auto_parent().
|
||||||
*
|
*
|
||||||
* @param blob FDT blob
|
* @param blob FDT blob
|
||||||
* @param node node to examine
|
* @param node node to examine
|
||||||
* @param prop_name name of property to find
|
* @param prop_name name of property to find
|
||||||
|
* @param sizep a pointer to store the size into. Use NULL if not required
|
||||||
* @return address, if found, or FDT_ADDR_T_NONE if not
|
* @return address, if found, or FDT_ADDR_T_NONE if not
|
||||||
*/
|
*/
|
||||||
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
||||||
|
|
118
lib/fdtdec.c
118
lib/fdtdec.c
|
@ -86,32 +86,104 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)
|
||||||
return compat_names[id];
|
return compat_names[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node,
|
||||||
|
const char *prop_name, int index, int na, int ns,
|
||||||
|
fdt_size_t *sizep)
|
||||||
|
{
|
||||||
|
const fdt32_t *prop, *prop_end;
|
||||||
|
const fdt32_t *prop_addr, *prop_size, *prop_after_size;
|
||||||
|
int len;
|
||||||
|
fdt_addr_t addr;
|
||||||
|
|
||||||
|
debug("%s: %s: ", __func__, prop_name);
|
||||||
|
|
||||||
|
if (na > (sizeof(fdt_addr_t) / sizeof(fdt32_t))) {
|
||||||
|
debug("(na too large for fdt_addr_t type)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns > (sizeof(fdt_size_t) / sizeof(fdt32_t))) {
|
||||||
|
debug("(ns too large for fdt_size_t type)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = fdt_getprop(blob, node, prop_name, &len);
|
||||||
|
if (!prop) {
|
||||||
|
debug("(not found)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
prop_end = prop + (len / sizeof(*prop));
|
||||||
|
|
||||||
|
prop_addr = prop + (index * (na + ns));
|
||||||
|
prop_size = prop_addr + na;
|
||||||
|
prop_after_size = prop_size + ns;
|
||||||
|
if (prop_after_size > prop_end) {
|
||||||
|
debug("(not enough data: expected >= %d cells, got %d cells)\n",
|
||||||
|
(u32)(prop_after_size - prop), ((u32)(prop_end - prop)));
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = fdtdec_get_number(prop_addr, na);
|
||||||
|
|
||||||
|
if (sizep) {
|
||||||
|
*sizep = fdtdec_get_number(prop_size, ns);
|
||||||
|
debug("addr=%08llx, size=%llx\n", (u64)addr, (u64)*sizep);
|
||||||
|
} else {
|
||||||
|
debug("addr=%08llx\n", (u64)addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent,
|
||||||
|
int node, const char *prop_name, int index, fdt_size_t *sizep)
|
||||||
|
{
|
||||||
|
int na, ns;
|
||||||
|
|
||||||
|
debug("%s: ", __func__);
|
||||||
|
|
||||||
|
na = fdt_address_cells(blob, parent);
|
||||||
|
if (na < 1) {
|
||||||
|
debug("(bad #address-cells)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ns = fdt_size_cells(blob, parent);
|
||||||
|
if (ns < 1) {
|
||||||
|
debug("(bad #size-cells)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("na=%d, ns=%d, ", na, ns);
|
||||||
|
|
||||||
|
return fdtdec_get_addr_size_fixed(blob, node, prop_name, index, na,
|
||||||
|
ns, sizep);
|
||||||
|
}
|
||||||
|
|
||||||
|
fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node,
|
||||||
|
const char *prop_name, int index, fdt_size_t *sizep)
|
||||||
|
{
|
||||||
|
int parent;
|
||||||
|
|
||||||
|
debug("%s: ", __func__);
|
||||||
|
|
||||||
|
parent = fdt_parent_offset(blob, node);
|
||||||
|
if (parent < 0) {
|
||||||
|
debug("(no parent found)\n");
|
||||||
|
return FDT_ADDR_T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdtdec_get_addr_size_auto_parent(blob, parent, node, prop_name,
|
||||||
|
index, sizep);
|
||||||
|
}
|
||||||
|
|
||||||
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
|
||||||
const char *prop_name, fdt_size_t *sizep)
|
const char *prop_name, fdt_size_t *sizep)
|
||||||
{
|
{
|
||||||
const fdt_addr_t *cell;
|
return fdtdec_get_addr_size_fixed(blob, node, prop_name, 0,
|
||||||
int len;
|
sizeof(fdt_addr_t) / sizeof(fdt32_t),
|
||||||
|
sizeof(fdt_size_t) / sizeof(fdt32_t),
|
||||||
debug("%s: %s: ", __func__, prop_name);
|
sizep);
|
||||||
cell = fdt_getprop(blob, node, prop_name, &len);
|
|
||||||
if (cell && ((!sizep && len == sizeof(fdt_addr_t)) ||
|
|
||||||
len == sizeof(fdt_addr_t) * 2)) {
|
|
||||||
fdt_addr_t addr = fdt_addr_to_cpu(*cell);
|
|
||||||
if (sizep) {
|
|
||||||
const fdt_size_t *size;
|
|
||||||
|
|
||||||
size = (fdt_size_t *)((char *)cell +
|
|
||||||
sizeof(fdt_addr_t));
|
|
||||||
*sizep = fdt_size_to_cpu(*size);
|
|
||||||
debug("addr=%08lx, size=%llx\n",
|
|
||||||
(ulong)addr, (u64)*sizep);
|
|
||||||
} else {
|
|
||||||
debug("%08lx\n", (ulong)addr);
|
|
||||||
}
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
debug("(not found)\n");
|
|
||||||
return FDT_ADDR_T_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
fdt_addr_t fdtdec_get_addr(const void *blob, int node,
|
||||||
|
|
Loading…
Add table
Reference in a new issue