#include <common.h>
#include <env.h>
+#include <log.h>
#include <mapmem.h>
+#include <net.h>
#include <stdio_dev.h>
#include <linux/ctype.h>
#include <linux/types.h>
return offset;
}
-/* rename to CONFIG_OF_STDOUT_PATH ? */
-#if defined(OF_STDOUT_PATH)
-static int fdt_fixup_stdout(void *fdt, int chosenoff)
-{
- return fdt_setprop(fdt, chosenoff, "linux,stdout-path",
- OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1);
-}
-#elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX)
+#if defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX)
static int fdt_fixup_stdout(void *fdt, int chosenoff)
{
int err;
return 0;
}
+/**
+ * board_fdt_chosen_bootargs - boards may override this function to use
+ * alternative kernel command line arguments
+ */
+__weak char *board_fdt_chosen_bootargs(void)
+{
+ return env_get("bootargs");
+}
+
int fdt_chosen(void *fdt)
{
int nodeoffset;
if (nodeoffset < 0)
return nodeoffset;
- str = env_get("bootargs");
+ str = board_fdt_chosen_bootargs();
+
if (str) {
err = fdt_setprop(fdt, nodeoffset, "bootargs", str,
strlen(str) + 1);
#else
#define MEMORY_BANKS_MAX 4
#endif
+
+/**
+ * fdt_fixup_memory_banks - Update DT memory node
+ * @blob: Pointer to DT blob
+ * @start: Pointer to memory start addresses array
+ * @size: Pointer to memory sizes array
+ * @banks: Number of memory banks
+ *
+ * Return: 0 on success, negative value on failure
+ *
+ * Based on the passed number of banks and arrays, the function is able to
+ * update existing DT memory nodes to match run time detected/changed memory
+ * configuration. Implementation is handling one specific case with only one
+ * memory node where multiple tuples could be added/updated.
+ * The case where multiple memory nodes with a single tuple (base, size) are
+ * used, this function is only updating the first memory node without removing
+ * others.
+ */
int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
{
int err, nodeoffset;
}
return 0;
}
+
+int fdt_set_usable_memory(void *blob, u64 start[], u64 size[], int areas)
+{
+ int err, nodeoffset;
+ int len;
+ u8 tmp[8 * 16]; /* Up to 64-bit address + 64-bit size */
+
+ if (areas > 8) {
+ printf("%s: num areas %d exceeds hardcoded limit %d\n",
+ __func__, areas, 8);
+ return -1;
+ }
+
+ err = fdt_check_header(blob);
+ if (err < 0) {
+ printf("%s: %s\n", __func__, fdt_strerror(err));
+ return err;
+ }
+
+ /* find or create "/memory" node. */
+ nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory");
+ if (nodeoffset < 0)
+ return nodeoffset;
+
+ len = fdt_pack_reg(blob, tmp, start, size, areas);
+
+ err = fdt_setprop(blob, nodeoffset, "linux,usable-memory", tmp, len);
+ if (err < 0) {
+ printf("WARNING: could not set %s %s.\n",
+ "reg", fdt_strerror(err));
+ return err;
+ }
+
+ return 0;
+}
#endif
int fdt_fixup_memory(void *blob, u64 start, u64 size)
for (j = 0; j < 6; j++) {
mac_addr[j] = tmp ?
- simple_strtoul(tmp, &end, 16) : 0;
+ hextoul(tmp, &end) : 0;
if (tmp)
tmp = (*end) ? end + 1 : end;
}
int fdt_record_loadable(void *blob, u32 index, const char *name,
uintptr_t load_addr, u32 size, uintptr_t entry_point,
- const char *type, const char *os)
+ const char *type, const char *os, const char *arch)
{
int err, node;
if (node < 0)
return node;
- /*
- * We record these as 32bit entities, possibly truncating addresses.
- * However, spl_fit.c is not 64bit safe either: i.e. we should not
- * have an issue here.
- */
- fdt_setprop_u32(blob, node, "load-addr", load_addr);
+ fdt_setprop_u64(blob, node, "load", load_addr);
if (entry_point != -1)
- fdt_setprop_u32(blob, node, "entry-point", entry_point);
+ fdt_setprop_u64(blob, node, "entry", entry_point);
fdt_setprop_u32(blob, node, "size", size);
if (type)
fdt_setprop_string(blob, node, "type", type);
if (os)
fdt_setprop_string(blob, node, "os", os);
+ if (arch)
+ fdt_setprop_string(blob, node, "arch", arch);
return node;
}
return 0;
}
-int fdt_node_set_part_info(void *blob, int parent_offset,
- struct mtd_device *dev)
+static int fdt_node_set_part_info(void *blob, int parent_offset,
+ struct mtd_device *dev)
{
struct list_head *pentry;
struct part_info *part;
struct mtd_device *dev;
int i, idx;
int noff;
-
- if (mtdparts_init() != 0)
- return;
+ bool inited = false;
for (i = 0; i < node_info_size; i++) {
idx = 0;
- noff = fdt_node_offset_by_compatible(blob, -1,
- node_info[i].compat);
- while (noff != -FDT_ERR_NOTFOUND) {
+ noff = -1;
+
+ while ((noff = fdt_node_offset_by_compatible(blob, noff,
+ node_info[i].compat)) >= 0) {
+ const char *prop;
+
+ prop = fdt_getprop(blob, noff, "status", NULL);
+ if (prop && !strcmp(prop, "disabled"))
+ continue;
+
debug("%s: %s, mtd dev type %d\n",
fdt_get_name(blob, noff, 0),
node_info[i].compat, node_info[i].type);
+
+ if (!inited) {
+ if (mtdparts_init() != 0)
+ return;
+ inited = true;
+ }
+
dev = device_find(node_info[i].type, idx++);
if (dev) {
if (fdt_node_set_part_info(blob, noff, dev))
return; /* return on error */
}
-
- /* Jump to next flash node */
- noff = fdt_node_offset_by_compatible(blob, noff,
- node_info[i].compat);
}
}
}
return __of_translate_address(blob, node_offset, in_addr, "dma-ranges");
}
+int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu,
+ dma_addr_t *bus, u64 *size)
+{
+ bool found_dma_ranges = false;
+ struct of_bus *bus_node;
+ const fdt32_t *ranges;
+ int na, ns, pna, pns;
+ int parent = node;
+ int ret = 0;
+ int len;
+
+ /* Find the closest dma-ranges property */
+ while (parent >= 0) {
+ ranges = fdt_getprop(blob, parent, "dma-ranges", &len);
+
+ /* Ignore empty ranges, they imply no translation required */
+ if (ranges && len > 0)
+ break;
+
+ /* Once we find 'dma-ranges', then a missing one is an error */
+ if (found_dma_ranges && !ranges) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ranges)
+ found_dma_ranges = true;
+
+ parent = fdt_parent_offset(blob, parent);
+ }
+
+ if (!ranges || parent < 0) {
+ debug("no dma-ranges found for node %s\n",
+ fdt_get_name(blob, node, NULL));
+ ret = -ENOENT;
+ goto out;
+ }
+
+ /* switch to that node */
+ node = parent;
+ parent = fdt_parent_offset(blob, node);
+ if (parent < 0) {
+ printf("Found dma-ranges in root node, shoudln't happen\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Get the address sizes both for the bus and its parent */
+ bus_node = of_match_bus(blob, node);
+ bus_node->count_cells(blob, node, &na, &ns);
+ if (!OF_CHECK_COUNTS(na, ns)) {
+ printf("%s: Bad cell count for %s\n", __FUNCTION__,
+ fdt_get_name(blob, node, NULL));
+ return -EINVAL;
+ goto out;
+ }
+
+ bus_node = of_match_bus(blob, parent);
+ bus_node->count_cells(blob, parent, &pna, &pns);
+ if (!OF_CHECK_COUNTS(pna, pns)) {
+ printf("%s: Bad cell count for %s\n", __FUNCTION__,
+ fdt_get_name(blob, parent, NULL));
+ return -EINVAL;
+ goto out;
+ }
+
+ *bus = fdt_read_number(ranges, na);
+ *cpu = fdt_translate_dma_address(blob, node, ranges + na);
+ *size = fdt_read_number(ranges + na + pna, ns);
+out:
+ return ret;
+}
+
/**
* fdt_node_offset_by_compat_reg: Find a node that matches compatiable and
* who's reg property matches a physical cpu address
}
/*
- * Read a property of size <prop_len>. Currently only supports 1 or 2 cells.
+ * Read a property of size <prop_len>. Currently only supports 1 or 2 cells,
+ * or 3 cells specially for a PCI address.
*/
static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off,
uint64_t *val, int cells)
{
- const fdt32_t *prop32 = &prop[cell_off];
- const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off];
+ const fdt32_t *prop32;
+ const unaligned_fdt64_t *prop64;
if ((cell_off + cells) > prop_len)
return -FDT_ERR_NOSPACE;
+ prop32 = &prop[cell_off];
+
+ /*
+ * Special handling for PCI address in PCI bus <ranges>
+ *
+ * PCI child address is made up of 3 cells. Advance the cell offset
+ * by 1 so that the PCI child address can be correctly read.
+ */
+ if (cells == 3)
+ cell_off += 1;
+ prop64 = (const fdt64_t *)&prop[cell_off];
+
switch (cells) {
case 1:
*val = fdt32_to_cpu(*prop32);
break;
case 2:
+ case 3:
*val = fdt64_to_cpu(*prop64);
break;
default:
return err;
}
#endif
+
+/**
+ * fdt_valid() - Check if an FDT is valid. If not, change it to NULL
+ *
+ * @blobp: Pointer to FDT pointer
+ * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL)
+ */
+int fdt_valid(struct fdt_header **blobp)
+{
+ const void *blob = *blobp;
+ int err;
+
+ if (!blob) {
+ printf("The address of the fdt is invalid (NULL).\n");
+ return 0;
+ }
+
+ err = fdt_check_header(blob);
+ if (err == 0)
+ return 1; /* valid */
+
+ if (err < 0) {
+ printf("libfdt fdt_check_header(): %s", fdt_strerror(err));
+ /*
+ * Be more informative on bad version.
+ */
+ if (err == -FDT_ERR_BADVERSION) {
+ if (fdt_version(blob) <
+ FDT_FIRST_SUPPORTED_VERSION) {
+ printf(" - too old, fdt %d < %d",
+ fdt_version(blob),
+ FDT_FIRST_SUPPORTED_VERSION);
+ }
+ if (fdt_last_comp_version(blob) >
+ FDT_LAST_SUPPORTED_VERSION) {
+ printf(" - too new, fdt %d > %d",
+ fdt_version(blob),
+ FDT_LAST_SUPPORTED_VERSION);
+ }
+ }
+ printf("\n");
+ *blobp = NULL;
+ return 0;
+ }
+ return 1;
+}