]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - common/fdt_support.c
fdt_support: fix obiwan error in OF_CHECK_COUNTS()
[karo-tx-uboot.git] / common / fdt_support.c
index 784a570a818bd94fdb8dd086a4020f5652c91c00..1353651c5c5ab5fcc2009d3d9f176c5af7560fa4 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <common.h>
+#include <inttypes.h>
 #include <stdio_dev.h>
 #include <linux/ctype.h>
 #include <linux/types.h>
 #include <libfdt.h>
 #include <fdt_support.h>
 #include <exports.h>
-
-/*
- * Get cells len in bytes
- *     if #NNNN-cells property is 2 then len is 8
- *     otherwise len is 4
- */
-static int get_cells_len(const void *fdt, const char *nr_cells_name)
-{
-       const fdt32_t *cell;
-
-       cell = fdt_getprop(fdt, 0, nr_cells_name, NULL);
-       if (cell && fdt32_to_cpu(*cell) == 2)
-               return 8;
-
-       return 4;
-}
+#include <fdtdec.h>
 
 /**
  * fdt_getprop_u32_default_node - Return a node's property or a default
@@ -113,7 +99,8 @@ int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
 }
 
 /**
- * fdt_find_or_add_subnode - find or possibly add a subnode of a given node
+ * fdt_find_or_add_subnode() - find or possibly add a subnode of a given node
+ *
  * @fdt: pointer to the device tree blob
  * @parentoffset: structure block offset of a node
  * @name: name of the subnode to locate
@@ -121,8 +108,7 @@ int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
  * fdt_subnode_offset() finds a subnode of the node with a given name.
  * If the subnode does not exist, it will be created.
  */
-static int fdt_find_or_add_subnode(void *fdt, int parentoffset,
-                                  const char *name)
+int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name)
 {
        int offset;
 
@@ -145,18 +131,6 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
                              OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1);
 }
 #elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX)
-static void fdt_fill_multisername(char *sername, size_t maxlen)
-{
-       const char *outname = stdio_devices[stdout]->name;
-
-       if (strcmp(outname, "serial") > 0)
-               strncpy(sername, outname, maxlen);
-
-       /* eserial? */
-       if (strcmp(outname + 1, "serial") > 0)
-               strncpy(sername, outname + 1, maxlen);
-}
-
 static int fdt_fixup_stdout(void *fdt, int chosenoff)
 {
        int err;
@@ -166,32 +140,35 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
        int len;
        char tmp[256]; /* long enough */
 
-       fdt_fill_multisername(sername, sizeof(sername) - 1);
-       if (!sername[0])
-               sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
+       sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
 
        aliasoff = fdt_path_offset(fdt, "/aliases");
        if (aliasoff < 0) {
                err = aliasoff;
-               goto error;
+               goto noalias;
        }
 
        path = fdt_getprop(fdt, aliasoff, sername, &len);
        if (!path) {
                err = len;
-               goto error;
+               goto noalias;
        }
 
        /* fdt_setprop may break "path" so we copy it to tmp buffer */
        memcpy(tmp, path, len);
 
        err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len);
-error:
        if (err < 0)
                printf("WARNING: could not set linux,stdout-path %s.\n",
                       fdt_strerror(err));
 
        return err;
+
+noalias:
+       printf("WARNING: %s: could not read %s alias: %s\n",
+              __func__, sername, fdt_strerror(err));
+
+       return 0;
 }
 #else
 static int fdt_fixup_stdout(void *fdt, int chosenoff)
@@ -209,6 +186,31 @@ static inline int fdt_setprop_uxx(void *fdt, int nodeoffset, const char *name,
                return fdt_setprop_u32(fdt, nodeoffset, name, (uint32_t)val);
 }
 
+int fdt_root(void *fdt)
+{
+       char *serial;
+       int err;
+
+       err = fdt_check_header(fdt);
+       if (err < 0) {
+               printf("fdt_root: %s\n", fdt_strerror(err));
+               return err;
+       }
+
+       serial = getenv("serial#");
+       if (serial) {
+               err = fdt_setprop(fdt, 0, "serial-number", serial,
+                                 strlen(serial) + 1);
+
+               if (err < 0) {
+                       printf("WARNING: could not set serial-number %s.\n",
+                              fdt_strerror(err));
+                       return err;
+               }
+       }
+
+       return 0;
+}
 
 int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
 {
@@ -246,7 +248,7 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
                return err;
        }
 
-       is_u64 = (get_cells_len(fdt, "#address-cells") == 8);
+       is_u64 = (fdt_address_cells(fdt, 0) == 2);
 
        err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start",
                              (uint64_t)initrd_start, is_u64);
@@ -382,26 +384,26 @@ void do_fixup_by_compat_u32(void *fdt, const char *compat,
 /*
  * fdt_pack_reg - pack address and size array into the "reg"-suitable stream
  */
-static int fdt_pack_reg(const void *fdt, void *buf, uint64_t *address,
-                       uint64_t *size, int n)
+static int fdt_pack_reg(const void *fdt, void *buf, u64 *address, u64 *size,
+                       int n)
 {
        int i;
-       int address_len = get_cells_len(fdt, "#address-cells");
-       int size_len = get_cells_len(fdt, "#size-cells");
+       int address_cells = fdt_address_cells(fdt, 0);
+       int size_cells = fdt_size_cells(fdt, 0);
        char *p = buf;
 
        for (i = 0; i < n; i++) {
-               if (address_len == 8)
+               if (address_cells == 2)
                        *(fdt64_t *)p = cpu_to_fdt64(address[i]);
                else
                        *(fdt32_t *)p = cpu_to_fdt32(address[i]);
-               p += address_len;
+               p += 4 * address_cells;
 
-               if (size_len == 8)
+               if (size_cells == 2)
                        *(fdt64_t *)p = cpu_to_fdt64(size[i]);
                else
                        *(fdt32_t *)p = cpu_to_fdt32(size[i]);
-               p += size_len;
+               p += 4 * size_cells;
        }
 
        return p - (char *)buf;
@@ -434,7 +436,7 @@ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
        /* find or create "/memory" node. */
        nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory");
        if (nodeoffset < 0)
-                       return nodeoffset;
+               return nodeoffset;
 
        err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
                        sizeof("memory"));
@@ -444,6 +446,9 @@ int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
                return err;
        }
 
+       if (!banks)
+               return 0;
+
        len = fdt_pack_reg(blob, tmp, start, size, banks);
 
        err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
@@ -462,48 +467,58 @@ int fdt_fixup_memory(void *blob, u64 start, u64 size)
 
 void fdt_fixup_ethernet(void *fdt)
 {
-       int node, i, j;
-       char enet[16], *tmp, *end;
+       int i, j, prop;
+       char *tmp, *end;
        char mac[16];
        const char *path;
        unsigned char mac_addr[6];
+       int offset;
 
-       node = fdt_path_offset(fdt, "/aliases");
-       if (node < 0)
+       if (fdt_path_offset(fdt, "/aliases") < 0)
                return;
 
-       if (!getenv("ethaddr")) {
-               if (getenv("usbethaddr")) {
-                       strcpy(mac, "usbethaddr");
-               } else {
-                       debug("No ethernet MAC Address defined\n");
-                       return;
-               }
-       } else {
-               strcpy(mac, "ethaddr");
-       }
+       /* Cycle through all aliases */
+       for (prop = 0; ; prop++) {
+               const char *name;
+               int len = strlen("ethernet");
 
-       i = 0;
-       while ((tmp = getenv(mac)) != NULL) {
-               sprintf(enet, "ethernet%d", i);
-               path = fdt_getprop(fdt, node, enet, NULL);
-               if (!path) {
-                       debug("No alias for %s\n", enet);
-                       sprintf(mac, "eth%daddr", ++i);
-                       continue;
-               }
+               /* FDT might have been edited, recompute the offset */
+               offset = fdt_first_property_offset(fdt,
+                       fdt_path_offset(fdt, "/aliases"));
+               /* Select property number 'prop' */
+               for (i = 0; i < prop; i++)
+                       offset = fdt_next_property_offset(fdt, offset);
 
-               for (j = 0; j < 6; j++) {
-                       mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
-                       if (tmp)
-                               tmp = (*end) ? end+1 : end;
-               }
+               if (offset < 0)
+                       break;
 
-               do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0);
-               do_fixup_by_path(fdt, path, "local-mac-address",
-                               &mac_addr, 6, 1);
+               path = fdt_getprop_by_offset(fdt, offset, &name, NULL);
+               if (!strncmp(name, "ethernet", len)) {
+                       i = trailing_strtol(name);
+                       if (i != -1) {
+                               if (i == 0)
+                                       strcpy(mac, "ethaddr");
+                               else
+                                       sprintf(mac, "eth%daddr", i);
+                       } else {
+                               continue;
+                       }
+                       tmp = getenv(mac);
+                       if (!tmp)
+                               continue;
+
+                       for (j = 0; j < 6; j++) {
+                               mac_addr[j] = tmp ?
+                                             simple_strtoul(tmp, &end, 16) : 0;
+                               if (tmp)
+                                       tmp = (*end) ? end + 1 : end;
+                       }
 
-               sprintf(mac, "eth%daddr", ++i);
+                       do_fixup_by_path(fdt, path, "mac-address",
+                                        &mac_addr, 6, 0);
+                       do_fixup_by_path(fdt, path, "local-mac-address",
+                                        &mac_addr, 6, 1);
+               }
        }
 }
 
@@ -930,22 +945,11 @@ void fdt_del_node_and_alias(void *blob, const char *alias)
        fdt_delprop(blob, off, alias);
 }
 
-/* Helper to read a big number; size is in cells (not bytes) */
-static inline u64 of_read_number(const fdt32_t *cell, int size)
-{
-       u64 r = 0;
-       while (size--)
-               r = (r << 32) | fdt32_to_cpu(*(cell++));
-       return r;
-}
-
-#define PRu64  "%llx"
-
 /* Max address size we deal with */
 #define OF_MAX_ADDR_CELLS      4
-#define OF_BAD_ADDR    ((u64)-1)
+#define OF_BAD_ADDR    FDT_ADDR_T_NONE
 #define OF_CHECK_COUNTS(na, ns)        ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
-                       (ns) > 0)
+                       (ns) >= 0)
 
 /* Debug utility */
 #ifdef DEBUG
@@ -960,11 +964,41 @@ static void of_dump_addr(const char *s, const fdt32_t *addr, int na)
 static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { }
 #endif
 
-/* Callbacks for bus specific translators */
+/**
+ * struct of_bus - Callbacks for bus specific translators
+ * @name:      A string used to identify this bus in debug output.
+ * @addresses: The name of the DT property from which addresses are
+ *             to be read, typically "reg".
+ * @match:     Return non-zero if the node whose parent is at
+ *             parentoffset in the FDT blob corresponds to a bus
+ *             of this type, otherwise return zero. If NULL a match
+ *             is assumed.
+ * @count_cells:Count how many cells (be32 values) a node whose parent
+ *             is at parentoffset in the FDT blob will require to
+ *             represent its address (written to *addrc) & size
+ *             (written to *sizec).
+ * @map:       Map the address addr from the address space of this
+ *             bus to that of its parent, making use of the ranges
+ *             read from DT to an array at range. na and ns are the
+ *             number of cells (be32 values) used to hold and address
+ *             or size, respectively, for this bus. pna is the number
+ *             of cells used to hold an address for the parent bus.
+ *             Returns the address in the address space of the parent
+ *             bus.
+ * @translate: Update the value of the address cells at addr within an
+ *             FDT by adding offset to it. na specifies the number of
+ *             cells used to hold the address being translated. Returns
+ *             zero on success, non-zero on error.
+ *
+ * Each bus type will include a struct of_bus in the of_busses array,
+ * providing implementations of some or all of the functions used to
+ * match the bus & handle address translation for its children.
+ */
 struct of_bus {
        const char      *name;
        const char      *addresses;
-       void            (*count_cells)(void *blob, int parentoffset,
+       int             (*match)(const void *blob, int parentoffset);
+       void            (*count_cells)(const void *blob, int parentoffset,
                                int *addrc, int *sizec);
        u64             (*map)(fdt32_t *addr, const fdt32_t *range,
                                int na, int ns, int pna);
@@ -972,18 +1006,13 @@ struct of_bus {
 };
 
 /* Default translator (generic bus) */
-static void of_bus_default_count_cells(void *blob, int parentoffset,
+void of_bus_default_count_cells(const void *blob, int parentoffset,
                                        int *addrc, int *sizec)
 {
        const fdt32_t *prop;
 
-       if (addrc) {
-               prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
-               if (prop)
-                       *addrc = be32_to_cpup(prop);
-               else
-                       *addrc = 2;
-       }
+       if (addrc)
+               *addrc = fdt_address_cells(blob, parentoffset);
 
        if (sizec) {
                prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
@@ -1003,8 +1032,8 @@ static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range,
        s  = of_read_number(range + na + pna, ns);
        da = of_read_number(addr, na);
 
-       debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n",
-           cp, s, da);
+       debug("OF: default map, cp=%" PRIu64 ", s=%" PRIu64
+             ", da=%" PRIu64 "\n", cp, s, da);
 
        if (da < cp || da >= (cp + s))
                return OF_BAD_ADDR;
@@ -1023,8 +1052,70 @@ static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na)
        return 0;
 }
 
+#ifdef CONFIG_OF_ISA_BUS
+
+/* ISA bus translator */
+static int of_bus_isa_match(const void *blob, int parentoffset)
+{
+       const char *name;
+
+       name = fdt_get_name(blob, parentoffset, NULL);
+       if (!name)
+               return 0;
+
+       return !strcmp(name, "isa");
+}
+
+static void of_bus_isa_count_cells(const void *blob, int parentoffset,
+                                  int *addrc, int *sizec)
+{
+       if (addrc)
+               *addrc = 2;
+       if (sizec)
+               *sizec = 1;
+}
+
+static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range,
+                         int na, int ns, int pna)
+{
+       u64 cp, s, da;
+
+       /* Check address type match */
+       if ((addr[0] ^ range[0]) & cpu_to_be32(1))
+               return OF_BAD_ADDR;
+
+       cp = of_read_number(range + 1, na - 1);
+       s  = of_read_number(range + na + pna, ns);
+       da = of_read_number(addr + 1, na - 1);
+
+       debug("OF: ISA map, cp=%" PRIu64 ", s=%" PRIu64
+             ", da=%" PRIu64 "\n", cp, s, da);
+
+       if (da < cp || da >= (cp + s))
+               return OF_BAD_ADDR;
+       return da - cp;
+}
+
+static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na)
+{
+       return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+#endif /* CONFIG_OF_ISA_BUS */
+
 /* Array of bus specific translators */
 static struct of_bus of_busses[] = {
+#ifdef CONFIG_OF_ISA_BUS
+       /* ISA */
+       {
+               .name = "isa",
+               .addresses = "reg",
+               .match = of_bus_isa_match,
+               .count_cells = of_bus_isa_count_cells,
+               .map = of_bus_isa_map,
+               .translate = of_bus_isa_translate,
+       },
+#endif /* CONFIG_OF_ISA_BUS */
        /* Default */
        {
                .name = "default",
@@ -1035,7 +1126,29 @@ static struct of_bus of_busses[] = {
        },
 };
 
-static int of_translate_one(void * blob, int parent, struct of_bus *bus,
+static struct of_bus *of_match_bus(const void *blob, int parentoffset)
+{
+       struct of_bus *bus;
+
+       if (ARRAY_SIZE(of_busses) == 1)
+               return of_busses;
+
+       for (bus = of_busses; bus; bus++) {
+               if (!bus->match || bus->match(blob, parentoffset))
+                       return bus;
+       }
+
+       /*
+        * We should always have matched the default bus at least, since
+        * it has a NULL match field. If we didn't then it somehow isn't
+        * in the of_busses array or something equally catastrophic has
+        * gone wrong.
+        */
+       assert(0);
+       return NULL;
+}
+
+static int of_translate_one(const void *blob, int parent, struct of_bus *bus,
                            struct of_bus *pbus, fdt32_t *addr,
                            int na, int ns, int pna, const char *rprop)
 {
@@ -1082,7 +1195,7 @@ static int of_translate_one(void * blob, int parent, struct of_bus *bus,
 
  finish:
        of_dump_addr("OF: parent translation for:", addr, pna);
-       debug("OF: with offset: "PRu64"\n", offset);
+       debug("OF: with offset: %" PRIu64 "\n", offset);
 
        /* Translate it into parent bus space */
        return pbus->translate(addr, offset, pna);
@@ -1098,8 +1211,8 @@ static int of_translate_one(void * blob, int parent, struct of_bus *bus,
  * that can be mapped to a cpu physical address). This is not really specified
  * that way, but this is traditionally the way IBM at least do things
  */
-static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in_addr,
-                                 const char *rprop)
+static u64 __of_translate_address(const void *blob, int node_offset,
+                                 const fdt32_t *in_addr, const char *rprop)
 {
        int parent;
        struct of_bus *bus, *pbus;
@@ -1114,13 +1227,13 @@ static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in
        parent = fdt_parent_offset(blob, node_offset);
        if (parent < 0)
                goto bail;
-       bus = &of_busses[0];
+       bus = of_match_bus(blob, parent);
 
-       /* Cound address cells & copy address locally */
+       /* Count address cells & copy address locally */
        bus->count_cells(blob, parent, &na, &ns);
        if (!OF_CHECK_COUNTS(na, ns)) {
-               printf("%s: Bad cell count for %s\n", __FUNCTION__,
-                      fdt_get_name(blob, node_offset, NULL));
+               printf("%s: Bad cell count #address-cells=%d #size-cells=%d for %s\n", __FUNCTION__,
+                       na, ns, fdt_get_name(blob, node_offset, NULL));
                goto bail;
        }
        memcpy(addr, in_addr, na * 4);
@@ -1143,11 +1256,11 @@ static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in
                }
 
                /* Get new parent bus and counts */
-               pbus = &of_busses[0];
+               pbus = of_match_bus(blob, parent);
                pbus->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, node_offset, NULL));
+                       printf("%s: Bad cell count #address-cells=%d #size-cells=%d for %s\n", __FUNCTION__,
+                               pna, pns, fdt_get_name(blob, node_offset, NULL));
                        break;
                }
 
@@ -1171,7 +1284,8 @@ static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in
        return result;
 }
 
-u64 fdt_translate_address(void *blob, int node_offset, const fdt32_t *in_addr)
+u64 fdt_translate_address(const void *blob, int node_offset,
+                         const fdt32_t *in_addr)
 {
        return __of_translate_address(blob, node_offset, in_addr, "ranges");
 }
@@ -1208,7 +1322,8 @@ int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
  */
 int fdt_alloc_phandle(void *blob)
 {
-       int offset, phandle = 0;
+       int offset;
+       uint32_t phandle = 0;
 
        for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
             offset = fdt_next_node(blob, offset, NULL)) {
@@ -1410,9 +1525,9 @@ int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr)
 
        dt_addr = fdt_translate_address(fdt, node, reg);
        if (addr != dt_addr) {
-               printf("Warning: U-Boot configured device %s at address %llx,\n"
-                      " but the device tree has it address %llx.\n",
-                      alias, addr, dt_addr);
+               printf("Warning: U-Boot configured device %s at address %"
+                      PRIx64 ",\n but the device tree has it address %"
+                      PRIx64 ".\n", alias, addr, dt_addr);
                return 0;
        }
 
@@ -1428,11 +1543,7 @@ u64 fdt_get_base_address(void *fdt, int node)
        u32 naddr;
        const fdt32_t *prop;
 
-       prop = fdt_getprop(fdt, node, "#address-cells", &size);
-       if (prop && size == 4)
-               naddr = be32_to_cpup(prop);
-       else
-               naddr = 2;
+       naddr = fdt_address_cells(fdt, node);
 
        prop = fdt_getprop(fdt, node, "ranges", &size);
 
@@ -1532,3 +1643,94 @@ int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
 
        return 0;
 }
+
+/**
+ * fdt_setup_simplefb_node - Fill and enable a simplefb node
+ *
+ * @fdt: ptr to device tree
+ * @node: offset of the simplefb node
+ * @base_address: framebuffer base address
+ * @width: width in pixels
+ * @height: height in pixels
+ * @stride: bytes per line
+ * @format: pixel format string
+ *
+ * Convenience function to fill and enable a simplefb node.
+ */
+int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
+                           u32 height, u32 stride, const char *format)
+{
+       char name[32];
+       fdt32_t cells[4];
+       int i, addrc, sizec, ret;
+
+       of_bus_default_count_cells(fdt, fdt_parent_offset(fdt, node),
+                                  &addrc, &sizec);
+       i = 0;
+       if (addrc == 2)
+               cells[i++] = cpu_to_fdt32(base_address >> 32);
+       cells[i++] = cpu_to_fdt32(base_address);
+       if (sizec == 2)
+               cells[i++] = 0;
+       cells[i++] = cpu_to_fdt32(height * stride);
+
+       ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i);
+       if (ret < 0)
+               return ret;
+
+       snprintf(name, sizeof(name), "framebuffer@%" PRIx64, base_address);
+       ret = fdt_set_name(fdt, node, name);
+       if (ret < 0)
+               return ret;
+
+       ret = fdt_setprop_u32(fdt, node, "width", width);
+       if (ret < 0)
+               return ret;
+
+       ret = fdt_setprop_u32(fdt, node, "height", height);
+       if (ret < 0)
+               return ret;
+
+       ret = fdt_setprop_u32(fdt, node, "stride", stride);
+       if (ret < 0)
+               return ret;
+
+       ret = fdt_setprop_string(fdt, node, "format", format);
+       if (ret < 0)
+               return ret;
+
+       ret = fdt_setprop_string(fdt, node, "status", "okay");
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * Update native-mode in display-timings from display environment variable.
+ * The node to update are specified by path.
+ */
+int fdt_fixup_display(void *blob, const char *path, const char *display)
+{
+       int off, toff;
+
+       if (!display || !path)
+               return -FDT_ERR_NOTFOUND;
+
+       toff = fdt_path_offset(blob, path);
+       if (toff >= 0)
+               toff = fdt_subnode_offset(blob, toff, "display-timings");
+       if (toff < 0)
+               return toff;
+
+       for (off = fdt_first_subnode(blob, toff);
+            off >= 0;
+            off = fdt_next_subnode(blob, off)) {
+               uint32_t h = fdt_get_phandle(blob, off);
+               debug("%s:0x%x\n", fdt_get_name(blob, off, NULL),
+                     fdt32_to_cpu(h));
+               if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0)
+                       return fdt_setprop_u32(blob, toff, "native-mode", h);
+       }
+       return toff;
+}