dm: core: support reading a single indexed u32 value
[oweals/u-boot.git] / drivers / core / ofnode.c
index d0bdea08df649852d4bafe04d4e188ff1d037d02..5bc3b02996ebf972d2575804830b5829f9a8af61 100644 (file)
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) 2017 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <dm.h>
 #include <fdtdec.h>
 #include <fdt_support.h>
-#include <libfdt.h>
+#include <malloc.h>
+#include <linux/libfdt.h>
 #include <dm/of_access.h>
 #include <dm/of_addr.h>
 #include <dm/ofnode.h>
@@ -40,7 +40,7 @@ int ofnode_read_u32(ofnode node, const char *propname, u32 *outp)
        return 0;
 }
 
-int ofnode_read_u32_default(ofnode node, const char *propname, u32 def)
+u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def)
 {
        assert(ofnode_valid(node));
        ofnode_read_u32(node, propname, &def);
@@ -48,6 +48,46 @@ int ofnode_read_u32_default(ofnode node, const char *propname, u32 def)
        return def;
 }
 
+int ofnode_read_u32_index(ofnode node, const char *propname, int index,
+                         u32 *outp)
+{
+       const fdt32_t *cell;
+       int len;
+
+       assert(ofnode_valid(node));
+       debug("%s: %s: ", __func__, propname);
+
+       if (ofnode_is_np(node))
+               return of_read_u32_index(ofnode_to_np(node), propname, index,
+                                        outp);
+
+       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
+                          &len);
+       if (!cell) {
+               debug("(not found)\n");
+               return -EINVAL;
+       }
+
+       if (len < (sizeof(int) * (index + 1))) {
+               debug("(not large enough)\n");
+               return -EOVERFLOW;
+       }
+
+       *outp = fdt32_to_cpu(cell[index]);
+       debug("%#x (%d)\n", *outp, *outp);
+
+       return 0;
+}
+
+u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index,
+                                 u32 def)
+{
+       assert(ofnode_valid(node));
+       ofnode_read_u32_index(node, propname, index, &def);
+
+       return def;
+}
+
 int ofnode_read_s32_default(ofnode node, const char *propname, s32 def)
 {
        assert(ofnode_valid(node));
@@ -56,6 +96,38 @@ int ofnode_read_s32_default(ofnode node, const char *propname, s32 def)
        return def;
 }
 
+int ofnode_read_u64(ofnode node, const char *propname, u64 *outp)
+{
+       const unaligned_fdt64_t *cell;
+       int len;
+
+       assert(ofnode_valid(node));
+       debug("%s: %s: ", __func__, propname);
+
+       if (ofnode_is_np(node))
+               return of_read_u64(ofnode_to_np(node), propname, outp);
+
+       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
+                          &len);
+       if (!cell || len < sizeof(*cell)) {
+               debug("(not found)\n");
+               return -EINVAL;
+       }
+       *outp = fdt64_to_cpu(cell[0]);
+       debug("%#llx (%lld)\n", (unsigned long long)*outp,
+             (unsigned long long)*outp);
+
+       return 0;
+}
+
+u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def)
+{
+       assert(ofnode_valid(node));
+       ofnode_read_u64(node, propname, &def);
+
+       return def;
+}
+
 bool ofnode_read_bool(ofnode node, const char *propname)
 {
        const void *prop;
@@ -70,30 +142,47 @@ bool ofnode_read_bool(ofnode node, const char *propname)
        return prop ? true : false;
 }
 
-const char *ofnode_read_string(ofnode node, const char *propname)
+const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep)
 {
-       const char *str = NULL;
-       int len = -1;
+       const char *val = NULL;
+       int len;
 
        assert(ofnode_valid(node));
        debug("%s: %s: ", __func__, propname);
 
        if (ofnode_is_np(node)) {
                struct property *prop = of_find_property(
-                               ofnode_to_np(node), propname, NULL);
+                               ofnode_to_np(node), propname, &len);
 
                if (prop) {
-                       str = prop->value;
+                       val = prop->value;
                        len = prop->length;
                }
        } else {
-               str = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
+               val = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
                                  propname, &len);
        }
-       if (!str) {
+       if (!val) {
                debug("<not found>\n");
+               if (sizep)
+                       *sizep = -FDT_ERR_NOTFOUND;
                return NULL;
        }
+       if (sizep)
+               *sizep = len;
+
+       return val;
+}
+
+const char *ofnode_read_string(ofnode node, const char *propname)
+{
+       const char *str;
+       int len;
+
+       str = ofnode_read_prop(node, propname, &len);
+       if (!str)
+               return NULL;
+
        if (strnlen(str, len) >= len) {
                debug("<invalid>\n");
                return NULL;
@@ -103,6 +192,16 @@ const char *ofnode_read_string(ofnode node, const char *propname)
        return str;
 }
 
+int ofnode_read_size(ofnode node, const char *propname)
+{
+       int len;
+
+       if (!ofnode_read_prop(node, propname, &len))
+               return -EINVAL;
+
+       return len;
+}
+
 ofnode ofnode_find_subnode(ofnode node, const char *subnode_name)
 {
        ofnode subnode;
@@ -181,7 +280,11 @@ ofnode ofnode_get_parent(ofnode node)
 
 const char *ofnode_get_name(ofnode node)
 {
-       assert(ofnode_valid(node));
+       if (!ofnode_valid(node)) {
+               debug("%s node not valid\n", __func__);
+               return NULL;
+       }
+
        if (ofnode_is_np(node))
                return strrchr(node.np->full_name, '/') + 1;
 
@@ -201,52 +304,48 @@ ofnode ofnode_get_by_phandle(uint phandle)
        return node;
 }
 
-int ofnode_read_size(ofnode node, const char *propname)
+fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size)
 {
-       int len;
+       int na, ns;
 
-       if (ofnode_is_np(node)) {
-               struct property *prop = of_find_property(
-                               ofnode_to_np(node), propname, NULL);
-
-               if (prop)
-                       return prop->length;
-       } else {
-               if (fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
-                               &len))
-                       return len;
-       }
-
-       return -EINVAL;
-}
-
-fdt_addr_t ofnode_get_addr_index(ofnode node, int index)
-{
        if (ofnode_is_np(node)) {
                const __be32 *prop_val;
+               u64 size64;
                uint flags;
-               u64 size;
-               int na;
 
-               prop_val = of_get_address(ofnode_to_np(node), index, &size,
+               prop_val = of_get_address(ofnode_to_np(node), index, &size64,
                                          &flags);
                if (!prop_val)
                        return FDT_ADDR_T_NONE;
+               if (size)
+                       *size = size64;
+
+               ns = of_n_size_cells(ofnode_to_np(node));
 
-               if (IS_ENABLED(CONFIG_OF_TRANSLATE)) {
+               if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) {
                        return of_translate_address(ofnode_to_np(node), prop_val);
                } else {
                        na = of_n_addr_cells(ofnode_to_np(node));
                        return of_read_number(prop_val, na);
                }
        } else {
-               return fdt_get_base_address(gd->fdt_blob,
-                                           ofnode_to_offset(node));
+               na = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
+               ns = ofnode_read_simple_size_cells(ofnode_get_parent(node));
+               return fdtdec_get_addr_size_fixed(gd->fdt_blob,
+                                                 ofnode_to_offset(node), "reg",
+                                                 index, na, ns, size, true);
        }
 
        return FDT_ADDR_T_NONE;
 }
 
+fdt_addr_t ofnode_get_addr_index(ofnode node, int index)
+{
+       fdt_size_t size;
+
+       return ofnode_get_addr_size_index(node, index, &size);
+}
+
 fdt_addr_t ofnode_get_addr(ofnode node)
 {
        return ofnode_get_addr_index(node, 0);
@@ -369,20 +468,25 @@ ofnode ofnode_path(const char *path)
                return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
 }
 
-const char *ofnode_get_chosen_prop(const char *name)
+const void *ofnode_read_chosen_prop(const char *propname, int *sizep)
 {
        ofnode chosen_node;
 
        chosen_node = ofnode_path("/chosen");
 
-       return ofnode_read_string(chosen_node, name);
+       return ofnode_read_prop(chosen_node, propname, sizep);
+}
+
+const char *ofnode_read_chosen_string(const char *propname)
+{
+       return ofnode_read_chosen_prop(propname, NULL);
 }
 
 ofnode ofnode_get_chosen_node(const char *name)
 {
        const char *prop;
 
-       prop = ofnode_get_chosen_prop(name);
+       prop = ofnode_read_chosen_prop(name, NULL);
        if (!prop)
                return ofnode_null();
 
@@ -507,9 +611,13 @@ fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property,
                if (!prop)
                        return FDT_ADDR_T_NONE;
                na = of_n_addr_cells(np);
-               ns = of_n_addr_cells(np);
+               ns = of_n_size_cells(np);
                *sizep = of_read_number(prop + na, ns);
-               return of_read_number(prop, na);
+
+               if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0)
+                       return of_translate_address(np, prop);
+               else
+                       return of_read_number(prop, na);
        } else {
                return fdtdec_get_addr_size(gd->fdt_blob,
                                            ofnode_to_offset(node), property,
@@ -566,7 +674,7 @@ int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type,
                        if ((fdt32_to_cpu(*cell) & type) == type) {
                                addr->phys_hi = fdt32_to_cpu(cell[0]);
                                addr->phys_mid = fdt32_to_cpu(cell[1]);
-                               addr->phys_lo = fdt32_to_cpu(cell[1]);
+                               addr->phys_lo = fdt32_to_cpu(cell[2]);
                                break;
                        }
 
@@ -589,6 +697,42 @@ fail:
        return ret;
 }
 
+int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device)
+{
+       const char *list, *end;
+       int len;
+
+       list = ofnode_get_property(node, "compatible", &len);
+       if (!list)
+               return -ENOENT;
+
+       end = list + len;
+       while (list < end) {
+               len = strlen(list);
+               if (len >= strlen("pciVVVV,DDDD")) {
+                       char *s = strstr(list, "pci");
+
+                       /*
+                        * check if the string is something like pciVVVV,DDDD.RR
+                        * or just pciVVVV,DDDD
+                        */
+                       if (s && s[7] == ',' &&
+                           (s[12] == '.' || s[12] == 0)) {
+                               s += 3;
+                               *vendor = simple_strtol(s, NULL, 16);
+
+                               s += 5;
+                               *device = simple_strtol(s, NULL, 16);
+
+                               return 0;
+                       }
+               }
+               list += (len + 1);
+       }
+
+       return -ENOENT;
+}
+
 int ofnode_read_addr_cells(ofnode node)
 {
        if (ofnode_is_np(node))
@@ -623,16 +767,18 @@ int ofnode_read_simple_size_cells(ofnode node)
 
 bool ofnode_pre_reloc(ofnode node)
 {
+#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD)
+       /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass
+        * had property dm-pre-reloc or u-boot,dm-spl/tpl.
+        * They are removed in final dtb (fdtgrep 2nd pass)
+        */
+       return true;
+#else
        if (ofnode_read_bool(node, "u-boot,dm-pre-reloc"))
                return true;
-
-#ifdef CONFIG_TPL_BUILD
-       if (ofnode_read_bool(node, "u-boot,dm-tpl"))
+       if (ofnode_read_bool(node, "u-boot,dm-pre-proper"))
                return true;
-#elif defined(CONFIG_SPL_BUILD)
-       if (ofnode_read_bool(node, "u-boot,dm-spl"))
-               return true;
-#else
+
        /*
         * In regular builds individual spl and tpl handling both
         * count as handled pre-relocation for later second init.
@@ -640,9 +786,9 @@ bool ofnode_pre_reloc(ofnode node)
        if (ofnode_read_bool(node, "u-boot,dm-spl") ||
            ofnode_read_bool(node, "u-boot,dm-tpl"))
                return true;
-#endif
 
        return false;
+#endif
 }
 
 int ofnode_read_resource(ofnode node, uint index, struct resource *res)
@@ -684,3 +830,120 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr)
        else
                return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
 }
+
+u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
+{
+       if (ofnode_is_np(node))
+               return of_translate_dma_address(ofnode_to_np(node), in_addr);
+       else
+               return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
+}
+
+int ofnode_device_is_compatible(ofnode node, const char *compat)
+{
+       if (ofnode_is_np(node))
+               return of_device_is_compatible(ofnode_to_np(node), compat,
+                                              NULL, NULL);
+       else
+               return !fdt_node_check_compatible(gd->fdt_blob,
+                                                 ofnode_to_offset(node),
+                                                 compat);
+}
+
+ofnode ofnode_by_compatible(ofnode from, const char *compat)
+{
+       if (of_live_active()) {
+               return np_to_ofnode(of_find_compatible_node(
+                       (struct device_node *)ofnode_to_np(from), NULL,
+                       compat));
+       } else {
+               return offset_to_ofnode(fdt_node_offset_by_compatible(
+                               gd->fdt_blob, ofnode_to_offset(from), compat));
+       }
+}
+
+ofnode ofnode_by_prop_value(ofnode from, const char *propname,
+                           const void *propval, int proplen)
+{
+       if (of_live_active()) {
+               return np_to_ofnode(of_find_node_by_prop_value(
+                       (struct device_node *)ofnode_to_np(from), propname,
+                       propval, proplen));
+       } else {
+               return offset_to_ofnode(fdt_node_offset_by_prop_value(
+                               gd->fdt_blob, ofnode_to_offset(from),
+                               propname, propval, proplen));
+       }
+}
+
+int ofnode_write_prop(ofnode node, const char *propname, int len,
+                     const void *value)
+{
+       const struct device_node *np = ofnode_to_np(node);
+       struct property *pp;
+       struct property *pp_last = NULL;
+       struct property *new;
+
+       if (!of_live_active())
+               return -ENOSYS;
+
+       if (!np)
+               return -EINVAL;
+
+       for (pp = np->properties; pp; pp = pp->next) {
+               if (strcmp(pp->name, propname) == 0) {
+                       /* Property exists -> change value */
+                       pp->value = (void *)value;
+                       pp->length = len;
+                       return 0;
+               }
+               pp_last = pp;
+       }
+
+       if (!pp_last)
+               return -ENOENT;
+
+       /* Property does not exist -> append new property */
+       new = malloc(sizeof(struct property));
+       if (!new)
+               return -ENOMEM;
+
+       new->name = strdup(propname);
+       if (!new->name) {
+               free(new);
+               return -ENOMEM;
+       }
+
+       new->value = (void *)value;
+       new->length = len;
+       new->next = NULL;
+
+       pp_last->next = new;
+
+       return 0;
+}
+
+int ofnode_write_string(ofnode node, const char *propname, const char *value)
+{
+       if (!of_live_active())
+               return -ENOSYS;
+
+       assert(ofnode_valid(node));
+
+       debug("%s: %s = %s", __func__, propname, value);
+
+       return ofnode_write_prop(node, propname, strlen(value) + 1, value);
+}
+
+int ofnode_set_enabled(ofnode node, bool value)
+{
+       if (!of_live_active())
+               return -ENOSYS;
+
+       assert(ofnode_valid(node));
+
+       if (value)
+               return ofnode_write_string(node, "status", "okay");
+       else
+               return ofnode_write_string(node, "status", "disabled");
+}