Merge branch '2021-09-24-arm64-optimized-str-funcs' into next
[platform/kernel/u-boot.git] / common / fdt_support.c
index f31e9b0..8992ac5 100644 (file)
@@ -7,7 +7,10 @@
  */
 
 #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>
@@ -122,14 +125,7 @@ int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name)
        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;
@@ -271,6 +267,15 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
        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;
@@ -288,7 +293,8 @@ int fdt_chosen(void *fdt)
        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);
@@ -414,6 +420,24 @@ static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size,
 #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;
@@ -466,6 +490,41 @@ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
        }
        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)
@@ -538,7 +597,7 @@ void fdt_fixup_ethernet(void *fdt)
 
                        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;
                        }
@@ -553,7 +612,7 @@ void fdt_fixup_ethernet(void *fdt)
 
 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;
 
@@ -573,19 +632,16 @@ int fdt_record_loadable(void *blob, u32 index, const char *name,
        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;
 }
@@ -671,30 +727,33 @@ int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) {
 
                dma_range[0] = 0;
                if (size >= 0x100000000ull)
-                       dma_range[0] |= FDT_PCI_MEM64;
+                       dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM64);
                else
-                       dma_range[0] |= FDT_PCI_MEM32;
+                       dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM32);
                if (hose->regions[r].flags & PCI_REGION_PREFETCH)
-                       dma_range[0] |= FDT_PCI_PREFETCH;
+                       dma_range[0] |= cpu_to_fdt32(FDT_PCI_PREFETCH);
 #ifdef CONFIG_SYS_PCI_64BIT
-               dma_range[1] = bus_start >> 32;
+               dma_range[1] = cpu_to_fdt32(bus_start >> 32);
 #else
                dma_range[1] = 0;
 #endif
-               dma_range[2] = bus_start & 0xffffffff;
+               dma_range[2] = cpu_to_fdt32(bus_start & 0xffffffff);
 
                if (addrcell == 2) {
-                       dma_range[3] = phys_start >> 32;
-                       dma_range[4] = phys_start & 0xffffffff;
+                       dma_range[3] = cpu_to_fdt32(phys_start >> 32);
+                       dma_range[4] = cpu_to_fdt32(phys_start & 0xffffffff);
                } else {
-                       dma_range[3] = phys_start & 0xffffffff;
+                       dma_range[3] = cpu_to_fdt32(phys_start & 0xffffffff);
                }
 
                if (sizecell == 2) {
-                       dma_range[3 + addrcell + 0] = size >> 32;
-                       dma_range[3 + addrcell + 1] = size & 0xffffffff;
+                       dma_range[3 + addrcell + 0] =
+                               cpu_to_fdt32(size >> 32);
+                       dma_range[3 + addrcell + 1] =
+                               cpu_to_fdt32(size & 0xffffffff);
                } else {
-                       dma_range[3 + addrcell + 0] = size & 0xffffffff;
+                       dma_range[3 + addrcell + 0] =
+                               cpu_to_fdt32(size & 0xffffffff);
                }
 
                dma_range += (3 + addrcell + sizecell);
@@ -775,8 +834,8 @@ static int fdt_del_partitions(void *blob, int parent_offset)
        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;
@@ -910,27 +969,35 @@ void fdt_fixup_mtdparts(void *blob, const struct node_info *node_info,
        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);
                }
        }
 }
@@ -1292,6 +1359,85 @@ u64 fdt_translate_address(const void *blob, int node_offset,
        return __of_translate_address(blob, node_offset, in_addr, "ranges");
 }
 
+u64 fdt_translate_dma_address(const void *blob, int node_offset,
+                             const fdt32_t *in_addr)
+{
+       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
@@ -1546,26 +1692,40 @@ u64 fdt_get_base_address(const void *fdt, int node)
 
        prop = fdt_getprop(fdt, node, "reg", &size);
 
-       return prop ? fdt_translate_address(fdt, node, prop) : 0;
+       return prop ? fdt_translate_address(fdt, node, prop) : OF_BAD_ADDR;
 }
 
 /*
- * 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:
@@ -1764,3 +1924,49 @@ int fdt_overlay_apply_verbose(void *fdt, void *fdto)
        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;
+}