]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/fdt_support.c
Merge branch 'master' of git://git.denx.de/u-boot-ti
[karo-tx-uboot.git] / common / fdt_support.c
index b6f252a87b0c9de15ec7ac02eca1990565392b43..33336be062ed6e4a3c0530b413988a081385ff39 100644 (file)
@@ -2,6 +2,8 @@
  * (C) Copyright 2007
  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
  *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
  * See file CREDITS for list of people who contributed to this
  * project.
  *
@@ -474,134 +476,6 @@ void fdt_fixup_ethernet(void *fdt)
        }
 }
 
-#ifdef CONFIG_HAS_FSL_DR_USB
-void fdt_fixup_dr_usb(void *blob, bd_t *bd)
-{
-       char *mode;
-       char *type;
-       const char *compat = "fsl-usb2-dr";
-       const char *prop_mode = "dr_mode";
-       const char *prop_type = "phy_type";
-       int node_offset;
-       int err;
-
-       mode = getenv("usb_dr_mode");
-       type = getenv("usb_phy_type");
-       if (!mode && !type)
-               return;
-
-       node_offset = fdt_node_offset_by_compatible(blob, 0, compat);
-       if (node_offset < 0) {
-               printf("WARNING: could not find compatible node %s: %s.\n",
-                       compat, fdt_strerror(node_offset));
-               return;
-       }
-
-       if (mode) {
-               err = fdt_setprop(blob, node_offset, prop_mode, mode,
-                                 strlen(mode) + 1);
-               if (err < 0)
-                       printf("WARNING: could not set %s for %s: %s.\n",
-                              prop_mode, compat, fdt_strerror(err));
-       }
-
-       if (type) {
-               err = fdt_setprop(blob, node_offset, prop_type, type,
-                                 strlen(type) + 1);
-               if (err < 0)
-                       printf("WARNING: could not set %s for %s: %s.\n",
-                              prop_type, compat, fdt_strerror(err));
-       }
-}
-#endif /* CONFIG_HAS_FSL_DR_USB */
-
-#if defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx)
-/*
- * update crypto node properties to a specified revision of the SEC
- * called with sec_rev == 0 if not on an mpc8xxxE processor
- */
-void fdt_fixup_crypto_node(void *blob, int sec_rev)
-{
-       const struct sec_rev_prop {
-               u32 sec_rev;
-               u32 num_channels;
-               u32 channel_fifo_len;
-               u32 exec_units_mask;
-               u32 descriptor_types_mask;
-       } sec_rev_prop_list [] = {
-               { 0x0200, 4, 24, 0x07e, 0x01010ebf }, /* SEC 2.0 */
-               { 0x0201, 4, 24, 0x0fe, 0x012b0ebf }, /* SEC 2.1 */
-               { 0x0202, 1, 24, 0x04c, 0x0122003f }, /* SEC 2.2 */
-               { 0x0204, 4, 24, 0x07e, 0x012b0ebf }, /* SEC 2.4 */
-               { 0x0300, 4, 24, 0x9fe, 0x03ab0ebf }, /* SEC 3.0 */
-               { 0x0303, 4, 24, 0x97c, 0x03ab0abf }, /* SEC 3.3 */
-       };
-       char compat_strlist[ARRAY_SIZE(sec_rev_prop_list) *
-                           sizeof("fsl,secX.Y")];
-       int crypto_node, sec_idx, err;
-       char *p;
-       u32 val;
-
-       /* locate crypto node based on lowest common compatible */
-       crypto_node = fdt_node_offset_by_compatible(blob, -1, "fsl,sec2.0");
-       if (crypto_node == -FDT_ERR_NOTFOUND)
-               return;
-
-       /* delete it if not on an E-processor */
-       if (crypto_node > 0 && !sec_rev) {
-               fdt_del_node(blob, crypto_node);
-               return;
-       }
-
-       /* else we got called for possible uprev */
-       for (sec_idx = 0; sec_idx < ARRAY_SIZE(sec_rev_prop_list); sec_idx++)
-               if (sec_rev_prop_list[sec_idx].sec_rev == sec_rev)
-                       break;
-
-       if (sec_idx == ARRAY_SIZE(sec_rev_prop_list)) {
-               puts("warning: unknown SEC revision number\n");
-               return;
-       }
-
-       val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].num_channels);
-       err = fdt_setprop(blob, crypto_node, "fsl,num-channels", &val, 4);
-       if (err < 0)
-               printf("WARNING: could not set crypto property: %s\n",
-                      fdt_strerror(err));
-
-       val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].descriptor_types_mask);
-       err = fdt_setprop(blob, crypto_node, "fsl,descriptor-types-mask", &val, 4);
-       if (err < 0)
-               printf("WARNING: could not set crypto property: %s\n",
-                      fdt_strerror(err));
-
-       val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].exec_units_mask);
-       err = fdt_setprop(blob, crypto_node, "fsl,exec-units-mask", &val, 4);
-       if (err < 0)
-               printf("WARNING: could not set crypto property: %s\n",
-                      fdt_strerror(err));
-
-       val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].channel_fifo_len);
-       err = fdt_setprop(blob, crypto_node, "fsl,channel-fifo-len", &val, 4);
-       if (err < 0)
-               printf("WARNING: could not set crypto property: %s\n",
-                      fdt_strerror(err));
-
-       val = 0;
-       while (sec_idx >= 0) {
-               p = compat_strlist + val;
-               val += sprintf(p, "fsl,sec%d.%d",
-                       (sec_rev_prop_list[sec_idx].sec_rev & 0xff00) >> 8,
-                       sec_rev_prop_list[sec_idx].sec_rev & 0x00ff) + 1;
-               sec_idx--;
-       }
-       err = fdt_setprop(blob, crypto_node, "compatible", &compat_strlist, val);
-       if (err < 0)
-               printf("WARNING: could not set crypto property: %s\n",
-                      fdt_strerror(err));
-}
-#endif /* defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx) */
-
 /* Resize the fdt to its actual size + a bit of padding */
 int fdt_resize(void *blob)
 {
@@ -624,11 +498,12 @@ int fdt_resize(void *blob)
 
        /*
         * Calculate the actual size of the fdt
-        * plus the size needed for two fdt_add_mem_rsv, one
-        * for the fdt itself and one for a possible initrd
+        * plus the size needed for 5 fdt_add_mem_rsv, one
+        * for the fdt itself and 4 for a possible initrd
+        * ((initrd-start + initrd-end) * 2 (name & value))
         */
        actualsize = fdt_off_dt_strings(blob) +
-               fdt_size_dt_strings(blob) + 2*sizeof(struct fdt_reserve_entry);
+               fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry);
 
        /* Make it so the fdt ends on a page boundary */
        actualsize = ALIGN(actualsize + ((uint)blob & 0xfff), 0x1000);
@@ -989,3 +864,310 @@ void fdt_del_node_and_alias(void *blob, const char *alias)
        off = fdt_path_offset(blob, "/aliases");
        fdt_delprop(blob, off, alias);
 }
+
+/* Helper to read a big number; size is in cells (not bytes) */
+static inline u64 of_read_number(const __be32 *cell, int size)
+{
+       u64 r = 0;
+       while (size--)
+               r = (r << 32) | be32_to_cpu(*(cell++));
+       return r;
+}
+
+static int of_n_cells(const void *blob, int nodeoffset, const char *name)
+{
+       int np;
+       const int *ip;
+
+       do {
+               np = fdt_parent_offset(blob, nodeoffset);
+
+               if (np >= 0)
+                       nodeoffset = np;
+               ip = (int *)fdt_getprop(blob, nodeoffset, name, NULL);
+               if (ip)
+                       return be32_to_cpup(ip);
+       } while (np >= 0);
+
+       /* No #<NAME>-cells property for the root node */
+       return 1;
+}
+
+int of_n_addr_cells(const void *blob, int nodeoffset)
+{
+       return of_n_cells(blob, nodeoffset, "#address-cells");
+}
+
+int of_n_size_cells(const void *blob, int nodeoffset)
+{
+       return of_n_cells(blob, nodeoffset, "#size-cells");
+}
+
+#define PRu64  "%llx"
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS      4
+#define OF_BAD_ADDR    ((u64)-1)
+#define OF_CHECK_COUNTS(na, ns)        ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+                       (ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void of_dump_addr(const char *s, const u32 *addr, int na)
+{
+       printf("%s", s);
+       while(na--)
+               printf(" %08x", *(addr++));
+       printf("\n");
+}
+#else
+static void of_dump_addr(const char *s, const u32 *addr, int na) { }
+#endif
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+       const char      *name;
+       const char      *addresses;
+       void            (*count_cells)(void *blob, int offset,
+                               int *addrc, int *sizec);
+       u64             (*map)(u32 *addr, const u32 *range,
+                               int na, int ns, int pna);
+       int             (*translate)(u32 *addr, u64 offset, int na);
+};
+
+/* Default translator (generic bus) */
+static void of_bus_default_count_cells(void *blob, int offset,
+                                       int *addrc, int *sizec)
+{
+       if (addrc)
+               *addrc = of_n_addr_cells(blob, offset);
+       if (sizec)
+               *sizec = of_n_size_cells(blob, offset);
+}
+
+static u64 of_bus_default_map(u32 *addr, const u32 *range,
+               int na, int ns, int pna)
+{
+       u64 cp, s, da;
+
+       cp = of_read_number(range, na);
+       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);
+
+       if (da < cp || da >= (cp + s))
+               return OF_BAD_ADDR;
+       return da - cp;
+}
+
+static int of_bus_default_translate(u32 *addr, u64 offset, int na)
+{
+       u64 a = of_read_number(addr, na);
+       memset(addr, 0, na * 4);
+       a += offset;
+       if (na > 1)
+               addr[na - 2] = a >> 32;
+       addr[na - 1] = a & 0xffffffffu;
+
+       return 0;
+}
+
+/* Array of bus specific translators */
+static struct of_bus of_busses[] = {
+       /* Default */
+       {
+               .name = "default",
+               .addresses = "reg",
+               .count_cells = of_bus_default_count_cells,
+               .map = of_bus_default_map,
+               .translate = of_bus_default_translate,
+       },
+};
+
+static int of_translate_one(void * blob, int parent, struct of_bus *bus,
+                           struct of_bus *pbus, u32 *addr,
+                           int na, int ns, int pna, const char *rprop)
+{
+       const u32 *ranges;
+       int rlen;
+       int rone;
+       u64 offset = OF_BAD_ADDR;
+
+       /* Normally, an absence of a "ranges" property means we are
+        * crossing a non-translatable boundary, and thus the addresses
+        * below the current not cannot be converted to CPU physical ones.
+        * Unfortunately, while this is very clear in the spec, it's not
+        * what Apple understood, and they do have things like /uni-n or
+        * /ht nodes with no "ranges" property and a lot of perfectly
+        * useable mapped devices below them. Thus we treat the absence of
+        * "ranges" as equivalent to an empty "ranges" property which means
+        * a 1:1 translation at that level. It's up to the caller not to try
+        * to translate addresses that aren't supposed to be translated in
+        * the first place. --BenH.
+        */
+       ranges = (u32 *)fdt_getprop(blob, parent, rprop, &rlen);
+       if (ranges == NULL || rlen == 0) {
+               offset = of_read_number(addr, na);
+               memset(addr, 0, pna * 4);
+               debug("OF: no ranges, 1:1 translation\n");
+               goto finish;
+       }
+
+       debug("OF: walking ranges...\n");
+
+       /* Now walk through the ranges */
+       rlen /= 4;
+       rone = na + pna + ns;
+       for (; rlen >= rone; rlen -= rone, ranges += rone) {
+               offset = bus->map(addr, ranges, na, ns, pna);
+               if (offset != OF_BAD_ADDR)
+                       break;
+       }
+       if (offset == OF_BAD_ADDR) {
+               debug("OF: not found !\n");
+               return 1;
+       }
+       memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+       of_dump_addr("OF: parent translation for:", addr, pna);
+       debug("OF: with offset: "PRu64"\n", offset);
+
+       /* Translate it into parent bus space */
+       return pbus->translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * 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
+ */
+u64 __of_translate_address(void *blob, int node_offset, const u32 *in_addr,
+                          const char *rprop)
+{
+       int parent;
+       struct of_bus *bus, *pbus;
+       u32 addr[OF_MAX_ADDR_CELLS];
+       int na, ns, pna, pns;
+       u64 result = OF_BAD_ADDR;
+
+       debug("OF: ** translation for device %s **\n",
+               fdt_get_name(blob, node_offset, NULL));
+
+       /* Get parent & match bus type */
+       parent = fdt_parent_offset(blob, node_offset);
+       if (parent < 0)
+               goto bail;
+       bus = &of_busses[0];
+
+       /* Cound address cells & copy address locally */
+       bus->count_cells(blob, node_offset, &na, &ns);
+       if (!OF_CHECK_COUNTS(na, ns)) {
+               printf("%s: Bad cell count for %s\n", __FUNCTION__,
+                      fdt_get_name(blob, node_offset, NULL));
+               goto bail;
+       }
+       memcpy(addr, in_addr, na * 4);
+
+       debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
+           bus->name, na, ns, fdt_get_name(blob, parent, NULL));
+       of_dump_addr("OF: translating address:", addr, na);
+
+       /* Translate */
+       for (;;) {
+               /* Switch to parent bus */
+               node_offset = parent;
+               parent = fdt_parent_offset(blob, node_offset);
+
+               /* If root, we have finished */
+               if (parent < 0) {
+                       debug("OF: reached root node\n");
+                       result = of_read_number(addr, na);
+                       break;
+               }
+
+               /* Get new parent bus and counts */
+               pbus = &of_busses[0];
+               pbus->count_cells(blob, node_offset, &pna, &pns);
+               if (!OF_CHECK_COUNTS(pna, pns)) {
+                       printf("%s: Bad cell count for %s\n", __FUNCTION__,
+                               fdt_get_name(blob, node_offset, NULL));
+                       break;
+               }
+
+               debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
+                   pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
+
+               /* Apply bus translation */
+               if (of_translate_one(blob, node_offset, bus, pbus,
+                                       addr, na, ns, pna, rprop))
+                       break;
+
+               /* Complete the move up one level */
+               na = pna;
+               ns = pns;
+               bus = pbus;
+
+               of_dump_addr("OF: one level translation:", addr, na);
+       }
+ bail:
+
+       return result;
+}
+
+u64 fdt_translate_address(void *blob, int node_offset, const u32 *in_addr)
+{
+       return __of_translate_address(blob, node_offset, in_addr, "ranges");
+}
+
+/**
+ * fdt_node_offset_by_compat_reg: Find a node that matches compatiable and
+ * who's reg property matches a physical cpu address
+ *
+ * @blob: ptr to device tree
+ * @compat: compatiable string to match
+ * @compat_off: property name
+ *
+ */
+int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
+                                       phys_addr_t compat_off)
+{
+       int len, off = fdt_node_offset_by_compatible(blob, -1, compat);
+       while (off != -FDT_ERR_NOTFOUND) {
+               u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", &len);
+               if (reg) {
+                       if (compat_off == fdt_translate_address(blob, off, reg))
+                               return off;
+               }
+               off = fdt_node_offset_by_compatible(blob, off, compat);
+       }
+
+       return -FDT_ERR_NOTFOUND;
+}
+
+/**
+ * fdt_alloc_phandle: Return next free phandle value
+ *
+ * @blob: ptr to device tree
+ */
+int fdt_alloc_phandle(void *blob)
+{
+       int offset, len, phandle = 0;
+       const u32 *val;
+
+       for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
+            offset = fdt_next_node(blob, offset, NULL)) {
+               val = fdt_getprop(blob, offset, "linux,phandle", &len);
+               if (val)
+                       phandle = max(*val, phandle);
+       }
+
+       return phandle + 1;
+}