Commit 02464e386bb5f0a022c121f95ae75cf583759d95

Authored by Stephen Warren
Committed by Simon Glass
1 parent 850f788709

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>

Showing 3 changed files with 200 additions and 30 deletions Side-by-side Diff

drivers/core/device.c
... ... @@ -581,7 +581,10 @@
581 581 #if CONFIG_IS_ENABLED(OF_CONTROL)
582 582 fdt_addr_t addr;
583 583  
584   - addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
  584 + addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
  585 + dev->parent->of_offset,
  586 + dev->of_offset, "reg",
  587 + 0, NULL);
585 588 if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
586 589 if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS)
587 590 addr = simple_bus_translate(dev->parent, addr);
... ... @@ -295,27 +295,122 @@
295 295 int fdtdec_next_compatible_subnode(const void *blob, int node,
296 296 enum fdt_compat_id id, int *depthp);
297 297  
298   -/**
299   - * Look up an address property in a node and return it as an address.
300   - * The property must hold either one address with no trailing data or
301   - * one address with a length. This is only tested on 32-bit machines.
  298 +/*
  299 + * Look up an address property in a node and return the parsed address, and
  300 + * optionally the parsed size.
302 301 *
  302 + * This variant assumes a known and fixed number of cells are used to
  303 + * represent the address and size.
  304 + *
  305 + * You probably don't want to use this function directly except to parse
  306 + * non-standard properties, and never to parse the "reg" property. Instead,
  307 + * use one of the "auto" variants below, which automatically honor the
  308 + * #address-cells and #size-cells properties in the parent node.
  309 + *
303 310 * @param blob FDT blob
304 311 * @param node node to examine
305 312 * @param prop_name name of property to find
  313 + * @param index which address to retrieve from a list of addresses. Often 0.
  314 + * @param na the number of cells used to represent an address
  315 + * @param ns the number of cells used to represent a size
  316 + * @param sizep a pointer to store the size into. Use NULL if not required
306 317 * @return address, if found, or FDT_ADDR_T_NONE if not
307 318 */
  319 +fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node,
  320 + const char *prop_name, int index, int na, int ns,
  321 + fdt_size_t *sizep);
  322 +
  323 +/*
  324 + * Look up an address property in a node and return the parsed address, and
  325 + * optionally the parsed size.
  326 + *
  327 + * This variant automatically determines the number of cells used to represent
  328 + * the address and size by parsing the provided parent node's #address-cells
  329 + * and #size-cells properties.
  330 + *
  331 + * @param blob FDT blob
  332 + * @param parent parent node of @node
  333 + * @param node node to examine
  334 + * @param prop_name name of property to find
  335 + * @param index which address to retrieve from a list of addresses. Often 0.
  336 + * @param sizep a pointer to store the size into. Use NULL if not required
  337 + * @return address, if found, or FDT_ADDR_T_NONE if not
  338 + */
  339 +fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent,
  340 + int node, const char *prop_name, int index, fdt_size_t *sizep);
  341 +
  342 +/*
  343 + * Look up an address property in a node and return the parsed address, and
  344 + * optionally the parsed size.
  345 + *
  346 + * This variant automatically determines the number of cells used to represent
  347 + * the address and size by parsing the parent node's #address-cells
  348 + * and #size-cells properties. The parent node is automatically found.
  349 + *
  350 + * The automatic parent lookup implemented by this function is slow.
  351 + * Consequently, fdtdec_get_addr_size_auto_parent() should be used where
  352 + * possible.
  353 + *
  354 + * @param blob FDT blob
  355 + * @param parent parent node of @node
  356 + * @param node node to examine
  357 + * @param prop_name name of property to find
  358 + * @param index which address to retrieve from a list of addresses. Often 0.
  359 + * @param sizep a pointer to store the size into. Use NULL if not required
  360 + * @return address, if found, or FDT_ADDR_T_NONE if not
  361 + */
  362 +fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node,
  363 + const char *prop_name, int index, fdt_size_t *sizep);
  364 +
  365 +/*
  366 + * Look up an address property in a node and return the parsed address.
  367 + *
  368 + * This variant hard-codes the number of cells used to represent the address
  369 + * and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also
  370 + * always returns the first address value in the property (index 0).
  371 + *
  372 + * Use of this function is not recommended due to the hard-coding of cell
  373 + * counts. There is no programmatic validation that these hard-coded values
  374 + * actually match the device tree content in any way at all. This assumption
  375 + * can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately
  376 + * set in the U-Boot build and exercising strict control over DT content to
  377 + * ensure use of matching #address-cells/#size-cells properties. However, this
  378 + * approach is error-prone; those familiar with DT will not expect the
  379 + * assumption to exist, and could easily invalidate it. If the assumption is
  380 + * invalidated, this function will not report the issue, and debugging will
  381 + * be required. Instead, use fdtdec_get_addr_size_auto_parent().
  382 + *
  383 + * @param blob FDT blob
  384 + * @param node node to examine
  385 + * @param prop_name name of property to find
  386 + * @return address, if found, or FDT_ADDR_T_NONE if not
  387 + */
308 388 fdt_addr_t fdtdec_get_addr(const void *blob, int node,
309 389 const char *prop_name);
310 390  
311   -/**
312   - * Look up an address property in a node and return it as an address.
313   - * The property must hold one address with a length. This is only tested
314   - * on 32-bit machines.
  391 +/*
  392 + * Look up an address property in a node and return the parsed address, and
  393 + * optionally the parsed size.
315 394 *
  395 + * This variant hard-codes the number of cells used to represent the address
  396 + * and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also
  397 + * always returns the first address value in the property (index 0).
  398 + *
  399 + * Use of this function is not recommended due to the hard-coding of cell
  400 + * counts. There is no programmatic validation that these hard-coded values
  401 + * actually match the device tree content in any way at all. This assumption
  402 + * can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately
  403 + * set in the U-Boot build and exercising strict control over DT content to
  404 + * ensure use of matching #address-cells/#size-cells properties. However, this
  405 + * approach is error-prone; those familiar with DT will not expect the
  406 + * assumption to exist, and could easily invalidate it. If the assumption is
  407 + * invalidated, this function will not report the issue, and debugging will
  408 + * be required. Instead, use fdtdec_get_addr_size_auto_parent().
  409 + *
316 410 * @param blob FDT blob
317 411 * @param node node to examine
318 412 * @param prop_name name of property to find
  413 + * @param sizep a pointer to store the size into. Use NULL if not required
319 414 * @return address, if found, or FDT_ADDR_T_NONE if not
320 415 */
321 416 fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
... ... @@ -86,32 +86,104 @@
86 86 return compat_names[id];
87 87 }
88 88  
89   -fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
90   - const char *prop_name, fdt_size_t *sizep)
  89 +fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node,
  90 + const char *prop_name, int index, int na, int ns,
  91 + fdt_size_t *sizep)
91 92 {
92   - const fdt_addr_t *cell;
  93 + const fdt32_t *prop, *prop_end;
  94 + const fdt32_t *prop_addr, *prop_size, *prop_after_size;
93 95 int len;
  96 + fdt_addr_t addr;
94 97  
95 98 debug("%s: %s: ", __func__, prop_name);
96   - cell = fdt_getprop(blob, node, prop_name, &len);
97   - if (cell && ((!sizep && len == sizeof(fdt_addr_t)) ||
98   - len == sizeof(fdt_addr_t) * 2)) {
99   - fdt_addr_t addr = fdt_addr_to_cpu(*cell);
100   - if (sizep) {
101   - const fdt_size_t *size;
102 99  
103   - size = (fdt_size_t *)((char *)cell +
104   - sizeof(fdt_addr_t));
105   - *sizep = fdt_size_to_cpu(*size);
106   - debug("addr=%08lx, size=%llx\n",
107   - (ulong)addr, (u64)*sizep);
108   - } else {
109   - debug("%08lx\n", (ulong)addr);
110   - }
111   - return addr;
  100 + if (na > (sizeof(fdt_addr_t) / sizeof(fdt32_t))) {
  101 + debug("(na too large for fdt_addr_t type)\n");
  102 + return FDT_ADDR_T_NONE;
112 103 }
113   - debug("(not found)\n");
114   - return FDT_ADDR_T_NONE;
  104 +
  105 + if (ns > (sizeof(fdt_size_t) / sizeof(fdt32_t))) {
  106 + debug("(ns too large for fdt_size_t type)\n");
  107 + return FDT_ADDR_T_NONE;
  108 + }
  109 +
  110 + prop = fdt_getprop(blob, node, prop_name, &len);
  111 + if (!prop) {
  112 + debug("(not found)\n");
  113 + return FDT_ADDR_T_NONE;
  114 + }
  115 + prop_end = prop + (len / sizeof(*prop));
  116 +
  117 + prop_addr = prop + (index * (na + ns));
  118 + prop_size = prop_addr + na;
  119 + prop_after_size = prop_size + ns;
  120 + if (prop_after_size > prop_end) {
  121 + debug("(not enough data: expected >= %d cells, got %d cells)\n",
  122 + (u32)(prop_after_size - prop), ((u32)(prop_end - prop)));
  123 + return FDT_ADDR_T_NONE;
  124 + }
  125 +
  126 + addr = fdtdec_get_number(prop_addr, na);
  127 +
  128 + if (sizep) {
  129 + *sizep = fdtdec_get_number(prop_size, ns);
  130 + debug("addr=%08llx, size=%llx\n", (u64)addr, (u64)*sizep);
  131 + } else {
  132 + debug("addr=%08llx\n", (u64)addr);
  133 + }
  134 +
  135 + return addr;
  136 +}
  137 +
  138 +fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent,
  139 + int node, const char *prop_name, int index, fdt_size_t *sizep)
  140 +{
  141 + int na, ns;
  142 +
  143 + debug("%s: ", __func__);
  144 +
  145 + na = fdt_address_cells(blob, parent);
  146 + if (na < 1) {
  147 + debug("(bad #address-cells)\n");
  148 + return FDT_ADDR_T_NONE;
  149 + }
  150 +
  151 + ns = fdt_size_cells(blob, parent);
  152 + if (ns < 1) {
  153 + debug("(bad #size-cells)\n");
  154 + return FDT_ADDR_T_NONE;
  155 + }
  156 +
  157 + debug("na=%d, ns=%d, ", na, ns);
  158 +
  159 + return fdtdec_get_addr_size_fixed(blob, node, prop_name, index, na,
  160 + ns, sizep);
  161 +}
  162 +
  163 +fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node,
  164 + const char *prop_name, int index, fdt_size_t *sizep)
  165 +{
  166 + int parent;
  167 +
  168 + debug("%s: ", __func__);
  169 +
  170 + parent = fdt_parent_offset(blob, node);
  171 + if (parent < 0) {
  172 + debug("(no parent found)\n");
  173 + return FDT_ADDR_T_NONE;
  174 + }
  175 +
  176 + return fdtdec_get_addr_size_auto_parent(blob, parent, node, prop_name,
  177 + index, sizep);
  178 +}
  179 +
  180 +fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
  181 + const char *prop_name, fdt_size_t *sizep)
  182 +{
  183 + return fdtdec_get_addr_size_fixed(blob, node, prop_name, 0,
  184 + sizeof(fdt_addr_t) / sizeof(fdt32_t),
  185 + sizeof(fdt_size_t) / sizeof(fdt32_t),
  186 + sizep);
115 187 }
116 188  
117 189 fdt_addr_t fdtdec_get_addr(const void *blob, int node,