Commit 02464e386bb5f0a022c121f95ae75cf583759d95
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
... | ... | @@ -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, |
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63
-
mentioned in commit d93b9a
-
mentioned in commit d93b9a
-
mentioned in commit ff0a63