diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 19 | ||||
-rw-r--r-- | drivers/of/Makefile | 2 | ||||
-rw-r--r-- | drivers/of/address.c | 39 | ||||
-rw-r--r-- | drivers/of/barebox.c | 24 | ||||
-rw-r--r-- | drivers/of/base.c | 754 | ||||
-rw-r--r-- | drivers/of/device.c | 11 | ||||
-rw-r--r-- | drivers/of/fdt.c | 320 | ||||
-rw-r--r-- | drivers/of/mem_generic.c | 1 | ||||
-rw-r--r-- | drivers/of/of_firmware.c | 5 | ||||
-rw-r--r-- | drivers/of/of_gpio.c | 74 | ||||
-rw-r--r-- | drivers/of/of_mtd.c | 5 | ||||
-rw-r--r-- | drivers/of/of_net.c | 4 | ||||
-rw-r--r-- | drivers/of/of_path.c | 110 | ||||
-rw-r--r-- | drivers/of/of_pci.c | 2 | ||||
-rw-r--r-- | drivers/of/overlay.c | 46 | ||||
-rw-r--r-- | drivers/of/partition.c | 101 | ||||
-rw-r--r-- | drivers/of/platform.c | 243 | ||||
-rw-r--r-- | drivers/of/reserved-mem.c | 87 | ||||
-rw-r--r-- | drivers/of/resolver.c | 6 |
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; |