summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig19
-rw-r--r--drivers/of/Makefile2
-rw-r--r--drivers/of/address.c39
-rw-r--r--drivers/of/barebox.c24
-rw-r--r--drivers/of/base.c754
-rw-r--r--drivers/of/device.c11
-rw-r--r--drivers/of/fdt.c320
-rw-r--r--drivers/of/mem_generic.c1
-rw-r--r--drivers/of/of_firmware.c5
-rw-r--r--drivers/of/of_gpio.c74
-rw-r--r--drivers/of/of_mtd.c5
-rw-r--r--drivers/of/of_net.c4
-rw-r--r--drivers/of/of_path.c110
-rw-r--r--drivers/of/of_pci.c2
-rw-r--r--drivers/of/overlay.c46
-rw-r--r--drivers/of/partition.c101
-rw-r--r--drivers/of/platform.c243
-rw-r--r--drivers/of/reserved-mem.c87
-rw-r--r--drivers/of/resolver.c6
19 files changed, 1398 insertions, 455 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index e58fe50f70..2791100a2d 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,20 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only
config OFTREE
select DTC
bool
config OFTREE_MEM_GENERIC
depends on OFTREE
- depends on PPC || ARM || EFI_BOOTUP || OPENRISC || SANDBOX || RISCV || KVX
+ depends on PPC || ARM || EFI_PAYLOAD || OPENRISC || SANDBOX || RISCV || KVX
def_bool y
config DTC
bool
+config OF
+ bool
+
config OFDEVICE
select OFTREE
+ select OF
select DTC
bool "Enable probing of devices from the devicetree"
+config FEATURE_CONTROLLER_FIXUP
+ bool "Fix up DT nodes gated by feature controller"
+ depends on FEATURE_CONTROLLER
+ default y
+ help
+ When specified, barebox feature controller drivers are consulted
+ prior to probing nodes to detect whether the device may not
+ be available (e.g. because support is fused out).
+ This option additionally fixes up the kernel device tree,
+ so it doesn't attempt probing these devices either.
+ If unsure, say y.
+
config OF_ADDRESS_PCI
bool
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index b6847752d2..4785128bd9 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,9 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-y += address.o base.o fdt.o platform.o of_path.o device.o
obj-$(CONFIG_OFTREE_MEM_GENERIC) += mem_generic.o
obj-$(CONFIG_OF_GPIO) += of_gpio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-y += partition.o
obj-y += of_net.o
+obj-y += reserved-mem.o
obj-$(CONFIG_MTD) += of_mtd.o
obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
obj-$(CONFIG_OF_OVERLAY) += overlay.o resolver.o of_firmware.o
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 524a0f6a79..03868406e2 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* address.c - address related devicetree functions
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <of.h>
@@ -133,8 +125,10 @@ static unsigned int of_bus_pci_get_flags(const __be32 *addr)
case 0x01:
flags |= IORESOURCE_IO;
break;
- case 0x02: /* 32 bits */
case 0x03: /* 64 bits */
+ flags |= IORESOURCE_MEM_64;
+ /* fallthrough */
+ case 0x02: /* 32 bits */
flags |= IORESOURCE_MEM;
break;
}
@@ -307,10 +301,13 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
*
* As far as we know, this damage only exists on Apple machines, so
* This code is only enabled on powerpc. --gcl
+ *
+ * This quirk also applies for 'dma-ranges' which frequently exist in
+ * child nodes without 'dma-ranges' in the parent nodes. --RobH
*/
ranges = of_get_property(parent, rprop, &rlen);
#if !defined(CONFIG_PPC)
- if (ranges == NULL) {
+ if (ranges == NULL && strcmp(rprop, "dma-ranges")) {
pr_vdebug("OF: no ranges; cannot translate\n");
return 1;
}
@@ -365,7 +362,7 @@ static u64 __of_translate_address(struct device_node *dev,
int na, ns, pna, pns;
u64 result = OF_BAD_ADDR;
- pr_vdebug("OF: ** translation for device %s **\n", dev->full_name);
+ pr_vdebug("OF: ** translation for device %pOF **\n", dev);
/* Get parent & match bus type */
parent = of_get_parent(dev);
@@ -376,14 +373,13 @@ static u64 __of_translate_address(struct device_node *dev,
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
- pr_vdebug("prom_parse: Bad cell count for %s\n",
- dev->full_name);
+ pr_vdebug("prom_parse: Bad cell count for %pOF\n", dev);
return OF_BAD_ADDR;
}
memcpy(addr, in_addr, na * 4);
- pr_vdebug("OF: bus is %s (na=%d, ns=%d) on %s\n",
- bus->name, na, ns, parent->full_name);
+ pr_vdebug("OF: bus is %s (na=%d, ns=%d) on %pOF\n",
+ bus->name, na, ns, parent);
of_dump_addr("OF: translating address:", addr, na);
/* Translate */
@@ -403,13 +399,12 @@ static u64 __of_translate_address(struct device_node *dev,
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
- dev->full_name);
+ printk(KERN_ERR "prom_parse: Bad cell count for %pOF\n", dev);
break;
}
- pr_vdebug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
- pbus->name, pna, pns, parent->full_name);
+ pr_vdebug("OF: parent bus is %s (na=%d, ns=%d) on %pOF\n",
+ pbus->name, pna, pns, parent);
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
@@ -676,8 +671,8 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz
dmaaddr = of_read_number(ranges, naddr);
*paddr = of_translate_dma_address(node, ranges + naddr);
if (*paddr == OF_BAD_ADDR) {
- pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
- dmaaddr, np);
+ pr_err("translation of DMA address(%llx) to CPU address failed node(%s)\n",
+ dmaaddr, np->name);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/of/barebox.c b/drivers/of/barebox.c
index aa0cfb6194..560d9c0d15 100644
--- a/drivers/of/barebox.c
+++ b/drivers/of/barebox.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* barebox.c
*
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -19,7 +10,6 @@
#include <io.h>
#include <of.h>
#include <malloc.h>
-#include <partition.h>
#include <envfs.h>
#include <fs.h>
@@ -28,7 +18,7 @@
/* If dev describes a file on a fs, mount the fs and change devpath to
* point to the file's path. Otherwise leave devpath alone. Does
* nothing in env in a file support isn't enabled. */
-static int environment_check_mount(struct device_d *dev, char **devpath)
+static int environment_check_mount(struct device *dev, char **devpath)
{
const char *filepath;
int ret;
@@ -36,7 +26,7 @@ static int environment_check_mount(struct device_d *dev, char **devpath)
if (!IS_ENABLED(CONFIG_OF_BAREBOX_ENV_IN_FS))
return 0;
- ret = of_property_read_string(dev->device_node, "file-path", &filepath);
+ ret = of_property_read_string(dev->of_node, "file-path", &filepath);
if (ret == -EINVAL) {
/* No file-path so just use device-path */
return 0;
@@ -62,12 +52,13 @@ static int environment_check_mount(struct device_d *dev, char **devpath)
return 0;
}
-static int environment_probe(struct device_d *dev)
+static int environment_probe(struct device *dev)
{
char *path;
int ret;
- ret = of_find_path(dev->device_node, "device-path", &path, OF_FIND_PATH_FLAGS_BB);
+ ret = of_find_path(dev->of_node, "device-path", &path,
+ OF_FIND_PATH_FLAGS_BB);
if (ret)
return ret;
@@ -89,8 +80,9 @@ static struct of_device_id environment_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, environment_dt_ids);
-static struct driver_d environment_driver = {
+static struct driver environment_driver = {
.name = "barebox-environment",
.probe = environment_probe,
.of_compatible = environment_dt_ids,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 42b8d24874..3b8878f34b 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* base.c - basic devicetree functions
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <deep-probe.h>
@@ -25,12 +17,43 @@
#include <linux/sizes.h>
#include <of_graph.h>
#include <string.h>
+#include <libfile.h>
+#include <linux/clk.h>
#include <linux/ctype.h>
-#include <linux/amba/bus.h>
#include <linux/err.h>
static struct device_node *root_node;
+/**
+ * of_node_has_prefix - Test if a node name has a given prefix
+ * @np: The node name to test
+ * @prefix: The prefix to see if @np starts with
+ *
+ * Returns:
+ * * strlen(@prefix) if @np starts with @prefix
+ * * 0 if @np does not start with @prefix
+ */
+size_t of_node_has_prefix(const struct device_node *np, const char *prefix)
+{
+ return np ? str_has_prefix(kbasename(np->full_name), prefix) : 0;
+}
+EXPORT_SYMBOL(of_node_has_prefix);
+
+bool of_node_name_eq(const struct device_node *np, const char *name)
+{
+ const char *node_name;
+ size_t len;
+
+ if (!np)
+ return false;
+
+ node_name = kbasename(np->full_name);
+ len = strchrnul(node_name, '@') - node_name;
+
+ return (strlen(name) == len) && (strncmp(node_name, name, len) == 0);
+}
+EXPORT_SYMBOL(of_node_name_eq);
+
/*
* Iterate over all nodes of a tree. As a devicetree does not
* have a dedicated list head, the start node (usually the root
@@ -146,8 +169,8 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
strncpy(ap->stem, stem, stem_len);
ap->stem[stem_len] = 0;
list_add_tail(&ap->link, &aliases_lookup);
- pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n",
- ap->alias, ap->stem, ap->id, np->full_name);
+ pr_debug("adding DT alias:%s: stem=%s id=%i node=%pOF\n",
+ ap->alias, ap->stem, ap->id, np);
}
static struct device_node *of_alias_resolve(struct device_node *root, struct property *pp)
@@ -179,8 +202,7 @@ static int of_alias_id_parse(const char *start, int *len)
* of_alias_scan - Scan all properties of 'aliases' node
*
* The function scans all the properties of 'aliases' node and populates
- * the global lookup table with the properties. It returns the
- * number of alias_prop found, or error code in error case.
+ * the global lookup table with the properties.
*/
void of_alias_scan(void)
{
@@ -524,7 +546,9 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
EXPORT_SYMBOL(of_get_cpu_node);
/** Checks if the given "compat" string matches one of the strings in
- * the device's "compatible" property
+ * the device's "compatible" property. Returns 0 on mismatch and a
+ * positive score on match with the maximum being OF_DEVICE_COMPATIBLE_MAX_SCORE,
+ * which is only returned if the first compatible matched.
*/
int of_device_is_compatible(const struct device_node *device,
const char *compat)
@@ -537,7 +561,7 @@ int of_device_is_compatible(const struct device_node *device,
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
- score = INT_MAX/2 - (index << 2);
+ score = OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2);
break;
}
}
@@ -547,6 +571,29 @@ int of_device_is_compatible(const struct device_node *device,
EXPORT_SYMBOL(of_device_is_compatible);
/**
+ * of_find_node_by_name_address - Find a node by its full name
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned.
+ * @name: The name string to match against
+ *
+ * Returns a pointer to the node found or NULL.
+ */
+struct device_node *of_find_node_by_name_address(struct device_node *from,
+ const char *name)
+{
+ struct device_node *np;
+
+ of_tree_for_each_node_from(np, from)
+ if (np->name && !of_node_cmp(np->name, name))
+ return np;
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_node_by_name_address);
+
+/**
* of_find_node_by_name - Find a node by its "name" property
* @from: The node to start searching from or NULL, the node
* you pass will not be searched, only the next one
@@ -562,7 +609,7 @@ struct device_node *of_find_node_by_name(struct device_node *from,
struct device_node *np;
of_tree_for_each_node_from(np, from)
- if (np->name && !of_node_cmp(np->name, name))
+ if (np->name && of_node_name_eq(np, name))
return np;
return NULL;
@@ -670,6 +717,9 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches,
if (score > best_score) {
best_match = matches;
best_score = score;
+
+ if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE)
+ break;
}
}
@@ -710,11 +760,11 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from,
}
EXPORT_SYMBOL(of_find_matching_node_and_match);
-int of_match(struct device_d *dev, struct driver_d *drv)
+int of_match(struct device *dev, struct driver *drv)
{
const struct of_device_id *id;
- id = of_match_node(drv->of_compatible, dev->device_node);
+ id = of_match_node(drv->of_compatible, dev->of_node);
if (!id)
return 1;
@@ -723,22 +773,24 @@ int of_match(struct device_d *dev, struct driver_d *drv)
return 0;
}
EXPORT_SYMBOL(of_match);
-
/**
* of_find_property_value_of_size
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
- * @len: requested length of property value
+ * @min: minimum allowed length of property value
+ * @max: maximum allowed length of property value (0 means unlimited)
+ * @len: if !=NULL, actual length is written to here
*
* Search for a property in a device node and valid the requested size.
- * Returns the property value on success, -EINVAL if the property does not
- * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
+ *
+ * Return: The property value on success, -EINVAL if the property does not
+ * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data is too small or too large.
*
*/
static const void *of_find_property_value_of_size(const struct device_node *np,
- const char *propname, u32 len)
+ const char *propname, u32 min, u32 max, size_t *len)
{
struct property *prop = of_find_property(np, propname, NULL);
const void *value;
@@ -748,9 +800,14 @@ static const void *of_find_property_value_of_size(const struct device_node *np,
value = of_property_get_value(prop);
if (!value)
return ERR_PTR(-ENODATA);
- if (len > prop->length)
+ if (prop->length < min)
+ return ERR_PTR(-EOVERFLOW);
+ if (max && prop->length > max)
return ERR_PTR(-EOVERFLOW);
+ if (len)
+ *len = prop->length;
+
return value;
}
@@ -774,7 +831,8 @@ int of_property_read_u32_index(const struct device_node *np,
u32 index, u32 *out_value)
{
const u32 *val = of_find_property_value_of_size(np, propname,
- ((index + 1) * sizeof(*out_value)));
+ ((index + 1) * sizeof(*out_value)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -803,7 +861,7 @@ int of_property_count_elems_of_size(const struct device_node *np,
if (!prop)
return -EINVAL;
- if (!prop->value)
+ if (!of_property_get_value(prop))
return -ENODATA;
if (prop->length % elem_size != 0) {
@@ -838,7 +896,8 @@ int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz)
{
const u8 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -871,7 +930,8 @@ int of_property_read_u16_array(const struct device_node *np,
const char *propname, u16 *out_values, size_t sz)
{
const __be16 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -903,7 +963,8 @@ int of_property_read_u32_array(const struct device_node *np,
size_t sz)
{
const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz * sizeof(*out_values)),
+ 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -931,7 +992,7 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
u64 *out_value)
{
const __be32 *val = of_find_property_value_of_size(np, propname,
- sizeof(*out_value));
+ sizeof(*out_value), 0, NULL);
if (IS_ERR(val))
return PTR_ERR(val);
@@ -942,7 +1003,154 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_read_u64);
/**
- * of_property_read_u64_array - Find and read an array of 64 bit integers
+ * of_property_read_variable_u8_array - Find and read an array of u8 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it.
+ *
+ * dts entry of array should be like:
+ * ``property = /bits/ 8 <0x50 0x60 0x70>;``
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_variable_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const u8 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = *val++;
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
+
+/**
+ * of_property_read_variable_u16_array - Find and read an array of u16 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it.
+ *
+ * dts entry of array should be like:
+ * ``property = /bits/ 16 <0x5000 0x6000 0x7000>;``
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_variable_u16_array(const struct device_node *np,
+ const char *propname, u16 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be16 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be16_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
+
+/**
+ * of_property_read_variable_u32_array - Find and read an array of 32 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return found values.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it.
+ *
+ * Return: The number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_variable_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be32_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
+
+/**
+ * of_property_read_variable_u64_array - Find and read an array of 64 bit integers
* from a property.
*
* @np: device node from which the property value is to be read.
@@ -959,15 +1167,22 @@ EXPORT_SYMBOL_GPL(of_property_read_u64);
*/
int of_property_read_variable_u64_array(const struct device_node *np,
const char *propname, u64 *out_values,
- size_t sz)
+ size_t sz_min, size_t sz_max)
{
- size_t count;
+ size_t sz, count;
const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz * sizeof(*out_values)));
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
if (IS_ERR(val))
return PTR_ERR(val);
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
count = sz;
while (count--) {
*out_values++ = of_read_number(val, 2);
@@ -1020,7 +1235,7 @@ EXPORT_SYMBOL_GPL(of_property_read_string);
* This function searches a string list property and returns the index
* of a specific string value.
*/
-int of_property_match_string(struct device_node *np, const char *propname,
+int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
{
struct property *prop = of_find_property(np, propname, NULL);
@@ -1101,6 +1316,7 @@ EXPORT_SYMBOL_GPL(of_prop_next_string);
*
* @np: device node from which the property is to be set.
* @propname: name of the property to be set.
+ * @value true to set, false to delete
*
* Search for a property in a device node and create or delete the property.
* If the property already exists and write value is false, the property is
@@ -1443,15 +1659,13 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
*/
node = of_find_node_by_phandle(phandle);
if (!node) {
- pr_err("%s: could not find phandle\n",
- np->full_name);
+ pr_err("%pOF: could not find phandle\n", np);
goto err;
}
if (cells_name &&
of_property_read_u32(node, cells_name, &count)) {
- pr_err("%s: could not get %s for %s\n",
- np->full_name, cells_name,
- node->full_name);
+ pr_err("%pOF: could not get %s for %pOF\n",
+ np, cells_name, node);
goto err;
}
@@ -1460,8 +1674,7 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
* remaining property data length
*/
if (list + count > list_end) {
- pr_err("%s: arguments longer than property\n",
- np->full_name);
+ pr_err("%pOF: arguments longer than property\n", np);
goto err;
}
}
@@ -1727,7 +1940,7 @@ int barebox_register_of(struct device_node *root)
of_fix_tree(root);
if (IS_ENABLED(CONFIG_OFDEVICE)) {
- of_clk_init(root, NULL);
+ of_clk_init();
if (!deep_probe_is_supported())
return of_probe();
}
@@ -1961,9 +2174,9 @@ int of_property_read_string_helper(const struct device_node *np,
if (!prop)
return -EINVAL;
- if (!prop->value)
+ p = of_property_get_value(prop);
+ if (!p)
return -ENODATA;
- p = prop->value;
end = p + prop->length;
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
@@ -1977,76 +2190,83 @@ int of_property_read_string_helper(const struct device_node *np,
return i <= 0 ? -ENODATA : i;
}
-static void __of_print_nodes(struct device_node *node, int indent, const char *prefix)
+static void __of_print_property_prefixed(const struct property *p, int indent,
+ unsigned maxpropsize, const char *prefix)
+{
+ unsigned length;
+
+ printf("%s%*s%s", prefix, indent * 8, "", p->name);
+
+ length = min_t(unsigned, p->length, maxpropsize);
+ if (length) {
+ printf(" = ");
+ of_print_property(of_property_get_value(p), length);
+ }
+ if (length != p->length)
+ printf(" /* %u more bytes omitted */", p->length - length);
+
+ printf(";\n");
+}
+
+static int __of_print_nodes(struct device_node *node, int indent,
+ unsigned maxpropsize, const char *prefix)
{
struct device_node *n;
struct property *p;
+ int ret;
if (!node)
- return;
+ return 0;
if (!prefix)
prefix = "";
printf("%s%*s%s%s\n", prefix, indent * 8, "", node->name, node->name ? " {" : "{");
- list_for_each_entry(p, &node->properties, list) {
- printf("%s%*s%s", prefix, (indent + 1) * 8, "", p->name);
- if (p->length) {
- printf(" = ");
- of_print_property(of_property_get_value(p), p->length);
- }
- printf(";\n");
- }
+ list_for_each_entry(p, &node->properties, list)
+ __of_print_property_prefixed(p, indent + 1, maxpropsize, prefix);
+
+ if (ctrlc())
+ return -EINTR;
list_for_each_entry(n, &node->children, parent_list) {
- __of_print_nodes(n, indent + 1, prefix);
+ ret = __of_print_nodes(n, indent + 1, maxpropsize, prefix);
+ if (ret)
+ return ret;
}
printf("%s%*s};\n", prefix, indent * 8, "");
+ return 0;
}
-void of_print_nodes(struct device_node *node, int indent)
+void of_print_nodes(struct device_node *node, int indent, unsigned maxpropsize)
{
- __of_print_nodes(node, indent, NULL);
+ __of_print_nodes(node, indent, maxpropsize, NULL);
}
-static void __of_print_property(struct property *p, int indent)
+static void __of_print_property(struct property *p, int indent, unsigned maxpropsize)
{
- int i;
-
- for (i = 0; i < indent; i++)
- printf("\t");
-
- printf("%s", p->name);
- if (p->length) {
- printf(" = ");
- of_print_property(of_property_get_value(p), p->length);
- }
- printf(";\n");
+ __of_print_property_prefixed(p, indent, maxpropsize, "");
}
-void of_print_properties(struct device_node *node)
+void of_print_properties(struct device_node *node, unsigned maxpropsize)
{
struct property *prop;
list_for_each_entry(prop, &node->properties, list)
- __of_print_property(prop, 0);
+ __of_print_property(prop, 0, maxpropsize);
}
static int __of_print_parents(struct device_node *node)
{
- int indent, i;
+ int indent;
if (!node->parent)
return 0;
indent = __of_print_parents(node->parent);
- for (i = 0; i < indent; i++)
- printf("\t");
-
- printf("%s {\n", node->name);
+ printf("%*s%s {\n", indent * 8, "", node->name);
return indent + 1;
}
@@ -2087,57 +2307,76 @@ static void of_print_close(struct device_node *node, int *printed)
* This function compares two device trees against each other and prints
* a diff-like result.
*/
-void of_diff(struct device_node *a, struct device_node *b, int indent)
+int of_diff(struct device_node *a, struct device_node *b, int indent)
{
struct property *ap, *bp;
struct device_node *ca, *cb;
- int printed = 0;
+ int printed = 0, diff = 0;
+ bool silent = indent < 0;
list_for_each_entry(ap, &a->properties, list) {
bp = of_find_property(b, ap->name, NULL);
if (!bp) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("- ");
- __of_print_property(ap, indent);
+ __of_print_property(ap, indent, ~0);
continue;
}
if (ap->length != bp->length || memcmp(of_property_get_value(ap), of_property_get_value(bp), bp->length)) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("- ");
- __of_print_property(ap, indent);
+ __of_print_property(ap, indent, ~0);
printf("+ ");
- __of_print_property(bp, indent);
+ __of_print_property(bp, indent, ~0);
}
}
list_for_each_entry(bp, &b->properties, list) {
ap = of_find_property(a, bp->name, NULL);
if (!ap) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("+ ");
- __of_print_property(bp, indent);
+ __of_print_property(bp, indent, ~0);
}
}
for_each_child_of_node(a, ca) {
cb = of_get_child_by_name(b, ca->name);
if (cb) {
- of_diff(ca, cb, indent + 1);
+ diff += of_diff(ca, cb, silent ? indent : indent + 1);
} else {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
- __of_print_nodes(ca, indent, "-");
+ __of_print_nodes(ca, indent, ~0, "- ");
}
}
for_each_child_of_node(b, cb) {
if (!of_get_child_by_name(a, cb->name)) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
- __of_print_nodes(cb, indent, "+");
+ __of_print_nodes(cb, indent, ~0, "+ ");
}
}
- of_print_close(a, &printed);
+ if (!silent)
+ of_print_close(a, &printed);
+
+ return diff;
}
struct device_node *of_new_node(struct device_node *parent, const char *name)
@@ -2154,8 +2393,8 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
if (parent) {
node->name = xstrdup(name);
- node->full_name = basprintf("%s/%s",
- node->parent->full_name, name);
+ node->full_name = basprintf("%pOF/%s",
+ node->parent, name);
list_add(&node->list, &parent->list);
} else {
node->name = xstrdup("");
@@ -2166,6 +2405,21 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
return node;
}
+struct property *__of_new_property(struct device_node *node, const char *name,
+ void *data, int len)
+{
+ struct property *prop;
+
+ prop = xzalloc(sizeof(*prop));
+ prop->name = xstrdup(name);
+ prop->length = len;
+ prop->value = data;
+
+ list_add_tail(&prop->list, &node->properties);
+
+ return prop;
+}
+
/**
* of_new_property - Add a new property to a node
* @node: device node to which the property is added
@@ -2181,19 +2435,13 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
struct property *of_new_property(struct device_node *node, const char *name,
const void *data, int len)
{
- struct property *prop;
-
- prop = xzalloc(sizeof(*prop));
- prop->name = xstrdup(name);
- prop->length = len;
- prop->value = xzalloc(len);
+ char *buf;
+ buf = xzalloc(len);
if (data)
- memcpy(prop->value, data, len);
+ memcpy(buf, data, len);
- list_add_tail(&prop->list, &node->properties);
-
- return prop;
+ return __of_new_property(node, name, buf, len);
}
/**
@@ -2237,6 +2485,41 @@ void of_delete_property(struct property *pp)
free(pp);
}
+struct property *of_rename_property(struct device_node *np,
+ const char *old_name, const char *new_name)
+{
+ struct property *pp;
+
+ pp = of_find_property(np, old_name, NULL);
+ if (!pp)
+ return NULL;
+
+ of_property_write_bool(np, new_name, false);
+
+ free(pp->name);
+ pp->name = xstrdup(new_name);
+ return pp;
+}
+
+struct property *of_copy_property(const struct device_node *src,
+ const char *propname,
+ struct device_node *dst)
+{
+ struct property *prop;
+
+ prop = of_find_property(src, propname, NULL);
+ if (!prop)
+ return NULL;
+
+ if (of_property_present(dst, propname))
+ return ERR_PTR(-EEXIST);
+
+ return of_new_property(dst, propname,
+ of_property_get_value(prop), prop->length);
+}
+EXPORT_SYMBOL_GPL(of_copy_property);
+
+
/**
* of_set_property - create a property for a given node
* @node - the node
@@ -2265,6 +2548,99 @@ int of_set_property(struct device_node *np, const char *name, const void *val, i
return 0;
}
+int of_append_property(struct device_node *np, const char *name, const void *val, int len)
+{
+ struct property *pp;
+ int orig_len;
+ void *buf;
+
+ if (!np)
+ return -ENOENT;
+
+ pp = of_find_property(np, name, NULL);
+ if (!pp) {
+ of_new_property(np, name, val, len);
+ return 0;
+ }
+
+ orig_len = pp->length;
+ buf = realloc(pp->value, orig_len + len);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf + orig_len, val, len);
+
+ pp->value = buf;
+ pp->length += len;
+
+ if (pp->value_const) {
+ memcpy(buf, pp->value_const, orig_len);
+ pp->value_const = NULL;
+ }
+
+ return 0;
+}
+
+int of_prepend_property(struct device_node *np, const char *name, const void *val, int len)
+{
+ struct property *pp;
+ const void *oldval;
+ void *buf;
+ int oldlen;
+
+ pp = of_find_property(np, name, &oldlen);
+ if (!pp) {
+ of_new_property(np, name, val, len);
+ return 0;
+ }
+
+ oldval = of_property_get_value(pp);
+
+ buf = malloc(len + oldlen);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, val, len);
+ memcpy(buf + len, oldval, oldlen);
+
+ free(pp->value);
+ pp->value = buf;
+ pp->length = len + oldlen;
+ pp->value_const = NULL;
+
+ return 0;
+}
+
+int of_property_sprintf(struct device_node *np,
+ const char *propname, const char *fmt, ...)
+{
+ struct property *pp;
+ struct va_format vaf;
+ char *buf = NULL;
+ va_list args;
+ int len;
+
+ if (!np)
+ return -ENOENT;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ len = asprintf(&buf, "%pV", &vaf);
+ va_end(args);
+
+ if (len < 0)
+ return -ENOMEM;
+
+ len++; /* trailing NUL */
+
+ pp = of_find_property(np, propname, NULL);
+ of_delete_property(pp);
+
+ __of_new_property(np, propname, buf, len);
+ return len;
+}
+
static int mem_bank_num;
int of_add_memory(struct device_node *node, bool dump)
@@ -2313,6 +2689,7 @@ const struct of_device_id of_default_bus_match_table[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_default_bus_match_table);
static int of_probe_memory(void)
{
@@ -2341,24 +2718,35 @@ mem_initcall(of_probe_memory);
static void of_platform_device_create_root(struct device_node *np)
{
- struct device_d *dev;
+ static struct device *dev;
int ret;
+ if (dev)
+ return;
+
dev = xzalloc(sizeof(*dev));
dev->id = DEVICE_ID_SINGLE;
- dev->device_node = np;
+ dev->of_node = np;
dev_set_name(dev, "machine");
ret = platform_device_register(dev);
if (ret)
- free(dev);
+ free_device(dev);
}
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "nvmem-rmem" },
{}
};
+MODULE_DEVICE_TABLE(of, reserved_mem_matches);
+/**
+ * of_probe - Probe unflattened device tree starting at of_get_root_node
+ *
+ * The function walks the device tree and creates devices as needed.
+ * With care, it can be called more than once, but if you really need that,
+ * consider first if deep probe would help instead.
+ */
int of_probe(void)
{
struct device_node *node;
@@ -2367,6 +2755,12 @@ int of_probe(void)
return -ENODEV;
/*
+ * We do this first thing, so board drivers can patch the device
+ * tree prior to device creation if needed.
+ */
+ of_platform_device_create_root(root_node);
+
+ /*
* Handle certain compatibles explicitly, since we don't want to create
* platform_devices for every node in /reserved-memory with a
* "compatible",
@@ -2378,8 +2772,6 @@ int of_probe(void)
if (node)
of_platform_populate(node, NULL, NULL);
- of_platform_device_create_root(root_node);
-
of_platform_populate(root_node, of_default_bus_match_table, NULL);
return 0;
@@ -2429,25 +2821,35 @@ out:
return dn;
}
-struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+void of_merge_nodes(struct device_node *np, const struct device_node *other)
{
- struct device_node *np, *child;
+ struct device_node *child;
struct property *pp;
- np = of_new_node(parent, other->name);
- np->phandle = other->phandle;
-
list_for_each_entry(pp, &other->properties, list)
of_new_property(np, pp->name, pp->value, pp->length);
for_each_child_of_node(other, child)
of_copy_node(np, child);
+}
+
+struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+{
+ struct device_node *np;
+
+ np = of_new_node(parent, other->name);
+ np->phandle = other->phandle;
+
+ of_merge_nodes(np, other);
return np;
}
struct device_node *of_dup(const struct device_node *root)
{
+ if (IS_ERR_OR_NULL(root))
+ return ERR_CAST(root);
+
return of_copy_node(NULL, root);
}
@@ -2480,30 +2882,51 @@ void of_delete_node(struct device_node *node)
free(node);
}
-struct device_node *of_get_stdoutpath(unsigned int *baudrate)
-{
+/*
+ * of_find_node_by_chosen - Find a node given a chosen property pointing at it
+ * @propname: the name of the property containing a path or alias
+ * The function will lookup the first string in the property
+ * value up to the first : character or till \0.
+ * @options The Remainder (without : or \0 at the end) will be written
+ * to *options if not NULL.
+ */
+struct device_node *of_find_node_by_chosen(const char *propname,
+ const char **options)
+{
+ const char *value, *p;
+ char *buf;
struct device_node *dn;
- const char *name;
- const char *p;
- char *q;
- name = of_get_property(of_chosen, "stdout-path", NULL);
- if (!name)
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ value = of_get_property(of_chosen, propname, NULL);
+ if (!value)
+ return NULL;
- if (!name)
- return 0;
+ p = strchrnul(value, ':');
+ buf = xstrndup(value, p - value);
- p = strchrnul(name, ':');
+ dn = of_find_node_by_path_or_alias(NULL, buf);
- q = xstrndup(name, p - name);
+ free(buf);
- dn = of_find_node_by_path_or_alias(NULL, q);
+ if (options && *p)
+ *options = p + 1;
- free(q);
+ return dn;
+}
- if (baudrate && *p) {
- unsigned rate = simple_strtoul(p + 1, NULL, 10);
+struct device_node *of_get_stdoutpath(unsigned int *baudrate)
+{
+ const char *opts = NULL;
+ struct device_node *dn;
+
+ dn = of_find_node_by_chosen("stdout-path", &opts);
+ if (!dn)
+ dn = of_find_node_by_chosen("linux,stdout-path", &opts);
+ if (!dn)
+ return NULL;
+
+ if (baudrate && opts) {
+ unsigned rate = simple_strtoul(opts, NULL, 10);
if (rate)
*baudrate = rate;
}
@@ -2511,11 +2934,11 @@ struct device_node *of_get_stdoutpath(unsigned int *baudrate)
return dn;
}
-int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate)
+int of_device_is_stdout_path(struct device *dev, unsigned int *baudrate)
{
unsigned int tmp = *baudrate;
- if (!dev || !dev->device_node || dev->device_node != of_get_stdoutpath(&tmp))
+ if (!dev || !dev->of_node || dev->of_node != of_get_stdoutpath(&tmp))
return false;
*baudrate = tmp;
@@ -2603,6 +3026,21 @@ int of_device_enable_path(const char *path)
}
/**
+ * of_device_enable_by_alias - enable a device node by alias
+ * @alias - the alias of the device tree node to enable
+ */
+int of_device_enable_by_alias(const char *alias)
+{
+ struct device_node *node;
+
+ node = of_find_node_by_alias(NULL, alias);
+ if (!node)
+ return -ENODEV;
+
+ return of_device_enable(node);
+}
+
+/**
* of_device_disable - disable a devicenode device
* @node - the node to disable
*
@@ -2646,6 +3084,37 @@ int of_device_disable_by_alias(const char *alias)
}
/**
+ * of_read_file - unflatten oftree file
+ * @filename - path to file to unflatten its contents
+ *
+ * Returns the root node of the tree or an error pointer on error.
+ */
+struct device_node *of_read_file(const char *filename)
+{
+ void *fdt;
+ size_t size;
+ struct device_node *root;
+
+ fdt = read_file(filename, &size);
+ if (!fdt) {
+ pr_err("unable to read %s: %m\n", filename);
+ return ERR_PTR(-errno);
+ }
+
+ if (IS_ENABLED(CONFIG_FILETYPE) && file_detect_type(fdt, size) != filetype_oftree) {
+ pr_err("%s is not a flat device tree file.\n", filename);
+ root = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ root = of_unflatten_dtb(fdt, size);
+out:
+ free(fdt);
+
+ return root;
+}
+
+/**
* of_get_reproducible_name() - get a reproducible name of a node
* @node: The node to get a name from
*
@@ -2728,12 +3197,23 @@ struct device_node *of_find_node_by_reproducible_name(struct device_node *from,
return NULL;
}
+struct device_node *of_get_node_by_reproducible_name(struct device_node *dstroot,
+ struct device_node *srcnp)
+{
+ struct device_node *dstnp;
+ char *name;
+
+ name = of_get_reproducible_name(srcnp);
+ dstnp = of_find_node_by_reproducible_name(dstroot, name);
+ free(name);
+
+ return dstnp;
+}
+
/**
* of_graph_parse_endpoint() - parse common endpoint node properties
* @node: pointer to endpoint device_node
* @endpoint: pointer to the OF endpoint data structure
- *
- * The caller should hold a reference to @node.
*/
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint)
@@ -2741,8 +3221,8 @@ int of_graph_parse_endpoint(const struct device_node *node,
struct device_node *port_node = of_get_parent(node);
if (!port_node)
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, node->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, node);
memset(endpoint, 0, sizeof(*endpoint));
@@ -2814,15 +3294,15 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
port = of_get_child_by_name(parent, "port");
if (!port) {
- pr_err("%s(): no port node found in %s\n",
- __func__, parent->full_name);
+ pr_err("%s(): no port node found in %pOF\n",
+ __func__, parent);
return NULL;
}
} else {
port = of_get_parent(prev);
if (!port) {
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, prev);
return NULL;
}
}
diff --git a/drivers/of/device.c b/drivers/of/device.c
index b3f522e1fa..77c027b57e 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <of.h>
#include <of_device.h>
@@ -11,16 +12,16 @@
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
- const struct device_d *dev)
+ const struct device *dev)
{
- if ((!matches) || (!dev->device_node))
+ if ((!matches) || (!dev->of_node))
return NULL;
- return of_match_node(matches, dev->device_node);
+ return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
-const void *of_device_get_match_data(const struct device_d *dev)
+const void *of_device_get_match_data(const struct device *dev)
{
const struct of_device_id *match;
@@ -32,7 +33,7 @@ const void *of_device_get_match_data(const struct device_d *dev)
}
EXPORT_SYMBOL(of_device_get_match_data);
-const char *of_device_get_match_compatible(const struct device_d *dev)
+const char *of_device_get_match_compatible(const struct device *dev)
{
const struct of_device_id *match;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index f72f5e3a30..8dca41990c 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fdt.c - flat devicetree functions
*
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <of.h>
@@ -22,8 +14,24 @@
#include <memory.h>
#include <linux/sizes.h>
#include <linux/ctype.h>
+#include <linux/log2.h>
+#include <linux/overflow.h>
+#include <linux/string_helpers.h>
#include <linux/err.h>
+static inline bool __dt_ptr_ok(const struct fdt_header *fdt, const void *p,
+ unsigned elem_size, unsigned elem_align)
+{
+ if (!p || (const void *)fdt > p || !PTR_IS_ALIGNED(p, elem_align) ||
+ p + elem_size > (const void *)fdt + be32_to_cpu(fdt->totalsize)) {
+ pr_err("unflatten: offset overflows or misaligns FDT\n");
+ return false;
+ }
+
+ return true;
+}
+#define dt_ptr_ok(fdt, p) __dt_ptr_ok(fdt, p, sizeof(*(p)), __alignof__(*(p)))
+
static inline uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int size)
{
dt += size;
@@ -35,29 +43,40 @@ static inline uint32_t dt_struct_advance(struct fdt_header *f, uint32_t dt, int
return dt;
}
-static inline char *dt_string(struct fdt_header *f, char *strstart, uint32_t ofs)
+static inline const char *dt_string(struct fdt_header *f, const char *strstart, uint32_t ofs)
{
+ const char *str;
+
if (ofs > f->size_dt_strings)
return NULL;
- else
- return strstart + ofs;
+
+ str = strstart + ofs;
+
+ return string_is_terminated(str, f->size_dt_strings - ofs) ? str : NULL;
}
static int of_reservemap_num_entries(const struct fdt_header *fdt)
{
- const struct fdt_reserve_entry *r;
+ /*
+ * FDT may violate spec mandated 8-byte alignment if unflattening it out of
+ * a FIT image property, so play it safe here.
+ */
+ const struct fdt_reserve_entry_unaligned {
+ fdt64_t address;
+ fdt64_t size;
+ } __packed *r;
int n = 0;
r = (void *)fdt + be32_to_cpu(fdt->off_mem_rsvmap);
- while (r->size) {
+ while (dt_ptr_ok(fdt, r) && r->size) {
n++;
r++;
if (n == OF_MAX_RESERVE_MAP)
return -EINVAL;
}
- return n;
+ return r->size == 0 ? n : -ESPIPE;
}
/**
@@ -78,7 +97,6 @@ static int of_unflatten_reservemap(struct device_node *root,
int n;
struct property *p;
struct device_node *memreserve;
- __be32 cells;
n = of_reservemap_num_entries(fdt);
if (n <= 0)
@@ -88,16 +106,6 @@ static int of_unflatten_reservemap(struct device_node *root,
if (!memreserve)
return -ENOMEM;
- cells = cpu_to_be32(2);
-
- p = of_new_property(memreserve, "#address-cells", &cells, sizeof(__be32));
- if (!p)
- return -ENOMEM;
-
- p = of_new_property(memreserve, "#size-cells", &cells, sizeof(__be32));
- if (!p)
- return -ENOMEM;
-
p = of_new_property(memreserve, "reg",
(void *)fdt + be32_to_cpu(fdt->off_mem_rsvmap),
n * sizeof(struct fdt_reserve_entry));
@@ -107,6 +115,44 @@ static int of_unflatten_reservemap(struct device_node *root,
return 0;
}
+static int fdt_parse_header(const struct fdt_header *fdt, size_t fdt_size,
+ struct fdt_header *out)
+{
+ if (fdt_size < sizeof(struct fdt_header))
+ return -EINVAL;
+
+ if (fdt->magic != cpu_to_fdt32(FDT_MAGIC)) {
+ pr_err("bad magic: 0x%08x\n", fdt32_to_cpu(fdt->magic));
+ return -EINVAL;
+ }
+
+ if (fdt->version != cpu_to_fdt32(17)) {
+ pr_err("bad dt version: 0x%08x\n", fdt32_to_cpu(fdt->version));
+ return -EINVAL;
+ }
+
+ out->totalsize = fdt32_to_cpu(fdt->totalsize);
+ out->off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct);
+ out->size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct);
+ out->off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
+ out->size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
+
+ if (out->totalsize > fdt_size)
+ return -EINVAL;
+
+ if (size_add(out->off_dt_struct, out->size_dt_struct) > out->totalsize) {
+ pr_err("unflatten: dt size exceeds total size\n");
+ return -ESPIPE;
+ }
+
+ if (size_add(out->off_dt_strings, out->size_dt_strings) > out->totalsize) {
+ pr_err("unflatten: string size exceeds total size\n");
+ return -ESPIPE;
+ }
+
+ return 0;
+}
+
/**
* of_unflatten_dtb - unflatten a dtb binary blob
* @infdt - the fdt blob to unflatten
@@ -132,37 +178,9 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
unsigned int maxlen;
const struct fdt_header *fdt = infdt;
- if (size < sizeof(struct fdt_header))
- return ERR_PTR(-EINVAL);
-
- if (fdt->magic != cpu_to_fdt32(FDT_MAGIC)) {
- pr_err("bad magic: 0x%08x\n", fdt32_to_cpu(fdt->magic));
- return ERR_PTR(-EINVAL);
- }
-
- if (fdt->version != cpu_to_fdt32(17)) {
- pr_err("bad dt version: 0x%08x\n", fdt32_to_cpu(fdt->version));
- return ERR_PTR(-EINVAL);
- }
-
- f.totalsize = fdt32_to_cpu(fdt->totalsize);
- f.off_dt_struct = fdt32_to_cpu(fdt->off_dt_struct);
- f.size_dt_struct = fdt32_to_cpu(fdt->size_dt_struct);
- f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
- f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
-
- if (f.totalsize > size)
- return ERR_PTR(-EINVAL);
-
- if (f.off_dt_struct + f.size_dt_struct > f.totalsize) {
- pr_err("unflatten: dt size exceeds total size\n");
- return ERR_PTR(-ESPIPE);
- }
-
- if (f.off_dt_strings + f.size_dt_strings > f.totalsize) {
- pr_err("unflatten: string size exceeds total size\n");
- return ERR_PTR(-ESPIPE);
- }
+ ret = fdt_parse_header(infdt, size, &f);
+ if (ret < 0)
+ return ERR_PTR(ret);
dt_struct = f.off_dt_struct;
dt_strings = (void *)fdt + f.off_dt_strings;
@@ -176,7 +194,13 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
goto err;
while (1) {
- tag = be32_to_cpu(*(uint32_t *)(infdt + dt_struct));
+ __be32 *tagp = (uint32_t *)(infdt + dt_struct);
+ if (!dt_ptr_ok(infdt, tagp)) {
+ ret = -ESPIPE;
+ goto err;
+ }
+
+ tag = be32_to_cpu(*tagp);
switch (tag) {
case FDT_BEGIN_NODE:
@@ -231,7 +255,7 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
nodep = fdt_prop->data;
name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff));
- if (!name) {
+ if (!name || !node) {
ret = -ESPIPE;
goto err;
}
@@ -321,15 +345,15 @@ static int lstrcpy(char *dest, const char *src)
int len = 0;
int maxlen = 1023;
- while (*src) {
- *dest++ = *src++;
+ do {
+ *dest++ = *src;
len++;
if (!maxlen)
return -ENOSPC;
maxlen--;
- }
+ } while (*src++);
- return len;
+ return len - 1;
}
static void *memalign_realloc(void *orig, size_t oldsize, size_t newsize)
@@ -362,24 +386,41 @@ static void *memalign_realloc(void *orig, size_t oldsize, size_t newsize)
static int fdt_ensure_space(struct fdt *fdt, int dtsize)
{
+ size_t new_size;
+ void *previous;
+
/*
* We assume strings and names have a maximum length of 1024
* whereas properties can be longer. We allocate new memory
* if we have less than 1024 bytes (+ the property size left.
*/
if (fdt->str_size - fdt->str_nextofs < 1024) {
- fdt->strings = realloc(fdt->strings, fdt->str_size * 2);
- if (!fdt->strings)
+ previous = fdt->strings;
+ new_size = fdt->str_size * 2;
+
+ fdt->strings = realloc(previous, new_size);
+ if (!fdt->strings) {
+ free(previous);
return -ENOMEM;
- fdt->str_size *= 2;
+ }
+
+ fdt->str_size = new_size;
}
if (fdt->dt_size - fdt->dt_nextofs < 1024 + dtsize) {
- fdt->dt = memalign_realloc(fdt->dt, fdt->dt_size,
- fdt->dt_size * 2);
- if (!fdt->dt)
+ previous = fdt->dt;
+ new_size = fdt->dt_size * 2;
+
+ if (new_size <= dtsize)
+ new_size = roundup_pow_of_two(fdt->dt_size + dtsize);
+
+ fdt->dt = memalign_realloc(previous, fdt->dt_size, new_size);
+ if (!fdt->dt) {
+ free(previous);
return -ENOMEM;
- fdt->dt_size *= 2;
+ }
+
+ fdt->dt_size = new_size;
}
return 0;
@@ -494,7 +535,7 @@ void *of_flatten_dtb(struct device_node *node)
if (ret)
goto out_free;
- memreserve = of_find_node_by_name(node, "memreserve");
+ memreserve = of_find_node_by_name_address(node, "memreserve");
if (memreserve) {
const void *entries = of_get_property(memreserve, "reg", &len);
@@ -573,9 +614,7 @@ void of_clean_reserve_map(void)
* @__fdt: The devicetree blob
*
* This adds the reservemap entries previously collected in
- * of_add_reserve_entry() to a devicetree binary blob. This also
- * adds the devicetree itself to the reserved list, so after calling
- * this function the tree should not be relocated anymore.
+ * of_add_reserve_entry() to a devicetree binary blob.
*/
void fdt_add_reserve_map(void *__fdt)
{
@@ -603,10 +642,135 @@ void fdt_add_reserve_map(void *__fdt)
fdt_res++;
}
- of_write_number(&fdt_res->address, (unsigned long)__fdt, 2);
- of_write_number(&fdt_res->size, be32_to_cpu(fdt->totalsize), 2);
- fdt_res++;
-
of_write_number(&fdt_res->address, 0, 2);
of_write_number(&fdt_res->size, 0, 2);
}
+
+void fdt_print_reserve_map(const void *__fdt)
+{
+ const struct fdt_header *fdt = __fdt;
+ const struct fdt_reserve_entry *fdt_res =
+ __fdt + be32_to_cpu(fdt->off_mem_rsvmap);
+ int n = 0;
+
+ while (1) {
+ uint64_t size = fdt64_to_cpu(fdt_res->size);
+ uint64_t address = fdt64_to_cpu(fdt_res->address);
+
+ if (!size)
+ break;
+
+ printf("/memreserve/ #%d: 0x%08llx - 0x%08llx\n", n, address, address + size - 1);
+
+ n++;
+ fdt_res++;
+ if (n == OF_MAX_RESERVE_MAP)
+ return;
+ }
+}
+
+static int fdt_string_is_compatible(const char *haystack, int haystack_len,
+ const char *needle, int needle_len)
+{
+ const char *p;
+ int index = 0;
+
+ while (haystack_len >= needle_len) {
+ if (memcmp(needle, haystack, needle_len + 1) == 0)
+ return OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2);
+
+ p = memchr(haystack, '\0', haystack_len);
+ if (!p)
+ return 0;
+ haystack_len -= (p - haystack) + 1;
+ haystack = p + 1;
+ index++;
+ }
+
+ return 0;
+}
+
+int fdt_machine_is_compatible(const struct fdt_header *fdt, size_t fdt_size, const char *compat)
+{
+ uint32_t tag;
+ const struct fdt_property *fdt_prop;
+ const char *name;
+ uint32_t dt_struct;
+ const struct fdt_node_header *fnh;
+ const void *dt_strings;
+ struct fdt_header f;
+ int ret, len;
+ int expect = FDT_BEGIN_NODE;
+ int compat_len = strlen(compat);
+
+ ret = fdt_parse_header(fdt, fdt_size, &f);
+ if (ret < 0)
+ return 0;
+
+ dt_struct = f.off_dt_struct;
+ dt_strings = (const void *)fdt + f.off_dt_strings;
+
+ while (1) {
+ const __be32 *tagp = (const void *)fdt + dt_struct;
+ if (!dt_ptr_ok(fdt, tagp))
+ return 0;
+
+ tag = be32_to_cpu(*tagp);
+ if (tag != FDT_NOP && tag != expect)
+ return 0;
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ fnh = (const void *)fdt + dt_struct;
+
+ /* The root node must have an empty name */
+ if (fnh->name[0] != '\0')
+ return 0;
+
+ dt_struct = dt_struct_advance(&f, dt_struct,
+ sizeof(struct fdt_node_header) + 1);
+
+ /*
+ * Quoting Device Tree Specification v0.4 §5.4.2:
+ *
+ * [T]his process requires that all property definitions for
+ * a particular node precede any subnode definitions for that
+ * node. Although the structure would not be ambiguous if
+ * properties and subnodes were intermingled, the code needed
+ * to process a flat tree is simplified by this requirement.
+ *
+ * So let's make use of this simplification.
+ */
+ expect = FDT_PROP;
+ break;
+
+ case FDT_PROP:
+ fdt_prop = (const void *)fdt + dt_struct;
+ len = fdt32_to_cpu(fdt_prop->len);
+
+ name = dt_string(&f, dt_strings, fdt32_to_cpu(fdt_prop->nameoff));
+ if (!name)
+ return 0;
+
+ if (strcmp(name, "compatible")) {
+ dt_struct = dt_struct_advance(&f, dt_struct,
+ sizeof(struct fdt_property) + len);
+ break;
+ }
+
+ return fdt_string_is_compatible(fdt_prop->data, len, compat, compat_len);
+
+ case FDT_NOP:
+ dt_struct = dt_struct_advance(&f, dt_struct, FDT_TAGSIZE);
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (!dt_struct)
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/drivers/of/mem_generic.c b/drivers/of/mem_generic.c
index 55d93bcb06..be618d51f5 100644
--- a/drivers/of/mem_generic.c
+++ b/drivers/of/mem_generic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <of.h>
#include <memory.h>
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
index 687e675302..c1b69aac04 100644
--- a/drivers/of/of_firmware.c
+++ b/drivers/of/of_firmware.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
*/
@@ -11,7 +11,8 @@ static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
struct device_node *mgr_node;
do {
- mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
+ mgr_node = of_parse_phandle_from(np, of_find_root_node(np),
+ "fpga-mgr", 0);
if (mgr_node)
return firmwaremgr_find_by_node(mgr_node);
} while ((np = of_get_parent(np)) != NULL);
diff --git a/drivers/of/of_gpio.c b/drivers/of/of_gpio.c
index e1cafdc848..25496a3cf1 100644
--- a/drivers/of/of_gpio.c
+++ b/drivers/of/of_gpio.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <of.h>
@@ -28,8 +30,8 @@ static void of_gpio_flags_quirks(struct device_node *np,
* be actively ignored.
*/
if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) {
- pr_warn("%s GPIO handle specifies active low - ignored\n",
- np->full_name);
+ pr_warn("%pOF GPIO handle specifies active low - ignored\n",
+ np);
*flags &= ~OF_GPIO_ACTIVE_LOW;
}
if (active_low)
@@ -44,6 +46,44 @@ static void of_gpio_flags_quirks(struct device_node *np,
}
+static struct gpio_chip *of_find_gpiochip_by_xlate(
+ struct of_phandle_args *gpiospec)
+{
+ struct gpio_chip *chip;
+ struct device *dev;
+
+ dev = of_find_device_by_node(gpiospec->np);
+ if (!dev) {
+ pr_debug("%s: unable to find device of node %pOF\n",
+ __func__, gpiospec->np);
+ return NULL;
+ }
+
+ chip = gpio_get_chip_by_dev(dev);
+ if (!chip) {
+ pr_debug("%s: unable to find gpiochip\n", __func__);
+ return NULL;
+ }
+
+ if (!chip->ops->of_xlate ||
+ chip->ops->of_xlate(chip, gpiospec, NULL) < 0) {
+ pr_err("%s: failed to execute of_xlate\n", __func__);
+ return NULL;
+ }
+
+ return chip;
+}
+
+static int of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
+ struct of_phandle_args *gpiospec,
+ enum of_gpio_flags *flags)
+{
+ if (chip->of_gpio_n_cells != gpiospec->args_count)
+ return -EINVAL;
+
+ return chip->ops->of_xlate(chip, gpiospec, flags);
+}
+
/**
* of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API
* @np: device node to get GPIO from
@@ -58,38 +98,36 @@ static void of_gpio_flags_quirks(struct device_node *np,
int of_get_named_gpio_flags(struct device_node *np, const char *propname,
int index, enum of_gpio_flags *flags)
{
- struct of_phandle_args out_args;
- struct device_d *dev;
+ struct of_phandle_args gpiospec;
+ struct gpio_chip *chip;
int ret;
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells",
- index, &out_args);
+ index, &gpiospec);
if (ret) {
pr_debug("%s: cannot parse %s property: %d\n",
__func__, propname, ret);
return ret;
}
- dev = of_find_device_by_node(out_args.np);
- if (!dev) {
- pr_debug("%s: unable to find device of node %s\n",
- __func__, out_args.np->full_name);
- return -EPROBE_DEFER;
+ chip = of_find_gpiochip_by_xlate(&gpiospec);
+ if (!chip) {
+ ret = -EPROBE_DEFER;
+ goto out;
}
- ret = gpio_get_num(dev, out_args.args[0]);
- if (ret == -EPROBE_DEFER)
- return ret;
+ ret = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
if (ret < 0) {
pr_err("%s: unable to get gpio num of device %s: %d\n",
- __func__, dev_name(dev), ret);
- return ret;
+ __func__, dev_name(chip->dev), ret);
+ goto out;
}
- if (flags) {
- *flags = out_args.args[1];
+ if (flags)
of_gpio_flags_quirks(np, propname, flags, index);
- }
+
+out:
+ of_node_put(gpiospec.np);
return ret;
}
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index c531518c1d..fa2c778138 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -1,10 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
*
* OF helpers for mtd.
- *
- * This file is released under the GPLv2
- *
*/
#include <common.h>
#include <of_mtd.h>
@@ -23,7 +21,6 @@ static const char *nand_ecc_modes[] = {
[NAND_ECC_HW] = "hw",
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_ON_DIE] = "on-die",
- [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
[NAND_ECC_SOFT_BCH] = "soft_bch",
};
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
index 67015160e2..75a24073da 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* OF helpers for network devices.
*
- * This file is released under the GPLv2
- *
* Initially copied out of arch/powerpc/kernel/prom_parse.c
*/
#include <common.h>
@@ -25,6 +24,7 @@ static const char *phy_modes[] = {
[PHY_INTERFACE_MODE_TBI] = "tbi",
[PHY_INTERFACE_MODE_REVMII] = "rev-mii",
[PHY_INTERFACE_MODE_RMII] = "rmii",
+ [PHY_INTERFACE_MODE_REVRMII] = "rev-rmii",
[PHY_INTERFACE_MODE_RGMII] = "rgmii",
[PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id",
[PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid",
diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c
index c237d9c6a8..42efb1ad1d 100644
--- a/drivers/of/of_path.c
+++ b/drivers/of/of_path.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* of_path.c
*
* Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -21,14 +12,14 @@
#include <linux/mtd/mtd.h>
-struct device_d *of_find_device_by_node_path(const char *path)
+struct device *of_find_device_by_node_path(const char *path)
{
- struct device_d *dev;
+ struct device *dev;
for_each_device(dev) {
- if (!dev->device_node)
+ if (!dev->of_node)
continue;
- if (!strcmp(path, dev->device_node->full_name))
+ if (!strcmp(path, dev->of_node->full_name))
return dev;
}
@@ -36,21 +27,27 @@ struct device_d *of_find_device_by_node_path(const char *path)
}
/**
- * __of_find_path
+ * __of_cdev_find
*
* @node: The node to find the cdev for, can be the device or a
* partition in the device
- * @part: Optionally, a description of a parition of @node. See of_find_path
- * @outpath: if this function returns 0 outpath will contain the path belonging
- * to the input path description. Must be freed with free().
- * @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available
+ * @part: Optionally, a description of a partition of @node. See of_find_path
*
*/
-static int __of_find_path(struct device_node *node, const char *part, char **outpath, unsigned flags)
+static struct cdev *__of_cdev_find(struct device_node *node, const char *part)
{
- struct device_d *dev;
+ struct device *dev;
struct cdev *cdev;
- bool add_bb = false;
+
+ /*
+ * On EFI, where devices are not instantiated from device tree, the
+ * state backend may point at a top-level fixed-partitions partition
+ * subnode with a partuuid property, which will be looked up globally.
+ *
+ * In order to support this binding, we do not early exit when
+ * of_partition_ensure_probed fails, but instead try the custom binding.
+ */
+ (void)of_partition_ensure_probed(node);
dev = of_find_device_by_node_path(node->full_name);
if (!dev) {
@@ -63,24 +60,17 @@ static int __of_find_path(struct device_node *node, const char *part, char **out
/* when partuuid is specified short-circuit the search for the cdev */
ret = of_property_read_string(node, "partuuid", &uuid);
- if (!ret) {
- cdev = cdev_by_partuuid(uuid);
- if (!cdev)
- return -ENODEV;
-
- *outpath = basprintf("/dev/%s", cdev->name);
-
- return 0;
- }
+ if (!ret)
+ return cdev_by_partuuid(uuid) ?: ERR_PTR(-ENODEV);
}
dev = of_find_device_by_node_path(devnode->full_name);
if (!dev)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
if (dev->bus && !dev->driver)
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
device_detect(dev);
@@ -89,8 +79,40 @@ static int __of_find_path(struct device_node *node, const char *part, char **out
else
cdev = cdev_by_device_node(node);
- if (!cdev)
- return -ENOENT;
+ return cdev ?: ERR_PTR(-ENOENT);
+}
+
+/**
+ * of_cdev_find
+ *
+ * @node: The node to find the cdev for, can be the device or a
+ * partition in the device
+ *
+ */
+struct cdev *of_cdev_find(struct device_node *node)
+{
+ return __of_cdev_find(node, NULL);
+}
+
+/**
+ * __of_find_path
+ *
+ * @node: The node to find the cdev for, can be the device or a
+ * partition in the device
+ * @part: Optionally, a description of a partition of @node. See of_find_path
+ * @outpath: if this function returns 0 outpath will contain the path belonging
+ * to the input path description. Must be freed with free().
+ * @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available
+ *
+ */
+static int __of_find_path(struct device_node *node, const char *part, char **outpath, unsigned flags)
+{
+ bool add_bb = false;
+ struct cdev *cdev;
+
+ cdev = __of_cdev_find(node, part);
+ if (IS_ERR(cdev))
+ return PTR_ERR(cdev);
if ((flags & OF_FIND_PATH_FLAGS_BB) && cdev->mtd &&
mtd_can_have_bb(cdev->mtd))
@@ -159,9 +181,9 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
part_size = cdev->size;
pr_debug("%s path %s: is a partition with offset 0x%08llx, size 0x%08llx\n",
__func__, path, part_offset, part_size);
- np = cdev->master->device_node;
+ np = cdev_of_node(cdev->master);
} else {
- np = cdev->device_node;
+ np = cdev_of_node(cdev);
}
/*
@@ -170,14 +192,14 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
*/
rnp = of_find_node_by_path_from(root, np->full_name);
if (!rnp) {
- pr_debug("%s path %s: %s not found in passed tree\n", __func__, path,
- np->full_name);
+ pr_debug("%s path %s: %pOF not found in passed tree\n", __func__, path,
+ np);
return NULL;
}
if (!is_partition) {
- pr_debug("%s path %s: returning full device node %s\n", __func__, path,
- rnp->full_name);
+ pr_debug("%s path %s: returning full device node %pOF\n", __func__, path,
+ rnp);
return rnp;
}
@@ -204,7 +226,7 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
ns = of_n_size_cells(np);
if (len < (na + ns) * sizeof(__be32)) {
- pr_err("reg property too small in %s\n", np->full_name);
+ pr_err("reg property too small in %pOF\n", np);
continue;
}
@@ -212,8 +234,8 @@ struct device_node *of_find_node_by_devpath(struct device_node *root, const char
size = of_read_number(reg + na, ns);
if (part_offset == offset && part_size == size) {
- pr_debug("%s path %s: found matching partition in %s\n", __func__, path,
- np->full_name);
+ pr_debug("%s path %s: found matching partition in %pOF\n", __func__, path,
+ np);
return np;
}
}
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 2d0fbd2e5f..ebb5ba6df3 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <of.h>
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 25140eed31..73c7a91db9 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Functions for working with device tree overlays
*
@@ -102,8 +102,10 @@ static char *of_overlay_fix_path(struct device_node *root,
if (of_get_child_by_name(fragment, "__overlay__"))
break;
}
- if (!fragment)
+ if (!fragment) {
+ pr_info("could not find __overlay__ node\n");
return NULL;
+ }
target = find_target(root, fragment);
if (!target)
@@ -112,11 +114,11 @@ static char *of_overlay_fix_path(struct device_node *root,
prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
path_tail = path + strlen(prefix);
- return basprintf("%s%s", target->full_name, path_tail);
+ return basprintf("%pOF%s", target, path_tail);
}
-static void of_overlay_apply_symbols(struct device_node *root,
- struct device_node *overlay)
+static int of_overlay_apply_symbols(struct device_node *root,
+ struct device_node *overlay)
{
const char *old_path;
char *new_path;
@@ -129,12 +131,12 @@ static void of_overlay_apply_symbols(struct device_node *root,
if (!overlay_symbols) {
pr_debug("overlay doesn't have a __symbols__ node\n");
- return;
+ return 0;
}
if (!root_symbols) {
pr_info("root doesn't have a __symbols__ node\n");
- return;
+ return 0;
}
list_for_each_entry(prop, &overlay_symbols->properties, list) {
@@ -143,11 +145,15 @@ static void of_overlay_apply_symbols(struct device_node *root,
old_path = of_property_get_value(prop);
new_path = of_overlay_fix_path(root, overlay, old_path);
+ if (!new_path)
+ return -EINVAL;
pr_debug("add symbol %s with new path %s\n",
prop->name, new_path);
of_property_write_string(root_symbols, prop->name, new_path);
}
+
+ return 0;
}
static int of_overlay_apply_fragment(struct device_node *root,
@@ -190,7 +196,9 @@ int of_overlay_apply_tree(struct device_node *root,
goto out_err;
/* Copy symbols from resolved overlay to base device tree */
- of_overlay_apply_symbols(root, resolved);
+ err = of_overlay_apply_symbols(root, resolved);
+ if (err)
+ goto out_err;
/* Copy nodes and properties from resolved overlay to root */
for_each_child_of_node(resolved, fragment) {
@@ -199,6 +207,10 @@ int of_overlay_apply_tree(struct device_node *root,
pr_warn("failed to apply %s\n", fragment->name);
}
+ /* We are patching the live tree, reload aliases */
+ if (root == of_get_root_node())
+ of_alias_scan();
+
out_err:
of_delete_node(resolved);
@@ -296,26 +308,15 @@ static bool of_overlay_matches_filter(const char *filename, struct device_node *
int of_overlay_apply_file(struct device_node *root, const char *filename,
bool filter)
{
- void *fdt;
struct device_node *ovl;
- size_t size;
int ret;
if (filter && !of_overlay_matches_filter(filename, NULL))
return 0;
- ret = read_file_2(filename, &size, &fdt, FILESIZE_MAX);
- if (ret)
- return ret;
-
- ovl = of_unflatten_dtb(fdt, size);
-
- free(fdt);
-
- if (IS_ERR(ovl)) {
- pr_err("Failed to unflatten %s: %pe\n", filename, ovl);
+ ovl = of_read_file(filename);
+ if (IS_ERR(ovl))
return PTR_ERR(ovl);
- }
if (filter && !of_overlay_matches_filter(NULL, ovl))
return 0;
@@ -414,7 +415,6 @@ void of_overlay_set_basedir(const char *path)
static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
bool filter)
{
- char *p, *path;
int ret = 0;
DIR *dir;
@@ -427,8 +427,6 @@ static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
if (!dir)
return -errno;
- p = path = strdup(of_overlay_filepattern);
-
while (1) {
struct dirent *ent;
char *filename;
diff --git a/drivers/of/partition.c b/drivers/of/partition.c
index 10081363de..df66751fe9 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* partition.c - devicetree partition parsing
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <of.h>
@@ -34,14 +26,12 @@ enum of_binding_name {
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
{
+ struct devfs_partition partinfo = {};
const char *partname;
char *filename;
struct cdev *new;
const __be32 *reg;
- u64 offset, size;
- const char *name;
int len;
- unsigned long flags = 0;
int na, ns;
if (!node)
@@ -55,12 +45,12 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
ns = of_n_size_cells(node);
if (len < (na + ns) * sizeof(__be32)) {
- pr_err("reg property too small in %s\n", node->full_name);
+ pr_err("reg property too small in %pOF\n", node);
return NULL;
}
- offset = of_read_number(reg, na);
- size = of_read_number(reg + na, ns);
+ partinfo.offset = of_read_number(reg, na);
+ partinfo.size = of_read_number(reg + na, ns);
partname = of_get_property(node, "label", NULL);
if (!partname)
@@ -68,21 +58,23 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
if (!partname)
return NULL;
- name = (char *)partname;
-
- debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, offset, size);
+ debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname,
+ partinfo.offset, partinfo.size);
if (of_get_property(node, "read-only", NULL))
- flags = DEVFS_PARTITION_READONLY;
+ partinfo.flags = DEVFS_PARTITION_READONLY;
- filename = basprintf("%s.%s", cdev->name, partname);
+ partinfo.name = filename = basprintf("%s.%s", cdev->name, partname);
- new = devfs_add_partition(cdev->name, offset, size, flags, filename);
- if (IS_ERR(new))
+ new = cdevfs_add_partition(cdev, &partinfo);
+ if (IS_ERR(new)) {
+ pr_err("Adding partition %s failed: %pe\n", filename, new);
new = NULL;
+ goto out;
+ }
- if (new)
- new->device_node = node;;
+ new->device_node = node;
+ new->flags |= DEVFS_PARTITION_FROM_OF | DEVFS_PARTITION_FOR_FIXUP;
if (IS_ENABLED(CONFIG_NVMEM) && of_device_is_compatible(node, "nvmem-cells")) {
struct nvmem_device *nvmem = nvmem_partition_register(new);
@@ -90,6 +82,7 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
dev_warn(cdev->dev, "nvmem registeration failed: %pe\n", nvmem);
}
+out:
free(filename);
return new;
@@ -102,7 +95,7 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
if (!node)
return -EINVAL;
- cdev->device_node = node;
+ cdev_set_of_node(cdev, node);
subnode = of_get_child_by_name(node, "partitions");
if (subnode) {
@@ -118,14 +111,47 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
return 0;
}
+/**
+ * of_partition_ensure_probed - ensure a parition is probed
+ * @np: pointer to a partition or to a partitionable device
+ * Unfortunately, there is no completely reliable way
+ * to differentiate partitions from devices prior to
+ * probing, because partitions may also have compatibles.
+ * We only handle nvmem-cells, so anything besides that
+ * is assumed to be a device that should be probed directly.
+ *
+ * Returns zero on success or a negative error code otherwise
+ */
int of_partition_ensure_probed(struct device_node *np)
{
- np = of_get_parent(np);
+ struct device_node *parent = of_get_parent(np);
+
+ /* root node is not a partition */
+ if (!parent)
+ return -EINVAL;
+
+ /* Check if modern partitions binding */
+ if (of_device_is_compatible(parent, "fixed-partitions")) {
+ parent = of_get_parent(parent);
+
+ /*
+ * Can't call of_partition_ensure_probed on root node.
+ * This catches barebox-specific partuuid binding
+ * (top-level partition node)
+ */
+ if (!of_get_parent(parent))
+ return -EINVAL;
- if (of_device_is_compatible(np, "fixed-partitions"))
- np = of_get_parent(np);
+ return of_device_ensure_probed(parent);
+ }
- return np ? of_device_ensure_probed(np) : -EINVAL;
+ /* Check if legacy partitions binding */
+ if (!of_property_present(np, "compatible") ||
+ of_device_is_compatible(np, "nvmem-cells"))
+ return of_device_ensure_probed(parent);
+
+ /* Doesn't look like a partition, so let's probe directly */
+ return of_device_ensure_probed(np);
}
EXPORT_SYMBOL_GPL(of_partition_ensure_probed);
@@ -152,7 +178,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
return 0;
list_for_each_entry(partcdev, &cdev->partitions, partition_entry) {
- if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP))
continue;
n_parts++;
}
@@ -203,7 +229,7 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */
loff_t partoffset;
- if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ if (!(partcdev->flags & DEVFS_PARTITION_FOR_FIXUP))
continue;
if (partcdev->mtd)
@@ -250,18 +276,21 @@ int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
static int of_partition_fixup(struct device_node *root, void *ctx)
{
struct cdev *cdev = ctx;
- struct device_node *np;
+ struct device_node *cdev_np, *np;
char *name;
- if (!cdev->device_node)
+ cdev_np = cdev_of_node(cdev);
+ if (!cdev_np)
return -EINVAL;
- name = of_get_reproducible_name(cdev->device_node);
+ if (list_empty(&cdev->partitions))
+ return 0;
+
+ name = of_get_reproducible_name(cdev_np);
np = of_find_node_by_reproducible_name(root, name);
free(name);
if (!np) {
- dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n",
- cdev->device_node->full_name);
+ dev_err(cdev->dev, "Cannot find nodepath %pOF, cannot fixup\n", cdev_np);
return -EINVAL;
}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 92b6caef5a..918607a518 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* platform.c - bus/device related devicetree functions
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <deep-probe.h>
@@ -20,6 +12,7 @@
#include <of.h>
#include <of_address.h>
#include <linux/amba/bus.h>
+#include <mmu.h>
/**
* of_find_device_by_node - Find the platform_device associated with a node
@@ -27,17 +20,18 @@
*
* Returns platform_device pointer, or NULL if not found
*/
-struct device_d *of_find_device_by_node(struct device_node *np)
+struct device *of_find_device_by_node(struct device_node *np)
{
- struct device_d *dev;
- int ret;
+ struct device *dev;
- ret = of_device_ensure_probed(np);
- if (ret)
- return NULL;
+ /* Not having a driver is not an error here */
+ (void)of_device_ensure_probed(np);
+
+ if (deep_probe_is_supported())
+ return np->dev;
for_each_device(dev)
- if (dev->device_node == np)
+ if (dev->of_node == np)
return dev;
return NULL;
}
@@ -51,9 +45,9 @@ EXPORT_SYMBOL(of_find_device_by_node);
* derive a unique name. If it cannot, then it will prepend names from
* parent nodes until a unique name can be derived.
*/
-static void of_device_make_bus_id(struct device_d *dev)
+static void of_device_make_bus_id(struct device *dev)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
const __be32 *reg;
u64 addr;
@@ -78,7 +72,64 @@ static void of_device_make_bus_id(struct device_d *dev)
}
}
-static void of_dma_configure(struct device_d *dev, struct device_node *np)
+static struct device_node *of_get_next_dma_parent(const struct device_node *np)
+{
+ struct of_phandle_args args;
+ int ret, index;
+
+ index = of_property_match_string(np, "interconnect-names", "dma-mem");
+ if (index < 0)
+ return of_get_parent(np);
+
+ ret = of_parse_phandle_with_args(np, "interconnects",
+ "#interconnect-cells",
+ index, &args);
+ if (ret < 0)
+ return of_get_parent(np);
+
+ return args.np;
+}
+
+static enum dev_dma_coherence of_dma_get_coherence(struct device_node *node)
+{
+ if (IS_ENABLED(CONFIG_OF_DMA_COHERENCY)) {
+ while (node) {
+ if (of_property_read_bool(node, "dma-coherent"))
+ return DEV_DMA_COHERENT;
+ if (of_property_read_bool(node, "dma-noncoherent"))
+ return DEV_DMA_NON_COHERENT;
+ node = of_get_next_dma_parent(node);
+ }
+ }
+
+ return DEV_DMA_COHERENCE_DEFAULT;
+}
+
+/**
+ * of_dma_is_coherent - Check if device is coherent
+ * @np: device node
+ *
+ * It returns true if "dma-coherent" property was found
+ * for this device in the DT, or if DMA is coherent by
+ * default for OF devices on the current platform and no
+ * "dma-noncoherent" property was found for this device.
+ */
+bool of_dma_is_coherent(struct device_node *node)
+{
+ switch (of_dma_get_coherence(node)) {
+ case DEV_DMA_COHERENT:
+ return true;
+ case DEV_DMA_NON_COHERENT:
+ return false;
+ case DEV_DMA_COHERENCE_DEFAULT:
+ return IS_ENABLED(CONFIG_ARCH_DMA_DEFAULT_COHERENT);
+ }
+
+ BUG();
+}
+EXPORT_SYMBOL_GPL(of_dma_is_coherent);
+
+static void of_dma_configure(struct device *dev, struct device_node *np)
{
u64 dma_addr, paddr, size = 0;
unsigned long offset;
@@ -92,6 +143,7 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
}
dev->dma_offset = offset;
+ dev->dma_coherent = of_dma_get_coherence(np);
}
/**
@@ -102,13 +154,14 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_platform_device_create(struct device_node *np,
- struct device_d *parent)
+struct device *of_platform_device_create(struct device_node *np,
+ struct device *parent)
{
- struct device_d *dev;
+ struct device *dev;
struct resource *res = NULL, temp_res;
resource_size_t resinval;
int i, ret, num_reg = 0;
+ u32 virt;
if (!of_device_is_available(np))
return NULL;
@@ -117,8 +170,10 @@ struct device_d *of_platform_device_create(struct device_node *np,
* Linux uses the OF_POPULATED flag to skip already populated/created
* devices.
*/
- if (np->dev)
+ if (np->dev) {
+ device_rescan(np->dev);
return np->dev;
+ }
/* count the io resources */
if (of_can_translate_address(np))
@@ -140,7 +195,7 @@ struct device_d *of_platform_device_create(struct device_node *np,
/* setup generic device info */
dev = xzalloc(sizeof(*dev));
dev->id = DEVICE_ID_SINGLE;
- dev->device_node = np;
+ dev->of_node = np;
dev->parent = parent;
dev->resource = res;
dev->num_resources = num_reg;
@@ -148,6 +203,24 @@ struct device_d *of_platform_device_create(struct device_node *np,
of_dma_configure(dev, np);
+ if (num_reg && !of_property_read_u32(np, "virtual-reg", &virt)) {
+ resource_size_t remap_offset = virt - res[0].start;
+
+ for (i = 0; i < num_reg; i++) {
+ void *new_virt = (void *)res[i].start + remap_offset;
+ resource_size_t size = resource_size(&res[i]);
+
+ ret = arch_remap_range(new_virt, res[i].start, size, MAP_UNCACHED);
+ if (!ret) {
+ debug("%s: remap device %s resource %d: %pa -> 0x%p\n",
+ __func__, dev_name(dev), i, &res[i].start, new_virt);
+
+ res[i].start = (resource_size_t)new_virt;
+ res[i].end = res[i].start + size - 1;
+ }
+ }
+ }
+
resinval = (-1);
debug("%s: register device %s, io=%pa\n",
@@ -163,17 +236,17 @@ struct device_d *of_platform_device_create(struct device_node *np,
np->dev = NULL;
- free(dev);
+ free_device(dev);
if (num_reg)
free(res);
return NULL;
}
-struct driver_d dummy_driver = {
+struct driver dummy_driver = {
.name = "dummy-driver",
};
-void of_platform_device_dummy_drv(struct device_d *dev)
+void of_platform_device_dummy_drv(struct device *dev)
{
dev->driver = &dummy_driver;
}
@@ -185,9 +258,9 @@ void of_platform_device_dummy_drv(struct device_d *dev)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register(struct device_node *np)
+struct device *of_device_enable_and_register(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
of_device_enable(np);
@@ -206,11 +279,11 @@ EXPORT_SYMBOL(of_device_enable_and_register);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_name(const char *name)
+struct device *of_device_enable_and_register_by_name(const char *name)
{
struct device_node *node;
- node = of_find_node_by_name(NULL, name);
+ node = of_find_node_by_name_address(NULL, name);
if (!node)
node = of_find_node_by_path(name);
@@ -228,7 +301,7 @@ EXPORT_SYMBOL(of_device_enable_and_register_by_name);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_alias(const char *alias)
+struct device *of_device_enable_and_register_by_alias(const char *alias)
{
struct device_node *node;
@@ -241,12 +314,12 @@ struct device_d *of_device_enable_and_register_by_alias(const char *alias)
EXPORT_SYMBOL(of_device_enable_and_register_by_alias);
#ifdef CONFIG_ARM_AMBA
-static struct device_d *of_amba_device_create(struct device_node *np)
+static struct device *of_amba_device_create(struct device_node *np)
{
struct amba_device *dev;
int ret;
- debug("Creating amba device %s\n", np->full_name);
+ debug("Creating amba device %pOF\n", np);
if (!of_device_is_available(np))
return NULL;
@@ -262,7 +335,7 @@ static struct device_d *of_amba_device_create(struct device_node *np)
/* setup generic device info */
dev->dev.id = DEVICE_ID_SINGLE;
- dev->dev.device_node = np;
+ dev->dev.of_node = np;
of_device_make_bus_id(&dev->dev);
ret = of_address_to_resource(np, 0, &dev->res);
@@ -286,11 +359,12 @@ static struct device_d *of_amba_device_create(struct device_node *np)
return &dev->dev;
amba_err_free:
+ free_device_res(&dev->dev);
free(dev);
return NULL;
}
#else /* CONFIG_ARM_AMBA */
-static inline struct device_d *of_amba_device_create(struct device_node *np)
+static inline struct device *of_amba_device_create(struct device_node *np)
{
return NULL;
}
@@ -307,16 +381,16 @@ static inline struct device_d *of_amba_device_create(struct device_node *np)
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
- struct device_d *dev;
+ struct device *dev;
int rc = 0;
/* Make sure it has a compatible property */
if (!of_get_property(bus, "compatible", NULL)) {
- pr_debug("%s() - skipping %s, no compatible prop\n",
- __func__, bus->full_name);
+ pr_debug("%s() - skipping %pOF, no compatible prop\n",
+ __func__, bus);
return 0;
}
@@ -330,7 +404,7 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
for_each_child_of_node(bus, child) {
- pr_debug(" create child: %s\n", child->full_name);
+ pr_debug(" create child: %pOF\n", child);
rc = of_platform_bus_create(child, matches, dev);
if (rc)
break;
@@ -353,7 +427,7 @@ static int of_platform_bus_create(struct device_node *bus,
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
int rc = 0;
@@ -373,17 +447,17 @@ int of_platform_populate(struct device_node *root,
}
EXPORT_SYMBOL_GPL(of_platform_populate);
-static struct device_d *of_device_create_on_demand(struct device_node *np)
+static struct device *of_device_create_on_demand(struct device_node *np)
{
struct device_node *parent;
- struct device_d *parent_dev, *dev;
+ struct device *parent_dev, *dev;
parent = of_get_parent(np);
if (!parent)
return NULL;
- if (!np->dev)
- pr_debug("Creating device for %s\n", np->full_name);
+ if (!np->dev && parent->dev)
+ device_rescan(parent->dev);
/* Create all parent devices needed for the requested device */
parent_dev = parent->dev ? : of_device_create_on_demand(parent);
@@ -398,12 +472,17 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
if (np->dev)
return np->dev;
+ if (!of_property_present(np, "compatible"))
+ return NULL;
+
+ pr_debug("Creating device for %pOF\n", np);
+
if (of_device_is_compatible(np, "arm,primecell"))
dev = of_amba_device_create(np);
else
dev = of_platform_device_create(np, parent_dev);
- return dev ? : ERR_PTR(-ENODEV);
+ return dev ? : ERR_PTR(-EPROBE_DEFER);
}
/**
@@ -415,21 +494,19 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
* it.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error.
*/
int of_device_ensure_probed(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
- if (!deep_probe_is_supported())
+ if (!np || !deep_probe_is_supported())
return 0;
dev = of_device_create_on_demand(np);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
-
- BUG_ON(!dev);
+ if (IS_ERR_OR_NULL(dev))
+ return -EPROBE_DEFER;
/*
* The deep-probe mechanism relies on the fact that all necessary
@@ -439,7 +516,7 @@ int of_device_ensure_probed(struct device_node *np)
* requirements are fulfilled if 'dev->driver' is not NULL.
*/
if (!dev->driver)
- return -ENODEV;
+ return -EPROBE_DEFER;
return 0;
}
@@ -454,7 +531,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed);
* populated and probed if found.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error
* %-EINVAL if alias can't be found
*/
@@ -462,6 +539,9 @@ int of_device_ensure_probed_by_alias(const char *alias)
{
struct device_node *dev_node;
+ if (!deep_probe_is_supported())
+ return 0;
+
dev_node = of_find_node_by_alias(NULL, alias);
if (!dev_node)
return -EINVAL;
@@ -479,7 +559,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed_by_alias);
* probes devices which match @ids.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
@@ -487,6 +567,9 @@ int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
struct device_node *np;
int err, ret = 0;
+ if (!deep_probe_is_supported())
+ return 0;
+
for_each_matching_node(np, ids) {
if (!of_device_is_available(np))
continue;
@@ -509,25 +592,51 @@ EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_dev_id);
* devices which matches @property_name.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_property(const char *property_name)
{
struct device_node *node;
+ int err, ret = 0;
+
+ if (!deep_probe_is_supported())
+ return 0;
for_each_node_with_property(node, property_name) {
- int ret;
+ if (!of_device_is_available(node))
+ continue;
- ret = of_device_ensure_probed(node);
- if (ret)
- return ret;
+ err = of_device_ensure_probed(node);
+ if (err)
+ ret = err;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_property);
+int of_devices_ensure_probed_by_name(const char *name)
+{
+ struct device_node *node;
+ int err, ret = 0;
+
+ if (!deep_probe_is_supported())
+ return 0;
+
+ for_each_node_by_name(node, name) {
+ if (!of_device_is_available(node))
+ continue;
+
+ err = of_device_ensure_probed(node);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_name);
+
static int of_stdoutpath_init(void)
{
struct device_node *np;
@@ -544,3 +653,11 @@ static int of_stdoutpath_init(void)
return of_device_ensure_probed(np);
}
postconsole_initcall(of_stdoutpath_init);
+
+static int of_timer_init(void)
+{
+ of_devices_ensure_probed_by_name("timer");
+
+ return 0;
+}
+postcore_initcall(of_timer_init);
diff --git a/drivers/of/reserved-mem.c b/drivers/of/reserved-mem.c
new file mode 100644
index 0000000000..599a7c968a
--- /dev/null
+++ b/drivers/of/reserved-mem.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2021 Rouven Czerwinski <r.czerwinski@pengutronix.de>, Pengutronix
+
+#define pr_fmt(fmt) "of-reserved-mem: " fmt
+
+#include <stdio.h>
+#include <of.h>
+#include <of_address.h>
+#include <memory.h>
+#include <linux/ioport.h>
+
+#define MEMRESERVE_NCELLS 2
+
+static void request_region(struct resource *r)
+{
+ struct memory_bank *bank;
+
+ for_each_memory_bank(bank) {
+ if (!resource_contains(bank->res, r))
+ continue;
+
+ pr_debug("reserving %s at %pad-%pad\n", r->name, &r->start, &r->end);
+
+ if (!reserve_sdram_region(r->name, r->start, resource_size(r)))
+ pr_warn("couldn't request reserved sdram region %pa-%pa\n",
+ &r->start, &r->end);
+ break;
+ }
+}
+
+static int of_reserved_mem_walk(void)
+{
+ struct device_node *node, *child;
+ int ncells = 0;
+ const __be32 *reg;
+
+ node = of_find_node_by_path("/reserved-memory");
+ if (node) {
+ for_each_available_child_of_node(node, child) {
+ struct resource resource = {};
+
+ /* skip e.g. linux,cma */
+ if (!of_get_property(child, "reg", NULL))
+ continue;
+
+ of_address_to_resource(child, 0, &resource);
+
+ resource.name = child->name;
+ resource.flags = IORESOURCE_MEM;
+
+ request_region(&resource);
+ }
+ }
+
+ node = of_find_node_by_path("/memreserve");
+ reg = of_get_property(node, "reg", &ncells);
+ ncells /= sizeof(__be32);
+ if (reg) {
+ char name[sizeof "fdt-memreserve-4294967295"];
+ int i = 0, n = 0;
+
+ while (i < ncells) {
+ struct resource resource = {};
+ u64 size;
+
+ snprintf(name, sizeof(name), "fdt-memreserve-%u", n++);
+ resource.name = name;
+ resource.flags = IORESOURCE_MEM;
+
+ resource.start = of_read_number(reg + i, MEMRESERVE_NCELLS);
+ i += MEMRESERVE_NCELLS;
+
+ size = of_read_number(reg + i, MEMRESERVE_NCELLS);
+ i += MEMRESERVE_NCELLS;
+
+ if (!size)
+ continue;
+
+ resource.end = resource.start + size - 1;
+
+ request_region(&resource);
+ }
+ }
+
+ return 0;
+}
+postmem_initcall(of_reserved_mem_walk);
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index 4f720cf860..2457ae96a4 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Functions for dealing with DT resolution
*
@@ -214,7 +214,7 @@ struct device_node *of_resolve_phandles(struct device_node *root,
* to a phandle defined in the overlay. We must update the references,
* because we just adjusted the definitions.
*/
- local_fixups = of_find_node_by_name(result, "__local_fixups__");
+ local_fixups = of_find_node_by_name_address(result, "__local_fixups__");
err = adjust_local_phandle_references(local_fixups, result, delta);
if (err) {
pr_err("failed to fix phandles in overlay\n");
@@ -227,7 +227,7 @@ struct device_node *of_resolve_phandles(struct device_node *root,
* the base device tree. We must update the references, because they
* are otherwise undefined.
*/
- overlay_fixups = of_find_node_by_name(result, "__fixups__");
+ overlay_fixups = of_find_node_by_name_address(result, "__fixups__");
if (!overlay_fixups) {
pr_debug("overlay does not contain phandles to base devicetree\n");
goto out;