From 072de836f53be56a70cecf70b43ae43b7ce17376 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 10:08:36 +0000
Subject: [PATCH] mk-rootfs.sh
---
kernel/drivers/of/base.c | 499 +++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 356 insertions(+), 143 deletions(-)
diff --git a/kernel/drivers/of/base.c b/kernel/drivers/of/base.c
index b28dee2..bcc88cb 100644
--- a/kernel/drivers/of/base.c
+++ b/kernel/drivers/of/base.c
@@ -16,6 +16,7 @@
#define pr_fmt(fmt) "OF: " fmt
+#include <linux/bitmap.h>
#include <linux/console.h>
#include <linux/ctype.h>
#include <linux/cpu.h>
@@ -78,33 +79,52 @@
}
EXPORT_SYMBOL(of_node_name_prefix);
-int of_n_addr_cells(struct device_node *np)
+static bool __of_node_is_type(const struct device_node *np, const char *type)
+{
+ const char *match = __of_get_property(np, "device_type", NULL);
+
+ return np && match && type && !strcmp(match, type);
+}
+
+int of_bus_n_addr_cells(struct device_node *np)
{
u32 cells;
- do {
- if (np->parent)
- np = np->parent;
+ for (; np; np = np->parent)
if (!of_property_read_u32(np, "#address-cells", &cells))
return cells;
- } while (np->parent);
+
/* No #address-cells property for the root node */
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
}
+
+int of_n_addr_cells(struct device_node *np)
+{
+ if (np->parent)
+ np = np->parent;
+
+ return of_bus_n_addr_cells(np);
+}
EXPORT_SYMBOL(of_n_addr_cells);
-int of_n_size_cells(struct device_node *np)
+int of_bus_n_size_cells(struct device_node *np)
{
u32 cells;
- do {
- if (np->parent)
- np = np->parent;
+ for (; np; np = np->parent)
if (!of_property_read_u32(np, "#size-cells", &cells))
return cells;
- } while (np->parent);
+
/* No #size-cells property for the root node */
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+}
+
+int of_n_size_cells(struct device_node *np)
+{
+ if (np->parent)
+ np = np->parent;
+
+ return of_bus_n_size_cells(np);
}
EXPORT_SYMBOL(of_n_size_cells);
@@ -115,115 +135,38 @@
}
#endif
-/*
- * Assumptions behind phandle_cache implementation:
- * - phandle property values are in a contiguous range of 1..n
- *
- * If the assumptions do not hold, then
- * - the phandle lookup overhead reduction provided by the cache
- * will likely be less
- */
+#define OF_PHANDLE_CACHE_BITS 7
+#define OF_PHANDLE_CACHE_SZ BIT(OF_PHANDLE_CACHE_BITS)
-static struct device_node **phandle_cache;
-static u32 phandle_cache_mask;
+static struct device_node *phandle_cache[OF_PHANDLE_CACHE_SZ];
+
+static u32 of_phandle_cache_hash(phandle handle)
+{
+ return hash_32(handle, OF_PHANDLE_CACHE_BITS);
+}
/*
* Caller must hold devtree_lock.
*/
-static void __of_free_phandle_cache(void)
+void __of_phandle_cache_inv_entry(phandle handle)
{
- u32 cache_entries = phandle_cache_mask + 1;
- u32 k;
-
- if (!phandle_cache)
- return;
-
- for (k = 0; k < cache_entries; k++)
- of_node_put(phandle_cache[k]);
-
- kfree(phandle_cache);
- phandle_cache = NULL;
-}
-
-int of_free_phandle_cache(void)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&devtree_lock, flags);
-
- __of_free_phandle_cache();
-
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- return 0;
-}
-#if !defined(CONFIG_MODULES)
-late_initcall_sync(of_free_phandle_cache);
-#endif
-
-/*
- * Caller must hold devtree_lock.
- */
-void __of_free_phandle_cache_entry(phandle handle)
-{
- phandle masked_handle;
+ u32 handle_hash;
struct device_node *np;
if (!handle)
return;
- masked_handle = handle & phandle_cache_mask;
+ handle_hash = of_phandle_cache_hash(handle);
- if (phandle_cache) {
- np = phandle_cache[masked_handle];
- if (np && handle == np->phandle) {
- of_node_put(np);
- phandle_cache[masked_handle] = NULL;
- }
- }
-}
-
-void of_populate_phandle_cache(void)
-{
- unsigned long flags;
- u32 cache_entries;
- struct device_node *np;
- u32 phandles = 0;
-
- raw_spin_lock_irqsave(&devtree_lock, flags);
-
- __of_free_phandle_cache();
-
- for_each_of_allnodes(np)
- if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL)
- phandles++;
-
- if (!phandles)
- goto out;
-
- cache_entries = roundup_pow_of_two(phandles);
- phandle_cache_mask = cache_entries - 1;
-
- phandle_cache = kcalloc(cache_entries, sizeof(*phandle_cache),
- GFP_ATOMIC);
- if (!phandle_cache)
- goto out;
-
- for_each_of_allnodes(np)
- if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) {
- of_node_get(np);
- phandle_cache[np->phandle & phandle_cache_mask] = np;
- }
-
-out:
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ np = phandle_cache[handle_hash];
+ if (np && handle == np->phandle)
+ phandle_cache[handle_hash] = NULL;
}
void __init of_core_init(void)
{
struct device_node *np;
- of_populate_phandle_cache();
/* Create the kset, and register existing nodes */
mutex_lock(&of_mutex);
@@ -233,8 +176,11 @@
pr_err("failed to register existing nodes\n");
return;
}
- for_each_of_allnodes(np)
+ for_each_of_allnodes(np) {
__of_attach_node_sysfs(np);
+ if (np->phandle && !phandle_cache[of_phandle_cache_hash(np->phandle)])
+ phandle_cache[of_phandle_cache_hash(np->phandle)] = np;
+ }
mutex_unlock(&of_mutex);
/* Symlink in /proc as required by userspace ABI */
@@ -373,6 +319,8 @@
ac = of_n_addr_cells(cpun);
cell = of_get_property(cpun, prop_name, &prop_len);
+ if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0))
+ return true;
if (!cell || !ac)
return false;
prop_len /= sizeof(*cell) * ac;
@@ -433,7 +381,7 @@
{
struct device_node *cpun;
- for_each_node_by_type(cpun, "cpu") {
+ for_each_of_cpu_node(cpun) {
if (arch_find_n_match_cpu_physical_id(cpun, cpu, thread))
return cpun;
}
@@ -466,6 +414,42 @@
return -ENODEV;
}
EXPORT_SYMBOL(of_cpu_node_to_id);
+
+/**
+ * of_get_cpu_state_node - Get CPU's idle state node at the given index
+ *
+ * @cpu_node: The device node for the CPU
+ * @index: The index in the list of the idle states
+ *
+ * Two generic methods can be used to describe a CPU's idle states, either via
+ * a flattened description through the "cpu-idle-states" binding or via the
+ * hierarchical layout, using the "power-domains" and the "domain-idle-states"
+ * bindings. This function check for both and returns the idle state node for
+ * the requested index.
+ *
+ * In case an idle state node is found at @index, the refcount is incremented
+ * for it, so call of_node_put() on it when done. Returns NULL if not found.
+ */
+struct device_node *of_get_cpu_state_node(struct device_node *cpu_node,
+ int index)
+{
+ struct of_phandle_args args;
+ int err;
+
+ err = of_parse_phandle_with_args(cpu_node, "power-domains",
+ "#power-domain-cells", 0, &args);
+ if (!err) {
+ struct device_node *state_node =
+ of_parse_phandle(args.np, "domain-idle-states", index);
+
+ of_node_put(args.np);
+ if (state_node)
+ return state_node;
+ }
+
+ return of_parse_phandle(cpu_node, "cpu-idle-states", index);
+}
+EXPORT_SYMBOL(of_get_cpu_state_node);
/**
* __of_device_is_compatible() - Check if the node matches given constraints
@@ -520,14 +504,14 @@
/* Matching type is better than matching name */
if (type && type[0]) {
- if (!device->type || of_node_cmp(type, device->type))
+ if (!__of_node_is_type(device, type))
return 0;
score += 2;
}
/* Matching name is a bit better than not */
if (name && name[0]) {
- if (!device->name || of_node_cmp(name, device->name))
+ if (!of_node_name_eq(device, name))
return 0;
score++;
}
@@ -788,6 +772,43 @@
EXPORT_SYMBOL(of_get_next_available_child);
/**
+ * of_get_next_cpu_node - Iterate on cpu nodes
+ * @prev: previous child of the /cpus node, or NULL to get first
+ *
+ * Returns a cpu node pointer with refcount incremented, use of_node_put()
+ * on it when done. Returns NULL when prev is the last child. Decrements
+ * the refcount of prev.
+ */
+struct device_node *of_get_next_cpu_node(struct device_node *prev)
+{
+ struct device_node *next = NULL;
+ unsigned long flags;
+ struct device_node *node;
+
+ if (!prev)
+ node = of_find_node_by_path("/cpus");
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ if (prev)
+ next = prev->sibling;
+ else if (node) {
+ next = node->child;
+ of_node_put(node);
+ }
+ for (; next; next = next->sibling) {
+ if (!(of_node_name_eq(next, "cpu") ||
+ __of_node_is_type(next, "cpu")))
+ continue;
+ if (of_node_get(next))
+ break;
+ }
+ of_node_put(prev);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return next;
+}
+EXPORT_SYMBOL(of_get_next_cpu_node);
+
+/**
* of_get_compatible_child - Find compatible child node
* @parent: parent node
* @compatible: compatible string
@@ -829,7 +850,7 @@
struct device_node *child;
for_each_child_of_node(node, child)
- if (child->name && (of_node_cmp(child->name, name) == 0))
+ if (of_node_name_eq(child, name))
break;
return child;
}
@@ -955,8 +976,7 @@
raw_spin_lock_irqsave(&devtree_lock, flags);
for_each_of_allnodes_from(from, np)
- if (np->name && (of_node_cmp(np->name, name) == 0)
- && of_node_get(np))
+ if (of_node_name_eq(np, name) && of_node_get(np))
break;
of_node_put(from);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
@@ -984,8 +1004,7 @@
raw_spin_lock_irqsave(&devtree_lock, flags);
for_each_of_allnodes_from(from, np)
- if (np->type && (of_node_cmp(np->type, type) == 0)
- && of_node_get(np))
+ if (__of_node_is_type(np, type) && of_node_get(np))
break;
of_node_put(from);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
@@ -1178,36 +1197,24 @@
{
struct device_node *np = NULL;
unsigned long flags;
- phandle masked_handle;
+ u32 handle_hash;
if (!handle)
return NULL;
+ handle_hash = of_phandle_cache_hash(handle);
+
raw_spin_lock_irqsave(&devtree_lock, flags);
- masked_handle = handle & phandle_cache_mask;
-
- if (phandle_cache) {
- if (phandle_cache[masked_handle] &&
- handle == phandle_cache[masked_handle]->phandle)
- np = phandle_cache[masked_handle];
- if (np && of_node_check_flag(np, OF_DETACHED)) {
- WARN_ON(1); /* did not uncache np on node removal */
- of_node_put(np);
- phandle_cache[masked_handle] = NULL;
- np = NULL;
- }
- }
+ if (phandle_cache[handle_hash] &&
+ handle == phandle_cache[handle_hash]->phandle)
+ np = phandle_cache[handle_hash];
if (!np) {
for_each_of_allnodes(np)
if (np->phandle == handle &&
!of_node_check_flag(np, OF_DETACHED)) {
- if (phandle_cache) {
- /* will put when removed from cache */
- of_node_get(np);
- phandle_cache[masked_handle] = np;
- }
+ phandle_cache[handle_hash] = np;
break;
}
}
@@ -1240,6 +1247,13 @@
int size;
memset(it, 0, sizeof(*it));
+
+ /*
+ * one of cell_count or cells_name must be provided to determine the
+ * argument length.
+ */
+ if (cell_count < 0 && !cells_name)
+ return -EINVAL;
list = of_get_property(np, list_name, &size);
if (!list)
@@ -1290,11 +1304,20 @@
if (of_property_read_u32(it->node, it->cells_name,
&count)) {
- pr_err("%pOF: could not get %s for %pOF\n",
- it->parent,
- it->cells_name,
- it->node);
- goto err;
+ /*
+ * If both cell_count and cells_name is given,
+ * fall back to cell_count in absence
+ * of the cells_name property
+ */
+ if (it->cell_count >= 0) {
+ count = it->cell_count;
+ } else {
+ pr_err("%pOF: could not get %s for %pOF\n",
+ it->parent,
+ it->cells_name,
+ it->node);
+ goto err;
+ }
}
} else {
count = it->cell_count;
@@ -1305,8 +1328,14 @@
* property data length
*/
if (it->cur + count > it->list_end) {
- pr_err("%pOF: arguments longer than property\n",
- it->parent);
+ if (it->cells_name)
+ pr_err("%pOF: %s = %d found %td\n",
+ it->parent, it->cells_name,
+ count, it->list_end - it->cur);
+ else
+ pr_err("%pOF: phandle %s needs %d, found %td\n",
+ it->parent, of_node_full_name(it->node),
+ count, it->list_end - it->cur);
goto err;
}
}
@@ -1342,7 +1371,6 @@
return count;
}
-EXPORT_SYMBOL_GPL(of_phandle_iterator_args);
static int __of_parse_phandle_with_args(const struct device_node *np,
const char *list_name,
@@ -1458,10 +1486,17 @@
const char *cells_name, int index,
struct of_phandle_args *out_args)
{
+ int cell_count = -1;
+
if (index < 0)
return -EINVAL;
- return __of_parse_phandle_with_args(np, list_name, cells_name, 0,
- index, out_args);
+
+ /* If cells_name is NULL we assume a cell count of 0 */
+ if (!cells_name)
+ cell_count = 0;
+
+ return __of_parse_phandle_with_args(np, list_name, cells_name,
+ cell_count, index, out_args);
}
EXPORT_SYMBOL(of_parse_phandle_with_args);
@@ -1543,7 +1578,7 @@
if (!pass_name)
goto free;
- ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
+ ret = __of_parse_phandle_with_args(np, list_name, cells_name, -1, index,
out_args);
if (ret)
goto free;
@@ -1711,7 +1746,24 @@
struct of_phandle_iterator it;
int rc, cur_index = 0;
- rc = of_phandle_iterator_init(&it, np, list_name, cells_name, 0);
+ /*
+ * If cells_name is NULL we assume a cell count of 0. This makes
+ * counting the phandles trivial as each 32bit word in the list is a
+ * phandle and no arguments are to consider. So we don't iterate through
+ * the list but just use the length to determine the phandle count.
+ */
+ if (!cells_name) {
+ const __be32 *list;
+ int size;
+
+ list = of_get_property(np, list_name, &size);
+ if (!list)
+ return -ENOENT;
+
+ return size / sizeof(*list);
+ }
+
+ rc = of_phandle_iterator_init(&it, np, list_name, cells_name, -1);
if (rc)
return rc;
@@ -1770,6 +1822,7 @@
return rc;
}
+EXPORT_SYMBOL_GPL(of_add_property);
int __of_remove_property(struct device_node *np, struct property *prop)
{
@@ -1822,6 +1875,7 @@
return rc;
}
+EXPORT_SYMBOL_GPL(of_remove_property);
int __of_update_property(struct device_node *np, struct property *newprop,
struct property **oldpropp)
@@ -1997,6 +2051,59 @@
EXPORT_SYMBOL_GPL(of_alias_get_id);
/**
+ * of_alias_get_alias_list - Get alias list for the given device driver
+ * @matches: Array of OF device match structures to search in
+ * @stem: Alias stem of the given device_node
+ * @bitmap: Bitmap field pointer
+ * @nbits: Maximum number of alias IDs which can be recorded in bitmap
+ *
+ * The function travels the lookup table to record alias ids for the given
+ * device match structures and alias stem.
+ *
+ * Return: 0 or -ENOSYS when !CONFIG_OF or
+ * -EOVERFLOW if alias ID is greater then allocated nbits
+ */
+int of_alias_get_alias_list(const struct of_device_id *matches,
+ const char *stem, unsigned long *bitmap,
+ unsigned int nbits)
+{
+ struct alias_prop *app;
+ int ret = 0;
+
+ /* Zero bitmap field to make sure that all the time it is clean */
+ bitmap_zero(bitmap, nbits);
+
+ mutex_lock(&of_mutex);
+ pr_debug("%s: Looking for stem: %s\n", __func__, stem);
+ list_for_each_entry(app, &aliases_lookup, link) {
+ pr_debug("%s: stem: %s, id: %d\n",
+ __func__, app->stem, app->id);
+
+ if (strcmp(app->stem, stem) != 0) {
+ pr_debug("%s: stem comparison didn't pass %s\n",
+ __func__, app->stem);
+ continue;
+ }
+
+ if (of_match_node(matches, app->np)) {
+ pr_debug("%s: Allocated ID %d\n", __func__, app->id);
+
+ if (app->id >= nbits) {
+ pr_warn("%s: ID %d >= than bitmap field %d\n",
+ __func__, app->id, nbits);
+ ret = -EOVERFLOW;
+ } else {
+ set_bit(app->id, bitmap);
+ }
+ }
+ }
+ mutex_unlock(&of_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_alias_get_alias_list);
+
+/**
* of_alias_get_highest_id - Get highest alias id for the given stem
* @stem: Alias stem to be examined
*
@@ -2067,9 +2174,9 @@
/* OF on pmac has nodes instead of properties named "l2-cache"
* beneath CPU nodes.
*/
- if (IS_ENABLED(CONFIG_PPC_PMAC) && !strcmp(np->type, "cpu"))
+ if (IS_ENABLED(CONFIG_PPC_PMAC) && of_node_is_type(np, "cpu"))
for_each_child_of_node(np, child)
- if (!strcmp(child->type, "cache"))
+ if (of_node_is_type(child, "cache"))
return child;
return NULL;
@@ -2099,3 +2206,109 @@
return cache_level;
}
+
+/**
+ * of_map_id - Translate an ID through a downstream mapping.
+ * @np: root complex device node.
+ * @id: device ID to map.
+ * @map_name: property name of the map to use.
+ * @map_mask_name: optional property name of the mask to use.
+ * @target: optional pointer to a target device node.
+ * @id_out: optional pointer to receive the translated ID.
+ *
+ * Given a device ID, look up the appropriate implementation-defined
+ * platform ID and/or the target device which receives transactions on that
+ * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or
+ * @id_out may be NULL if only the other is required. If @target points to
+ * a non-NULL device node pointer, only entries targeting that node will be
+ * matched; if it points to a NULL value, it will receive the device node of
+ * the first matching target phandle, with a reference held.
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_map_id(struct device_node *np, u32 id,
+ const char *map_name, const char *map_mask_name,
+ struct device_node **target, u32 *id_out)
+{
+ u32 map_mask, masked_id;
+ int map_len;
+ const __be32 *map = NULL;
+
+ if (!np || !map_name || (!target && !id_out))
+ return -EINVAL;
+
+ map = of_get_property(np, map_name, &map_len);
+ if (!map) {
+ if (target)
+ return -ENODEV;
+ /* Otherwise, no map implies no translation */
+ *id_out = id;
+ return 0;
+ }
+
+ if (!map_len || map_len % (4 * sizeof(*map))) {
+ pr_err("%pOF: Error: Bad %s length: %d\n", np,
+ map_name, map_len);
+ return -EINVAL;
+ }
+
+ /* The default is to select all bits. */
+ map_mask = 0xffffffff;
+
+ /*
+ * Can be overridden by "{iommu,msi}-map-mask" property.
+ * If of_property_read_u32() fails, the default is used.
+ */
+ if (map_mask_name)
+ of_property_read_u32(np, map_mask_name, &map_mask);
+
+ masked_id = map_mask & id;
+ for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
+ struct device_node *phandle_node;
+ u32 id_base = be32_to_cpup(map + 0);
+ u32 phandle = be32_to_cpup(map + 1);
+ u32 out_base = be32_to_cpup(map + 2);
+ u32 id_len = be32_to_cpup(map + 3);
+
+ if (id_base & ~map_mask) {
+ pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n",
+ np, map_name, map_name,
+ map_mask, id_base);
+ return -EFAULT;
+ }
+
+ if (masked_id < id_base || masked_id >= id_base + id_len)
+ continue;
+
+ phandle_node = of_find_node_by_phandle(phandle);
+ if (!phandle_node)
+ return -ENODEV;
+
+ if (target) {
+ if (*target)
+ of_node_put(phandle_node);
+ else
+ *target = phandle_node;
+
+ if (*target != phandle_node)
+ continue;
+ }
+
+ if (id_out)
+ *id_out = masked_id - id_base + out_base;
+
+ pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n",
+ np, map_name, map_mask, id_base, out_base,
+ id_len, id, masked_id - id_base + out_base);
+ return 0;
+ }
+
+ pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name,
+ id, target && *target ? *target : NULL);
+
+ /* Bypasses translation */
+ if (id_out)
+ *id_out = id;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_map_id);
--
Gitblit v1.6.2