summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/.kunitconfig5
-rw-r--r--drivers/of/Kconfig30
-rw-r--r--drivers/of/Makefile7
-rw-r--r--drivers/of/address.c319
-rw-r--r--drivers/of/base.c438
-rw-r--r--drivers/of/cpu.c2
-rw-r--r--drivers/of/device.c176
-rw-r--r--drivers/of/dynamic.c425
-rw-r--r--drivers/of/empty_root.dts13
-rw-r--r--drivers/of/fdt.c374
-rw-r--r--drivers/of/fdt_address.c25
-rw-r--r--drivers/of/irq.c379
-rw-r--r--drivers/of/kexec.c49
-rw-r--r--drivers/of/kobj.c10
-rw-r--r--drivers/of/kunit_overlay_test.dtso9
-rw-r--r--drivers/of/module.c19
-rw-r--r--drivers/of/of_kunit_helpers.c93
-rw-r--r--drivers/of/of_numa.c13
-rw-r--r--drivers/of/of_private.h64
-rw-r--r--drivers/of/of_reserved_mem.c467
-rw-r--r--drivers/of/of_test.c178
-rw-r--r--drivers/of/overlay.c96
-rw-r--r--drivers/of/overlay_test.c120
-rw-r--r--drivers/of/pdt.c2
-rw-r--r--drivers/of/platform.c165
-rw-r--r--drivers/of/property.c396
-rw-r--r--drivers/of/resolver.c100
-rw-r--r--drivers/of/unittest-data/Makefile4
-rw-r--r--drivers/of/unittest-data/overlay.dtso32
-rw-r--r--drivers/of/unittest-data/overlay_0.dtso11
-rw-r--r--drivers/of/unittest-data/overlay_1.dtso11
-rw-r--r--drivers/of/unittest-data/overlay_11.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_12.dtso11
-rw-r--r--drivers/of/unittest-data/overlay_13.dtso11
-rw-r--r--drivers/of/unittest-data/overlay_15.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_4.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_node.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso9
-rw-r--r--drivers/of/unittest-data/overlay_bad_phandle.dtso5
-rw-r--r--drivers/of/unittest-data/overlay_bad_symbol.dtso5
-rw-r--r--drivers/of/unittest-data/overlay_bad_unresolved.dtso7
-rw-r--r--drivers/of/unittest-data/overlay_common.dtsi36
-rw-r--r--drivers/of/unittest-data/overlay_gpio_01.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02a.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_gpio_02b.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_gpio_03.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04a.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_gpio_04b.dtso1
-rw-r--r--drivers/of/unittest-data/overlay_pci_node.dtso22
-rw-r--r--drivers/of/unittest-data/testcases_common.dtsi1
-rw-r--r--drivers/of/unittest-data/tests-address.dtsi103
-rw-r--r--drivers/of/unittest-data/tests-interrupts.dtsi14
-rw-r--r--drivers/of/unittest-data/tests-overlay.dtsi1
-rw-r--r--drivers/of/unittest-data/tests-phandle.dtsi12
-rw-r--r--drivers/of/unittest-data/tests-platform.dtsi28
-rw-r--r--drivers/of/unittest.c986
56 files changed, 3691 insertions, 1610 deletions
diff --git a/drivers/of/.kunitconfig b/drivers/of/.kunitconfig
new file mode 100644
index 000000000000..7d570cb922a1
--- /dev/null
+++ b/drivers/of/.kunitconfig
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_OF=y
+CONFIG_OF_KUNIT_TEST=y
+CONFIG_OF_OVERLAY=y
+CONFIG_OF_OVERLAY_KUNIT_TEST=y
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index da9826accb1b..50697cc3b07e 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -2,6 +2,12 @@
config DTC
bool
+config GENERIC_BUILTIN_DTB
+ bool
+
+config BUILTIN_DTB_ALL
+ bool
+
menuconfig OF
bool "Device Tree and Open Firmware support"
help
@@ -14,9 +20,8 @@ if OF
config OF_UNITTEST
bool "Device Tree runtime unit tests"
- depends on !SPARC
+ depends on OF_EARLY_FLATTREE
select IRQ_DOMAIN
- select OF_EARLY_FLATTREE
select OF_RESOLVE
help
This option builds in test cases for the device tree infrastructure
@@ -37,6 +42,15 @@ config OF_UNITTEST
If unsure, say N here. This option is not safe to enable.
+config OF_KUNIT_TEST
+ tristate "Devicetree KUnit Test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This option builds KUnit unit tests for device tree infrastructure.
+
+ If unsure, say N here, but this option is safe to enable.
+
config OF_ALL_DTBS
bool "Build all Device Tree Blobs"
depends on COMPILE_TEST
@@ -54,7 +68,7 @@ config OF_FLATTREE
select CRC32
config OF_EARLY_FLATTREE
- bool
+ def_bool OF && !(SPARC || ALPHA || HEXAGON || M68K || PARISC || S390)
select DMA_DECLARE_COHERENT if HAS_DMA && HAS_IOMEM
select OF_FLATTREE
@@ -99,6 +113,16 @@ config OF_OVERLAY
While this option is selected automatically when needed, you can
enable it manually to improve device tree unit test coverage.
+config OF_OVERLAY_KUNIT_TEST
+ tristate "Device Tree overlay KUnit tests" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ select DTC
+ help
+ This option builds KUnit unit tests for the device tree overlay code.
+
+ If unsure, say N here, but this option is safe to enable.
+
config OF_NUMA
bool
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index eff624854575..379a0afcbdc0 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -2,7 +2,7 @@
obj-y = base.o cpu.o device.o module.o platform.o property.o
obj-$(CONFIG_OF_KOBJ) += kobj.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
-obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_FLATTREE) += fdt.o empty_root.dtb.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
@@ -19,4 +19,9 @@ obj-y += kexec.o
endif
endif
+obj-$(CONFIG_KUNIT) += of_kunit_helpers.o
+obj-$(CONFIG_OF_KUNIT_TEST) += of_test.o
+obj-$(CONFIG_OF_OVERLAY_KUNIT_TEST) += overlay-test.o
+overlay-test-y := overlay_test.o kunit_overlay_test.dtbo.o
+
obj-$(CONFIG_OF_UNITTEST) += unittest-data/
diff --git a/drivers/of/address.c b/drivers/of/address.c
index e692809ff822..4034d798c55a 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -8,6 +8,7 @@
#include <linux/logic_pio.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/sizes.h>
@@ -15,25 +16,12 @@
#include <linux/string.h>
#include <linux/dma-direct.h> /* for bus_dma_region */
-#include "of_private.h"
+#include <kunit/visibility.h>
-/* Max address size we deal with */
-#define OF_MAX_ADDR_CELLS 4
-#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
-#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0)
+/* Uncomment me to enable of_dump_addr() debugging output */
+// #define DEBUG
-/* Debug utility */
-#ifdef DEBUG
-static void of_dump_addr(const char *s, const __be32 *addr, int na)
-{
- pr_debug("%s", s);
- while (na--)
- pr_cont(" %08x", be32_to_cpu(*(addr++)));
- pr_cont("\n");
-}
-#else
-static void of_dump_addr(const char *s, const __be32 *addr, int na) { }
-#endif
+#include "of_private.h"
/* Callbacks for bus specific translators */
struct of_bus {
@@ -43,9 +31,9 @@ struct of_bus {
void (*count_cells)(struct device_node *child,
int *addrc, int *sizec);
u64 (*map)(__be32 *addr, const __be32 *range,
- int na, int ns, int pna);
+ int na, int ns, int pna, int fna);
int (*translate)(__be32 *addr, u64 offset, int na);
- bool has_flags;
+ int flag_cells;
unsigned int (*get_flags)(const __be32 *addr);
};
@@ -63,13 +51,13 @@ static void of_bus_default_count_cells(struct device_node *dev,
}
static u64 of_bus_default_map(__be32 *addr, const __be32 *range,
- int na, int ns, int pna)
+ int na, int ns, int pna, int fna)
{
u64 cp, s, da;
- cp = of_read_number(range, na);
+ cp = of_read_number(range + fna, na - fna);
s = of_read_number(range + na + pna, ns);
- da = of_read_number(addr, na);
+ da = of_read_number(addr + fna, na - fna);
pr_debug("default map, cp=%llx, s=%llx, da=%llx\n", cp, s, da);
@@ -100,6 +88,21 @@ static unsigned int of_bus_default_get_flags(const __be32 *addr)
return IORESOURCE_MEM;
}
+static u64 of_bus_default_flags_map(__be32 *addr, const __be32 *range, int na,
+ int ns, int pna, int fna)
+{
+ /* Check that flags match */
+ if (*addr != *range)
+ return OF_BAD_ADDR;
+
+ return of_bus_default_map(addr, range, na, ns, pna, fna);
+}
+
+static int of_bus_default_flags_translate(__be32 *addr, u64 offset, int na)
+{
+ /* Keep "flags" part (high cell) in translated address */
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
#ifdef CONFIG_PCI
static unsigned int of_bus_pci_get_flags(const __be32 *addr)
@@ -131,7 +134,7 @@ static unsigned int of_bus_pci_get_flags(const __be32 *addr)
* PCI bus specific translator
*/
-static bool of_node_is_pcie(struct device_node *np)
+static bool of_node_is_pcie(const struct device_node *np)
{
bool is_pcie = of_node_name_eq(np, "pcie");
@@ -166,9 +169,8 @@ static void of_bus_pci_count_cells(struct device_node *np,
}
static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns,
- int pna)
+ int pna, int fna)
{
- u64 cp, s, da;
unsigned int af, rf;
af = of_bus_pci_get_flags(addr);
@@ -178,23 +180,26 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns,
if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO))
return OF_BAD_ADDR;
- /* Read address values, skipping high cell */
- cp = of_read_number(range + 1, na - 1);
- s = of_read_number(range + na + pna, ns);
- da = of_read_number(addr + 1, na - 1);
-
- pr_debug("PCI map, cp=%llx, s=%llx, da=%llx\n", cp, s, da);
-
- if (da < cp || da >= (cp + s))
- return OF_BAD_ADDR;
- return da - cp;
+ return of_bus_default_map(addr, range, na, ns, pna, fna);
}
-static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
+#endif /* CONFIG_PCI */
+
+VISIBLE_IF_KUNIT int __of_address_resource_bounds(struct resource *r, u64 start, u64 size)
{
- return of_bus_default_translate(addr + 1, offset, na - 1);
+ if (overflows_type(start, r->start))
+ return -EOVERFLOW;
+
+ r->start = start;
+
+ if (!size)
+ r->end = wrapping_sub(typeof(r->end), r->start, 1);
+ else if (size && check_add_overflow(r->start, size - 1, &r->end))
+ return -EOVERFLOW;
+
+ return 0;
}
-#endif /* CONFIG_PCI */
+EXPORT_SYMBOL_IF_KUNIT(__of_address_resource_bounds);
/*
* of_pci_range_to_resource - Create a resource from an of_pci_range
@@ -211,9 +216,10 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
* To guard against that we try to register the IO range first.
* If that fails we know that pci_address_to_pio() will do too.
*/
-int of_pci_range_to_resource(struct of_pci_range *range,
- struct device_node *np, struct resource *res)
+int of_pci_range_to_resource(const struct of_pci_range *range,
+ const struct device_node *np, struct resource *res)
{
+ u64 start;
int err;
res->flags = range->flags;
res->parent = res->child = res->sibling = NULL;
@@ -230,18 +236,11 @@ int of_pci_range_to_resource(struct of_pci_range *range,
err = -EINVAL;
goto invalid_range;
}
- res->start = port;
+ start = port;
} else {
- if ((sizeof(resource_size_t) < 8) &&
- upper_32_bits(range->cpu_addr)) {
- err = -EINVAL;
- goto invalid_range;
- }
-
- res->start = range->cpu_addr;
+ start = range->cpu_addr;
}
- res->end = res->start + range->size - 1;
- return 0;
+ return __of_address_resource_bounds(res, start, range->size);
invalid_range:
res->start = (resource_size_t)OF_BAD_ADDR;
@@ -257,8 +256,8 @@ EXPORT_SYMBOL(of_pci_range_to_resource);
* @res: pointer to a valid resource that will be updated to
* reflect the values contained in the range.
*
- * Returns ENOENT if the entry is not found or EINVAL if the range cannot be
- * converted to resource.
+ * Returns -ENOENT if the entry is not found or -EOVERFLOW if the range
+ * cannot be converted to resource.
*/
int of_range_to_resource(struct device_node *np, int index, struct resource *res)
{
@@ -297,29 +296,13 @@ static void of_bus_isa_count_cells(struct device_node *child,
}
static u64 of_bus_isa_map(__be32 *addr, const __be32 *range, int na, int ns,
- int pna)
+ int pna, int fna)
{
- u64 cp, s, da;
-
/* Check address type match */
if ((addr[0] ^ range[0]) & cpu_to_be32(1))
return OF_BAD_ADDR;
- /* Read address values, skipping high cell */
- cp = of_read_number(range + 1, na - 1);
- s = of_read_number(range + na + pna, ns);
- da = of_read_number(addr + 1, na - 1);
-
- pr_debug("ISA map, cp=%llx, s=%llx, da=%llx\n", cp, s, da);
-
- if (da < cp || da >= (cp + s))
- return OF_BAD_ADDR;
- return da - cp;
-}
-
-static int of_bus_isa_translate(__be32 *addr, u64 offset, int na)
-{
- return of_bus_default_translate(addr + 1, offset, na - 1);
+ return of_bus_default_map(addr, range, na, ns, pna, fna);
}
static unsigned int of_bus_isa_get_flags(const __be32 *addr)
@@ -336,14 +319,23 @@ static unsigned int of_bus_isa_get_flags(const __be32 *addr)
static int of_bus_default_flags_match(struct device_node *np)
{
- return of_bus_n_addr_cells(np) == 3;
+ /*
+ * Check for presence first since of_bus_n_addr_cells() will warn when
+ * walking parent nodes.
+ */
+ return of_property_present(np, "#address-cells") && (of_bus_n_addr_cells(np) == 3);
+}
+
+static int of_bus_default_match(struct device_node *np)
+{
+ return of_property_present(np, "#address-cells");
}
/*
* Array of bus specific translators
*/
-static struct of_bus of_busses[] = {
+static const struct of_bus of_busses[] = {
#ifdef CONFIG_PCI
/* PCI */
{
@@ -352,8 +344,8 @@ static struct of_bus of_busses[] = {
.match = of_bus_pci_match,
.count_cells = of_bus_pci_count_cells,
.map = of_bus_pci_map,
- .translate = of_bus_pci_translate,
- .has_flags = true,
+ .translate = of_bus_default_flags_translate,
+ .flag_cells = 1,
.get_flags = of_bus_pci_get_flags,
},
#endif /* CONFIG_PCI */
@@ -364,8 +356,8 @@ static struct of_bus of_busses[] = {
.match = of_bus_isa_match,
.count_cells = of_bus_isa_count_cells,
.map = of_bus_isa_map,
- .translate = of_bus_isa_translate,
- .has_flags = true,
+ .translate = of_bus_default_flags_translate,
+ .flag_cells = 1,
.get_flags = of_bus_isa_get_flags,
},
/* Default with flags cell */
@@ -374,16 +366,16 @@ static struct of_bus of_busses[] = {
.addresses = "reg",
.match = of_bus_default_flags_match,
.count_cells = of_bus_default_count_cells,
- .map = of_bus_default_map,
- .translate = of_bus_default_translate,
- .has_flags = true,
+ .map = of_bus_default_flags_map,
+ .translate = of_bus_default_flags_translate,
+ .flag_cells = 1,
.get_flags = of_bus_default_flags_get_flags,
},
/* Default */
{
.name = "default",
.addresses = "reg",
- .match = NULL,
+ .match = of_bus_default_match,
.count_cells = of_bus_default_count_cells,
.map = of_bus_default_map,
.translate = of_bus_default_translate,
@@ -391,18 +383,17 @@ static struct of_bus of_busses[] = {
},
};
-static struct of_bus *of_match_bus(struct device_node *np)
+static const struct of_bus *of_match_bus(struct device_node *np)
{
int i;
for (i = 0; i < ARRAY_SIZE(of_busses); i++)
if (!of_busses[i].match || of_busses[i].match(np))
return &of_busses[i];
- BUG();
return NULL;
}
-static int of_empty_ranges_quirk(struct device_node *np)
+static int of_empty_ranges_quirk(const struct device_node *np)
{
if (IS_ENABLED(CONFIG_PPC)) {
/* To save cycles, we cache the result for global "Mac" setting */
@@ -422,8 +413,8 @@ static int of_empty_ranges_quirk(struct device_node *np)
return false;
}
-static int of_translate_one(struct device_node *parent, struct of_bus *bus,
- struct of_bus *pbus, __be32 *addr,
+static int of_translate_one(const struct device_node *parent, const struct of_bus *bus,
+ const struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
{
const __be32 *ranges;
@@ -458,7 +449,8 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
}
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
- memset(addr, 0, pna * 4);
+ /* set address to zero, pass flags through */
+ memset(addr + pbus->flag_cells, 0, (pna - pbus->flag_cells) * 4);
pr_debug("empty ranges; 1:1 translation\n");
goto finish;
}
@@ -469,7 +461,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
rlen /= 4;
rone = na + pna + ns;
for (; rlen >= rone; rlen -= rone, ranges += rone) {
- offset = bus->map(addr, ranges, na, ns, pna);
+ offset = bus->map(addr, ranges, na, ns, pna, bus->flag_cells);
if (offset != OF_BAD_ADDR)
break;
}
@@ -501,34 +493,32 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
* device that had registered logical PIO mapping, and the return code is
* relative to that node.
*/
-static u64 __of_translate_address(struct device_node *dev,
+static u64 __of_translate_address(struct device_node *node,
struct device_node *(*get_parent)(const struct device_node *),
const __be32 *in_addr, const char *rprop,
struct device_node **host)
{
- struct device_node *parent = NULL;
- struct of_bus *bus, *pbus;
+ struct device_node *dev __free(device_node) = of_node_get(node);
+ struct device_node *parent __free(device_node) = get_parent(dev);
+ const struct of_bus *bus, *pbus;
__be32 addr[OF_MAX_ADDR_CELLS];
int na, ns, pna, pns;
- u64 result = OF_BAD_ADDR;
pr_debug("** translation for device %pOF **\n", dev);
- /* Increase refcount at current level */
- of_node_get(dev);
-
*host = NULL;
- /* Get parent & match bus type */
- parent = get_parent(dev);
+
if (parent == NULL)
- goto bail;
+ return OF_BAD_ADDR;
bus = of_match_bus(parent);
+ if (!bus)
+ return OF_BAD_ADDR;
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
pr_debug("Bad cell count for %pOF\n", dev);
- goto bail;
+ return OF_BAD_ADDR;
}
memcpy(addr, in_addr, na * 4);
@@ -548,8 +538,7 @@ static u64 __of_translate_address(struct device_node *dev,
/* If root, we have finished */
if (parent == NULL) {
pr_debug("reached root node\n");
- result = of_read_number(addr, na);
- break;
+ return of_read_number(addr, na);
}
/*
@@ -558,19 +547,21 @@ static u64 __of_translate_address(struct device_node *dev,
*/
iorange = find_io_range_by_fwnode(&dev->fwnode);
if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) {
- result = of_read_number(addr + 1, na - 1);
+ u64 result = of_read_number(addr + 1, na - 1);
pr_debug("indirectIO matched(%pOF) 0x%llx\n",
dev, result);
- *host = of_node_get(dev);
- break;
+ *host = no_free_ptr(dev);
+ return result;
}
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
+ if (!pbus)
+ return OF_BAD_ADDR;
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
pr_err("Bad cell count for %pOF\n", dev);
- break;
+ return OF_BAD_ADDR;
}
pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n",
@@ -578,7 +569,7 @@ static u64 __of_translate_address(struct device_node *dev,
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
- break;
+ return OF_BAD_ADDR;
/* Complete the move up one level */
na = pna;
@@ -587,11 +578,8 @@ static u64 __of_translate_address(struct device_node *dev,
of_dump_addr("one level translation:", addr, na);
}
- bail:
- of_node_put(parent);
- of_node_put(dev);
- return result;
+ unreachable();
}
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
@@ -626,7 +614,7 @@ struct device_node *__of_get_dma_parent(const struct device_node *np)
if (ret < 0)
return of_get_parent(np);
- return of_node_get(args.np);
+ return args.np;
}
#endif
@@ -669,19 +657,16 @@ EXPORT_SYMBOL(of_translate_dma_address);
const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop,
phys_addr_t *start, size_t *length)
{
- struct device_node *parent;
+ struct device_node *parent __free(device_node) = __of_get_dma_parent(dev);
u64 address, size;
int na, ns;
- parent = __of_get_dma_parent(dev);
if (!parent)
return NULL;
na = of_bus_n_addr_cells(parent);
ns = of_bus_n_size_cells(parent);
- of_node_put(parent);
-
address = of_translate_dma_address(dev, prop);
if (address == OF_BAD_ADDR)
return NULL;
@@ -703,22 +688,16 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
{
const __be32 *prop;
unsigned int psize;
- struct device_node *parent;
- struct of_bus *bus;
+ struct device_node *parent __free(device_node) = of_get_parent(dev);
+ const struct of_bus *bus;
int onesize, i, na, ns;
- /* Get parent & match bus type */
- parent = of_get_parent(dev);
if (parent == NULL)
return NULL;
+
+ /* match the parent's bus type */
bus = of_match_bus(parent);
- if (strcmp(bus->name, "pci") && (bar_no >= 0)) {
- of_node_put(parent);
- return NULL;
- }
- bus->count_cells(dev, &na, &ns);
- of_node_put(parent);
- if (!OF_CHECK_ADDR_COUNT(na))
+ if (!bus || (strcmp(bus->name, "pci") && (bar_no >= 0)))
return NULL;
/* Get "reg" or "assigned-addresses" property */
@@ -727,6 +706,10 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
return NULL;
psize /= 4;
+ bus->count_cells(dev, &na, &ns);
+ if (!OF_CHECK_ADDR_COUNT(na))
+ return NULL;
+
onesize = na + ns;
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) {
u32 val = be32_to_cpu(prop[0]);
@@ -809,7 +792,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
int na = parser->na;
int ns = parser->ns;
int np = parser->pna + na + ns;
- int busflag_na = 0;
+ int busflag_na = parser->bus->flag_cells;
if (!range)
return NULL;
@@ -819,10 +802,6 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
range->flags = parser->bus->get_flags(parser->range);
- /* A extra cell for resource flags */
- if (parser->bus->has_flags)
- busflag_na = 1;
-
range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na);
if (parser->dma)
@@ -831,6 +810,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
else
range->cpu_addr = of_translate_address(parser->node,
parser->range + na);
+
+ range->parent_bus_addr = of_read_number(parser->range + na, parser->pna);
range->size = of_read_number(parser->range + parser->pna + na, ns);
parser->range += np;
@@ -907,14 +888,13 @@ static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr,
*/
int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
{
- struct device_node *node = of_node_get(np);
+ struct device_node *node __free(device_node) = of_node_get(np);
const __be32 *ranges = NULL;
bool found_dma_ranges = false;
struct of_range_parser parser;
struct of_range range;
struct bus_dma_region *r;
int len, num_ranges = 0;
- int ret = 0;
while (node) {
ranges = of_get_property(node, "dma-ranges", &len);
@@ -924,10 +904,9 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
break;
/* Once we find 'dma-ranges', then a missing one is an error */
- if (found_dma_ranges && !ranges) {
- ret = -ENODEV;
- goto out;
- }
+ if (found_dma_ranges && !ranges)
+ return -ENODEV;
+
found_dma_ranges = true;
node = of_get_next_dma_parent(node);
@@ -935,10 +914,8 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
if (!node || !ranges) {
pr_debug("no dma-ranges found for node(%pOF)\n", np);
- ret = -ENODEV;
- goto out;
+ return -ENODEV;
}
-
of_dma_range_parser_init(&parser, node);
for_each_of_range(&parser, &range) {
if (range.cpu_addr == OF_BAD_ADDR) {
@@ -949,16 +926,12 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
num_ranges++;
}
- if (!num_ranges) {
- ret = -EINVAL;
- goto out;
- }
+ if (!num_ranges)
+ return -EINVAL;
r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL);
- if (!r) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!r)
+ return -ENOMEM;
/*
* Record all info in the generic DMA ranges array for struct device,
@@ -974,12 +947,9 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
r->cpu_start = range.cpu_addr;
r->dma_start = range.bus_addr;
r->size = range.size;
- r->offset = range.cpu_addr - range.bus_addr;
r++;
}
-out:
- of_node_put(node);
- return ret;
+ return 0;
}
#endif /* CONFIG_HAS_DMA */
@@ -1036,24 +1006,18 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np)
*/
bool of_dma_is_coherent(struct device_node *np)
{
- struct device_node *node;
- bool is_coherent = dma_default_coherent;
-
- node = of_node_get(np);
+ struct device_node *node __free(device_node) = of_node_get(np);
while (node) {
- if (of_property_read_bool(node, "dma-coherent")) {
- is_coherent = true;
- break;
- }
- if (of_property_read_bool(node, "dma-noncoherent")) {
- is_coherent = false;
- break;
- }
+ if (of_property_read_bool(node, "dma-coherent"))
+ return true;
+
+ if (of_property_read_bool(node, "dma-noncoherent"))
+ return false;
+
node = of_get_next_dma_parent(node);
}
- of_node_put(node);
- return is_coherent;
+ return dma_default_coherent;
}
EXPORT_SYMBOL_GPL(of_dma_is_coherent);
@@ -1063,26 +1027,15 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent);
*
* Returns true if the "nonposted-mmio" property was found for
* the device's bus.
- *
- * This is currently only enabled on builds that support Apple ARM devices, as
- * an optimization.
*/
-static bool of_mmio_is_nonposted(struct device_node *np)
+static bool of_mmio_is_nonposted(const struct device_node *np)
{
- struct device_node *parent;
- bool nonposted;
-
- if (!IS_ENABLED(CONFIG_ARCH_APPLE))
- return false;
+ struct device_node *parent __free(device_node) = of_get_parent(np);
- parent = of_get_parent(np);
- if (!parent)
- return false;
-
- nonposted = of_property_read_bool(parent, "nonposted-mmio");
+ if (of_property_read_bool(np, "nonposted-mmio"))
+ return true;
- of_node_put(parent);
- return nonposted;
+ return parent && of_property_read_bool(parent, "nonposted-mmio");
}
static int __of_address_to_resource(struct device_node *dev, int index, int bar_no,
@@ -1116,12 +1069,10 @@ static int __of_address_to_resource(struct device_node *dev, int index, int bar_
if (of_mmio_is_nonposted(dev))
flags |= IORESOURCE_MEM_NONPOSTED;
- r->start = taddr;
- r->end = taddr + size - 1;
r->flags = flags;
r->name = name ? name : dev->full_name;
- return 0;
+ return __of_address_resource_bounds(r, taddr, size);
}
/**
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 166fb7d75337..0b65039ece53 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -16,6 +16,7 @@
#define pr_fmt(fmt) "OF: " fmt
+#include <linux/cleanup.h>
#include <linux/console.h>
#include <linux/ctype.h>
#include <linux/cpu.h>
@@ -86,15 +87,26 @@ static bool __of_node_is_type(const struct device_node *np, const char *type)
return np && match && type && !strcmp(match, type);
}
+#define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \
+ IS_ENABLED(CONFIG_SPARC) || \
+ of_find_compatible_node(NULL, NULL, "coreboot") \
+)
+
int of_bus_n_addr_cells(struct device_node *np)
{
u32 cells;
- for (; np; np = np->parent)
+ for (; np; np = np->parent) {
if (!of_property_read_u32(np, "#address-cells", &cells))
return cells;
-
- /* No #address-cells property for the root node */
+ /*
+ * Default root value and walking parent nodes for "#address-cells"
+ * is deprecated. Any platforms which hit this warning should
+ * be added to the excluded list.
+ */
+ WARN_ONCE(!EXCLUDED_DEFAULT_CELLS_PLATFORMS,
+ "Missing '#address-cells' in %pOF\n", np);
+ }
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
}
@@ -111,11 +123,17 @@ int of_bus_n_size_cells(struct device_node *np)
{
u32 cells;
- for (; np; np = np->parent)
+ for (; np; np = np->parent) {
if (!of_property_read_u32(np, "#size-cells", &cells))
return cells;
-
- /* No #size-cells property for the root node */
+ /*
+ * Default root value and walking parent nodes for "#size-cells"
+ * is deprecated. Any platforms which hit this warning should
+ * be added to the excluded list.
+ */
+ WARN_ONCE(!EXCLUDED_DEFAULT_CELLS_PLATFORMS,
+ "Missing '#size-cells' in %pOF\n", np);
+ }
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
}
@@ -167,6 +185,7 @@ void __init of_core_init(void)
{
struct device_node *np;
+ of_platform_register_reconfig_notifier();
/* Create the kset, and register existing nodes */
mutex_lock(&of_mutex);
@@ -268,7 +287,7 @@ EXPORT_SYMBOL(of_find_all_nodes);
const void *__of_get_property(const struct device_node *np,
const char *name, int *lenp)
{
- struct property *pp = __of_find_property(np, name, lenp);
+ const struct property *pp = __of_find_property(np, name, lenp);
return pp ? pp->value : NULL;
}
@@ -280,7 +299,7 @@ const void *__of_get_property(const struct device_node *np,
const void *of_get_property(const struct device_node *np, const char *name,
int *lenp)
{
- struct property *pp = of_find_property(np, name, lenp);
+ const struct property *pp = of_find_property(np, name, lenp);
return pp ? pp->value : NULL;
}
@@ -319,7 +338,7 @@ EXPORT_SYMBOL(of_get_property);
static int __of_device_is_compatible(const struct device_node *device,
const char *compat, const char *type, const char *name)
{
- struct property *prop;
+ const struct property *prop;
const char *cp;
int index = 0, score = 0;
@@ -394,35 +413,76 @@ int of_device_compatible_match(const struct device_node *device,
EXPORT_SYMBOL_GPL(of_device_compatible_match);
/**
- * of_machine_is_compatible - Test root of device tree for a given compatible value
- * @compat: compatible string to look for in root node's compatible property.
+ * of_machine_compatible_match - Test root of device tree against a compatible array
+ * @compats: NULL terminated array of compatible strings to look for in root node's compatible property.
*
- * Return: A positive integer if the root node has the given value in its
+ * Returns true if the root node has any of the given compatible values in its
* compatible property.
*/
-int of_machine_is_compatible(const char *compat)
+bool of_machine_compatible_match(const char *const *compats)
{
struct device_node *root;
int rc = 0;
root = of_find_node_by_path("/");
if (root) {
- rc = of_device_is_compatible(root, compat);
+ rc = of_device_compatible_match(root, compats);
of_node_put(root);
}
- return rc;
+
+ return rc != 0;
}
-EXPORT_SYMBOL(of_machine_is_compatible);
+EXPORT_SYMBOL(of_machine_compatible_match);
/**
- * __of_device_is_available - check if a device is available for use
+ * of_machine_device_match - Test root of device tree against a of_device_id array
+ * @matches: NULL terminated array of of_device_id match structures to search in
*
- * @device: Node to check for availability, with locks already held
+ * Returns true if the root node has any of the given compatible values in its
+ * compatible property.
+ */
+bool of_machine_device_match(const struct of_device_id *matches)
+{
+ struct device_node *root;
+ const struct of_device_id *match = NULL;
+
+ root = of_find_node_by_path("/");
+ if (root) {
+ match = of_match_node(matches, root);
+ of_node_put(root);
+ }
+
+ return match != NULL;
+}
+EXPORT_SYMBOL(of_machine_device_match);
+
+/**
+ * of_machine_get_match_data - Tell if root of device tree has a matching of_match structure
+ * @matches: NULL terminated array of of_device_id match structures to search in
*
- * Return: True if the status property is absent or set to "okay" or "ok",
- * false otherwise
+ * Returns data associated with matched entry or NULL
*/
-static bool __of_device_is_available(const struct device_node *device)
+const void *of_machine_get_match_data(const struct of_device_id *matches)
+{
+ const struct of_device_id *match;
+ struct device_node *root;
+
+ root = of_find_node_by_path("/");
+ if (!root)
+ return NULL;
+
+ match = of_match_node(matches, root);
+ of_node_put(root);
+
+ if (!match)
+ return NULL;
+
+ return match->data;
+}
+EXPORT_SYMBOL(of_machine_get_match_data);
+
+static bool __of_device_is_status(const struct device_node *device,
+ const char * const*strings)
{
const char *status;
int statlen;
@@ -432,17 +492,60 @@ static bool __of_device_is_available(const struct device_node *device)
status = __of_get_property(device, "status", &statlen);
if (status == NULL)
- return true;
+ return false;
if (statlen > 0) {
- if (!strcmp(status, "okay") || !strcmp(status, "ok"))
- return true;
+ while (*strings) {
+ unsigned int len = strlen(*strings);
+
+ if ((*strings)[len - 1] == '-') {
+ if (!strncmp(status, *strings, len))
+ return true;
+ } else {
+ if (!strcmp(status, *strings))
+ return true;
+ }
+ strings++;
+ }
}
return false;
}
/**
+ * __of_device_is_available - check if a device is available for use
+ *
+ * @device: Node to check for availability, with locks already held
+ *
+ * Return: True if the status property is absent or set to "okay" or "ok",
+ * false otherwise
+ */
+static bool __of_device_is_available(const struct device_node *device)
+{
+ static const char * const ok[] = {"okay", "ok", NULL};
+
+ if (!device)
+ return false;
+
+ return !__of_get_property(device, "status", NULL) ||
+ __of_device_is_status(device, ok);
+}
+
+/**
+ * __of_device_is_reserved - check if a device is reserved
+ *
+ * @device: Node to check for availability, with locks already held
+ *
+ * Return: True if the status property is set to "reserved", false otherwise
+ */
+static bool __of_device_is_reserved(const struct device_node *device)
+{
+ static const char * const reserved[] = {"reserved", NULL};
+
+ return __of_device_is_status(device, reserved);
+}
+
+/**
* of_device_is_available - check if a device is available for use
*
* @device: Node to check for availability
@@ -473,16 +576,9 @@ EXPORT_SYMBOL(of_device_is_available);
*/
static bool __of_device_is_fail(const struct device_node *device)
{
- const char *status;
-
- if (!device)
- return false;
+ static const char * const fail[] = {"fail", "fail-", NULL};
- status = __of_get_property(device, "status", NULL);
- if (status == NULL)
- return false;
-
- return !strcmp(status, "fail") || !strncmp(status, "fail-", 5);
+ return __of_device_is_status(device, fail);
}
/**
@@ -597,15 +693,20 @@ struct device_node *of_get_next_child(const struct device_node *node,
EXPORT_SYMBOL(of_get_next_child);
/**
- * of_get_next_available_child - Find the next available child node
+ * of_get_next_child_with_prefix - Find the next child node with prefix
* @node: parent node
* @prev: previous child of the parent node, or NULL to get first
+ * @prefix: prefix that the node name should have
*
- * This function is like of_get_next_child(), except that it
- * automatically skips any disabled nodes (i.e. status = "disabled").
+ * This function is like of_get_next_child(), except that it automatically
+ * skips any nodes whose name doesn't have the given prefix.
+ *
+ * Return: A node pointer with refcount incremented, use
+ * of_node_put() on it when done.
*/
-struct device_node *of_get_next_available_child(const struct device_node *node,
- struct device_node *prev)
+struct device_node *of_get_next_child_with_prefix(const struct device_node *node,
+ struct device_node *prev,
+ const char *prefix)
{
struct device_node *next;
unsigned long flags;
@@ -616,7 +717,7 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
raw_spin_lock_irqsave(&devtree_lock, flags);
next = prev ? prev->sibling : node->child;
for (; next; next = next->sibling) {
- if (!__of_device_is_available(next))
+ if (!of_node_name_prefix(next, prefix))
continue;
if (of_node_get(next))
break;
@@ -625,9 +726,62 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return next;
}
+EXPORT_SYMBOL(of_get_next_child_with_prefix);
+
+static struct device_node *of_get_next_status_child(const struct device_node *node,
+ struct device_node *prev,
+ bool (*checker)(const struct device_node *))
+{
+ struct device_node *next;
+ unsigned long flags;
+
+ if (!node)
+ return NULL;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ next = prev ? prev->sibling : node->child;
+ for (; next; next = next->sibling) {
+ if (!checker(next))
+ continue;
+ if (of_node_get(next))
+ break;
+ }
+ of_node_put(prev);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return next;
+}
+
+/**
+ * of_get_next_available_child - Find the next available child node
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * This function is like of_get_next_child(), except that it
+ * automatically skips any disabled nodes (i.e. status = "disabled").
+ */
+struct device_node *of_get_next_available_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ return of_get_next_status_child(node, prev, __of_device_is_available);
+}
EXPORT_SYMBOL(of_get_next_available_child);
/**
+ * of_get_next_reserved_child - Find the next reserved child node
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * This function is like of_get_next_child(), except that it
+ * automatically skips any disabled nodes (i.e. status = "disabled").
+ */
+struct device_node *of_get_next_reserved_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ return of_get_next_status_child(node, prev, __of_device_is_reserved);
+}
+EXPORT_SYMBOL(of_get_next_reserved_child);
+
+/**
* of_get_next_cpu_node - Iterate on cpu nodes
* @prev: previous child of the /cpus node, or NULL to get first
*
@@ -717,7 +871,34 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
}
EXPORT_SYMBOL(of_get_child_by_name);
-struct device_node *__of_find_node_by_path(struct device_node *parent,
+/**
+ * of_get_available_child_by_name - Find the available child node by name for a given parent
+ * @node: parent node
+ * @name: child name to look for.
+ *
+ * This function looks for child node for given matching name and checks the
+ * device's availability for use.
+ *
+ * Return: A node pointer if found, with refcount incremented, use
+ * of_node_put() on it when done.
+ * Returns NULL if node is not found.
+ */
+struct device_node *of_get_available_child_by_name(const struct device_node *node,
+ const char *name)
+{
+ struct device_node *child;
+
+ child = of_get_child_by_name(node, name);
+ if (child && !of_device_is_available(child)) {
+ of_node_put(child);
+ return NULL;
+ }
+
+ return child;
+}
+EXPORT_SYMBOL(of_get_available_child_by_name);
+
+struct device_node *__of_find_node_by_path(const struct device_node *parent,
const char *path)
{
struct device_node *child;
@@ -774,7 +955,7 @@ struct device_node *__of_find_node_by_full_path(struct device_node *node,
struct device_node *of_find_node_opts_by_path(const char *path, const char **opts)
{
struct device_node *np = NULL;
- struct property *pp;
+ const struct property *pp;
unsigned long flags;
const char *separator = strchr(path, ':');
@@ -787,10 +968,10 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt
/* The path could begin with an alias */
if (*path != '/') {
int len;
- const char *p = separator;
+ const char *p = strchrnul(path, '/');
- if (!p)
- p = strchrnul(path, '/');
+ if (separator && separator < p)
+ p = separator;
len = p - path;
/* of_aliases must not be NULL */
@@ -920,19 +1101,15 @@ struct device_node *of_find_node_with_property(struct device_node *from,
const char *prop_name)
{
struct device_node *np;
- struct property *pp;
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
for_each_of_allnodes_from(from, np) {
- for (pp = np->properties; pp; pp = pp->next) {
- if (of_prop_cmp(pp->name, prop_name) == 0) {
- of_node_get(np);
- goto out;
- }
+ if (__of_find_property(np, prop_name, NULL)) {
+ of_node_get(np);
+ break;
}
}
-out:
of_node_put(from);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
@@ -1340,12 +1517,14 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
const char *stem_name,
int index, struct of_phandle_args *out_args)
{
- char *cells_name, *map_name = NULL, *mask_name = NULL;
- char *pass_name = NULL;
+ char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
+ char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name);
+ char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
+ char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
struct device_node *cur, *new = NULL;
const __be32 *map, *mask, *pass;
- static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
- static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 };
+ static const __be32 dummy_mask[] = { [0 ... (MAX_PHANDLE_ARGS - 1)] = cpu_to_be32(~0) };
+ static const __be32 dummy_pass[] = { [0 ... (MAX_PHANDLE_ARGS - 1)] = cpu_to_be32(0) };
__be32 initial_match_array[MAX_PHANDLE_ARGS];
const __be32 *match_array = initial_match_array;
int i, ret, map_len, match;
@@ -1354,27 +1533,13 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
if (index < 0)
return -EINVAL;
- cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
- if (!cells_name)
+ if (!cells_name || !map_name || !mask_name || !pass_name)
return -ENOMEM;
- ret = -ENOMEM;
- map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name);
- if (!map_name)
- goto free;
-
- mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
- if (!mask_name)
- goto free;
-
- pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
- if (!pass_name)
- goto free;
-
ret = __of_parse_phandle_with_args(np, list_name, cells_name, -1, index,
out_args);
if (ret)
- goto free;
+ return ret;
/* Get the #<list>-cells property */
cur = out_args->np;
@@ -1391,8 +1556,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
/* Get the <list>-map property */
map = of_get_property(cur, map_name, &map_len);
if (!map) {
- ret = 0;
- goto free;
+ return 0;
}
map_len /= sizeof(u32);
@@ -1414,8 +1578,10 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
map_len--;
/* Check if not found */
- if (!new)
+ if (!new) {
+ ret = -EINVAL;
goto put;
+ }
if (!of_device_is_available(new))
match = 0;
@@ -1425,17 +1591,20 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
goto put;
/* Check for malformed properties */
- if (WARN_ON(new_size > MAX_PHANDLE_ARGS))
- goto put;
- if (map_len < new_size)
+ if (WARN_ON(new_size > MAX_PHANDLE_ARGS) ||
+ map_len < new_size) {
+ ret = -EINVAL;
goto put;
+ }
/* Move forward by new node's #<list>-cells amount */
map += new_size;
map_len -= new_size;
}
- if (!match)
+ if (!match) {
+ ret = -ENOENT;
goto put;
+ }
/* Get the <list>-map-pass-thru property (optional) */
pass = of_get_property(cur, pass_name, NULL);
@@ -1447,7 +1616,6 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
* specifier into the out_args structure, keeping the
* bits specified in <list>-map-pass-thru.
*/
- match_array = map - new_size;
for (i = 0; i < new_size; i++) {
__be32 val = *(map - new_size + i);
@@ -1456,6 +1624,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
val |= cpu_to_be32(out_args->args[i]) & pass[i];
}
+ initial_match_array[i] = val;
out_args->args[i] = be32_to_cpu(val);
}
out_args->args_count = list_size = new_size;
@@ -1463,16 +1632,11 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
out_args->np = new;
of_node_put(cur);
cur = new;
+ new = NULL;
}
put:
of_node_put(cur);
of_node_put(new);
-free:
- kfree(mask_name);
- kfree(map_name);
- kfree(cells_name);
- kfree(pass_name);
-
return ret;
}
EXPORT_SYMBOL(of_parse_phandle_with_args_map);
@@ -1529,6 +1693,20 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na
}
EXPORT_SYMBOL(of_count_phandle_with_args);
+static struct property *__of_remove_property_from_list(struct property **list, struct property *prop)
+{
+ struct property **next;
+
+ for (next = list; *next; next = &(*next)->next) {
+ if (*next == prop) {
+ *next = prop->next;
+ prop->next = NULL;
+ return prop;
+ }
+ }
+ return NULL;
+}
+
/**
* __of_add_property - Add a property to a node without lock operations
* @np: Caller's Device Node
@@ -1536,19 +1714,32 @@ EXPORT_SYMBOL(of_count_phandle_with_args);
*/
int __of_add_property(struct device_node *np, struct property *prop)
{
+ int rc = 0;
+ unsigned long flags;
struct property **next;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+
+ __of_remove_property_from_list(&np->deadprops, prop);
+
prop->next = NULL;
next = &np->properties;
while (*next) {
- if (strcmp(prop->name, (*next)->name) == 0)
+ if (of_prop_cmp(prop->name, (*next)->name) == 0) {
/* duplicate ! don't insert it */
- return -EEXIST;
-
+ rc = -EEXIST;
+ goto out_unlock;
+ }
next = &(*next)->next;
}
*next = prop;
+out_unlock:
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ if (rc)
+ return rc;
+
+ __of_add_property_sysfs(np, prop);
return 0;
}
@@ -1559,18 +1750,10 @@ int __of_add_property(struct device_node *np, struct property *prop)
*/
int of_add_property(struct device_node *np, struct property *prop)
{
- unsigned long flags;
int rc;
mutex_lock(&of_mutex);
-
- raw_spin_lock_irqsave(&devtree_lock, flags);
rc = __of_add_property(np, prop);
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- if (!rc)
- __of_add_property_sysfs(np, prop);
-
mutex_unlock(&of_mutex);
if (!rc)
@@ -1582,20 +1765,23 @@ EXPORT_SYMBOL_GPL(of_add_property);
int __of_remove_property(struct device_node *np, struct property *prop)
{
- struct property **next;
+ unsigned long flags;
+ int rc = -ENODEV;
- for (next = &np->properties; *next; next = &(*next)->next) {
- if (*next == prop)
- break;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+
+ if (__of_remove_property_from_list(&np->properties, prop)) {
+ /* Found the property, add it to deadprops list */
+ prop->next = np->deadprops;
+ np->deadprops = prop;
+ rc = 0;
}
- if (*next == NULL)
- return -ENODEV;
- /* found the node */
- *next = prop->next;
- prop->next = np->deadprops;
- np->deadprops = prop;
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ if (rc)
+ return rc;
+ __of_remove_property_sysfs(np, prop);
return 0;
}
@@ -1611,21 +1797,13 @@ int __of_remove_property(struct device_node *np, struct property *prop)
*/
int of_remove_property(struct device_node *np, struct property *prop)
{
- unsigned long flags;
int rc;
if (!prop)
return -ENODEV;
mutex_lock(&of_mutex);
-
- raw_spin_lock_irqsave(&devtree_lock, flags);
rc = __of_remove_property(np, prop);
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- if (!rc)
- __of_remove_property_sysfs(np, prop);
-
mutex_unlock(&of_mutex);
if (!rc)
@@ -1639,6 +1817,11 @@ int __of_update_property(struct device_node *np, struct property *newprop,
struct property **oldpropp)
{
struct property **next, *oldprop;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+
+ __of_remove_property_from_list(&np->deadprops, newprop);
for (next = &np->properties; *next; next = &(*next)->next) {
if (of_prop_cmp((*next)->name, newprop->name) == 0)
@@ -1658,6 +1841,10 @@ int __of_update_property(struct device_node *np, struct property *newprop,
*next = newprop;
}
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ __of_update_property_sysfs(np, newprop, oldprop);
+
return 0;
}
@@ -1673,21 +1860,13 @@ int __of_update_property(struct device_node *np, struct property *newprop,
int of_update_property(struct device_node *np, struct property *newprop)
{
struct property *oldprop;
- unsigned long flags;
int rc;
if (!newprop->name)
return -EINVAL;
mutex_lock(&of_mutex);
-
- raw_spin_lock_irqsave(&devtree_lock, flags);
rc = __of_update_property(np, newprop, &oldprop);
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- if (!rc)
- __of_update_property_sysfs(np, newprop, oldprop);
-
mutex_unlock(&of_mutex);
if (!rc)
@@ -1713,12 +1892,11 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
* for storing the resulting tree
*
* The function scans all the properties of the 'aliases' node and populates
- * the global lookup table with the properties. It returns the
- * number of alias properties found, or an error code in case of failure.
+ * the global lookup table with the properties.
*/
void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
- struct property *pp;
+ const struct property *pp;
of_aliases = of_find_node_by_path("/aliases");
of_chosen = of_find_node_by_path("/chosen");
@@ -1751,9 +1929,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
int id, len;
/* Skip those we do not want to proceed */
- if (!strcmp(pp->name, "name") ||
- !strcmp(pp->name, "phandle") ||
- !strcmp(pp->name, "linux,phandle"))
+ if (is_pseudo_property(pp->name))
continue;
np = of_find_node_by_path(pp->value);
@@ -1789,7 +1965,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
*
* Return: The alias id if found.
*/
-int of_alias_get_id(struct device_node *np, const char *stem)
+int of_alias_get_id(const struct device_node *np, const char *stem)
{
struct alias_prop *app;
int id = -ENODEV;
@@ -1847,7 +2023,7 @@ EXPORT_SYMBOL_GPL(of_alias_get_highest_id);
*
* Return: TRUE if console successfully setup. Otherwise return FALSE.
*/
-bool of_console_check(struct device_node *dn, char *name, int index)
+bool of_console_check(const struct device_node *dn, char *name, int index)
{
if (!dn || dn != of_stdout || console_set_on_cmdline)
return false;
@@ -1935,7 +2111,7 @@ int of_find_last_cache_level(unsigned int cpu)
*
* Return: 0 on success or a standard error code on failure.
*/
-int of_map_id(struct device_node *np, u32 id,
+int of_map_id(const struct device_node *np, u32 id,
const char *map_name, const char *map_mask_name,
struct device_node **target, u32 *id_out)
{
diff --git a/drivers/of/cpu.c b/drivers/of/cpu.c
index d17b2f851082..5214dc3d05ae 100644
--- a/drivers/of/cpu.c
+++ b/drivers/of/cpu.c
@@ -188,7 +188,7 @@ EXPORT_SYMBOL(of_cpu_node_to_id);
* Return: An idle state node if found at @index. The refcount is incremented
* for it, so call of_node_put() on it when done. Returns NULL if not found.
*/
-struct device_node *of_get_cpu_state_node(struct device_node *cpu_node,
+struct device_node *of_get_cpu_state_node(const struct device_node *cpu_node,
int index)
{
struct of_phandle_args args;
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 0f00f1b80708..f7e75e527667 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -17,8 +17,8 @@
/**
* of_match_device - Tell if a struct device matches an of_device_id list
- * @matches: array of of device match structures to search in
- * @dev: the of device structure to match against
+ * @matches: array of of_device_id match structures to search in
+ * @dev: the OF device structure to match against
*
* Used by a driver to check whether an platform_device present in the
* system is in its list of supported devices.
@@ -32,66 +32,38 @@ const struct of_device_id *of_match_device(const struct of_device_id *matches,
}
EXPORT_SYMBOL(of_match_device);
-int of_device_add(struct platform_device *ofdev)
-{
- BUG_ON(ofdev->dev.of_node == NULL);
-
- /* name and id have to be set so that the platform bus doesn't get
- * confused on matching */
- ofdev->name = dev_name(&ofdev->dev);
- ofdev->id = PLATFORM_DEVID_NONE;
-
- /*
- * If this device has not binding numa node in devicetree, that is
- * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this
- * device is on the same node as the parent.
- */
- set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
-
- return device_add(&ofdev->dev);
-}
-
static void
of_dma_set_restricted_buffer(struct device *dev, struct device_node *np)
{
- struct device_node *node, *of_node = dev->of_node;
- int count, i;
+ struct device_node *of_node = dev->of_node;
+ struct of_phandle_iterator it;
+ int rc, i = 0;
if (!IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL))
return;
- count = of_property_count_elems_of_size(of_node, "memory-region",
- sizeof(u32));
/*
* If dev->of_node doesn't exist or doesn't contain memory-region, try
* the OF node having DMA configuration.
*/
- if (count <= 0) {
+ if (!of_property_present(of_node, "memory-region"))
of_node = np;
- count = of_property_count_elems_of_size(
- of_node, "memory-region", sizeof(u32));
- }
- for (i = 0; i < count; i++) {
- node = of_parse_phandle(of_node, "memory-region", i);
+ of_for_each_phandle(&it, rc, of_node, "memory-region", NULL, 0) {
/*
* There might be multiple memory regions, but only one
* restricted-dma-pool region is allowed.
*/
- if (of_device_is_compatible(node, "restricted-dma-pool") &&
- of_device_is_available(node)) {
- of_node_put(node);
+ if (of_device_is_compatible(it.node, "restricted-dma-pool") &&
+ of_device_is_available(it.node)) {
+ if (of_reserved_mem_device_init_by_idx(dev, of_node, i))
+ dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n");
+ of_node_put(it.node);
break;
}
- of_node_put(node);
+ i++;
}
- /*
- * Attempt to initialize a restricted-dma-pool region if one was found.
- * Note that count can hold a negative error code.
- */
- if (i < count && of_reserved_mem_device_init_by_idx(dev, of_node, i))
- dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n");
}
/**
@@ -112,14 +84,17 @@ of_dma_set_restricted_buffer(struct device *dev, struct device_node *np)
int of_dma_configure_id(struct device *dev, struct device_node *np,
bool force_dma, const u32 *id)
{
- const struct iommu_ops *iommu;
const struct bus_dma_region *map = NULL;
struct device_node *bus_np;
- u64 dma_start = 0;
- u64 mask, end, size = 0;
- bool coherent;
+ u64 mask, end = 0;
+ bool coherent, set_map = false;
int ret;
+ if (dev->dma_range_map) {
+ dev_dbg(dev, "dma_range_map already set\n");
+ goto skip_map;
+ }
+
if (np == dev->of_node)
bus_np = __of_get_dma_parent(np);
else
@@ -136,36 +111,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
if (!force_dma)
return ret == -ENODEV ? 0 : ret;
} else {
- const struct bus_dma_region *r = map;
- u64 dma_end = 0;
-
/* Determine the overall bounds of all DMA regions */
- for (dma_start = ~0; r->size; r++) {
- /* Take lower and upper limits */
- if (r->dma_start < dma_start)
- dma_start = r->dma_start;
- if (r->dma_start + r->size > dma_end)
- dma_end = r->dma_start + r->size;
- }
- size = dma_end - dma_start;
-
- /*
- * Add a work around to treat the size as mask + 1 in case
- * it is defined in DT as a mask.
- */
- if (size & 1) {
- dev_warn(dev, "Invalid size 0x%llx for dma-range(s)\n",
- size);
- size = size + 1;
- }
-
- if (!size) {
- dev_err(dev, "Adjusted size 0x%llx invalid\n", size);
- kfree(map);
- return -EINVAL;
- }
+ end = dma_range_map_max(map);
+ set_map = true;
}
-
+skip_map:
/*
* If @dev is expected to be DMA-capable then the bus code that created
* it should have initialised its dma_mask pointer by this point. For
@@ -177,21 +127,20 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
dev->dma_mask = &dev->coherent_dma_mask;
}
- if (!size && dev->coherent_dma_mask)
- size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1);
- else if (!size)
- size = 1ULL << 32;
+ if (!end && dev->coherent_dma_mask)
+ end = dev->coherent_dma_mask;
+ else if (!end)
+ end = (1ULL << 32) - 1;
/*
* Limit coherent and dma mask based on size and default mask
* set by the driver.
*/
- end = dma_start + size - 1;
mask = DMA_BIT_MASK(ilog2(end) + 1);
dev->coherent_dma_mask &= mask;
*dev->dma_mask &= mask;
/* ...but only set bus limit and range map if we found valid dma-ranges earlier */
- if (!ret) {
+ if (set_map) {
dev->bus_dma_limit = end;
dev->dma_range_map = map;
}
@@ -200,40 +149,27 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
dev_dbg(dev, "device is%sdma coherent\n",
coherent ? " " : " not ");
- iommu = of_iommu_configure(dev, np, id);
- if (PTR_ERR(iommu) == -EPROBE_DEFER) {
+ ret = of_iommu_configure(dev, np, id);
+ if (ret == -EPROBE_DEFER) {
/* Don't touch range map if it wasn't set from a valid dma-ranges */
- if (!ret)
+ if (set_map)
dev->dma_range_map = NULL;
kfree(map);
return -EPROBE_DEFER;
}
-
+ /* Take all other IOMMU errors to mean we'll just carry on without it */
dev_dbg(dev, "device is%sbehind an iommu\n",
- iommu ? " " : " not ");
+ !ret ? " " : " not ");
- arch_setup_dma_ops(dev, dma_start, size, iommu, coherent);
+ arch_setup_dma_ops(dev, coherent);
- if (!iommu)
+ if (ret)
of_dma_set_restricted_buffer(dev, np);
return 0;
}
EXPORT_SYMBOL_GPL(of_dma_configure_id);
-int of_device_register(struct platform_device *pdev)
-{
- device_initialize(&pdev->dev);
- return of_device_add(pdev);
-}
-EXPORT_SYMBOL(of_device_register);
-
-void of_device_unregister(struct platform_device *ofdev)
-{
- device_unregister(&ofdev->dev);
-}
-EXPORT_SYMBOL(of_device_unregister);
-
const void *of_device_get_match_data(const struct device *dev)
{
const struct of_device_id *match;
@@ -312,6 +248,7 @@ void of_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
}
mutex_unlock(&of_mutex);
}
+EXPORT_SYMBOL_GPL(of_device_uevent);
int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env)
{
@@ -335,3 +272,44 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *
return 0;
}
EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
+
+/**
+ * of_device_make_bus_id - Use the device node data to assign a unique name
+ * @dev: pointer to device structure that is linked to a device tree node
+ *
+ * This routine will first try using the translated bus address to
+ * derive a unique name. If it cannot, then it will prepend names from
+ * parent nodes until a unique name can be derived.
+ */
+void of_device_make_bus_id(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+ const __be32 *reg;
+ u64 addr;
+ u32 mask;
+
+ /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+ while (node->parent) {
+ /*
+ * If the address can be translated, then that is as much
+ * uniqueness as we need. Make it the first component and return
+ */
+ reg = of_get_property(node, "reg", NULL);
+ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+ if (!of_property_read_u32(node, "mask", &mask))
+ dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
+ addr, ffs(mask) - 1, node, dev_name(dev));
+
+ else
+ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
+ addr, node, dev_name(dev));
+ return;
+ }
+
+ /* format arguments only used if dev_name() resolves to NULL */
+ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+ kbasename(node->full_name), dev_name(dev));
+ node = node->parent;
+ }
+}
+EXPORT_SYMBOL_GPL(of_device_make_bus_id);
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index e311d406b170..2eaaddcb0ec4 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -9,6 +9,8 @@
#define pr_fmt(fmt) "OF: " fmt
+#include <linux/cleanup.h>
+#include <linux/device.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
@@ -63,37 +65,30 @@ int of_reconfig_notifier_unregister(struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
-#ifdef DEBUG
-const char *action_names[] = {
+static const char *action_names[] = {
+ [0] = "INVALID",
[OF_RECONFIG_ATTACH_NODE] = "ATTACH_NODE",
[OF_RECONFIG_DETACH_NODE] = "DETACH_NODE",
[OF_RECONFIG_ADD_PROPERTY] = "ADD_PROPERTY",
[OF_RECONFIG_REMOVE_PROPERTY] = "REMOVE_PROPERTY",
[OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY",
};
-#endif
+
+#define _do_print(func, prefix, action, node, prop, ...) ({ \
+ func("changeset: " prefix "%-15s %pOF%s%s\n", \
+ ##__VA_ARGS__, action_names[action], node, \
+ prop ? ":" : "", prop ? prop->name : ""); \
+})
+#define of_changeset_action_err(...) _do_print(pr_err, __VA_ARGS__)
+#define of_changeset_action_debug(...) _do_print(pr_debug, __VA_ARGS__)
int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p)
{
int rc;
-#ifdef DEBUG
struct of_reconfig_data *pr = p;
- switch (action) {
- case OF_RECONFIG_ATTACH_NODE:
- case OF_RECONFIG_DETACH_NODE:
- pr_debug("notify %-15s %pOF\n", action_names[action],
- pr->dn);
- break;
- case OF_RECONFIG_ADD_PROPERTY:
- case OF_RECONFIG_REMOVE_PROPERTY:
- case OF_RECONFIG_UPDATE_PROPERTY:
- pr_debug("notify %-15s %pOF:%s\n", action_names[action],
- pr->dn, pr->prop->name);
- break;
+ of_changeset_action_debug("notify: ", action, pr->dn, pr->prop);
- }
-#endif
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
return notifier_to_errno(rc);
}
@@ -105,8 +100,9 @@ int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p)
*
* Returns the new state of a device based on the notifier used.
*
- * Return: 0 on device going from enabled to disabled, 1 on device
- * going from disabled to enabled and -1 on no change.
+ * Return: OF_RECONFIG_CHANGE_REMOVE on device going from enabled to
+ * disabled, OF_RECONFIG_CHANGE_ADD on device going from disabled to
+ * enabled and OF_RECONFIG_NO_CHANGE on no change.
*/
int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *pr)
{
@@ -205,6 +201,9 @@ static void __of_attach_node(struct device_node *np)
{
const __be32 *phandle;
int sz;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
if (!of_node_check_flag(np, OF_OVERLAY)) {
np->name = __of_get_property(np, "name", NULL);
@@ -227,6 +226,10 @@ static void __of_attach_node(struct device_node *np)
np->parent->child = np;
of_node_clear_flag(np, OF_DETACHED);
np->fwnode.flags |= FWNODE_FLAG_NOT_DEVICE;
+
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ __of_attach_node_sysfs(np);
}
/**
@@ -236,17 +239,12 @@ static void __of_attach_node(struct device_node *np)
int of_attach_node(struct device_node *np)
{
struct of_reconfig_data rd;
- unsigned long flags;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex);
- raw_spin_lock_irqsave(&devtree_lock, flags);
__of_attach_node(np);
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- __of_attach_node_sysfs(np);
mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd);
@@ -257,13 +255,15 @@ int of_attach_node(struct device_node *np)
void __of_detach_node(struct device_node *np)
{
struct device_node *parent;
+ unsigned long flags;
- if (WARN_ON(of_node_check_flag(np, OF_DETACHED)))
- return;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
parent = np->parent;
- if (WARN_ON(!parent))
+ if (WARN_ON(of_node_check_flag(np, OF_DETACHED) || !parent)) {
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return;
+ }
if (parent->child == np)
parent->child = np->sibling;
@@ -280,6 +280,10 @@ void __of_detach_node(struct device_node *np)
/* race with of_find_node_by_phandle() prevented by devtree_lock */
__of_phandle_cache_inv_entry(np->phandle);
+
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ __of_detach_node_sysfs(np);
}
/**
@@ -289,17 +293,12 @@ void __of_detach_node(struct device_node *np)
int of_detach_node(struct device_node *np)
{
struct of_reconfig_data rd;
- unsigned long flags;
memset(&rd, 0, sizeof(rd));
rd.dn = np;
mutex_lock(&of_mutex);
- raw_spin_lock_irqsave(&devtree_lock, flags);
__of_detach_node(np);
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
-
- __of_detach_node_sysfs(np);
mutex_unlock(&of_mutex);
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd);
@@ -308,15 +307,20 @@ int of_detach_node(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_detach_node);
+void __of_prop_free(struct property *prop)
+{
+ kfree(prop->name);
+ kfree(prop->value);
+ kfree(prop);
+}
+
static void property_list_free(struct property *prop_list)
{
struct property *prop, *next;
for (prop = prop_list; prop != NULL; prop = next) {
next = prop->next;
- kfree(prop->name);
- kfree(prop->value);
- kfree(prop);
+ __of_prop_free(prop);
}
}
@@ -429,9 +433,7 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
return new;
err_free:
- kfree(new->name);
- kfree(new->value);
- kfree(new);
+ __of_prop_free(new);
return NULL;
}
@@ -473,9 +475,7 @@ struct device_node *__of_node_dup(const struct device_node *np,
if (!new_pp)
goto err_prop;
if (__of_add_property(node, new_pp)) {
- kfree(new_pp->name);
- kfree(new_pp->value);
- kfree(new_pp);
+ __of_prop_free(new_pp);
goto err_prop;
}
}
@@ -487,6 +487,38 @@ struct device_node *__of_node_dup(const struct device_node *np,
return NULL;
}
+/**
+ * of_changeset_create_node - Dynamically create a device node and attach to
+ * a given changeset.
+ *
+ * @ocs: Pointer to changeset
+ * @parent: Pointer to parent device node
+ * @full_name: Node full name
+ *
+ * Return: Pointer to the created device node or NULL in case of an error.
+ */
+struct device_node *of_changeset_create_node(struct of_changeset *ocs,
+ struct device_node *parent,
+ const char *full_name)
+{
+ struct device_node *np;
+ int ret;
+
+ np = __of_node_dup(NULL, full_name);
+ if (!np)
+ return NULL;
+ np->parent = parent;
+
+ ret = of_changeset_attach_node(ocs, np);
+ if (ret) {
+ of_node_put(np);
+ return NULL;
+ }
+
+ return np;
+}
+EXPORT_SYMBOL(of_changeset_create_node);
+
static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
{
if (ce->action == OF_RECONFIG_ATTACH_NODE &&
@@ -504,31 +536,7 @@ static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
kfree(ce);
}
-#ifdef DEBUG
-static void __of_changeset_entry_dump(struct of_changeset_entry *ce)
-{
- switch (ce->action) {
- case OF_RECONFIG_ADD_PROPERTY:
- case OF_RECONFIG_REMOVE_PROPERTY:
- case OF_RECONFIG_UPDATE_PROPERTY:
- pr_debug("cset<%p> %-15s %pOF/%s\n", ce, action_names[ce->action],
- ce->np, ce->prop->name);
- break;
- case OF_RECONFIG_ATTACH_NODE:
- case OF_RECONFIG_DETACH_NODE:
- pr_debug("cset<%p> %-15s %pOF\n", ce, action_names[ce->action],
- ce->np);
- break;
- }
-}
-#else
-static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce)
-{
- /* empty */
-}
-#endif
-
-static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
+static void __of_changeset_entry_invert(const struct of_changeset_entry *ce,
struct of_changeset_entry *rce)
{
memcpy(rce, ce, sizeof(*rce));
@@ -595,13 +603,10 @@ static int __of_changeset_entry_notify(struct of_changeset_entry *ce,
static int __of_changeset_entry_apply(struct of_changeset_entry *ce)
{
- struct property *old_prop, **propp;
- unsigned long flags;
int ret = 0;
- __of_changeset_entry_dump(ce);
+ of_changeset_action_debug("apply: ", ce->action, ce->np, ce->prop);
- raw_spin_lock_irqsave(&devtree_lock, flags);
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
__of_attach_node(ce->np);
@@ -610,82 +615,28 @@ static int __of_changeset_entry_apply(struct of_changeset_entry *ce)
__of_detach_node(ce->np);
break;
case OF_RECONFIG_ADD_PROPERTY:
- /* If the property is in deadprops then it must be removed */
- for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
- if (*propp == ce->prop) {
- *propp = ce->prop->next;
- ce->prop->next = NULL;
- break;
- }
- }
-
ret = __of_add_property(ce->np, ce->prop);
- if (ret) {
- pr_err("changeset: add_property failed @%pOF/%s\n",
- ce->np,
- ce->prop->name);
- break;
- }
break;
case OF_RECONFIG_REMOVE_PROPERTY:
ret = __of_remove_property(ce->np, ce->prop);
- if (ret) {
- pr_err("changeset: remove_property failed @%pOF/%s\n",
- ce->np,
- ce->prop->name);
- break;
- }
break;
case OF_RECONFIG_UPDATE_PROPERTY:
- /* If the property is in deadprops then it must be removed */
- for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
- if (*propp == ce->prop) {
- *propp = ce->prop->next;
- ce->prop->next = NULL;
- break;
- }
- }
-
- ret = __of_update_property(ce->np, ce->prop, &old_prop);
- if (ret) {
- pr_err("changeset: update_property failed @%pOF/%s\n",
- ce->np,
- ce->prop->name);
- break;
- }
+ ret = __of_update_property(ce->np, ce->prop, &ce->old_prop);
break;
default:
ret = -EINVAL;
}
- raw_spin_unlock_irqrestore(&devtree_lock, flags);
- if (ret)
+ if (ret) {
+ of_changeset_action_err("apply failed: ", ce->action, ce->np, ce->prop);
return ret;
-
- switch (ce->action) {
- case OF_RECONFIG_ATTACH_NODE:
- __of_attach_node_sysfs(ce->np);
- break;
- case OF_RECONFIG_DETACH_NODE:
- __of_detach_node_sysfs(ce->np);
- break;
- case OF_RECONFIG_ADD_PROPERTY:
- /* ignore duplicate names */
- __of_add_property_sysfs(ce->np, ce->prop);
- break;
- case OF_RECONFIG_REMOVE_PROPERTY:
- __of_remove_property_sysfs(ce->np, ce->prop);
- break;
- case OF_RECONFIG_UPDATE_PROPERTY:
- __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop);
- break;
}
return 0;
}
-static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce)
+static inline int __of_changeset_entry_revert(const struct of_changeset_entry *ce)
{
struct of_changeset_entry ce_inverted;
@@ -719,6 +670,17 @@ void of_changeset_destroy(struct of_changeset *ocs)
{
struct of_changeset_entry *ce, *cen;
+ /*
+ * When a device is deleted, the device links to/from it are also queued
+ * for deletion. Until these device links are freed, the devices
+ * themselves aren't freed. If the device being deleted is due to an
+ * overlay change, this device might be holding a reference to a device
+ * node that will be freed. So, wait until all already pending device
+ * links are deleted before freeing a device node. This ensures we don't
+ * free any device node that has a non-zero reference count.
+ */
+ device_link_wait_removal();
+
list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
__of_changeset_entry_destroy(ce);
}
@@ -943,6 +905,9 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
{
struct of_changeset_entry *ce;
+ if (WARN_ON(action >= ARRAY_SIZE(action_names)))
+ return -EINVAL;
+
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
if (!ce)
return -ENOMEM;
@@ -952,11 +917,207 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
ce->np = of_node_get(np);
ce->prop = prop;
- if (action == OF_RECONFIG_UPDATE_PROPERTY && prop)
- ce->old_prop = of_find_property(np, prop->name, NULL);
-
/* add it to the list */
list_add_tail(&ce->node, &ocs->entries);
return 0;
}
EXPORT_SYMBOL_GPL(of_changeset_action);
+
+static int of_changeset_add_prop_helper(struct of_changeset *ocs,
+ struct device_node *np,
+ const struct property *pp)
+{
+ struct property *new_pp;
+ int ret;
+
+ new_pp = __of_prop_dup(pp, GFP_KERNEL);
+ if (!new_pp)
+ return -ENOMEM;
+
+ ret = of_changeset_add_property(ocs, np, new_pp);
+ if (ret) {
+ __of_prop_free(new_pp);
+ return ret;
+ }
+
+ new_pp->next = np->deadprops;
+ np->deadprops = new_pp;
+
+ return 0;
+}
+
+/**
+ * of_changeset_add_prop_string - Add a string property to a changeset
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @prop_name: name of the property to be added
+ * @str: pointer to null terminated string
+ *
+ * Create a string property and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_add_prop_string(struct of_changeset *ocs,
+ struct device_node *np,
+ const char *prop_name, const char *str)
+{
+ struct property prop;
+
+ prop.name = (char *)prop_name;
+ prop.length = strlen(str) + 1;
+ prop.value = (void *)str;
+
+ return of_changeset_add_prop_helper(ocs, np, &prop);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_prop_string);
+
+/**
+ * of_changeset_add_prop_string_array - Add a string list property to
+ * a changeset
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @prop_name: name of the property to be added
+ * @str_array: pointer to an array of null terminated strings
+ * @sz: number of string array elements
+ *
+ * Create a string list property and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_add_prop_string_array(struct of_changeset *ocs,
+ struct device_node *np,
+ const char *prop_name,
+ const char * const *str_array, size_t sz)
+{
+ struct property prop;
+ int i, ret;
+ char *vp;
+
+ prop.name = (char *)prop_name;
+
+ prop.length = 0;
+ for (i = 0; i < sz; i++)
+ prop.length += strlen(str_array[i]) + 1;
+
+ prop.value = kmalloc(prop.length, GFP_KERNEL);
+ if (!prop.value)
+ return -ENOMEM;
+
+ vp = prop.value;
+ for (i = 0; i < sz; i++) {
+ vp += snprintf(vp, (char *)prop.value + prop.length - vp, "%s",
+ str_array[i]) + 1;
+ }
+ ret = of_changeset_add_prop_helper(ocs, np, &prop);
+ kfree(prop.value);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_prop_string_array);
+
+/**
+ * of_changeset_add_prop_u32_array - Add a property of 32 bit integers
+ * property to a changeset
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @prop_name: name of the property to be added
+ * @array: pointer to an array of 32 bit integers
+ * @sz: number of array elements
+ *
+ * Create a property of 32 bit integers and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_add_prop_u32_array(struct of_changeset *ocs,
+ struct device_node *np,
+ const char *prop_name,
+ const u32 *array, size_t sz)
+{
+ struct property prop;
+ __be32 *val __free(kfree) = kcalloc(sz, sizeof(__be32), GFP_KERNEL);
+ int i;
+
+ if (!val)
+ return -ENOMEM;
+
+ for (i = 0; i < sz; i++)
+ val[i] = cpu_to_be32(array[i]);
+ prop.name = (char *)prop_name;
+ prop.length = sizeof(u32) * sz;
+ prop.value = (void *)val;
+
+ return of_changeset_add_prop_helper(ocs, np, &prop);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_prop_u32_array);
+
+/**
+ * of_changeset_add_prop_bool - Add a boolean property (i.e. a property without
+ * any values) to a changeset.
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @prop_name: name of the property to be added
+ *
+ * Create a boolean property and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_add_prop_bool(struct of_changeset *ocs, struct device_node *np,
+ const char *prop_name)
+{
+ struct property prop;
+
+ prop.name = (char *)prop_name;
+ prop.length = 0;
+ prop.value = NULL;
+
+ return of_changeset_add_prop_helper(ocs, np, &prop);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_prop_bool);
+
+static int of_changeset_update_prop_helper(struct of_changeset *ocs,
+ struct device_node *np,
+ const struct property *pp)
+{
+ struct property *new_pp;
+ int ret;
+
+ new_pp = __of_prop_dup(pp, GFP_KERNEL);
+ if (!new_pp)
+ return -ENOMEM;
+
+ ret = of_changeset_update_property(ocs, np, new_pp);
+ if (ret)
+ __of_prop_free(new_pp);
+
+ return ret;
+}
+
+/**
+ * of_changeset_update_prop_string - Add a string property update to a changeset
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @prop_name: name of the property to be updated
+ * @str: pointer to null terminated string
+ *
+ * Create a string property to be updated and add it to a changeset.
+ *
+ * Return: 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_update_prop_string(struct of_changeset *ocs,
+ struct device_node *np,
+ const char *prop_name, const char *str)
+{
+ struct property prop = {
+ .name = (char *)prop_name,
+ .length = strlen(str) + 1,
+ .value = (void *)str,
+ };
+
+ return of_changeset_update_prop_helper(ocs, np, &prop);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_prop_string);
diff --git a/drivers/of/empty_root.dts b/drivers/of/empty_root.dts
new file mode 100644
index 000000000000..cbe169ba3db5
--- /dev/null
+++ b/drivers/of/empty_root.dts
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/dts-v1/;
+
+/ {
+ /*
+ * #address-cells/#size-cells are required properties at root node.
+ * Use 2 cells for both address cells and size cells in order to fully
+ * support 64-bit addresses and sizes on systems using this empty root
+ * node.
+ */
+ #address-cells = <0x02>;
+ #size-cells = <0x02>;
+};
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index bf502ba8da95..d378d4b4109f 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -16,7 +16,6 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
-#include <linux/of_reserved_mem.h>
#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -26,6 +25,7 @@
#include <linux/serial_core.h>
#include <linux/sysfs.h>
#include <linux/random.h>
+#include <linux/kexec_handover.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#include <asm/page.h>
@@ -33,6 +33,13 @@
#include "of_private.h"
/*
+ * __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by
+ * cmd_wrap_S_dtb in scripts/Makefile.dtbs
+ */
+extern uint8_t __dtb_empty_root_begin[];
+extern uint8_t __dtb_empty_root_end[];
+
+/*
* of_fdt_limit_memory - limit the number of regions in the /memory node
* @limit: maximum entries
*
@@ -45,28 +52,7 @@ void __init of_fdt_limit_memory(int limit)
int memory;
int len;
const void *val;
- int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
- int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
- const __be32 *addr_prop;
- const __be32 *size_prop;
- int root_offset;
- int cell_size;
-
- root_offset = fdt_path_offset(initial_boot_params, "/");
- if (root_offset < 0)
- return;
-
- addr_prop = fdt_getprop(initial_boot_params, root_offset,
- "#address-cells", NULL);
- if (addr_prop)
- nr_address_cells = fdt32_to_cpu(*addr_prop);
-
- size_prop = fdt_getprop(initial_boot_params, root_offset,
- "#size-cells", NULL);
- if (size_prop)
- nr_size_cells = fdt32_to_cpu(*size_prop);
-
- cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells);
+ int cell_size = sizeof(uint32_t)*(dt_root_addr_cells + dt_root_size_cells);
memory = fdt_path_offset(initial_boot_params, "/memory");
if (memory > 0) {
@@ -80,7 +66,7 @@ void __init of_fdt_limit_memory(int limit)
}
}
-static bool of_fdt_device_is_available(const void *blob, unsigned long node)
+bool of_fdt_device_is_available(const void *blob, unsigned long node)
{
const char *status = fdt_getprop(blob, node, "status", NULL);
@@ -471,131 +457,12 @@ int __initdata dt_root_addr_cells;
int __initdata dt_root_size_cells;
void *initial_boot_params __ro_after_init;
+phys_addr_t initial_boot_params_pa __ro_after_init;
#ifdef CONFIG_OF_EARLY_FLATTREE
static u32 of_fdt_crc32;
-static int __init early_init_dt_reserve_memory(phys_addr_t base,
- phys_addr_t size, bool nomap)
-{
- if (nomap) {
- /*
- * If the memory is already reserved (by another region), we
- * should not allow it to be marked nomap, but don't worry
- * if the region isn't memory as it won't be mapped.
- */
- if (memblock_overlaps_region(&memblock.memory, base, size) &&
- memblock_is_region_reserved(base, size))
- return -EBUSY;
-
- return memblock_mark_nomap(base, size);
- }
- return memblock_reserve(base, size);
-}
-
-/*
- * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property
- */
-static int __init __reserved_mem_reserve_reg(unsigned long node,
- const char *uname)
-{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
- phys_addr_t base, size;
- int len;
- const __be32 *prop;
- int first = 1;
- bool nomap;
-
- prop = of_get_flat_dt_prop(node, "reg", &len);
- if (!prop)
- return -ENOENT;
-
- if (len && len % t_len != 0) {
- pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
- uname);
- return -EINVAL;
- }
-
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
-
- while (len >= t_len) {
- base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
- if (size &&
- early_init_dt_reserve_memory(base, size, nomap) == 0)
- pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
- uname, &base, (unsigned long)(size / SZ_1M));
- else
- pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
- uname, &base, (unsigned long)(size / SZ_1M));
-
- len -= t_len;
- if (first) {
- fdt_reserved_mem_save_node(node, uname, base, size);
- first = 0;
- }
- }
- return 0;
-}
-
-/*
- * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
- * in /reserved-memory matches the values supported by the current implementation,
- * also check if ranges property has been provided
- */
-static int __init __reserved_mem_check_root(unsigned long node)
-{
- const __be32 *prop;
-
- prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
- if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
- return -EINVAL;
-
- prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
- if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
- return -EINVAL;
-
- prop = of_get_flat_dt_prop(node, "ranges", NULL);
- if (!prop)
- return -EINVAL;
- return 0;
-}
-
-/*
- * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
- */
-static int __init fdt_scan_reserved_mem(void)
-{
- int node, child;
- const void *fdt = initial_boot_params;
-
- node = fdt_path_offset(fdt, "/reserved-memory");
- if (node < 0)
- return -ENODEV;
-
- if (__reserved_mem_check_root(node) != 0) {
- pr_err("Reserved memory: unsupported node format, ignoring\n");
- return -EINVAL;
- }
-
- fdt_for_each_subnode(child, fdt, node) {
- const char *uname;
- int err;
-
- if (!of_fdt_device_is_available(fdt, child))
- continue;
-
- uname = fdt_get_name(fdt, child, NULL);
-
- err = __reserved_mem_reserve_reg(child, uname);
- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
- fdt_reserved_mem_save_node(child, uname, 0, 0);
- }
- return 0;
-}
-
/*
* fdt_reserve_elfcorehdr() - reserves memory for elf core header
*
@@ -630,6 +497,7 @@ static void __init fdt_reserve_elfcorehdr(void)
void __init early_init_fdt_scan_reserved_mem(void)
{
int n;
+ int res;
u64 base, size;
if (!initial_boot_params)
@@ -640,13 +508,15 @@ void __init early_init_fdt_scan_reserved_mem(void)
/* Process header /memreserve/ fields */
for (n = 0; ; n++) {
- fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
+ res = fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
+ if (res) {
+ pr_err("Invalid memory reservation block index %d\n", n);
+ break;
+ }
if (!size)
break;
memblock_reserve(base, size);
}
-
- fdt_init_reserved_mem();
}
/**
@@ -755,6 +625,47 @@ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
return fdt_getprop(initial_boot_params, node, name, size);
}
+const __be32 *__init of_flat_dt_get_addr_size_prop(unsigned long node,
+ const char *name,
+ int *entries)
+{
+ const __be32 *prop;
+ int len, elen = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+
+ prop = of_get_flat_dt_prop(node, name, &len);
+ if (!prop || len % elen) {
+ *entries = 0;
+ return NULL;
+ }
+
+ *entries = len / elen;
+ return prop;
+}
+
+bool __init of_flat_dt_get_addr_size(unsigned long node, const char *name,
+ u64 *addr, u64 *size)
+{
+ const __be32 *prop;
+ int entries;
+
+ prop = of_flat_dt_get_addr_size_prop(node, name, &entries);
+ if (!prop || entries != 1)
+ return false;
+
+ of_flat_dt_read_addr_size(prop, 0, addr, size);
+ return true;
+}
+
+void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index,
+ u64 *addr, u64 *size)
+{
+ int entry_cells = dt_root_addr_cells + dt_root_size_cells;
+ prop += entry_cells * entry_index;
+
+ *addr = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ *size = dt_mem_next_cell(dt_root_size_cells, &prop);
+}
+
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
@@ -942,21 +853,15 @@ static void __init early_init_dt_check_for_initrd(unsigned long node)
*/
static void __init early_init_dt_check_for_elfcorehdr(unsigned long node)
{
- const __be32 *prop;
- int len;
-
if (!IS_ENABLED(CONFIG_CRASH_DUMP))
return;
pr_debug("Looking for elfcorehdr property... ");
- prop = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len);
- if (!prop || (len < (dt_root_addr_cells + dt_root_size_cells)))
+ if (!of_flat_dt_get_addr_size(node, "linux,elfcorehdr",
+ &elfcorehdr_addr, &elfcorehdr_size))
return;
- elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &prop);
- elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n",
elfcorehdr_addr, elfcorehdr_size);
}
@@ -979,8 +884,9 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND;
void __init early_init_dt_check_for_usable_mem_range(void)
{
struct memblock_region rgn[MAX_USABLE_RANGES] = {0};
- const __be32 *prop, *endp;
+ const __be32 *prop;
int len, i;
+ u64 base, size;
unsigned long node = chosen_node_offset;
if ((long)node < 0)
@@ -988,14 +894,17 @@ void __init early_init_dt_check_for_usable_mem_range(void)
pr_debug("Looking for usable-memory-range property... ");
- prop = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len);
- if (!prop || (len % (dt_root_addr_cells + dt_root_size_cells)))
+ prop = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory-range",
+ &len);
+ if (!prop)
return;
- endp = prop + (len / sizeof(__be32));
- for (i = 0; i < MAX_USABLE_RANGES && prop < endp; i++) {
- rgn[i].base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- rgn[i].size = dt_mem_next_cell(dt_root_size_cells, &prop);
+ len = min(len, MAX_USABLE_RANGES);
+
+ for (i = 0; i < len; i++) {
+ of_flat_dt_read_addr_size(prop, i, &base, &size);
+ rgn[i].base = base;
+ rgn[i].size = size;
pr_debug("cap_mem_regions[%d]: base=%pa, size=%pa\n",
i, &rgn[i].base, &rgn[i].size);
@@ -1006,6 +915,28 @@ void __init early_init_dt_check_for_usable_mem_range(void)
memblock_add(rgn[i].base, rgn[i].size);
}
+/**
+ * early_init_dt_check_kho - Decode info required for kexec handover from DT
+ */
+static void __init early_init_dt_check_kho(void)
+{
+ unsigned long node = chosen_node_offset;
+ u64 fdt_start, fdt_size, scratch_start, scratch_size;
+
+ if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER) || (long)node < 0)
+ return;
+
+ if (!of_flat_dt_get_addr_size(node, "linux,kho-fdt",
+ &fdt_start, &fdt_size))
+ return;
+
+ if (!of_flat_dt_get_addr_size(node, "linux,kho-scratch",
+ &scratch_start, &scratch_size))
+ return;
+
+ kho_populate(fdt_start, fdt_size, scratch_start, scratch_size);
+}
+
#ifdef CONFIG_SERIAL_EARLYCON
int __init early_init_dt_scan_chosen_stdout(void)
@@ -1072,12 +1003,12 @@ int __init early_init_dt_scan_root(void)
dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
- if (prop)
+ if (!WARN(!prop, "No '#size-cells' in root node\n"))
dt_root_size_cells = be32_to_cpup(prop);
pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);
prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
- if (prop)
+ if (!WARN(!prop, "No '#address-cells' in root node\n"))
dt_root_addr_cells = be32_to_cpup(prop);
pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);
@@ -1102,8 +1033,8 @@ int __init early_init_dt_scan_memory(void)
fdt_for_each_subnode(node, fdt, 0) {
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
- const __be32 *reg, *endp;
- int l;
+ const __be32 *reg;
+ int i, l;
bool hotpluggable;
/* We are scanning "memory" nodes only */
@@ -1113,23 +1044,21 @@ int __init early_init_dt_scan_memory(void)
if (!of_fdt_device_is_available(fdt, node))
continue;
- reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
+ reg = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
- reg = of_get_flat_dt_prop(node, "reg", &l);
+ reg = of_flat_dt_get_addr_size_prop(node, "reg", &l);
if (reg == NULL)
continue;
- endp = reg + (l / sizeof(__be32));
hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);
- pr_debug("memory scan node %s, reg size %d,\n",
+ pr_debug("memory scan node %s, reg {addr,size} entries %d,\n",
fdt_get_name(fdt, node, NULL), l);
- while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ for (i = 0; i < l; i++) {
u64 base, size;
- base = dt_mem_next_cell(dt_root_addr_cells, &reg);
- size = dt_mem_next_cell(dt_root_size_cells, &reg);
+ of_flat_dt_read_addr_size(reg, i, &base, &size);
if (size == 0)
continue;
@@ -1261,28 +1190,27 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
- void *ptr = memblock_alloc(size, align);
-
- if (!ptr)
- panic("%s: Failed to allocate %llu bytes align=0x%llx\n",
- __func__, size, align);
-
- return ptr;
+ return memblock_alloc_or_panic(size, align);
}
-bool __init early_init_dt_verify(void *params)
+bool __init early_init_dt_verify(void *dt_virt, phys_addr_t dt_phys)
{
- if (!params)
+ if (!dt_virt)
return false;
/* check device tree validity */
- if (fdt_check_header(params))
+ if (fdt_check_header(dt_virt))
return false;
/* Setup flat device-tree pointer */
- initial_boot_params = params;
+ initial_boot_params = dt_virt;
+ initial_boot_params_pa = dt_phys;
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params));
+
+ /* Initialize {size,address}-cells info */
+ early_init_dt_scan_root();
+
return true;
}
@@ -1291,9 +1219,6 @@ void __init early_init_dt_scan_nodes(void)
{
int rc;
- /* Initialize {size,address}-cells info */
- early_init_dt_scan_root();
-
/* Retrieve various information from the /chosen node */
rc = early_init_dt_scan_chosen(boot_command_line);
if (rc)
@@ -1304,13 +1229,16 @@ void __init early_init_dt_scan_nodes(void)
/* Handle linux,usable-memory-range property */
early_init_dt_check_for_usable_mem_range();
+
+ /* Handle kexec handover */
+ early_init_dt_check_kho();
}
-bool __init early_init_dt_scan(void *params)
+bool __init early_init_dt_scan(void *dt_virt, phys_addr_t dt_phys)
{
bool status;
- status = early_init_dt_verify(params);
+ status = early_init_dt_verify(dt_virt, dt_phys);
if (!status)
return false;
@@ -1318,6 +1246,21 @@ bool __init early_init_dt_scan(void *params)
return true;
}
+static void *__init copy_device_tree(void *fdt)
+{
+ int size;
+ void *dt;
+
+ size = fdt_totalsize(fdt);
+ dt = early_init_dt_alloc_memory_arch(size,
+ roundup_pow_of_two(FDT_V17_SIZE));
+
+ if (dt)
+ memcpy(dt, fdt, size);
+
+ return dt;
+}
+
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
@@ -1328,7 +1271,25 @@ bool __init early_init_dt_scan(void *params)
*/
void __init unflatten_device_tree(void)
{
- __unflatten_device_tree(initial_boot_params, NULL, &of_root,
+ void *fdt = initial_boot_params;
+
+ /* Save the statically-placed regions in the reserved_mem array */
+ fdt_scan_reserved_mem_reg_nodes();
+
+ /* Populate an empty root node when bootloader doesn't provide one */
+ if (!fdt) {
+ fdt = (void *) __dtb_empty_root_begin;
+ /* fdt_totalsize() will be used for copy size */
+ if (fdt_totalsize(fdt) >
+ __dtb_empty_root_end - __dtb_empty_root_begin) {
+ pr_err("invalid size in dtb_empty_root\n");
+ return;
+ }
+ of_fdt_crc32 = crc32_be(~0, fdt, fdt_totalsize(fdt));
+ fdt = copy_device_tree(fdt);
+ }
+
+ __unflatten_device_tree(fdt, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
@@ -1350,38 +1311,16 @@ void __init unflatten_device_tree(void)
*/
void __init unflatten_and_copy_device_tree(void)
{
- int size;
- void *dt;
-
- if (!initial_boot_params) {
- pr_warn("No valid device tree found, continuing without\n");
- return;
- }
-
- size = fdt_totalsize(initial_boot_params);
- dt = early_init_dt_alloc_memory_arch(size,
- roundup_pow_of_two(FDT_V17_SIZE));
+ if (initial_boot_params)
+ initial_boot_params = copy_device_tree(initial_boot_params);
- if (dt) {
- memcpy(dt, initial_boot_params, size);
- initial_boot_params = dt;
- }
unflatten_device_tree();
}
#ifdef CONFIG_SYSFS
-static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- memcpy(buf, initial_boot_params + off, count);
- return count;
-}
-
static int __init of_fdt_raw_init(void)
{
- static struct bin_attribute of_fdt_raw_attr =
- __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);
+ static __ro_after_init BIN_ATTR_SIMPLE_ADMIN_RO(fdt);
if (!initial_boot_params)
return 0;
@@ -1391,8 +1330,9 @@ static int __init of_fdt_raw_init(void)
pr_warn("not creating '/sys/firmware/fdt': CRC check failed\n");
return 0;
}
- of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
- return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
+ bin_attr_fdt.private = initial_boot_params;
+ bin_attr_fdt.size = fdt_totalsize(initial_boot_params);
+ return sysfs_create_bin_file(firmware_kobj, &bin_attr_fdt);
}
late_initcall(of_fdt_raw_init);
#endif
diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c
index 1dc15ab78b10..f358d2c80754 100644
--- a/drivers/of/fdt_address.c
+++ b/drivers/of/fdt_address.c
@@ -17,23 +17,10 @@
#include <linux/of_fdt.h>
#include <linux/sizes.h>
-/* Max address size we deal with */
-#define OF_MAX_ADDR_CELLS 4
-#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
- (ns) > 0)
-
-/* Debug utility */
-#ifdef DEBUG
-static void __init of_dump_addr(const char *s, const __be32 *addr, int na)
-{
- pr_debug("%s", s);
- while(na--)
- pr_cont(" %08x", *(addr++));
- pr_cont("\n");
-}
-#else
-static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { }
-#endif
+/* Uncomment me to enable of_dump_addr() debugging output */
+// #define DEBUG
+
+#include "of_private.h"
/* Callbacks for bus specific translators */
struct of_bus {
@@ -55,7 +42,7 @@ static void __init fdt_bus_default_count_cells(const void *blob, int parentoffse
if (prop)
*addrc = be32_to_cpup(prop);
else
- *addrc = dt_root_addr_cells;
+ *addrc = -1;
}
if (sizec) {
@@ -63,7 +50,7 @@ static void __init fdt_bus_default_count_cells(const void *blob, int parentoffse
if (prop)
*sizec = be32_to_cpup(prop);
else
- *sizec = dt_root_size_cells;
+ *sizec = -1;
}
}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 174900072c18..e3816819dbfe 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -16,6 +16,7 @@
#define pr_fmt(fmt) "OF: " fmt
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
@@ -25,6 +26,8 @@
#include <linux/string.h>
#include <linux/slab.h>
+#include "of_private.h"
+
/**
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
* @dev: Device node of the device whose interrupt is to be mapped
@@ -36,11 +39,15 @@
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
struct of_phandle_args oirq;
+ unsigned int ret;
if (of_irq_parse_one(dev, index, &oirq))
return 0;
- return irq_create_of_mapping(&oirq);
+ ret = irq_create_of_mapping(&oirq);
+ of_node_put(oirq.np);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
@@ -48,8 +55,8 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
* of_irq_find_parent - Given a device node, find its interrupt parent node
* @child: pointer to device node
*
- * Return: A pointer to the interrupt parent node, or NULL if the interrupt
- * parent could not be determined.
+ * Return: A pointer to the interrupt parent node with refcount increased
+ * or NULL if the interrupt parent could not be determined.
*/
struct device_node *of_irq_find_parent(struct device_node *child)
{
@@ -79,7 +86,8 @@ EXPORT_SYMBOL_GPL(of_irq_find_parent);
/*
* These interrupt controllers abuse interrupt-map for unspeakable
* reasons and rely on the core code to *ignore* it (the drivers do
- * their own parsing of the property).
+ * their own parsing of the property). The PAsemi entry covers a
+ * non-sensical interrupt-map that is better left ignored.
*
* If you think of adding to the list for something *new*, think
* again. There is a high chance that you will be sent back to the
@@ -93,31 +101,86 @@ static const char * const of_irq_imap_abusers[] = {
"fsl,ls1043a-extirq",
"fsl,ls1088a-extirq",
"renesas,rza1-irqc",
+ "pasemi,rootbus",
NULL,
};
+const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_phandle_args *out_irq)
+{
+ u32 intsize, addrsize;
+ struct device_node *np;
+
+ /* Get the interrupt parent */
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ np = of_node_get(of_irq_dflt_pic);
+ else
+ np = of_find_node_by_phandle(be32_to_cpup(imap));
+ imap++;
+ len--;
+
+ /* Check if not found */
+ if (!np) {
+ pr_debug(" -> imap parent not found !\n");
+ return NULL;
+ }
+
+ /* Get #interrupt-cells and #address-cells of new parent */
+ if (of_property_read_u32(np, "#interrupt-cells",
+ &intsize)) {
+ pr_debug(" -> parent lacks #interrupt-cells!\n");
+ of_node_put(np);
+ return NULL;
+ }
+ if (of_property_read_u32(np, "#address-cells",
+ &addrsize))
+ addrsize = 0;
+
+ pr_debug(" -> intsize=%d, addrsize=%d\n",
+ intsize, addrsize);
+
+ /* Check for malformed properties */
+ if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)
+ || (len < (addrsize + intsize))) {
+ of_node_put(np);
+ return NULL;
+ }
+
+ pr_debug(" -> imaplen=%d\n", len);
+
+ imap += addrsize + intsize;
+
+ out_irq->np = np;
+ for (int i = 0; i < intsize; i++)
+ out_irq->args[i] = be32_to_cpup(imap - intsize + i);
+ out_irq->args_count = intsize;
+
+ return imap;
+}
+
/**
* of_irq_parse_raw - Low level interrupt tree parsing
* @addr: address specifier (start of "reg" property of the device) in be32 format
* @out_irq: structure of_phandle_args updated by this function
*
* This function is a low-level interrupt tree walking function. It
- * can be used to do a partial walk with synthetized reg and interrupts
+ * can be used to do a partial walk with synthesized reg and interrupts
* properties, for example when resolving PCI interrupts when no device
* node exist for the parent. It takes an interrupt specifier structure as
* input, walks the tree looking for any interrupt-map properties, translates
* the specifier for each map, and then returns the translated map.
*
* Return: 0 on success and a negative number on error
+ *
+ * Note: refcount of node @out_irq->np is increased by 1 on success.
*/
int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
{
- struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
+ struct device_node *ipar, *tnode, *old = NULL;
__be32 initial_match_array[MAX_PHANDLE_ARGS];
const __be32 *match_array = initial_match_array;
- const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) };
- u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
- int imaplen, match, i, rc = -EINVAL;
+ const __be32 *tmp, dummy_imask[] = { [0 ... (MAX_PHANDLE_ARGS - 1)] = cpu_to_be32(~0) };
+ u32 intsize = 1, addrsize;
+ int i, rc = -EINVAL;
#ifdef DEBUG
of_print_phandle_args("of_irq_parse_raw: ", out_irq);
@@ -176,6 +239,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
/* Now start the actual "proper" walk of the interrupt tree */
while (ipar != NULL) {
+ int imaplen, match;
+ const __be32 *imap, *oldimap, *imask;
+ struct device_node *newpar;
/*
* Now check if cursor is an interrupt-controller and
* if it is then we are done, unless there is an
@@ -216,7 +282,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
/* Parse interrupt-map */
match = 0;
- while (imaplen > (addrsize + intsize + 1) && !match) {
+ while (imaplen > (addrsize + intsize + 1)) {
/* Compare specifiers */
match = 1;
for (i = 0; i < (addrsize + intsize); i++, imaplen--)
@@ -224,83 +290,45 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
- /* Get the interrupt parent */
- if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
- newpar = of_node_get(of_irq_dflt_pic);
- else
- newpar = of_find_node_by_phandle(be32_to_cpup(imap));
- imap++;
- --imaplen;
-
- /* Check if not found */
- if (newpar == NULL) {
- pr_debug(" -> imap parent not found !\n");
+ oldimap = imap;
+ imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq);
+ if (!imap)
goto fail;
- }
-
- if (!of_device_is_available(newpar))
- match = 0;
-
- /* Get #interrupt-cells and #address-cells of new
- * parent
- */
- if (of_property_read_u32(newpar, "#interrupt-cells",
- &newintsize)) {
- pr_debug(" -> parent lacks #interrupt-cells!\n");
- goto fail;
- }
- if (of_property_read_u32(newpar, "#address-cells",
- &newaddrsize))
- newaddrsize = 0;
- pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
- newintsize, newaddrsize);
-
- /* Check for malformed properties */
- if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS)
- || (imaplen < (newaddrsize + newintsize))) {
- rc = -EFAULT;
- goto fail;
- }
-
- imap += newaddrsize + newintsize;
- imaplen -= newaddrsize + newintsize;
+ match &= of_device_is_available(out_irq->np);
+ if (match)
+ break;
+ of_node_put(out_irq->np);
+ imaplen -= imap - oldimap;
pr_debug(" -> imaplen=%d\n", imaplen);
}
- if (!match) {
- if (intc) {
- /*
- * The PASEMI Nemo is a known offender, so
- * let's only warn for anyone else.
- */
- WARN(!IS_ENABLED(CONFIG_PPC_PASEMI),
- "%pOF interrupt-map failed, using interrupt-controller\n",
- ipar);
- return 0;
- }
-
+ if (!match)
goto fail;
- }
/*
* Successfully parsed an interrupt-map translation; copy new
* interrupt specifier into the out_irq structure
*/
- match_array = imap - newaddrsize - newintsize;
- for (i = 0; i < newintsize; i++)
- out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
- out_irq->args_count = intsize = newintsize;
- addrsize = newaddrsize;
+ match_array = oldimap + 1;
+
+ newpar = out_irq->np;
+ intsize = out_irq->args_count;
+ addrsize = (imap - match_array) - intsize;
if (ipar == newpar) {
+ /*
+ * We got @ipar's refcount, but the refcount was
+ * gotten again by of_irq_parse_imap_parent() via its
+ * alias @newpar.
+ */
+ of_node_put(ipar);
pr_debug("%pOF interrupt-map entry to self\n", ipar);
return 0;
}
skiplevel:
/* Iterate again with new parent */
- out_irq->np = newpar;
pr_debug(" -> new parent: %pOF\n", newpar);
of_node_put(ipar);
ipar = newpar;
@@ -310,7 +338,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
fail:
of_node_put(ipar);
- of_node_put(newpar);
return rc;
}
@@ -325,13 +352,16 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw);
* This function resolves an interrupt for a node by walking the interrupt tree,
* finding which interrupt controller node it is attached to, and returning the
* interrupt specifier that can be used to retrieve a Linux IRQ number.
+ *
+ * Note: refcount of node @out_irq->np is increased by 1 on success.
*/
int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{
- struct device_node *p;
+ struct device_node __free(device_node) *p = NULL;
const __be32 *addr;
u32 intsize;
- int i, res;
+ int i, res, addr_len;
+ __be32 addr_buf[3] = { 0 };
pr_debug("of_irq_parse_one: dev=%pOF, index=%d\n", device, index);
@@ -340,46 +370,45 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar
return of_irq_parse_oldworld(device, index, out_irq);
/* Get the reg property (if any) */
- addr = of_get_property(device, "reg", NULL);
+ addr_len = 0;
+ addr = of_get_property(device, "reg", &addr_len);
+
+ /* Prevent out-of-bounds read in case of longer interrupt parent address size */
+ if (addr_len > sizeof(addr_buf))
+ addr_len = sizeof(addr_buf);
+ if (addr)
+ memcpy(addr_buf, addr, addr_len);
/* Try the new-style interrupts-extended first */
res = of_parse_phandle_with_args(device, "interrupts-extended",
"#interrupt-cells", index, out_irq);
- if (!res)
- return of_irq_parse_raw(addr, out_irq);
-
- /* Look for the interrupt parent. */
- p = of_irq_find_parent(device);
- if (p == NULL)
- return -EINVAL;
-
- /* Get size of interrupt specifier */
- if (of_property_read_u32(p, "#interrupt-cells", &intsize)) {
- res = -EINVAL;
- goto out;
- }
-
- pr_debug(" parent=%pOF, intsize=%d\n", p, intsize);
+ if (!res) {
+ p = out_irq->np;
+ } else {
+ /* Look for the interrupt parent. */
+ p = of_irq_find_parent(device);
+ /* Get size of interrupt specifier */
+ if (!p || of_property_read_u32(p, "#interrupt-cells", &intsize))
+ return -EINVAL;
+
+ pr_debug(" parent=%pOF, intsize=%d\n", p, intsize);
+
+ /* Copy intspec into irq structure */
+ out_irq->np = p;
+ out_irq->args_count = intsize;
+ for (i = 0; i < intsize; i++) {
+ res = of_property_read_u32_index(device, "interrupts",
+ (index * intsize) + i,
+ out_irq->args + i);
+ if (res)
+ return res;
+ }
- /* Copy intspec into irq structure */
- out_irq->np = p;
- out_irq->args_count = intsize;
- for (i = 0; i < intsize; i++) {
- res = of_property_read_u32_index(device, "interrupts",
- (index * intsize) + i,
- out_irq->args + i);
- if (res)
- goto out;
+ pr_debug(" intspec=%d\n", *out_irq->args);
}
- pr_debug(" intspec=%d\n", *out_irq->args);
-
-
/* Check if there are any interrupt-map translations to process */
- res = of_irq_parse_raw(addr, out_irq);
- out:
- of_node_put(p);
- return res;
+ return of_irq_parse_raw(addr_buf, out_irq);
}
EXPORT_SYMBOL_GPL(of_irq_parse_one);
@@ -409,9 +438,8 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
of_property_read_string_index(dev, "interrupt-names", index,
&name);
- r->start = r->end = irq;
- r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
- r->name = name ? name : of_node_full_name(dev);
+ *r = DEFINE_RES_IRQ_NAMED(irq, name ?: of_node_full_name(dev));
+ r->flags |= irq_get_trigger_type(irq);
}
return irq;
@@ -451,6 +479,26 @@ out:
}
EXPORT_SYMBOL_GPL(of_irq_get);
+const struct cpumask *of_irq_get_affinity(struct device_node *dev, int index)
+{
+ struct of_phandle_args oirq;
+ struct irq_fwspec_info info;
+ struct irq_fwspec fwspec;
+ int rc;
+
+ rc = of_irq_parse_one(dev, index, &oirq);
+ if (rc)
+ return NULL;
+
+ of_phandle_args_to_fwspec(oirq.np, oirq.args, oirq.args_count,
+ &fwspec);
+
+ if (irq_populate_fwspec_info(&fwspec, &info))
+ return NULL;
+
+ return info.affinity;
+}
+
/**
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
@@ -484,11 +532,14 @@ int of_irq_count(struct device_node *dev)
struct of_phandle_args irq;
int nr = 0;
- while (of_irq_parse_one(dev, nr, &irq) == 0)
+ while (of_irq_parse_one(dev, nr, &irq) == 0) {
+ of_node_put(irq.np);
nr++;
+ }
return nr;
}
+EXPORT_SYMBOL_GPL(of_irq_count);
/**
* of_irq_to_resource_table - Fill in resource table with node's IRQ info
@@ -562,8 +613,10 @@ void __init of_irq_init(const struct of_device_id *matches)
* are the same distance away from the root irq controller.
*/
desc->interrupt_parent = of_parse_phandle(np, "interrupts-extended", 0);
- if (!desc->interrupt_parent)
+ if (!desc->interrupt_parent && of_property_present(np, "interrupts"))
desc->interrupt_parent = of_irq_find_parent(np);
+ else if (!desc->interrupt_parent)
+ desc->interrupt_parent = of_parse_phandle(np, "interrupt-parent", 0);
if (desc->interrupt_parent == np) {
of_node_put(desc->interrupt_parent);
desc->interrupt_parent = NULL;
@@ -602,6 +655,8 @@ void __init of_irq_init(const struct of_device_id *matches)
__func__, desc->dev, desc->dev,
desc->interrupt_parent);
of_node_clear_flag(desc->dev, OF_POPULATED);
+ of_node_put(desc->interrupt_parent);
+ of_node_put(desc->dev);
kfree(desc);
continue;
}
@@ -632,43 +687,75 @@ void __init of_irq_init(const struct of_device_id *matches)
err:
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
list_del(&desc->list);
+ of_node_put(desc->interrupt_parent);
of_node_put(desc->dev);
kfree(desc);
}
}
-static u32 __of_msi_map_id(struct device *dev, struct device_node **np,
- u32 id_in)
+static int of_check_msi_parent(struct device_node *dev_node, struct device_node **msi_node)
{
- struct device *parent_dev;
- u32 id_out = id_in;
+ struct of_phandle_args msi_spec;
+ int ret;
/*
- * Walk up the device parent links looking for one with a
- * "msi-map" property.
+ * An msi-parent phandle with a missing or == 0 #msi-cells
+ * property identifies a 1:1 ID translation mapping.
+ *
+ * Set the msi controller node if the firmware matches this
+ * condition.
*/
- for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
- if (!of_map_id(parent_dev->of_node, id_in, "msi-map",
- "msi-map-mask", np, &id_out))
- break;
- return id_out;
+ ret = of_parse_phandle_with_optional_args(dev_node, "msi-parent", "#msi-cells",
+ 0, &msi_spec);
+ if (ret)
+ return ret;
+
+ if ((*msi_node && *msi_node != msi_spec.np) || msi_spec.args_count != 0)
+ ret = -EINVAL;
+
+ if (!ret) {
+ /* Return with a node reference held */
+ *msi_node = msi_spec.np;
+ return 0;
+ }
+ of_node_put(msi_spec.np);
+
+ return ret;
}
/**
- * of_msi_map_id - Map a MSI ID for a device.
+ * of_msi_xlate - map a MSI ID and find relevant MSI controller node
* @dev: device for which the mapping is to be done.
- * @msi_np: device node of the expected msi controller.
- * @id_in: unmapped MSI ID for the device.
+ * @msi_np: Pointer to target MSI controller node
+ * @id_in: Device ID.
*
* Walk up the device hierarchy looking for devices with a "msi-map"
- * property. If found, apply the mapping to @id_in.
+ * or "msi-parent" property. If found, apply the mapping to @id_in.
+ * If @msi_np points to a non-NULL device node pointer, only entries targeting
+ * that node will be matched; if it points to a NULL value, it will receive the
+ * device node of the first matching target phandle, with a reference held.
*
- * Return: The mapped MSI ID.
+ * Returns: The mapped MSI id.
*/
-u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in)
+u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in)
{
- return __of_msi_map_id(dev, &msi_np, id_in);
+ struct device *parent_dev;
+ u32 id_out = id_in;
+
+ /*
+ * Walk up the device parent links looking for one with a
+ * "msi-map" or an "msi-parent" property.
+ */
+ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
+ if (!of_map_id(parent_dev->of_node, id_in, "msi-map",
+ "msi-map-mask", msi_np, &id_out))
+ break;
+ if (!of_check_msi_parent(parent_dev->of_node, msi_np))
+ break;
+ }
+ return id_out;
}
+EXPORT_SYMBOL_GPL(of_msi_xlate);
/**
* of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain
@@ -686,7 +773,7 @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id,
{
struct device_node *np = NULL;
- __of_msi_map_id(dev, &np, id);
+ of_msi_xlate(dev, &np, id);
return irq_find_matching_host(np, bus_token);
}
@@ -696,41 +783,23 @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id,
* @np: device node for @dev
* @token: bus type for this domain
*
- * Parse the msi-parent property (both the simple and the complex
- * versions), and returns the corresponding MSI domain.
+ * Parse the msi-parent property and returns the corresponding MSI domain.
*
* Returns: the MSI domain for this device (or NULL on failure).
*/
struct irq_domain *of_msi_get_domain(struct device *dev,
- struct device_node *np,
+ const struct device_node *np,
enum irq_domain_bus_token token)
{
- struct device_node *msi_np;
+ struct of_phandle_iterator it;
struct irq_domain *d;
+ int err;
- /* Check for a single msi-parent property */
- msi_np = of_parse_phandle(np, "msi-parent", 0);
- if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) {
- d = irq_find_matching_host(msi_np, token);
- if (!d)
- of_node_put(msi_np);
- return d;
- }
-
- if (token == DOMAIN_BUS_PLATFORM_MSI) {
- /* Check for the complex msi-parent version */
- struct of_phandle_args args;
- int index = 0;
-
- while (!of_parse_phandle_with_args(np, "msi-parent",
- "#msi-cells",
- index, &args)) {
- d = irq_find_matching_host(args.np, token);
- if (d)
- return d;
-
- of_node_put(args.np);
- index++;
+ of_for_each_phandle(&it, err, np, "msi-parent", "#msi-cells", 0) {
+ d = irq_find_matching_host(it.node, token);
+ if (d) {
+ of_node_put(it.node);
+ return d;
}
}
@@ -743,7 +812,7 @@ EXPORT_SYMBOL_GPL(of_msi_get_domain);
* @dev: device structure to associate with an MSI irq domain
* @np: device node for that device
*/
-void of_msi_configure(struct device *dev, struct device_node *np)
+void of_msi_configure(struct device *dev, const struct device_node *np)
{
dev_set_msi_domain(dev,
of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c
index f26d2ba8a371..1ee2d31816ae 100644
--- a/drivers/of/kexec.c
+++ b/drivers/of/kexec.c
@@ -184,7 +184,8 @@ int __init ima_free_kexec_buffer(void)
if (ret)
return ret;
- return memblock_phys_free(addr, size);
+ memblock_free_late(addr, size);
+ return 0;
}
#endif
@@ -263,6 +264,43 @@ static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
}
#endif /* CONFIG_IMA_KEXEC */
+static int kho_add_chosen(const struct kimage *image, void *fdt, int chosen_node)
+{
+ int ret = 0;
+#ifdef CONFIG_KEXEC_HANDOVER
+ phys_addr_t fdt_mem = 0;
+ phys_addr_t fdt_len = 0;
+ phys_addr_t scratch_mem = 0;
+ phys_addr_t scratch_len = 0;
+
+ ret = fdt_delprop(fdt, chosen_node, "linux,kho-fdt");
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+ ret = fdt_delprop(fdt, chosen_node, "linux,kho-scratch");
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ if (!image->kho.fdt || !image->kho.scratch)
+ return 0;
+
+ fdt_mem = image->kho.fdt;
+ fdt_len = PAGE_SIZE;
+ scratch_mem = image->kho.scratch->mem;
+ scratch_len = image->kho.scratch->bufsz;
+
+ pr_debug("Adding kho metadata to DT");
+
+ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, "linux,kho-fdt",
+ fdt_mem, fdt_len);
+ if (ret)
+ return ret;
+ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, "linux,kho-scratch",
+ scratch_mem, scratch_len);
+
+#endif /* CONFIG_KEXEC_HANDOVER */
+ return ret;
+}
+
/*
* of_kexec_alloc_and_setup_fdt - Alloc and setup a new Flattened Device Tree
*
@@ -300,7 +338,7 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
}
/* Remove memory reservation for the current device tree. */
- ret = fdt_find_and_del_mem_rsv(fdt, __pa(initial_boot_params),
+ ret = fdt_find_and_del_mem_rsv(fdt, initial_boot_params_pa,
fdt_totalsize(initial_boot_params));
if (ret == -EINVAL) {
pr_err("Error removing memory reservation.\n");
@@ -394,6 +432,7 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
if (ret)
goto out;
+#ifdef CONFIG_CRASH_DUMP
/* add linux,usable-memory-range */
ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
"linux,usable-memory-range", crashk_res.start,
@@ -409,8 +448,14 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
if (ret)
goto out;
}
+#endif
}
+ /* Add kho metadata if this is a KHO image */
+ ret = kho_add_chosen(image, fdt, chosen_node);
+ if (ret)
+ goto out;
+
/* add bootargs */
if (cmdline) {
ret = fdt_setprop_string(fdt, chosen_node, "bootargs", cmdline);
diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c
index 3dbce1e6f184..1bb61a2c3399 100644
--- a/drivers/of/kobj.c
+++ b/drivers/of/kobj.c
@@ -29,7 +29,7 @@ const struct kobj_type of_node_ktype = {
};
static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct property *pp = container_of(bin_attr, struct property, attr);
@@ -37,7 +37,7 @@ static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
}
/* always return newly allocated name, caller must free after use */
-static const char *safe_name(struct kobject *kobj, const char *orig_name)
+static const char *safe_name(const struct kobject *kobj, const char *orig_name)
{
const char *name = orig_name;
struct kernfs_node *kn;
@@ -84,7 +84,7 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp)
return rc;
}
-void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
+void __of_sysfs_remove_bin_file(struct device_node *np, const struct property *prop)
{
if (!IS_ENABLED(CONFIG_SYSFS))
return;
@@ -93,7 +93,7 @@ void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
kfree(prop->attr.attr.name);
}
-void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
+void __of_remove_property_sysfs(struct device_node *np, const struct property *prop)
{
/* at early boot, bail here and defer setup to of_init() */
if (of_kset && of_node_is_attached(np))
@@ -101,7 +101,7 @@ void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
}
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
- struct property *oldprop)
+ const struct property *oldprop)
{
/* At early boot, bail out and defer setup to of_init() */
if (!of_kset)
diff --git a/drivers/of/kunit_overlay_test.dtso b/drivers/of/kunit_overlay_test.dtso
new file mode 100644
index 000000000000..85f20b4b4c16
--- /dev/null
+++ b/drivers/of/kunit_overlay_test.dtso
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ kunit-test {
+ compatible = "test,empty";
+ };
+};
diff --git a/drivers/of/module.c b/drivers/of/module.c
index 0e8aa974f0f2..1e735fc130ad 100644
--- a/drivers/of/module.c
+++ b/drivers/of/module.c
@@ -16,22 +16,29 @@ ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
ssize_t csize;
ssize_t tsize;
+ /*
+ * Prevent a kernel oops in vsnprintf() -- it only allows passing a
+ * NULL ptr when the length is also 0. Also filter out the negative
+ * lengths...
+ */
+ if ((len > 0 && !str) || len < 0)
+ return -EINVAL;
+
/* Name & Type */
/* %p eats all alphanum characters, so %c must be used here */
csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
of_node_get_device_type(np));
tsize = csize;
+ if (csize >= len)
+ csize = len > 0 ? len - 1 : 0;
len -= csize;
- if (str)
- str += csize;
+ str += csize;
of_property_for_each_string(np, "compatible", p, compat) {
- csize = strlen(compat) + 1;
+ csize = snprintf(str, len, "C%s", compat);
tsize += csize;
- if (csize > len)
+ if (csize >= len)
continue;
-
- csize = snprintf(str, len, "C%s", compat);
for (c = str; c; ) {
c = strchr(c, ' ');
if (c)
diff --git a/drivers/of/of_kunit_helpers.c b/drivers/of/of_kunit_helpers.c
new file mode 100644
index 000000000000..f6ed1af8b62a
--- /dev/null
+++ b/drivers/of/of_kunit_helpers.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test managed DeviceTree APIs
+ */
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <kunit/of.h>
+#include <kunit/test.h>
+#include <kunit/resource.h>
+
+#include "of_private.h"
+
+/**
+ * of_root_kunit_skip() - Skip test if the root node isn't populated
+ * @test: test to skip if the root node isn't populated
+ */
+void of_root_kunit_skip(struct kunit *test)
+{
+ if ((IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_RISCV)) &&
+ IS_ENABLED(CONFIG_ACPI) && !of_root)
+ kunit_skip(test, "arm64/riscv+acpi doesn't populate a root node");
+}
+EXPORT_SYMBOL_GPL(of_root_kunit_skip);
+
+#if defined(CONFIG_OF_OVERLAY) && defined(CONFIG_OF_EARLY_FLATTREE)
+
+static void of_overlay_fdt_apply_kunit_exit(void *ovcs_id)
+{
+ of_overlay_remove(ovcs_id);
+}
+
+/**
+ * of_overlay_fdt_apply_kunit() - Test managed of_overlay_fdt_apply()
+ * @test: test context
+ * @overlay_fdt: device tree overlay to apply
+ * @overlay_fdt_size: size in bytes of @overlay_fdt
+ * @ovcs_id: identifier of overlay, used to remove the overlay
+ *
+ * Just like of_overlay_fdt_apply(), except the overlay is managed by the test
+ * case and is automatically removed with of_overlay_remove() after the test
+ * case concludes.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int of_overlay_fdt_apply_kunit(struct kunit *test, void *overlay_fdt,
+ u32 overlay_fdt_size, int *ovcs_id)
+{
+ int ret;
+ int *copy_id;
+
+ of_root_kunit_skip(test);
+
+ copy_id = kunit_kmalloc(test, sizeof(*copy_id), GFP_KERNEL);
+ if (!copy_id)
+ return -ENOMEM;
+
+ ret = of_overlay_fdt_apply(overlay_fdt, overlay_fdt_size,
+ ovcs_id, NULL);
+ if (ret)
+ return ret;
+
+ *copy_id = *ovcs_id;
+
+ return kunit_add_action_or_reset(test, of_overlay_fdt_apply_kunit_exit,
+ copy_id);
+}
+EXPORT_SYMBOL_GPL(of_overlay_fdt_apply_kunit);
+
+#endif
+
+KUNIT_DEFINE_ACTION_WRAPPER(of_node_put_wrapper, of_node_put, struct device_node *);
+
+/**
+ * of_node_put_kunit() - Test managed of_node_put()
+ * @test: test context
+ * @node: node to pass to `of_node_put()`
+ *
+ * Just like of_node_put(), except the node is managed by the test case and is
+ * automatically put with of_node_put() after the test case concludes.
+ */
+void of_node_put_kunit(struct kunit *test, struct device_node *node)
+{
+ if (kunit_add_action(test, of_node_put_wrapper, node)) {
+ KUNIT_FAIL(test,
+ "Can't allocate a kunit resource to put of_node\n");
+ }
+}
+EXPORT_SYMBOL_GPL(of_node_put_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Test managed DeviceTree APIs");
diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c
index 5949829a1b00..cd2dc8e825c9 100644
--- a/drivers/of/of_numa.c
+++ b/drivers/of/of_numa.c
@@ -10,12 +10,10 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/nodemask.h>
+#include <linux/numa_memblks.h>
#include <asm/numa.h>
-/* define default numa node to 0 */
-#define DEFAULT_NODE 0
-
/*
* Even though we connect cpus to numa domains later in SMP
* init, we need to know the node ids now for all cpus.
@@ -44,7 +42,7 @@ static int __init of_numa_parse_memory_nodes(void)
struct device_node *np = NULL;
struct resource rsrc;
u32 nid;
- int i, r;
+ int i, r = -EINVAL;
for_each_node_by_type(np, "memory") {
r = of_property_read_u32(np, "numa-node-id", &nid);
@@ -61,8 +59,11 @@ static int __init of_numa_parse_memory_nodes(void)
r = -EINVAL;
}
- for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++)
+ for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++) {
r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
+ if (!r)
+ node_set(nid, numa_nodes_parsed);
+ }
if (!i || r) {
of_node_put(np);
@@ -71,7 +72,7 @@ static int __init of_numa_parse_memory_nodes(void)
}
}
- return 0;
+ return r;
}
static int __init of_numa_parse_distance_map_v1(struct device_node *map)
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index b57f1014e419..df0bb00349e0 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -9,6 +9,7 @@
*/
#define FDT_ALIGN_SIZE 8
+#define MAX_RESERVED_REGIONS 64
/**
* struct alias_prop - Alias property in 'aliases' node
@@ -42,6 +43,9 @@ extern raw_spinlock_t devtree_lock;
extern struct list_head aliases_lookup;
extern struct kset *of_kset;
+struct kunit;
+extern void of_root_kunit_skip(struct kunit *test);
+
#if defined(CONFIG_OF_DYNAMIC)
extern int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop);
@@ -60,12 +64,18 @@ static inline int of_property_notify(int action, struct device_node *np,
}
#endif /* CONFIG_OF_DYNAMIC */
+#if defined(CONFIG_OF_DYNAMIC) && defined(CONFIG_OF_ADDRESS)
+void of_platform_register_reconfig_notifier(void);
+#else
+static inline void of_platform_register_reconfig_notifier(void) { }
+#endif
+
#if defined(CONFIG_OF_KOBJ)
int of_node_is_attached(const struct device_node *node);
int __of_add_property_sysfs(struct device_node *np, struct property *pp);
-void __of_remove_property_sysfs(struct device_node *np, struct property *prop);
+void __of_remove_property_sysfs(struct device_node *np, const struct property *prop);
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
- struct property *oldprop);
+ const struct property *oldprop);
int __of_attach_node_sysfs(struct device_node *np);
void __of_detach_node_sysfs(struct device_node *np);
#else
@@ -73,9 +83,9 @@ static inline int __of_add_property_sysfs(struct device_node *np, struct propert
{
return 0;
}
-static inline void __of_remove_property_sysfs(struct device_node *np, struct property *prop) {}
+static inline void __of_remove_property_sysfs(struct device_node *np, const struct property *prop) {}
static inline void __of_update_property_sysfs(struct device_node *np,
- struct property *newprop, struct property *oldprop) {}
+ struct property *newprop, const struct property *oldprop) {}
static inline int __of_attach_node_sysfs(struct device_node *np)
{
return 0;
@@ -109,6 +119,8 @@ extern void *__unflatten_device_tree(const void *blob,
void *(*dt_alloc)(u64 size, u64 align),
bool detached);
+void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align));
+
/**
* General utilities for working with live trees.
*
@@ -117,10 +129,11 @@ extern void *__unflatten_device_tree(const void *blob,
* own the devtree lock or work on detached trees only.
*/
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
+void __of_prop_free(struct property *prop);
struct device_node *__of_node_dup(const struct device_node *np,
const char *full_name);
-struct device_node *__of_find_node_by_path(struct device_node *parent,
+struct device_node *__of_find_node_by_path(const struct device_node *parent,
const char *path);
struct device_node *__of_find_node_by_full_path(struct device_node *node,
const char *path);
@@ -135,7 +148,7 @@ extern int __of_update_property(struct device_node *np,
extern void __of_detach_node(struct device_node *np);
extern void __of_sysfs_remove_bin_file(struct device_node *np,
- struct property *prop);
+ const struct property *prop);
/* illegal phandle value (set when unresolved) */
#define OF_PHANDLE_ILLEGAL 0xdeadbeef
@@ -152,6 +165,9 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np,
extern int of_bus_n_addr_cells(struct device_node *np);
extern int of_bus_n_size_cells(struct device_node *np);
+const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len,
+ struct of_phandle_args *out_irq);
+
struct bus_dma_region;
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA)
int of_dma_get_range(struct device_node *np,
@@ -169,8 +185,38 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
}
#endif
-void fdt_init_reserved_mem(void);
-void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
- phys_addr_t base, phys_addr_t size);
+int fdt_scan_reserved_mem(void);
+void __init fdt_scan_reserved_mem_reg_nodes(void);
+
+bool of_fdt_device_is_available(const void *blob, unsigned long node);
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
+#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void __maybe_unused of_dump_addr(const char *s, const __be32 *addr, int na)
+{
+ pr_debug("%s", s);
+ while (na--)
+ pr_cont(" %08x", be32_to_cpu(*(addr++)));
+ pr_cont("\n");
+}
+#else
+static void __maybe_unused of_dump_addr(const char *s, const __be32 *addr, int na) { }
+#endif
+
+static inline bool is_pseudo_property(const char *prop_name)
+{
+ return !of_prop_cmp(prop_name, "name") ||
+ !of_prop_cmp(prop_name, "phandle") ||
+ !of_prop_cmp(prop_name, "linux,phandle");
+}
+
+#if IS_ENABLED(CONFIG_KUNIT)
+int __of_address_resource_bounds(struct resource *r, u64 start, u64 size);
+#endif
#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 7ec94cfcbddb..5619ec917858 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -12,6 +12,8 @@
#define pr_fmt(fmt) "OF: reserved mem: " fmt
#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/libfdt.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
@@ -23,11 +25,13 @@
#include <linux/memblock.h>
#include <linux/kmemleak.h>
#include <linux/cma.h>
+#include <linux/dma-map-ops.h>
#include "of_private.h"
-#define MAX_RESERVED_REGIONS 64
-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata;
+static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
+static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
static int reserved_mem_count;
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -50,20 +54,66 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
memblock_phys_free(base, size);
}
- kmemleak_ignore_phys(base);
+ if (!err)
+ kmemleak_ignore_phys(base);
return err;
}
/*
+ * alloc_reserved_mem_array() - allocate memory for the reserved_mem
+ * array using memblock
+ *
+ * This function is used to allocate memory for the reserved_mem
+ * array according to the total number of reserved memory regions
+ * defined in the DT.
+ * After the new array is allocated, the information stored in
+ * the initial static array is copied over to this new array and
+ * the new array is used from this point on.
+ */
+static void __init alloc_reserved_mem_array(void)
+{
+ struct reserved_mem *new_array;
+ size_t alloc_size, copy_size, memset_size;
+
+ alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
+ if (alloc_size == SIZE_MAX) {
+ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW);
+ return;
+ }
+
+ new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
+ if (!new_array) {
+ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM);
+ return;
+ }
+
+ copy_size = array_size(reserved_mem_count, sizeof(*new_array));
+ if (copy_size == SIZE_MAX) {
+ memblock_free(new_array, alloc_size);
+ total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW);
+ return;
+ }
+
+ memset_size = alloc_size - copy_size;
+
+ memcpy(new_array, reserved_mem, copy_size);
+ memset(new_array + reserved_mem_count, 0, memset_size);
+
+ reserved_mem = new_array;
+}
+
+static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem);
+/*
* fdt_reserved_mem_save_node() - save fdt node for second pass initialization
*/
-void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
- phys_addr_t base, phys_addr_t size)
+static void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
+ phys_addr_t base, phys_addr_t size)
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
+ if (reserved_mem_count == total_reserved_mem_cnt) {
pr_err("not enough space for all defined regions.\n");
return;
}
@@ -73,10 +123,211 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
rmem->base = base;
rmem->size = size;
+ /* Call the region specific initialization function */
+ fdt_init_reserved_mem_node(rmem);
+
reserved_mem_count++;
return;
}
+static int __init early_init_dt_reserve_memory(phys_addr_t base,
+ phys_addr_t size, bool nomap)
+{
+ if (nomap) {
+ /*
+ * If the memory is already reserved (by another region), we
+ * should not allow it to be marked nomap, but don't worry
+ * if the region isn't memory as it won't be mapped.
+ */
+ if (memblock_overlaps_region(&memblock.memory, base, size) &&
+ memblock_is_region_reserved(base, size))
+ return -EBUSY;
+
+ return memblock_mark_nomap(base, size);
+ }
+ return memblock_reserve(base, size);
+}
+
+/*
+ * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property
+ */
+static int __init __reserved_mem_reserve_reg(unsigned long node,
+ const char *uname)
+{
+ phys_addr_t base, size;
+ int i, len;
+ const __be32 *prop;
+ bool nomap;
+
+ prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
+ if (!prop)
+ return -ENOENT;
+
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+
+ for (i = 0; i < len; i++) {
+ u64 b, s;
+
+ of_flat_dt_read_addr_size(prop, i, &b, &s);
+
+ base = b;
+ size = s;
+
+ if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
+ /* Architecture specific contiguous memory fixup. */
+ if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
+ of_get_flat_dt_prop(node, "reusable", NULL))
+ dma_contiguous_early_fixup(base, size);
+ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
+ uname, &base, (unsigned long)(size / SZ_1M));
+ } else {
+ pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
+ uname, &base, (unsigned long)(size / SZ_1M));
+ }
+ }
+ return 0;
+}
+
+/*
+ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
+ * in /reserved-memory matches the values supported by the current implementation,
+ * also check if ranges property has been provided
+ */
+static int __init __reserved_mem_check_root(unsigned long node)
+{
+ const __be32 *prop;
+
+ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "ranges", NULL);
+ if (!prop)
+ return -EINVAL;
+ return 0;
+}
+
+static void __init __rmem_check_for_overlap(void);
+
+/**
+ * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined
+ * reserved memory regions.
+ *
+ * This function is used to scan through the DT and store the
+ * information for the reserved memory regions that are defined using
+ * the "reg" property. The region node number, name, base address, and
+ * size are all stored in the reserved_mem array by calling the
+ * fdt_reserved_mem_save_node() function.
+ */
+void __init fdt_scan_reserved_mem_reg_nodes(void)
+{
+ const void *fdt = initial_boot_params;
+ phys_addr_t base, size;
+ int node, child;
+
+ if (!fdt)
+ return;
+
+ node = fdt_path_offset(fdt, "/reserved-memory");
+ if (node < 0) {
+ pr_info("Reserved memory: No reserved-memory node in the DT\n");
+ return;
+ }
+
+ /* Attempt dynamic allocation of a new reserved_mem array */
+ alloc_reserved_mem_array();
+
+ if (__reserved_mem_check_root(node)) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ return;
+ }
+
+ fdt_for_each_subnode(child, fdt, node) {
+ const char *uname;
+ u64 b, s;
+
+ if (!of_fdt_device_is_available(fdt, child))
+ continue;
+
+ if (!of_flat_dt_get_addr_size(child, "reg", &b, &s))
+ continue;
+
+ base = b;
+ size = s;
+
+ if (size) {
+ uname = fdt_get_name(fdt, child, NULL);
+ fdt_reserved_mem_save_node(child, uname, base, size);
+ }
+ }
+
+ /* check for overlapping reserved regions */
+ __rmem_check_for_overlap();
+}
+
+static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname);
+
+/*
+ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
+ */
+int __init fdt_scan_reserved_mem(void)
+{
+ int node, child;
+ int dynamic_nodes_cnt = 0, count = 0;
+ int dynamic_nodes[MAX_RESERVED_REGIONS];
+ const void *fdt = initial_boot_params;
+
+ node = fdt_path_offset(fdt, "/reserved-memory");
+ if (node < 0)
+ return -ENODEV;
+
+ if (__reserved_mem_check_root(node) != 0) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ return -EINVAL;
+ }
+
+ fdt_for_each_subnode(child, fdt, node) {
+ const char *uname;
+ int err;
+
+ if (!of_fdt_device_is_available(fdt, child))
+ continue;
+
+ uname = fdt_get_name(fdt, child, NULL);
+
+ err = __reserved_mem_reserve_reg(child, uname);
+ if (!err)
+ count++;
+ /*
+ * Save the nodes for the dynamically-placed regions
+ * into an array which will be used for allocation right
+ * after all the statically-placed regions are reserved
+ * or marked as no-map. This is done to avoid dynamically
+ * allocating from one of the statically-placed regions.
+ */
+ if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
+ dynamic_nodes[dynamic_nodes_cnt] = child;
+ dynamic_nodes_cnt++;
+ }
+ }
+ for (int i = 0; i < dynamic_nodes_cnt; i++) {
+ const char *uname;
+ int err;
+
+ child = dynamic_nodes[i];
+ uname = fdt_get_name(fdt, child, NULL);
+ err = __reserved_mem_alloc_size(child, uname);
+ if (!err)
+ count++;
+ }
+ total_reserved_mem_cnt = count;
+ return 0;
+}
+
/*
* __reserved_mem_alloc_in_range() - allocate reserved memory described with
* 'alloc-ranges'. Choose bottom-up/top-down depending on nearby existing
@@ -132,13 +383,11 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
* __reserved_mem_alloc_size() - allocate reserved memory described by
* 'size', 'alignment' and 'alloc-ranges' properties.
*/
-static int __init __reserved_mem_alloc_size(unsigned long node,
- const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
+static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t start = 0, end = 0;
phys_addr_t base = 0, align = 0, size;
- int len;
+ int i, len;
const __be32 *prop;
bool nomap;
int ret;
@@ -172,22 +421,17 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
&& !nomap)
align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES);
- prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
+ prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len);
if (prop) {
+ for (i = 0; i < len; i++) {
+ u64 b, s;
- if (len % t_len != 0) {
- pr_err("invalid alloc-ranges property in '%s', skipping node.\n",
- uname);
- return -EINVAL;
- }
-
- base = 0;
+ of_flat_dt_read_addr_size(prop, i, &b, &s);
- while (len > 0) {
- start = dt_mem_next_cell(dt_root_addr_cells, &prop);
- end = start + dt_mem_next_cell(dt_root_size_cells,
- &prop);
+ start = b;
+ end = b + s;
+ base = 0;
ret = __reserved_mem_alloc_in_range(size, align,
start, end, nomap, &base);
if (ret == 0) {
@@ -196,9 +440,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
(unsigned long)(size / SZ_1M));
break;
}
- len -= t_len;
}
-
} else {
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
0, 0, nomap, &base);
@@ -212,10 +454,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
-
- *res_base = base;
- *res_size = size;
-
+ /* Architecture specific contiguous memory fixup. */
+ if (of_flat_dt_is_compatible(node, "shared-dma-pool") &&
+ of_get_flat_dt_prop(node, "reusable", NULL))
+ dma_contiguous_early_fixup(base, size);
+ /* Save region in the reserved_mem array */
+ fdt_reserved_mem_save_node(node, uname, base, size);
return 0;
}
@@ -304,71 +548,40 @@ static void __init __rmem_check_for_overlap(void)
}
/**
- * fdt_init_reserved_mem() - allocate and init all saved reserved memory regions
+ * fdt_init_reserved_mem_node() - Initialize a reserved memory region
+ * @rmem: reserved_mem struct of the memory region to be initialized.
+ *
+ * This function is used to call the region specific initialization
+ * function for a reserved memory region.
*/
-void __init fdt_init_reserved_mem(void)
+static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem)
{
- int i;
-
- /* check for overlapping reserved regions */
- __rmem_check_for_overlap();
+ unsigned long node = rmem->fdt_node;
+ int err = 0;
+ bool nomap;
- for (i = 0; i < reserved_mem_count; i++) {
- struct reserved_mem *rmem = &reserved_mem[i];
- unsigned long node = rmem->fdt_node;
- int len;
- const __be32 *prop;
- int err = 0;
- bool nomap;
-
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
- prop = of_get_flat_dt_prop(node, "phandle", &len);
- if (!prop)
- prop = of_get_flat_dt_prop(node, "linux,phandle", &len);
- if (prop)
- rmem->phandle = of_read_number(prop, len/4);
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
- if (rmem->size == 0)
- err = __reserved_mem_alloc_size(node, rmem->name,
- &rmem->base, &rmem->size);
- if (err == 0) {
- err = __reserved_mem_init_node(rmem);
- if (err != 0 && err != -ENOENT) {
- pr_info("node %s compatible matching fail\n",
- rmem->name);
- if (nomap)
- memblock_clear_nomap(rmem->base, rmem->size);
- else
- memblock_phys_free(rmem->base,
- rmem->size);
- } else {
- phys_addr_t end = rmem->base + rmem->size - 1;
- bool reusable =
- (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
-
- pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
- &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
- nomap ? "nomap" : "map",
- reusable ? "reusable" : "non-reusable",
- rmem->name ? rmem->name : "unknown");
- }
- }
+ err = __reserved_mem_init_node(rmem);
+ if (err != 0 && err != -ENOENT) {
+ pr_info("node %s compatible matching fail\n", rmem->name);
+ if (nomap)
+ memblock_clear_nomap(rmem->base, rmem->size);
+ else
+ memblock_phys_free(rmem->base, rmem->size);
+ } else {
+ phys_addr_t end = rmem->base + rmem->size - 1;
+ bool reusable =
+ (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
+
+ pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
+ &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
+ nomap ? "nomap" : "map",
+ reusable ? "reusable" : "non-reusable",
+ rmem->name ? rmem->name : "unknown");
}
}
-static inline struct reserved_mem *__find_rmem(struct device_node *node)
-{
- unsigned int i;
-
- if (!node->phandle)
- return NULL;
-
- for (i = 0; i < reserved_mem_count; i++)
- if (reserved_mem[i].phandle == node->phandle)
- return &reserved_mem[i];
- return NULL;
-}
-
struct rmem_assigned_device {
struct device *dev;
struct reserved_mem *rmem;
@@ -413,7 +626,7 @@ int of_reserved_mem_device_init_by_idx(struct device *dev,
return 0;
}
- rmem = __find_rmem(target);
+ rmem = of_reserved_mem_lookup(target);
of_node_put(target);
if (!rmem || !rmem->ops || !rmem->ops->device_init)
@@ -513,3 +726,83 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
return NULL;
}
EXPORT_SYMBOL_GPL(of_reserved_mem_lookup);
+
+/**
+ * of_reserved_mem_region_to_resource() - Get a reserved memory region as a resource
+ * @np: node containing 'memory-region' property
+ * @idx: index of 'memory-region' property to lookup
+ * @res: Pointer to a struct resource to fill in with reserved region
+ *
+ * This function allows drivers to lookup a node's 'memory-region' property
+ * entries by index and return a struct resource for the entry.
+ *
+ * Returns 0 on success with @res filled in. Returns -ENODEV if 'memory-region'
+ * is missing or unavailable, -EINVAL for any other error.
+ */
+int of_reserved_mem_region_to_resource(const struct device_node *np,
+ unsigned int idx, struct resource *res)
+{
+ struct reserved_mem *rmem;
+
+ if (!np)
+ return -EINVAL;
+
+ struct device_node __free(device_node) *target = of_parse_phandle(np, "memory-region", idx);
+ if (!target || !of_device_is_available(target))
+ return -ENODEV;
+
+ rmem = of_reserved_mem_lookup(target);
+ if (!rmem)
+ return -EINVAL;
+
+ resource_set_range(res, rmem->base, rmem->size);
+ res->flags = IORESOURCE_MEM;
+ res->name = rmem->name;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource);
+
+/**
+ * of_reserved_mem_region_to_resource_byname() - Get a reserved memory region as a resource
+ * @np: node containing 'memory-region' property
+ * @name: name of 'memory-region' property entry to lookup
+ * @res: Pointer to a struct resource to fill in with reserved region
+ *
+ * This function allows drivers to lookup a node's 'memory-region' property
+ * entries by name and return a struct resource for the entry.
+ *
+ * Returns 0 on success with @res filled in, or a negative error-code on
+ * failure.
+ */
+int of_reserved_mem_region_to_resource_byname(const struct device_node *np,
+ const char *name,
+ struct resource *res)
+{
+ int idx;
+
+ if (!name)
+ return -EINVAL;
+
+ idx = of_property_match_string(np, "memory-region-names", name);
+ if (idx < 0)
+ return idx;
+
+ return of_reserved_mem_region_to_resource(np, idx, res);
+}
+EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource_byname);
+
+/**
+ * of_reserved_mem_region_count() - Return the number of 'memory-region' entries
+ * @np: node containing 'memory-region' property
+ *
+ * This function allows drivers to retrieve the number of entries for a node's
+ * 'memory-region' property.
+ *
+ * Returns the number of entries on success, or negative error code on a
+ * malformed property.
+ */
+int of_reserved_mem_region_count(const struct device_node *np)
+{
+ return of_count_phandle_with_args(np, "memory-region", NULL);
+}
+EXPORT_SYMBOL_GPL(of_reserved_mem_region_count);
diff --git a/drivers/of/of_test.c b/drivers/of/of_test.c
new file mode 100644
index 000000000000..8bba5a72c9c7
--- /dev/null
+++ b/drivers/of/of_test.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for OF APIs
+ */
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <kunit/test.h>
+
+#include "of_private.h"
+
+/*
+ * Test that the root node "/" can be found by path.
+ */
+static void of_dtb_root_node_found_by_path(struct kunit *test)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_path("/");
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, np);
+ of_node_put(np);
+}
+
+/*
+ * Test that the 'of_root' global variable is always populated when DT code is
+ * enabled. Remove this test once of_root is removed from global access.
+ */
+static void of_dtb_root_node_populates_of_root(struct kunit *test)
+{
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, of_root);
+}
+
+static struct kunit_case of_dtb_test_cases[] = {
+ KUNIT_CASE(of_dtb_root_node_found_by_path),
+ KUNIT_CASE(of_dtb_root_node_populates_of_root),
+ {}
+};
+
+static int of_dtb_test_init(struct kunit *test)
+{
+ of_root_kunit_skip(test);
+ if (!IS_ENABLED(CONFIG_OF_EARLY_FLATTREE))
+ kunit_skip(test, "requires CONFIG_OF_EARLY_FLATTREE");
+
+ return 0;
+}
+
+/*
+ * Test suite to confirm a DTB is loaded.
+ */
+static struct kunit_suite of_dtb_suite = {
+ .name = "of_dtb",
+ .test_cases = of_dtb_test_cases,
+ .init = of_dtb_test_init,
+};
+
+struct of_address_resource_bounds_case {
+ u64 start;
+ u64 size;
+ int ret;
+
+ u64 res_start;
+ u64 res_end;
+};
+
+static void of_address_resource_bounds_case_desc(const struct of_address_resource_bounds_case *p,
+ char *name)
+{
+ snprintf(name, KUNIT_PARAM_DESC_SIZE, "start=0x%016llx,size=0x%016llx", p->start, p->size);
+}
+
+static const struct of_address_resource_bounds_case of_address_resource_bounds_cases[] = {
+ {
+ .start = 0,
+ .size = 0,
+ .ret = 0,
+ .res_start = 0,
+ .res_end = -1,
+ },
+ {
+ .start = 0,
+ .size = 0x1000,
+ .ret = 0,
+ .res_start = 0,
+ .res_end = 0xfff,
+ },
+ {
+ .start = 0x1000,
+ .size = 0,
+ .ret = 0,
+ .res_start = 0x1000,
+ .res_end = 0xfff,
+ },
+ {
+ .start = 0x1000,
+ .size = 0x1000,
+ .ret = 0,
+ .res_start = 0x1000,
+ .res_end = 0x1fff,
+ },
+ {
+ .start = 1,
+ .size = RESOURCE_SIZE_MAX,
+ .ret = 0,
+ .res_start = 1,
+ .res_end = RESOURCE_SIZE_MAX,
+ },
+ {
+ .start = RESOURCE_SIZE_MAX,
+ .size = 1,
+ .ret = 0,
+ .res_start = RESOURCE_SIZE_MAX,
+ .res_end = RESOURCE_SIZE_MAX,
+ },
+ {
+ .start = 2,
+ .size = RESOURCE_SIZE_MAX,
+ .ret = -EOVERFLOW,
+ },
+ {
+ .start = RESOURCE_SIZE_MAX,
+ .size = 2,
+ .ret = -EOVERFLOW,
+ },
+ {
+ .start = ULL(0x100000000),
+ .size = 1,
+ .ret = sizeof(resource_size_t) > sizeof(u32) ? 0 : -EOVERFLOW,
+ .res_start = ULL(0x100000000),
+ .res_end = ULL(0x100000000),
+ },
+ {
+ .start = 0x1000,
+ .size = 0xffffffff,
+ .ret = sizeof(resource_size_t) > sizeof(u32) ? 0 : -EOVERFLOW,
+ .res_start = 0x1000,
+ .res_end = ULL(0x100000ffe),
+ },
+};
+
+KUNIT_ARRAY_PARAM(of_address_resource_bounds,
+ of_address_resource_bounds_cases, of_address_resource_bounds_case_desc);
+
+static void of_address_resource_bounds(struct kunit *test)
+{
+ const struct of_address_resource_bounds_case *param = test->param_value;
+ struct resource r; /* Intentionally uninitialized */
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_OF_ADDRESS))
+ kunit_skip(test, "CONFIG_OF_ADDRESS not enabled\n");
+
+ ret = __of_address_resource_bounds(&r, param->start, param->size);
+ KUNIT_EXPECT_EQ(test, param->ret, ret);
+ if (ret == 0) {
+ KUNIT_EXPECT_EQ(test, (resource_size_t)param->res_start, r.start);
+ KUNIT_EXPECT_EQ(test, (resource_size_t)param->res_end, r.end);
+ KUNIT_EXPECT_EQ(test, param->size, resource_size(&r));
+ }
+}
+
+static struct kunit_case of_address_test_cases[] = {
+ KUNIT_CASE_PARAM(of_address_resource_bounds, of_address_resource_bounds_gen_params),
+ {}
+};
+
+static struct kunit_suite of_address_suite = {
+ .name = "of_address",
+ .test_cases = of_address_test_cases,
+};
+
+kunit_test_suites(
+ &of_dtb_suite, &of_address_suite,
+);
+MODULE_DESCRIPTION("KUnit tests for OF APIs");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_LICENSE("GPL");
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 7feb643f1370..5b4f42230e6c 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -45,8 +45,8 @@ struct target {
/**
* struct fragment - info about fragment nodes in overlay expanded device tree
- * @target: target of the overlay operation
* @overlay: pointer to the __overlay__ node
+ * @target: target of the overlay operation
*/
struct fragment {
struct device_node *overlay;
@@ -84,6 +84,12 @@ static int devicetree_state_flags;
#define DTSF_APPLY_FAIL 0x01
#define DTSF_REVERT_FAIL 0x02
+static int of_prop_val_eq(const struct property *p1, const struct property *p2)
+{
+ return p1->length == p2->length &&
+ !memcmp(p1->value, p2->value, (size_t)p1->length);
+}
+
/*
* If a changeset apply or revert encounters an error, an attempt will
* be made to undo partial changes, but may fail. If the undo fails
@@ -129,7 +135,7 @@ static BLOCKING_NOTIFIER_HEAD(overlay_notify_chain);
* @nb: Notifier block to register
*
* Register for notification on overlay operations on device tree nodes. The
- * reported actions definied by @of_reconfig_change. The notifier callback
+ * reported actions defined by @of_reconfig_change. The notifier callback
* furthermore receives a pointer to the affected device tree node.
*
* Note that a notifier callback is not supposed to store pointers to a device
@@ -262,9 +268,7 @@ static struct property *dup_and_fixup_symbol_prop(
return new_prop;
err_free_new_prop:
- kfree(new_prop->name);
- kfree(new_prop->value);
- kfree(new_prop);
+ __of_prop_free(new_prop);
err_free_target_path:
kfree(target_path);
@@ -298,16 +302,15 @@ err_free_target_path:
* invalid @overlay.
*/
static int add_changeset_property(struct overlay_changeset *ovcs,
- struct target *target, struct property *overlay_prop,
+ struct target *target, const struct property *overlay_prop,
bool is_symbols_prop)
{
- struct property *new_prop = NULL, *prop;
+ struct property *new_prop = NULL;
+ const struct property *prop;
int ret = 0;
if (target->in_livetree)
- if (!of_prop_cmp(overlay_prop->name, "name") ||
- !of_prop_cmp(overlay_prop->name, "phandle") ||
- !of_prop_cmp(overlay_prop->name, "linux,phandle"))
+ if (is_pseudo_property(overlay_prop->name))
return 0;
if (target->in_livetree)
@@ -361,11 +364,8 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n",
target->np, new_prop->name);
- if (ret) {
- kfree(new_prop->name);
- kfree(new_prop->value);
- kfree(new_prop);
- }
+ if (ret)
+ __of_prop_free(new_prop);
return ret;
}
@@ -403,7 +403,7 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
* invalid @overlay.
*/
static int add_changeset_node(struct overlay_changeset *ovcs,
- struct target *target, struct device_node *node)
+ struct target *target, const struct device_node *node)
{
const char *node_kbasename;
const __be32 *phandle;
@@ -477,7 +477,6 @@ static int add_changeset_node(struct overlay_changeset *ovcs,
static int build_changeset_next_level(struct overlay_changeset *ovcs,
struct target *target, const struct device_node *overlay_node)
{
- struct device_node *child;
struct property *prop;
int ret;
@@ -490,12 +489,11 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs,
}
}
- for_each_child_of_node(overlay_node, child) {
+ for_each_child_of_node_scoped(overlay_node, child) {
ret = add_changeset_node(ovcs, target, child);
if (ret) {
pr_debug("Failed to apply node @%pOF/%pOFn, err=%d\n",
target->np, child, ret);
- of_node_put(child);
return ret;
}
}
@@ -682,9 +680,11 @@ static int build_changeset(struct overlay_changeset *ovcs)
* 1) "target" property containing the phandle of the target
* 2) "target-path" property containing the path of the target
*/
-static struct device_node *find_target(struct device_node *info_node)
+static struct device_node *find_target(const struct device_node *info_node,
+ const struct device_node *target_base)
{
struct device_node *node;
+ char *target_path;
const char *path;
u32 val;
int ret;
@@ -700,10 +700,23 @@ static struct device_node *find_target(struct device_node *info_node)
ret = of_property_read_string(info_node, "target-path", &path);
if (!ret) {
- node = of_find_node_by_path(path);
- if (!node)
- pr_err("find target, node: %pOF, path '%s' not found\n",
- info_node, path);
+ if (target_base) {
+ target_path = kasprintf(GFP_KERNEL, "%pOF%s", target_base, path);
+ if (!target_path)
+ return NULL;
+ node = of_find_node_by_path(target_path);
+ if (!node) {
+ pr_err("find target, node: %pOF, path '%s' not found\n",
+ info_node, target_path);
+ }
+ kfree(target_path);
+ } else {
+ node = of_find_node_by_path(path);
+ if (!node) {
+ pr_err("find target, node: %pOF, path '%s' not found\n",
+ info_node, path);
+ }
+ }
return node;
}
@@ -715,6 +728,7 @@ static struct device_node *find_target(struct device_node *info_node)
/**
* init_overlay_changeset() - initialize overlay changeset from overlay tree
* @ovcs: Overlay changeset to build
+ * @target_base: Point to the target node to apply overlay
*
* Initialize @ovcs. Populate @ovcs->fragments with node information from
* the top level of @overlay_root. The relevant top level nodes are the
@@ -725,7 +739,8 @@ static struct device_node *find_target(struct device_node *info_node)
* detected in @overlay_root. On error return, the caller of
* init_overlay_changeset() must call free_overlay_changeset().
*/
-static int init_overlay_changeset(struct overlay_changeset *ovcs)
+static int init_overlay_changeset(struct overlay_changeset *ovcs,
+ const struct device_node *target_base)
{
struct device_node *node, *overlay_node;
struct fragment *fragment;
@@ -752,8 +767,6 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs)
if (!of_node_is_root(ovcs->overlay_root))
pr_debug("%s() ovcs->overlay_root is not root\n", __func__);
- of_changeset_init(&ovcs->cset);
-
cnt = 0;
/* fragment nodes */
@@ -786,7 +799,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs)
fragment = &fragments[cnt];
fragment->overlay = overlay_node;
- fragment->target = find_target(node);
+ fragment->target = find_target(node, target_base);
if (!fragment->target) {
of_node_put(fragment->overlay);
ret = -EINVAL;
@@ -877,6 +890,7 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
*
* of_overlay_apply() - Create and apply an overlay changeset
* @ovcs: overlay changeset
+ * @base: point to the target node to apply overlay
*
* Creates and applies an overlay changeset.
*
@@ -900,7 +914,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
* the caller of of_overlay_apply() must call free_overlay_changeset().
*/
-static int of_overlay_apply(struct overlay_changeset *ovcs)
+static int of_overlay_apply(struct overlay_changeset *ovcs,
+ const struct device_node *base)
{
int ret = 0, ret_revert, ret_tmp;
@@ -908,7 +923,7 @@ static int of_overlay_apply(struct overlay_changeset *ovcs)
if (ret)
goto out;
- ret = init_overlay_changeset(ovcs);
+ ret = init_overlay_changeset(ovcs, base);
if (ret)
goto out;
@@ -947,11 +962,12 @@ out:
return ret;
}
-/*
+/**
* of_overlay_fdt_apply() - Create and apply an overlay changeset
* @overlay_fdt: pointer to overlay FDT
* @overlay_fdt_size: number of bytes in @overlay_fdt
* @ret_ovcs_id: pointer for returning created changeset id
+ * @base: pointer for the target node to apply overlay
*
* Creates and applies an overlay changeset.
*
@@ -967,7 +983,7 @@ out:
*/
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
- int *ret_ovcs_id)
+ int *ret_ovcs_id, const struct device_node *base)
{
void *new_fdt;
void *new_fdt_align;
@@ -1013,6 +1029,7 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
INIT_LIST_HEAD(&ovcs->ovcs_list);
list_add_tail(&ovcs->ovcs_list, &ovcs_list);
+ of_changeset_init(&ovcs->cset);
/*
* Must create permanent copy of FDT because of_fdt_unflatten_tree()
@@ -1037,7 +1054,7 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
}
ovcs->overlay_mem = overlay_mem;
- ret = of_overlay_apply(ovcs);
+ ret = of_overlay_apply(ovcs, base);
/*
* If of_overlay_apply() error, calling free_overlay_changeset() may
* result in a memory leak if the apply partly succeeded, so do NOT
@@ -1062,18 +1079,14 @@ EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
*
* Returns 1 if @np is @tree or is contained in @tree, else 0
*/
-static int find_node(struct device_node *tree, struct device_node *np)
+static int find_node(const struct device_node *tree, struct device_node *np)
{
- struct device_node *child;
-
if (tree == np)
return 1;
- for_each_child_of_node(tree, child) {
- if (find_node(child, np)) {
- of_node_put(child);
+ for_each_child_of_node_scoped(tree, child) {
+ if (find_node(child, np))
return 1;
- }
}
return 0;
@@ -1177,6 +1190,9 @@ int of_overlay_remove(int *ovcs_id)
struct overlay_changeset *ovcs;
int ret, ret_apply, ret_tmp;
+ if (*ovcs_id == 0)
+ return 0;
+
if (devicetree_corrupt()) {
pr_err("suspect devicetree state, refuse to remove overlay\n");
ret = -EBUSY;
diff --git a/drivers/of/overlay_test.c b/drivers/of/overlay_test.c
new file mode 100644
index 000000000000..c787524c5a88
--- /dev/null
+++ b/drivers/of/overlay_test.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for device tree overlays
+ */
+#include <linux/device/bus.h>
+#include <linux/kconfig.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <kunit/of.h>
+#include <kunit/test.h>
+
+#include "of_private.h"
+
+static const char * const kunit_node_name = "kunit-test";
+static const char * const kunit_compatible = "test,empty";
+
+/* Test that of_overlay_apply_kunit() adds a node to the live tree */
+static void of_overlay_apply_kunit_apply(struct kunit *test)
+{
+ struct device_node *np;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ of_overlay_apply_kunit(test, kunit_overlay_test));
+
+ np = of_find_node_by_name(NULL, kunit_node_name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, np);
+ of_node_put(np);
+}
+
+/*
+ * Test that of_overlay_apply_kunit() creates platform devices with the
+ * expected device_node
+ */
+static void of_overlay_apply_kunit_platform_device(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ of_overlay_apply_kunit(test, kunit_overlay_test));
+
+ np = of_find_node_by_name(NULL, kunit_node_name);
+ of_node_put_kunit(test, np);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np);
+
+ pdev = of_find_device_by_node(np);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, pdev);
+ if (pdev)
+ put_device(&pdev->dev);
+}
+
+static int of_overlay_bus_match_compatible(struct device *dev, const void *data)
+{
+ return of_device_is_compatible(dev->of_node, data);
+}
+
+/* Test that of_overlay_apply_kunit() cleans up after the test is finished */
+static void of_overlay_apply_kunit_cleanup(struct kunit *test)
+{
+ struct kunit fake;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct device_node *np;
+
+ of_root_kunit_skip(test);
+ if (!IS_ENABLED(CONFIG_OF_OVERLAY))
+ kunit_skip(test, "requires CONFIG_OF_OVERLAY to apply overlay");
+ if (!IS_ENABLED(CONFIG_OF_EARLY_FLATTREE))
+ kunit_skip(test, "requires CONFIG_OF_EARLY_FLATTREE for root node");
+
+ kunit_init_test(&fake, "fake test", NULL);
+ KUNIT_ASSERT_EQ(test, fake.status, KUNIT_SUCCESS);
+
+ KUNIT_ASSERT_EQ(test, 0,
+ of_overlay_apply_kunit(&fake, kunit_overlay_test));
+
+ np = of_find_node_by_name(NULL, kunit_node_name);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np);
+ of_node_put_kunit(&fake, np);
+
+ pdev = of_find_device_by_node(np);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ put_device(&pdev->dev); /* Not derefing 'pdev' after this */
+
+ /* Remove overlay */
+ kunit_cleanup(&fake);
+
+ /* The node and device should be removed */
+ np = of_find_node_by_name(NULL, kunit_node_name);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, np);
+ of_node_put(np);
+
+ dev = bus_find_device(&platform_bus_type, NULL, kunit_compatible,
+ of_overlay_bus_match_compatible);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, dev);
+ put_device(dev);
+}
+
+static struct kunit_case of_overlay_apply_kunit_test_cases[] = {
+ KUNIT_CASE(of_overlay_apply_kunit_apply),
+ KUNIT_CASE(of_overlay_apply_kunit_platform_device),
+ KUNIT_CASE(of_overlay_apply_kunit_cleanup),
+ {}
+};
+
+/*
+ * Test suite for test managed device tree overlays.
+ */
+static struct kunit_suite of_overlay_apply_kunit_suite = {
+ .name = "of_overlay_apply_kunit",
+ .test_cases = of_overlay_apply_kunit_test_cases,
+};
+
+kunit_test_suites(
+ &of_overlay_apply_kunit_suite,
+);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for device tree overlays");
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 7eda43c66c91..cb0cb374b21f 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -19,6 +19,8 @@
#include <linux/of.h>
#include <linux/of_pdt.h>
+#include "of_private.h"
+
static struct of_pdt_ops *of_pdt_prom_ops __initdata;
#if defined(CONFIG_SPARC)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 0c3475e7d2ff..f77cb19973a5 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -20,21 +20,9 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/sysfb.h>
-const struct of_device_id of_default_bus_match_table[] = {
- { .compatible = "simple-bus", },
- { .compatible = "simple-mfd", },
- { .compatible = "isa", },
-#ifdef CONFIG_ARM_AMBA
- { .compatible = "arm,amba-bus", },
-#endif /* CONFIG_ARM_AMBA */
- {} /* Empty terminated list */
-};
-
-static const struct of_device_id of_skipped_node_table[] = {
- { .compatible = "operating-points-v2", },
- {} /* Empty terminated list */
-};
+#include "of_private.h"
/**
* of_find_device_by_node - Find the platform_device associated with a node
@@ -54,7 +42,44 @@ struct platform_device *of_find_device_by_node(struct device_node *np)
}
EXPORT_SYMBOL(of_find_device_by_node);
+int of_device_add(struct platform_device *ofdev)
+{
+ BUG_ON(ofdev->dev.of_node == NULL);
+
+ /* name and id have to be set so that the platform bus doesn't get
+ * confused on matching */
+ ofdev->name = dev_name(&ofdev->dev);
+ ofdev->id = PLATFORM_DEVID_NONE;
+
+ /*
+ * If this device has not binding numa node in devicetree, that is
+ * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this
+ * device is on the same node as the parent.
+ */
+ set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
+
+ return device_add(&ofdev->dev);
+}
+
+int of_device_register(struct platform_device *pdev)
+{
+ device_initialize(&pdev->dev);
+ return of_device_add(pdev);
+}
+EXPORT_SYMBOL(of_device_register);
+
+void of_device_unregister(struct platform_device *ofdev)
+{
+ device_unregister(&ofdev->dev);
+}
+EXPORT_SYMBOL(of_device_unregister);
+
#ifdef CONFIG_OF_ADDRESS
+static const struct of_device_id of_skipped_node_table[] = {
+ { .compatible = "operating-points-v2", },
+ {} /* Empty terminated list */
+};
+
/*
* The following routines scan a subtree and registers a device for
* each applicable node.
@@ -64,46 +89,6 @@ EXPORT_SYMBOL(of_find_device_by_node);
*/
/**
- * of_device_make_bus_id - Use the device node data to assign a unique name
- * @dev: pointer to device structure that is linked to a device tree node
- *
- * This routine will first try using the translated bus address to
- * 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 *dev)
-{
- struct device_node *node = dev->of_node;
- const __be32 *reg;
- u64 addr;
- u32 mask;
-
- /* Construct the name, using parent nodes if necessary to ensure uniqueness */
- while (node->parent) {
- /*
- * If the address can be translated, then that is as much
- * uniqueness as we need. Make it the first component and return
- */
- reg = of_get_property(node, "reg", NULL);
- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
- if (!of_property_read_u32(node, "mask", &mask))
- dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
- addr, ffs(mask) - 1, node, dev_name(dev));
-
- else
- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
- addr, node, dev_name(dev));
- return;
- }
-
- /* format arguments only used if dev_name() resolves to NULL */
- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
- kbasename(node->full_name), dev_name(dev));
- node = node->parent;
- }
-}
-
-/**
* of_device_alloc - Allocate and initialize an of_device
* @np: device node to assign to device
* @bus_id: Name to assign to the device. May be null to use default name.
@@ -141,7 +126,7 @@ struct platform_device *of_device_alloc(struct device_node *np,
}
/* setup generic device info */
- device_set_node(&dev->dev, of_fwnode_handle(np));
+ device_set_node(&dev->dev, of_fwnode_handle(of_node_get(np)));
dev->dev.parent = parent ? : &platform_bus;
if (bus_id)
@@ -171,6 +156,8 @@ static struct platform_device *of_platform_device_create_pdata(
{
struct platform_device *dev;
+ pr_debug("create platform device: %pOF\n", np);
+
if (!of_device_is_available(np) ||
of_node_test_and_set_flag(np, OF_POPULATED))
return NULL;
@@ -341,14 +328,13 @@ static int of_platform_bus_create(struct device_node *bus,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
- struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
- if (strict && (!of_get_property(bus, "compatible", NULL))) {
+ if (strict && (!of_property_present(bus, "compatible"))) {
pr_debug("%s() - skipping %pOF, no compatible prop\n",
__func__, bus);
return 0;
@@ -385,13 +371,11 @@ static int of_platform_bus_create(struct device_node *bus,
if (!dev || !of_match_node(matches, bus))
return 0;
- for_each_child_of_node(bus, child) {
+ for_each_child_of_node_scoped(bus, child) {
pr_debug(" create child: %pOF\n", child);
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
- if (rc) {
- of_node_put(child);
+ if (rc)
break;
- }
}
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
@@ -462,7 +446,6 @@ int of_platform_populate(struct device_node *root,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
- struct device_node *child;
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path("/");
@@ -473,12 +456,10 @@ int of_platform_populate(struct device_node *root,
pr_debug(" starting at: %pOF\n", root);
device_links_supplier_sync_state_pause();
- for_each_child_of_node(root, child) {
+ for_each_child_of_node_scoped(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
- if (rc) {
- of_node_put(child);
+ if (rc)
break;
- }
}
device_links_supplier_sync_state_resume();
@@ -493,8 +474,17 @@ int of_platform_default_populate(struct device_node *root,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
- return of_platform_populate(root, of_default_bus_match_table, lookup,
- parent);
+ static const struct of_device_id match_table[] = {
+ { .compatible = "simple-bus", },
+ { .compatible = "simple-mfd", },
+ { .compatible = "isa", },
+#ifdef CONFIG_ARM_AMBA
+ { .compatible = "arm,amba-bus", },
+#endif /* CONFIG_ARM_AMBA */
+ {} /* Empty terminated list */
+ };
+
+ return of_platform_populate(root, match_table, lookup, parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
@@ -515,9 +505,6 @@ static int __init of_platform_default_populate_init(void)
device_links_supplier_sync_state_pause();
- if (!of_have_populated_dt())
- return -ENODEV;
-
if (IS_ENABLED(CONFIG_PPC)) {
struct device_node *boot_display = NULL;
struct platform_device *dev;
@@ -549,8 +536,8 @@ static int __init of_platform_default_populate_init(void)
* ignore errors for the rest.
*/
for_each_node_by_type(node, "display") {
- if (!of_get_property(node, "linux,opened", NULL) ||
- !of_get_property(node, "linux,boot-display", NULL))
+ if (!of_property_read_bool(node, "linux,opened") ||
+ !of_property_read_bool(node, "linux,boot-display"))
continue;
dev = of_platform_device_create(node, "of-display", NULL);
of_node_put(node);
@@ -564,7 +551,7 @@ static int __init of_platform_default_populate_init(void)
char buf[14];
const char *of_display_format = "of-display.%d";
- if (!of_get_property(node, "linux,opened", NULL) || node == boot_display)
+ if (!of_property_read_bool(node, "linux,opened") || node == boot_display)
continue;
ret = snprintf(buf, sizeof(buf), of_display_format, display_number++);
if (ret < sizeof(buf))
@@ -587,8 +574,21 @@ static int __init of_platform_default_populate_init(void)
}
node = of_get_compatible_child(of_chosen, "simple-framebuffer");
- of_platform_device_create(node, NULL, NULL);
- of_node_put(node);
+ if (node) {
+ /*
+ * Since a "simple-framebuffer" device is already added
+ * here, disable the Generic System Framebuffers (sysfb)
+ * to prevent it from registering another device for the
+ * system framebuffer later (e.g: using the screen_info
+ * data that may had been filled as well).
+ *
+ * This can happen for example on DT systems that do EFI
+ * booting and may provide a GOP handle to the EFI stub.
+ */
+ sysfb_disable(NULL);
+ of_platform_device_create(node, NULL, NULL);
+ of_node_put(node);
+ }
/* Populate everything else. */
of_platform_default_populate(NULL, NULL, NULL);
@@ -634,7 +634,7 @@ EXPORT_SYMBOL_GPL(of_platform_device_destroy);
* @parent: device which children will be removed
*
* Complementary to of_platform_populate(), this function removes children
- * of the given device (and, recurrently, their children) that have been
+ * of the given device (and, recursively, their children) that have been
* created from their respective device tree nodes (and only those,
* leaving others - eg. manually created - unharmed).
*/
@@ -703,7 +703,7 @@ static int devm_of_platform_match(struct device *dev, void *res, void *data)
* @dev: device that requested to depopulate from device tree data
*
* Complementary to devm_of_platform_populate(), this function removes children
- * of the given device (and, recurrently, their children) that have been
+ * of the given device (and, recursively, their children) that have been
* created from their respective device tree nodes (and only those,
* leaving others - eg. manually created - unharmed).
*/
@@ -725,11 +725,14 @@ static int of_platform_notify(struct notifier_block *nb,
struct of_reconfig_data *rd = arg;
struct platform_device *pdev_parent, *pdev;
bool children_left;
+ struct device_node *parent;
switch (of_reconfig_get_state_change(action, rd)) {
case OF_RECONFIG_CHANGE_ADD:
- /* verify that the parent is a bus */
- if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
+ parent = rd->dn->parent;
+ /* verify that the parent is a bus (or the root node) */
+ if (!of_node_is_root(parent) &&
+ !of_node_check_flag(parent, OF_POPULATED_BUS))
return NOTIFY_OK; /* not for us */
/* already populated? (driver using of_populate manually) */
@@ -742,7 +745,7 @@ static int of_platform_notify(struct notifier_block *nb,
*/
rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE;
/* pdev_parent may be NULL when no bus platform device */
- pdev_parent = of_find_device_by_node(rd->dn->parent);
+ pdev_parent = of_find_device_by_node(parent);
pdev = of_platform_device_create(rd->dn, NULL,
pdev_parent ? &pdev_parent->dev : NULL);
platform_device_put(pdev_parent);
diff --git a/drivers/of/property.c b/drivers/of/property.c
index ddc75cd50825..c1feb631e383 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -32,6 +32,32 @@
#include "of_private.h"
/**
+ * of_property_read_bool - Find a property
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a boolean property in a device node. Usage on non-boolean
+ * property types is deprecated.
+ *
+ * Return: true if the property exists false otherwise.
+ */
+bool of_property_read_bool(const struct device_node *np, const char *propname)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ /*
+ * Boolean properties should not have a value. Testing for property
+ * presence should either use of_property_present() or just read the
+ * property value and check the returned error code.
+ */
+ if (prop && prop->length)
+ pr_warn("%pOF: Read of boolean property '%s' with a value.\n", np, propname);
+
+ return prop ? true : false;
+}
+EXPORT_SYMBOL(of_property_read_bool);
+
+/**
* of_graph_is_present() - check graph's presence
* @node: pointer to device_node containing graph port
*
@@ -40,15 +66,12 @@
*/
bool of_graph_is_present(const struct device_node *node)
{
- struct device_node *ports, *port;
+ struct device_node *ports __free(device_node) = of_get_child_by_name(node, "ports");
- ports = of_get_child_by_name(node, "ports");
if (ports)
node = ports;
- port = of_get_child_by_name(node, "port");
- of_node_put(ports);
- of_node_put(port);
+ struct device_node *port __free(device_node) = of_get_child_by_name(node, "port");
return !!port;
}
@@ -71,7 +94,7 @@ EXPORT_SYMBOL(of_graph_is_present);
int of_property_count_elems_of_size(const struct device_node *np,
const char *propname, int elem_size)
{
- struct property *prop = of_find_property(np, propname, NULL);
+ const struct property *prop = of_find_property(np, propname, NULL);
if (!prop)
return -EINVAL;
@@ -107,7 +130,7 @@ EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
static void *of_find_property_value_of_size(const struct device_node *np,
const char *propname, u32 min, u32 max, size_t *len)
{
- struct property *prop = of_find_property(np, propname, NULL);
+ const struct property *prop = of_find_property(np, propname, NULL);
if (!prop)
return ERR_PTR(-EINVAL);
@@ -125,6 +148,39 @@ static void *of_find_property_value_of_size(const struct device_node *np,
}
/**
+ * of_property_read_u16_index - Find and read a u16 from a multi-value property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the u16 in the list of values
+ * @out_value: pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 16-bit value from
+ * it.
+ *
+ * Return: 0 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.
+ *
+ * The out_value is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_u16_index(const struct device_node *np,
+ const char *propname,
+ u32 index, u16 *out_value)
+{
+ const u16 *val = of_find_property_value_of_size(np, propname,
+ ((index + 1) * sizeof(*out_value)),
+ 0, NULL);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ *out_value = be16_to_cpup(((__be16 *)val) + index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u16_index);
+
+/**
* of_property_read_u32_index - Find and read a u32 from a multi-value property.
*
* @np: device node from which the property value is to be read.
@@ -441,6 +497,7 @@ int of_property_read_string(const struct device_node *np, const char *propname,
const char **out_string)
{
const struct property *prop = of_find_property(np, propname, NULL);
+
if (!prop)
return -EINVAL;
if (!prop->length)
@@ -454,12 +511,17 @@ EXPORT_SYMBOL_GPL(of_property_read_string);
/**
* of_property_match_string() - Find string in a list and return index
- * @np: pointer to node containing string list property
+ * @np: pointer to the node containing the string list property
* @propname: string list property name
- * @string: pointer to string to search for in string list
+ * @string: pointer to the string to search for in the string list
+ *
+ * Search for an exact match of string in a device node property which is a
+ * string of lists.
*
- * This function searches a string list property and returns the index
- * of a specific string value.
+ * Return: the index of the first occurrence of the string on success, -EINVAL
+ * if the property does not exist, -ENODATA if the property does not have a
+ * value, and -EILSEQ if the string is not null-terminated within the length of
+ * the property data.
*/
int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
@@ -527,7 +589,7 @@ int of_property_read_string_helper(const struct device_node *np,
}
EXPORT_SYMBOL_GPL(of_property_read_string_helper);
-const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
+const __be32 *of_prop_next_u32(const struct property *prop, const __be32 *cur,
u32 *pu)
{
const void *curv = cur;
@@ -550,7 +612,7 @@ out_val:
}
EXPORT_SYMBOL_GPL(of_prop_next_u32);
-const char *of_prop_next_string(struct property *prop, const char *cur)
+const char *of_prop_next_string(const struct property *prop, const char *cur)
{
const void *curv = cur;
@@ -578,7 +640,8 @@ EXPORT_SYMBOL_GPL(of_prop_next_string);
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint)
{
- struct device_node *port_node = of_get_parent(node);
+ struct device_node *port_node __free(device_node) =
+ of_get_parent(node);
WARN_ONCE(!port_node, "%s(): endpoint %pOF has no parent node\n",
__func__, node);
@@ -593,8 +656,6 @@ int of_graph_parse_endpoint(const struct device_node *node,
of_property_read_u32(port_node, "reg", &endpoint->port);
of_property_read_u32(node, "reg", &endpoint->id);
- of_node_put(port_node);
-
return 0;
}
EXPORT_SYMBOL(of_graph_parse_endpoint);
@@ -609,29 +670,90 @@ EXPORT_SYMBOL(of_graph_parse_endpoint);
*/
struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
{
- struct device_node *node, *port;
+ struct device_node *node __free(device_node) = of_get_child_by_name(parent, "ports");
- node = of_get_child_by_name(parent, "ports");
if (node)
parent = node;
- for_each_child_of_node(parent, port) {
+ for_each_child_of_node_scoped(parent, port) {
u32 port_id = 0;
if (!of_node_name_eq(port, "port"))
continue;
of_property_read_u32(port, "reg", &port_id);
if (id == port_id)
- break;
+ return_ptr(port);
}
- of_node_put(node);
-
- return port;
+ return NULL;
}
EXPORT_SYMBOL(of_graph_get_port_by_id);
/**
+ * of_graph_get_next_port() - get next port node.
+ * @parent: pointer to the parent device node, or parent ports node
+ * @prev: previous port node, or NULL to get first
+ *
+ * Parent device node can be used as @parent whether device node has ports node
+ * or not. It will work same as ports@0 node.
+ *
+ * Return: A 'port' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *node __free(device_node) =
+ of_get_child_by_name(parent, "ports");
+
+ if (node)
+ parent = node;
+
+ return of_get_child_by_name(parent, "port");
+ }
+
+ do {
+ prev = of_get_next_child(parent, prev);
+ if (!prev)
+ break;
+ } while (!of_node_name_eq(prev, "port"));
+
+ return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port);
+
+/**
+ * of_graph_get_next_port_endpoint() - get next endpoint node in port.
+ * If it reached to end of the port, it will return NULL.
+ * @port: pointer to the target port node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port,
+ struct device_node *prev)
+{
+ while (1) {
+ prev = of_get_next_child(port, prev);
+ if (!prev)
+ break;
+ if (WARN(!of_node_name_eq(prev, "endpoint"),
+ "non endpoint node is used (%pOF)", prev))
+ continue;
+
+ break;
+ }
+
+ return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port_endpoint);
+
+/**
* of_graph_get_next_endpoint() - get next endpoint node
* @parent: pointer to the parent device node
* @prev: previous endpoint node, or NULL to get first
@@ -654,17 +776,9 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
* parent port node.
*/
if (!prev) {
- struct device_node *node;
-
- node = of_get_child_by_name(parent, "ports");
- if (node)
- parent = node;
-
- port = of_get_child_by_name(parent, "port");
- of_node_put(node);
-
+ port = of_graph_get_next_port(parent, NULL);
if (!port) {
- pr_err("graph: no port node found in %pOF\n", parent);
+ pr_debug("graph: no port node found in %pOF\n", parent);
return NULL;
}
} else {
@@ -680,7 +794,7 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
* getting the next child. If the previous endpoint is NULL this
* will return the first child.
*/
- endpoint = of_get_next_child(port, prev);
+ endpoint = of_graph_get_next_port_endpoint(port, prev);
if (endpoint) {
of_node_put(port);
return endpoint;
@@ -689,11 +803,9 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
/* No more endpoints under this port, try the next one. */
prev = NULL;
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (!of_node_name_eq(port, "port"));
+ port = of_graph_get_next_port(parent, port);
+ if (!port)
+ return NULL;
}
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);
@@ -762,7 +874,9 @@ struct device_node *of_graph_get_port_parent(struct device_node *node)
/* Walk 3 levels up only if there is 'ports' node. */
for (depth = 3; depth && node; depth--) {
node = of_get_next_parent(node);
- if (depth == 2 && !of_node_name_eq(node, "ports"))
+ if (depth == 2 && !of_node_name_eq(node, "ports") &&
+ !of_node_name_eq(node, "in-ports") &&
+ !of_node_name_eq(node, "out-ports"))
break;
}
return node;
@@ -779,16 +893,11 @@ EXPORT_SYMBOL(of_graph_get_port_parent);
struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node)
{
- struct device_node *np, *pp;
-
/* Get remote endpoint node. */
- np = of_graph_get_remote_endpoint(node);
-
- pp = of_graph_get_port_parent(np);
+ struct device_node *np __free(device_node) =
+ of_graph_get_remote_endpoint(node);
- of_node_put(np);
-
- return pp;
+ return of_graph_get_port_parent(np);
}
EXPORT_SYMBOL(of_graph_get_remote_port_parent);
@@ -811,10 +920,16 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
}
EXPORT_SYMBOL(of_graph_get_remote_port);
-int of_graph_get_endpoint_count(const struct device_node *np)
+/**
+ * of_graph_get_endpoint_count() - get the number of endpoints in a device node
+ * @np: parent device node containing ports and endpoints
+ *
+ * Return: count of endpoint of this device node
+ */
+unsigned int of_graph_get_endpoint_count(const struct device_node *np)
{
struct device_node *endpoint;
- int num = 0;
+ unsigned int num = 0;
for_each_endpoint_of_node(np, endpoint)
num++;
@@ -824,6 +939,23 @@ int of_graph_get_endpoint_count(const struct device_node *np)
EXPORT_SYMBOL(of_graph_get_endpoint_count);
/**
+ * of_graph_get_port_count() - get the number of port in a device or ports node
+ * @np: pointer to the device or ports node
+ *
+ * Return: count of port of this device or ports node
+ */
+unsigned int of_graph_get_port_count(struct device_node *np)
+{
+ unsigned int num = 0;
+
+ for_each_of_graph_port(np, port)
+ num++;
+
+ return num;
+}
+EXPORT_SYMBOL(of_graph_get_port_count);
+
+/**
* of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
* @node: pointer to parent device_node containing graph port/endpoint
* @port: identifier (value of reg property) of the parent port node
@@ -893,6 +1025,12 @@ of_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode)
static bool of_fwnode_property_present(const struct fwnode_handle *fwnode,
const char *propname)
{
+ return of_property_present(to_of_node(fwnode), propname);
+}
+
+static bool of_fwnode_property_read_bool(const struct fwnode_handle *fwnode,
+ const char *propname)
+{
return of_property_read_bool(to_of_node(fwnode), propname);
}
@@ -1043,15 +1181,13 @@ static int of_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint)
{
const struct device_node *node = to_of_node(fwnode);
- struct device_node *port_node = of_get_parent(node);
+ struct device_node *port_node __free(device_node) = of_get_parent(node);
endpoint->local_fwnode = fwnode;
of_property_read_u32(port_node, "reg", &endpoint->port);
of_property_read_u32(node, "reg", &endpoint->id);
- of_node_put(port_node);
-
return 0;
}
@@ -1062,57 +1198,24 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
return of_device_get_match_data(dev);
}
-static struct device_node *of_get_compat_node(struct device_node *np)
-{
- of_node_get(np);
-
- while (np) {
- if (!of_device_is_available(np)) {
- of_node_put(np);
- np = NULL;
- }
-
- if (of_property_present(np, "compatible"))
- break;
-
- np = of_get_next_parent(np);
- }
-
- return np;
-}
-
-static struct device_node *of_get_compat_node_parent(struct device_node *np)
-{
- struct device_node *parent, *node;
-
- parent = of_get_parent(np);
- node = of_get_compat_node(parent);
- of_node_put(parent);
-
- return node;
-}
-
static void of_link_to_phandle(struct device_node *con_np,
- struct device_node *sup_np)
+ struct device_node *sup_np,
+ u8 flags)
{
- struct device_node *tmp_np = of_node_get(sup_np);
+ struct device_node *tmp_np __free(device_node) = of_node_get(sup_np);
/* Check that sup_np and its ancestors are available. */
while (tmp_np) {
- if (of_fwnode_handle(tmp_np)->dev) {
- of_node_put(tmp_np);
+ if (of_fwnode_handle(tmp_np)->dev)
break;
- }
- if (!of_device_is_available(tmp_np)) {
- of_node_put(tmp_np);
+ if (!of_device_is_available(tmp_np))
return;
- }
tmp_np = of_get_next_parent(tmp_np);
}
- fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np));
+ fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np), flags);
}
/**
@@ -1217,14 +1320,16 @@ static struct device_node *parse_##fname(struct device_node *np, \
*
* @parse_prop: function name
* parse_prop() finds the node corresponding to a supplier phandle
- * @parse_prop.np: Pointer to device node holding supplier phandle property
- * @parse_prop.prop_name: Name of property holding a phandle value
- * @parse_prop.index: For properties holding a list of phandles, this is the
+ * parse_prop.np: Pointer to device node holding supplier phandle property
+ * parse_prop.prop_name: Name of property holding a phandle value
+ * parse_prop.index: For properties holding a list of phandles, this is the
* index into the list
+ * @get_con_dev: If the consumer node containing the property is never converted
+ * to a struct device, implement this ops so fw_devlink can use it
+ * to find the true consumer.
* @optional: Describes whether a supplier is mandatory or not
- * @node_not_dev: The consumer node containing the property is never converted
- * to a struct device. Instead, parse ancestor nodes for the
- * compatible property to find a node corresponding to a device.
+ * @fwlink_flags: Optional fwnode link flags to use when creating a fwnode link
+ * for this property.
*
* Returns:
* parse_prop() return values are
@@ -1235,16 +1340,17 @@ static struct device_node *parse_##fname(struct device_node *np, \
struct supplier_bindings {
struct device_node *(*parse_prop)(struct device_node *np,
const char *prop_name, int index);
+ struct device_node *(*get_con_dev)(struct device_node *np);
bool optional;
- bool node_not_dev;
+ u8 fwlink_flags;
};
DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells")
DEFINE_SIMPLE_PROP(iommus, "iommus", "#iommu-cells")
DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells")
-DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells")
-DEFINE_SIMPLE_PROP(interrupt_parent, "interrupt-parent", NULL)
+DEFINE_SIMPLE_PROP(io_channels, "io-channels", "#io-channel-cells")
+DEFINE_SIMPLE_PROP(io_backends, "io-backends", "#io-backend-cells")
DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells")
DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells")
@@ -1261,11 +1367,16 @@ DEFINE_SIMPLE_PROP(pinctrl5, "pinctrl-5", NULL)
DEFINE_SIMPLE_PROP(pinctrl6, "pinctrl-6", NULL)
DEFINE_SIMPLE_PROP(pinctrl7, "pinctrl-7", NULL)
DEFINE_SIMPLE_PROP(pinctrl8, "pinctrl-8", NULL)
-DEFINE_SIMPLE_PROP(remote_endpoint, "remote-endpoint", NULL)
DEFINE_SIMPLE_PROP(pwms, "pwms", "#pwm-cells")
DEFINE_SIMPLE_PROP(resets, "resets", "#reset-cells")
DEFINE_SIMPLE_PROP(leds, "leds", NULL)
DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
+DEFINE_SIMPLE_PROP(panel, "panel", NULL)
+DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells")
+DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL)
+DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells")
+DEFINE_SIMPLE_PROP(pses, "pses", "#pse-cells")
+DEFINE_SIMPLE_PROP(power_supplies, "power-supplies", NULL)
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
@@ -1325,6 +1436,58 @@ static struct device_node *parse_interrupts(struct device_node *np,
return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np;
}
+static struct device_node *parse_interrupt_map(struct device_node *np,
+ const char *prop_name, int index)
+{
+ const __be32 *imap, *imap_end;
+ struct of_phandle_args sup_args;
+ u32 addrcells, intcells;
+ int imaplen;
+
+ if (!IS_ENABLED(CONFIG_OF_IRQ))
+ return NULL;
+
+ if (strcmp(prop_name, "interrupt-map"))
+ return NULL;
+
+ if (of_property_read_u32(np, "#interrupt-cells", &intcells))
+ return NULL;
+ addrcells = of_bus_n_addr_cells(np);
+
+ imap = of_get_property(np, "interrupt-map", &imaplen);
+ if (!imap)
+ return NULL;
+ imaplen /= sizeof(*imap);
+
+ imap_end = imap + imaplen;
+
+ for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) {
+ imap += addrcells + intcells;
+
+ imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args);
+ if (!imap)
+ return NULL;
+
+ if (i == index)
+ return sup_args.np;
+
+ of_node_put(sup_args.np);
+ }
+
+ return NULL;
+}
+
+static struct device_node *parse_remote_endpoint(struct device_node *np,
+ const char *prop_name,
+ int index)
+{
+ /* Return NULL for index > 0 to signify end of remote-endpoints. */
+ if (index > 0 || strcmp(prop_name, "remote-endpoint"))
+ return NULL;
+
+ return of_graph_get_remote_port_parent(np);
+}
+
static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_clocks, },
{ .parse_prop = parse_interconnects, },
@@ -1332,7 +1495,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_iommu_maps, .optional = true, },
{ .parse_prop = parse_mboxes, },
{ .parse_prop = parse_io_channels, },
- { .parse_prop = parse_interrupt_parent, },
+ { .parse_prop = parse_io_backends, },
{ .parse_prop = parse_dmas, .optional = true, },
{ .parse_prop = parse_power_domains, },
{ .parse_prop = parse_hwlocks, },
@@ -1349,16 +1512,29 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_pinctrl6, },
{ .parse_prop = parse_pinctrl7, },
{ .parse_prop = parse_pinctrl8, },
- { .parse_prop = parse_remote_endpoint, .node_not_dev = true, },
+ {
+ .parse_prop = parse_remote_endpoint,
+ .get_con_dev = of_graph_get_port_parent,
+ },
{ .parse_prop = parse_pwms, },
{ .parse_prop = parse_resets, },
{ .parse_prop = parse_leds, },
{ .parse_prop = parse_backlight, },
+ { .parse_prop = parse_panel, },
+ { .parse_prop = parse_msi_parent, },
+ { .parse_prop = parse_pses, },
+ { .parse_prop = parse_power_supplies, },
{ .parse_prop = parse_gpio_compat, },
{ .parse_prop = parse_interrupts, },
+ { .parse_prop = parse_interrupt_map, },
+ { .parse_prop = parse_access_controllers, },
{ .parse_prop = parse_regulators, },
{ .parse_prop = parse_gpio, },
{ .parse_prop = parse_gpios, },
+ {
+ .parse_prop = parse_post_init_providers,
+ .fwlink_flags = FWLINK_FLAG_IGNORE,
+ },
{}
};
@@ -1396,16 +1572,13 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
}
while ((phandle = s->parse_prop(con_np, prop_name, i))) {
- struct device_node *con_dev_np;
+ struct device_node *con_dev_np __free(device_node) =
+ s->get_con_dev ? s->get_con_dev(con_np) : of_node_get(con_np);
- con_dev_np = s->node_not_dev
- ? of_get_compat_node_parent(con_np)
- : of_node_get(con_np);
matched = true;
i++;
- of_link_to_phandle(con_dev_np, phandle);
+ of_link_to_phandle(con_dev_np, phandle, s->fwlink_flags);
of_node_put(phandle);
- of_node_put(con_dev_np);
}
s++;
}
@@ -1429,7 +1602,7 @@ static int of_fwnode_irq_get(const struct fwnode_handle *fwnode,
static int of_fwnode_add_links(struct fwnode_handle *fwnode)
{
- struct property *p;
+ const struct property *p;
struct device_node *con_np = to_of_node(fwnode);
if (IS_ENABLED(CONFIG_X86))
@@ -1452,6 +1625,7 @@ const struct fwnode_operations of_fwnode_ops = {
.device_dma_supported = of_fwnode_device_dma_supported,
.device_get_dma_attr = of_fwnode_device_get_dma_attr,
.property_present = of_fwnode_property_present,
+ .property_read_bool = of_fwnode_property_read_bool,
.property_read_int_array = of_fwnode_property_read_int_array,
.property_read_string_array = of_fwnode_property_read_string_array,
.get_name = of_fwnode_get_name,
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index b278ab4338ce..86424e145919 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "OF: resolver: " fmt
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -41,7 +42,7 @@ static void adjust_overlay_phandles(struct device_node *overlay,
int phandle_delta)
{
struct device_node *child;
- struct property *prop;
+ const struct property *prop;
phandle phandle;
/* adjust node's phandle in node */
@@ -70,15 +71,15 @@ static void adjust_overlay_phandles(struct device_node *overlay,
}
static int update_usages_of_a_phandle_reference(struct device_node *overlay,
- struct property *prop_fixup, phandle phandle)
+ const struct property *prop_fixup, phandle phandle)
{
struct device_node *refnode;
- struct property *prop;
- char *value, *cur, *end, *node_path, *prop_name, *s;
+ const struct property *prop;
+ char *value __free(kfree) = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
+ char *cur, *end, *node_path, *prop_name, *s;
int offset, len;
int err = 0;
- value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
if (!value)
return -ENOMEM;
@@ -89,23 +90,19 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay,
node_path = cur;
s = strchr(cur, ':');
- if (!s) {
- err = -EINVAL;
- goto err_fail;
- }
+ if (!s)
+ return -EINVAL;
*s++ = '\0';
prop_name = s;
s = strchr(s, ':');
- if (!s) {
- err = -EINVAL;
- goto err_fail;
- }
+ if (!s)
+ return -EINVAL;
*s++ = '\0';
err = kstrtoint(s, 10, &offset);
if (err)
- goto err_fail;
+ return err;
refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path);
if (!refnode)
@@ -117,22 +114,16 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay,
}
of_node_put(refnode);
- if (!prop) {
- err = -ENOENT;
- goto err_fail;
- }
+ if (!prop)
+ return -ENOENT;
- if (offset < 0 || offset + sizeof(__be32) > prop->length) {
- err = -EINVAL;
- goto err_fail;
- }
+ if (offset < 0 || offset + sizeof(__be32) > prop->length)
+ return -EINVAL;
*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
}
-err_fail:
- kfree(value);
- return err;
+ return 0;
}
/* compare nodes taking into account that 'name' strips out the @ part */
@@ -156,11 +147,11 @@ static int node_name_cmp(const struct device_node *dn1,
* of offsets of the phandle reference(s) within the respective property
* value(s). The values at these offsets will be fixed up.
*/
-static int adjust_local_phandle_references(struct device_node *local_fixups,
- struct device_node *overlay, int phandle_delta)
+static int adjust_local_phandle_references(const struct device_node *local_fixups,
+ const struct device_node *overlay, int phandle_delta)
{
- struct device_node *child, *overlay_child;
- struct property *prop_fix, *prop;
+ struct device_node *overlay_child;
+ const struct property *prop_fix, *prop;
int err, i, count;
unsigned int off;
@@ -170,9 +161,7 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
for_each_property_of_node(local_fixups, prop_fix) {
/* skip properties added automatically */
- if (!of_prop_cmp(prop_fix->name, "name") ||
- !of_prop_cmp(prop_fix->name, "phandle") ||
- !of_prop_cmp(prop_fix->name, "linux,phandle"))
+ if (is_pseudo_property(prop_fix->name))
continue;
if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
@@ -203,7 +192,7 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
* The roots of the subtrees are the overlay's __local_fixups__ node
* and the overlay's root node.
*/
- for_each_child_of_node(local_fixups, child) {
+ for_each_child_of_node_scoped(local_fixups, child) {
for_each_child_of_node(overlay, overlay_child)
if (!node_name_cmp(child, overlay_child)) {
@@ -211,17 +200,13 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
break;
}
- if (!overlay_child) {
- of_node_put(child);
+ if (!overlay_child)
return -EINVAL;
- }
err = adjust_local_phandle_references(child, overlay_child,
phandle_delta);
- if (err) {
- of_node_put(child);
+ if (err)
return err;
- }
}
return 0;
@@ -262,25 +247,22 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
*/
int of_resolve_phandles(struct device_node *overlay)
{
- struct device_node *child, *local_fixups, *refnode;
- struct device_node *tree_symbols, *overlay_fixups;
+ struct device_node *child, *refnode;
+ struct device_node *overlay_fixups;
+ struct device_node __free(device_node) *local_fixups = NULL;
struct property *prop;
const char *refpath;
phandle phandle, phandle_delta;
int err;
- tree_symbols = NULL;
-
if (!overlay) {
pr_err("null overlay\n");
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
if (!of_node_check_flag(overlay, OF_DETACHED)) {
pr_err("overlay not detached\n");
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
phandle_delta = live_tree_max_phandle() + 1;
@@ -292,7 +274,7 @@ int of_resolve_phandles(struct device_node *overlay)
err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
if (err)
- goto out;
+ return err;
overlay_fixups = NULL;
@@ -301,16 +283,13 @@ int of_resolve_phandles(struct device_node *overlay)
overlay_fixups = child;
}
- if (!overlay_fixups) {
- err = 0;
- goto out;
- }
+ if (!overlay_fixups)
+ return 0;
- tree_symbols = of_find_node_by_path("/__symbols__");
+ struct device_node __free(device_node) *tree_symbols = of_find_node_by_path("/__symbols__");
if (!tree_symbols) {
pr_err("no symbols in root of device tree.\n");
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
for_each_property_of_node(overlay_fixups, prop) {
@@ -324,14 +303,12 @@ int of_resolve_phandles(struct device_node *overlay)
if (err) {
pr_err("node label '%s' not found in live devicetree symbols table\n",
prop->name);
- goto out;
+ return err;
}
refnode = of_find_node_by_path(refpath);
- if (!refnode) {
- err = -ENOENT;
- goto out;
- }
+ if (!refnode)
+ return -ENOENT;
phandle = refnode->phandle;
of_node_put(refnode);
@@ -341,11 +318,8 @@ int of_resolve_phandles(struct device_node *overlay)
break;
}
-out:
if (err)
pr_err("overlay phandle fixup failed: %d\n", err);
- of_node_put(tree_symbols);
-
return err;
}
EXPORT_SYMBOL_GPL(of_resolve_phandles);
diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile
index ea5f4da68e23..01a966e39f23 100644
--- a/drivers/of/unittest-data/Makefile
+++ b/drivers/of/unittest-data/Makefile
@@ -32,7 +32,9 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtbo.o \
overlay_gpio_02b.dtbo.o \
overlay_gpio_03.dtbo.o \
overlay_gpio_04a.dtbo.o \
- overlay_gpio_04b.dtbo.o
+ overlay_gpio_04b.dtbo.o \
+ overlay_pci_node.dtbo.o \
+ overlay_bad_unresolved.dtbo.o
# enable creation of __symbols__ node
DTC_FLAGS_overlay += -@
diff --git a/drivers/of/unittest-data/overlay.dtso b/drivers/of/unittest-data/overlay.dtso
index 3bbc59e922fe..b3e807b99852 100644
--- a/drivers/of/unittest-data/overlay.dtso
+++ b/drivers/of/unittest-data/overlay.dtso
@@ -3,13 +3,12 @@
/plugin/;
&electric_1 {
-
status = "okay";
hvac_2: hvac-large-1 {
compatible = "ot,hvac-large";
- heat-range = < 40 75 >;
- cool-range = < 65 80 >;
+ heat-range = <40 75>;
+ cool-range = <65 80>;
};
};
@@ -24,11 +23,11 @@
#size-cells = <1>;
track@30 {
- incline-up = < 48 32 16 >;
+ incline-up = <48 32 16>;
};
track@40 {
- incline-up = < 47 31 15 >;
+ incline-up = <47 31 15>;
};
};
@@ -36,29 +35,28 @@
#address-cells = <1>;
#size-cells = <1>;
compatible = "ot,ferris-wheel";
- reg = < 0x00000200 0x100 >;
- hvac-provider = < &hvac_2 >;
- hvac-thermostat = < 27 32 > ;
- hvac-zones = < 12 5 >;
+ reg = <0x00000200 0x100>;
+ hvac-provider = <&hvac_2>;
+ hvac-thermostat = <27 32> ;
+ hvac-zones = <12 5>;
hvac-zone-names = "operator", "snack-bar";
- spin-controller = < &spin_ctrl_1 3 >;
- spin-rph = < 30 >;
- gondolas = < 16 >;
- gondola-capacity = < 6 >;
+ spin-controller = <&spin_ctrl_1 3>;
+ spin-rph = <30>;
+ gondolas = <16>;
+ gondola-capacity = <6>;
ride_200_left: track@10 {
- reg = < 0x00000010 0x10 >;
+ reg = <0x00000010 0x10>;
};
ride_200_right: track@20 {
- reg = < 0x00000020 0x10 >;
+ reg = <0x00000020 0x10>;
};
};
};
&lights_2 {
-
status = "okay";
color = "purple", "white", "red", "green";
- rate = < 3 256 >;
+ rate = <3 256>;
};
diff --git a/drivers/of/unittest-data/overlay_0.dtso b/drivers/of/unittest-data/overlay_0.dtso
index ac0f9e0fe65f..bb46582e0485 100644
--- a/drivers/of/unittest-data/overlay_0.dtso
+++ b/drivers/of/unittest-data/overlay_0.dtso
@@ -2,13 +2,8 @@
/dts-v1/;
/plugin/;
-/ {
- /* overlay_0 - enable using absolute target path */
+/* overlay_0 - enable using absolute target path */
- fragment@0 {
- target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
- __overlay__ {
- status = "okay";
- };
- };
+&{/testcase-data/overlay-node/test-bus/test-unittest0} {
+ status = "okay";
};
diff --git a/drivers/of/unittest-data/overlay_1.dtso b/drivers/of/unittest-data/overlay_1.dtso
index e92a626e2948..9c0fc8ffa4a1 100644
--- a/drivers/of/unittest-data/overlay_1.dtso
+++ b/drivers/of/unittest-data/overlay_1.dtso
@@ -2,13 +2,8 @@
/dts-v1/;
/plugin/;
-/ {
- /* overlay_1 - disable using absolute target path */
+/* overlay_1 - disable using absolute target path */
- fragment@0 {
- target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
- __overlay__ {
- status = "disabled";
- };
- };
+&{/testcase-data/overlay-node/test-bus/test-unittest1} {
+ status = "disabled";
};
diff --git a/drivers/of/unittest-data/overlay_11.dtso b/drivers/of/unittest-data/overlay_11.dtso
index 9a79b253a809..7d04ff503a18 100644
--- a/drivers/of/unittest-data/overlay_11.dtso
+++ b/drivers/of/unittest-data/overlay_11.dtso
@@ -23,6 +23,5 @@
status = "okay";
reg = <1>;
};
-
};
};
diff --git a/drivers/of/unittest-data/overlay_12.dtso b/drivers/of/unittest-data/overlay_12.dtso
index ca3441e2cbec..8d5087793eb4 100644
--- a/drivers/of/unittest-data/overlay_12.dtso
+++ b/drivers/of/unittest-data/overlay_12.dtso
@@ -2,13 +2,8 @@
/dts-v1/;
/plugin/;
-/ {
- /* overlay_12 - enable using absolute target path (i2c) */
+/* overlay_12 - enable using absolute target path (i2c) */
- fragment@0 {
- target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
- __overlay__ {
- status = "okay";
- };
- };
+&{/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12} {
+ status = "okay";
};
diff --git a/drivers/of/unittest-data/overlay_13.dtso b/drivers/of/unittest-data/overlay_13.dtso
index 3c30dec63894..da200ae94f45 100644
--- a/drivers/of/unittest-data/overlay_13.dtso
+++ b/drivers/of/unittest-data/overlay_13.dtso
@@ -2,13 +2,8 @@
/dts-v1/;
/plugin/;
-/ {
- /* overlay_13 - disable using absolute target path (i2c) */
+/* overlay_13 - disable using absolute target path (i2c) */
- fragment@0 {
- target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
- __overlay__ {
- status = "disabled";
- };
- };
+&{/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13} {
+ status = "disabled";
};
diff --git a/drivers/of/unittest-data/overlay_15.dtso b/drivers/of/unittest-data/overlay_15.dtso
index 5728490474f6..ba02ae1fed38 100644
--- a/drivers/of/unittest-data/overlay_15.dtso
+++ b/drivers/of/unittest-data/overlay_15.dtso
@@ -7,6 +7,7 @@
&unittest_i2c_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
test-unittest15 {
reg = <11>;
compatible = "unittest-i2c-mux";
diff --git a/drivers/of/unittest-data/overlay_4.dtso b/drivers/of/unittest-data/overlay_4.dtso
index a8a77ddf9abe..9b9eadddb4a0 100644
--- a/drivers/of/unittest-data/overlay_4.dtso
+++ b/drivers/of/unittest-data/overlay_4.dtso
@@ -5,7 +5,6 @@
/* overlay_4 - test insertion of a full node */
&unittest_test_bus {
-
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso b/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso
index 145dfc3b1024..9b53412b2079 100644
--- a/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso
+++ b/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso
@@ -13,16 +13,15 @@
*/
&electric_1 {
-
motor-1 {
controller {
- power_bus = < 0x1 0x2 >;
+ power_bus = <0x1 0x2>;
};
};
};
&spin_ctrl_1 {
- controller {
- power_bus_emergency = < 0x101 0x102 >;
- };
+ controller {
+ power_bus_emergency = <0x101 0x102>;
+ };
};
diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso
index 6327d1ffb963..e03f791655b0 100644
--- a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso
+++ b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso
@@ -24,16 +24,15 @@
*/
&electric_1 {
-
motor-1 {
electric {
- rpm_avail = < 100 >;
+ rpm_avail = <100>;
};
};
};
&spin_ctrl_1 {
- electric {
- rpm_avail = < 100 200 >;
- };
+ electric {
+ rpm_avail = <100 200>;
+ };
};
diff --git a/drivers/of/unittest-data/overlay_bad_phandle.dtso b/drivers/of/unittest-data/overlay_bad_phandle.dtso
index 83b797360318..a61ffc0738e3 100644
--- a/drivers/of/unittest-data/overlay_bad_phandle.dtso
+++ b/drivers/of/unittest-data/overlay_bad_phandle.dtso
@@ -3,12 +3,11 @@
/plugin/;
&electric_1 {
-
// This label should cause an error when the overlay
// is applied. There is already a phandle value
// in the base tree for motor-1.
spin_ctrl_1_conflict: motor-1 {
- accelerate = < 3 >;
- decelerate = < 5 >;
+ accelerate = <3>;
+ decelerate = <5>;
};
};
diff --git a/drivers/of/unittest-data/overlay_bad_symbol.dtso b/drivers/of/unittest-data/overlay_bad_symbol.dtso
index 98c6d1de144a..07f730384cdd 100644
--- a/drivers/of/unittest-data/overlay_bad_symbol.dtso
+++ b/drivers/of/unittest-data/overlay_bad_symbol.dtso
@@ -3,14 +3,13 @@
/plugin/;
&electric_1 {
-
// This label should cause an error when the overlay
// is applied. There is already a symbol hvac_1
// in the base tree
hvac_1: hvac-medium-2 {
compatible = "ot,hvac-medium";
- heat-range = < 50 75 >;
- cool-range = < 60 80 >;
+ heat-range = <50 75>;
+ cool-range = <60 80>;
};
};
diff --git a/drivers/of/unittest-data/overlay_bad_unresolved.dtso b/drivers/of/unittest-data/overlay_bad_unresolved.dtso
new file mode 100644
index 000000000000..f34d8780d42d
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_bad_unresolved.dtso
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+&this_label_does_not_exist {
+ status = "okay";
+};
diff --git a/drivers/of/unittest-data/overlay_common.dtsi b/drivers/of/unittest-data/overlay_common.dtsi
index 08874a72556e..a9d7cdbd5ddc 100644
--- a/drivers/of/unittest-data/overlay_common.dtsi
+++ b/drivers/of/unittest-data/overlay_common.dtsi
@@ -16,19 +16,19 @@
electric_1: substation@100 {
compatible = "ot,big-volts-control";
- reg = < 0x00000100 0x100 >;
+ reg = <0x00000100 0x100>;
status = "disabled";
hvac_1: hvac-medium-1 {
compatible = "ot,hvac-medium";
- heat-range = < 50 75 >;
- cool-range = < 60 80 >;
+ heat-range = <50 75>;
+ cool-range = <60 80>;
};
spin_ctrl_1: motor-1 {
compatible = "ot,ferris-wheel-motor";
spin = "clockwise";
- rpm_avail = < 50 >;
+ rpm_avail = <50>;
};
spin_ctrl_2: motor-8 {
@@ -41,27 +41,27 @@
#size-cells = <1>;
compatible = "ot,rides";
status = "disabled";
- orientation = < 127 >;
+ orientation = <127>;
ride@100 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "ot,roller-coaster";
- reg = < 0x00000100 0x100 >;
- hvac-provider = < &hvac_1 >;
- hvac-thermostat = < 29 > ;
- hvac-zones = < 14 >;
+ reg = <0x00000100 0x100>;
+ hvac-provider = <&hvac_1>;
+ hvac-thermostat = <29> ;
+ hvac-zones = <14>;
hvac-zone-names = "operator";
- spin-controller = < &spin_ctrl_2 5 &spin_ctrl_2 7 >;
+ spin-controller = <&spin_ctrl_2 5 &spin_ctrl_2 7>;
spin-controller-names = "track_1", "track_2";
- queues = < 2 >;
+ queues = <2>;
track@30 {
- reg = < 0x00000030 0x10 >;
+ reg = <0x00000030 0x10>;
};
track@40 {
- reg = < 0x00000040 0x10 >;
+ reg = <0x00000040 0x10>;
};
};
@@ -69,23 +69,21 @@
lights_1: lights@30000 {
compatible = "ot,work-lights";
- reg = < 0x00030000 0x1000 >;
+ reg = <0x00030000 0x1000>;
status = "disabled";
};
lights_2: lights@40000 {
compatible = "ot,show-lights";
- reg = < 0x00040000 0x1000 >;
+ reg = <0x00040000 0x1000>;
status = "disabled";
- rate = < 13 138 >;
+ rate = <13 138>;
};
retail_1: vending@50000 {
- reg = < 0x00050000 0x1000 >;
+ reg = <0x00050000 0x1000>;
compatible = "ot,tickets";
status = "disabled";
};
-
};
};
-
diff --git a/drivers/of/unittest-data/overlay_gpio_01.dtso b/drivers/of/unittest-data/overlay_gpio_01.dtso
index 699ff104ae10..bb3a31a2137a 100644
--- a/drivers/of/unittest-data/overlay_gpio_01.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_01.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@0 {
compatible = "unittest-gpio";
reg = <0>;
diff --git a/drivers/of/unittest-data/overlay_gpio_02a.dtso b/drivers/of/unittest-data/overlay_gpio_02a.dtso
index ec59aff6ed47..da955537df74 100644
--- a/drivers/of/unittest-data/overlay_gpio_02a.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_02a.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@2 {
compatible = "unittest-gpio";
reg = <2>;
diff --git a/drivers/of/unittest-data/overlay_gpio_02b.dtso b/drivers/of/unittest-data/overlay_gpio_02b.dtso
index 43ce111d41ce..79503965d3d7 100644
--- a/drivers/of/unittest-data/overlay_gpio_02b.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_02b.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@2 {
line-a {
gpio-hog;
diff --git a/drivers/of/unittest-data/overlay_gpio_03.dtso b/drivers/of/unittest-data/overlay_gpio_03.dtso
index 6e0312340a1b..d8c709616029 100644
--- a/drivers/of/unittest-data/overlay_gpio_03.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_03.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@3 {
compatible = "unittest-gpio";
reg = <3>;
diff --git a/drivers/of/unittest-data/overlay_gpio_04a.dtso b/drivers/of/unittest-data/overlay_gpio_04a.dtso
index 7b1e04ebfa7a..de86511972c2 100644
--- a/drivers/of/unittest-data/overlay_gpio_04a.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_04a.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@4 {
compatible = "unittest-gpio";
reg = <4>;
diff --git a/drivers/of/unittest-data/overlay_gpio_04b.dtso b/drivers/of/unittest-data/overlay_gpio_04b.dtso
index a14e95c6699a..dc6eff22f927 100644
--- a/drivers/of/unittest-data/overlay_gpio_04b.dtso
+++ b/drivers/of/unittest-data/overlay_gpio_04b.dtso
@@ -5,6 +5,7 @@
&unittest_test_bus {
#address-cells = <1>;
#size-cells = <0>;
+
gpio@4 {
line-c {
gpio-hog;
diff --git a/drivers/of/unittest-data/overlay_pci_node.dtso b/drivers/of/unittest-data/overlay_pci_node.dtso
new file mode 100644
index 000000000000..c05e52e9e44a
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_pci_node.dtso
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/ {
+ fragment@0 {
+ target-path="";
+ __overlay__ {
+ #address-cells = <3>;
+ #size-cells = <2>;
+ pci-ep-bus@0 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x0 0x0 0x0 0x1000>;
+ reg = <0 0 0 0 0>;
+ unittest-pci@100 {
+ compatible = "unittest-pci";
+ reg = <0x100 0x200>;
+ };
+ };
+ };
+ };
+};
diff --git a/drivers/of/unittest-data/testcases_common.dtsi b/drivers/of/unittest-data/testcases_common.dtsi
index e7887f2301c1..1c2cdf353ae3 100644
--- a/drivers/of/unittest-data/testcases_common.dtsi
+++ b/drivers/of/unittest-data/testcases_common.dtsi
@@ -5,6 +5,7 @@
changeset {
prop-update = "hello";
prop-remove = "world";
+
node-remove {
};
};
diff --git a/drivers/of/unittest-data/tests-address.dtsi b/drivers/of/unittest-data/tests-address.dtsi
index bc0029cbf8ea..f02a181bb125 100644
--- a/drivers/of/unittest-data/tests-address.dtsi
+++ b/drivers/of/unittest-data/tests-address.dtsi
@@ -51,5 +51,108 @@
};
};
+
+ address-tests2 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+
+ ranges = <0x10000000 0x01000000 0xa0000000 0x01000000>,
+ <0x10000000 0x02000000 0xb0000000 0x01000000>,
+ <0x20000000 0x01000000 0xc0000000 0x01000000>,
+ <0x20000000 0x02000000 0xd0000000 0x01000000>,
+ <0x00000000 0xd1000000 0xd1000000 0x01000000>,
+ <0x00000000 0xe8000000 0xe8000000 0x07f00000>,
+ <0x00000000 0xefff0000 0xefff0000 0x00010000>;
+
+ bus-2cell@10000000 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges = <0x100000 0x10000 0x10000000 0x1a00000 0x10000>,
+ <0x100000 0x20000 0x10000000 0x1b00000 0x10000>,
+ <0x200000 0x10000 0x20000000 0x1c00000 0x10000>,
+ <0x200000 0x20000 0x20000000 0x2d00000 0x10000>;
+
+ device@100000 {
+ reg = <0x100000 0x11000 0x100>,
+ <0x100000 0x12000 0x100>,
+ <0x200000 0x11000 0x100>,
+ <0x200000 0x21000 0x100>;
+ };
+ };
+
+ bus-3cell@20000000 {
+ #address-cells = <3>;
+ #size-cells = <1>;
+ ranges = <0x1 0x100000 0x10000 0x10000000 0x1a00000 0x10000>,
+ <0x2 0x100000 0x10000 0x10000000 0x1b00000 0x10000>,
+ <0x3 0x200000 0x10000 0x20000000 0x1c00000 0x10000>,
+ <0x4 0x200000 0x20000 0x20000000 0x2d00000 0x10000>;
+
+ local-bus@100000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0xf1000000 0x1 0x100000 0x10000 0x10000>,
+ <0xf2000000 0x2 0x100000 0x10000 0x10000>,
+ <0xf3000000 0x3 0x200000 0x10000 0x08000>,
+ <0xf3800000 0x3 0x200000 0x18000 0x08000>,
+ <0xf4000000 0x4 0x200000 0x20000 0x10000>;
+
+ device@f1001000 {
+ reg = <0xf1001000 0x100>,
+ <0xf2002000 0x100>,
+ <0xf3001000 0x100>,
+ <0xf3801000 0x100>,
+ <0xf4001000 0x100>;
+ };
+ };
+ };
+
+ pcie@d1070000 {
+ #address-cells = <0x03>;
+ #size-cells = <0x02>;
+ bus-range = <0x00 0xff>;
+ device_type = "pci";
+ ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x7f00000>,
+ <0x81000000 0 0x00000000 0 0xefff0000 0 0x0010000>;
+ dma-ranges = <0x43000000 0x10 0x00 0x00 0x00 0x00 0x10000000>;
+ reg = <0x00000000 0xd1070000 0x20000>;
+
+ pci@0,0 {
+ #address-cells = <0x03>;
+ #size-cells = <0x02>;
+ bus-range = <0x01 0x01>;
+ device_type = "pci";
+ ranges = <0x82000000 0 0xe8000000
+ 0x82000000 0 0xe8000000
+ 0 0x4400000>;
+ reg = <0x00 0x00 0x00 0x00 0x00>;
+
+ dev@0,0 {
+ #address-cells = <0x03>;
+ #size-cells = <0x02>;
+ ranges = <0 0 0 0x82010000 0 0xe8000000 0 0x2000000>,
+ <1 0 0 0x82010000 0 0xea000000 0 0x1000000>,
+ <2 0 0 0x82010000 0 0xeb000000 0 0x0800000>,
+ <3 0 0 0x82010000 0 0xeb800000 0 0x0800000>,
+ <4 0 0 0x82010000 0 0xec000000 0 0x0020000>,
+ <5 0 0 0x82010000 0 0xec020000 0 0x0002000>;
+ reg = <0x10000 0x00 0x00 0x00 0x00>;
+
+ local-bus@0 {
+ #address-cells = <0x01>;
+ #size-cells = <0x01>;
+ ranges = <0xa0000000 0 0 0 0x2000000>,
+ <0xb0000000 1 0 0 0x1000000>;
+ dma-ranges = <0xc0000000 0x43000000 0x10 0x00 0x10000000>;
+
+ dev@e0000000 {
+ reg = <0xa0001000 0x1000>,
+ <0xb0002000 0x2000>;
+ };
+ };
+ };
+ };
+ };
+ };
};
};
diff --git a/drivers/of/unittest-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi
index ecc74dbcc373..4ccb54f91c30 100644
--- a/drivers/of/unittest-data/tests-interrupts.dtsi
+++ b/drivers/of/unittest-data/tests-interrupts.dtsi
@@ -5,6 +5,7 @@
interrupts {
#address-cells = <1>;
#size-cells = <1>;
+
test_intc0: intc0 {
interrupt-controller;
#interrupt-cells = <1>;
@@ -49,6 +50,13 @@
interrupt-map = <0x5000 1 2 &test_intc0 15>;
};
+ test_intc_intmap0: intc-intmap0 {
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ interrupt-controller;
+ interrupt-map = <0x6000 1 &test_intc_intmap0 0x7000 2>;
+ };
+
interrupts0 {
interrupt-parent = <&test_intc0>;
interrupts = <1>, <2>, <3>, <4>;
@@ -59,6 +67,12 @@
interrupts = <1>, <2>, <3>, <4>;
};
+ interrupts2 {
+ reg = <0x6000 0x100>;
+ interrupt-parent = <&test_intc_intmap0>;
+ interrupts = <1>;
+ };
+
interrupts-extended0 {
reg = <0x5000 0x100>;
/*
diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi
index 4ea024d908ee..eb35e8aa5d5a 100644
--- a/drivers/of/unittest-data/tests-overlay.dtsi
+++ b/drivers/of/unittest-data/tests-overlay.dtsi
@@ -3,7 +3,6 @@
/ {
testcase-data {
overlay-node {
-
/* test bus */
unittest_test_bus: test-bus {
compatible = "simple-bus";
diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi
index 6b33be4c4416..554a996b2ef1 100644
--- a/drivers/of/unittest-data/tests-phandle.dtsi
+++ b/drivers/of/unittest-data/tests-phandle.dtsi
@@ -8,7 +8,9 @@
testcase: testcase-data {
security-password = "password";
duplicate-name = "duplicate";
+
duplicate-name { };
+
phandle-tests {
provider0: provider0 {
#phandle-cells = <0>;
@@ -38,6 +40,13 @@
phandle-map-pass-thru = <0x0 0xf0>;
};
+ provider5: provider5 {
+ #phandle-cells = <2>;
+ phandle-map = <2 7 &provider4 2 3>;
+ phandle-map-mask = <0xff 0xf>;
+ phandle-map-pass-thru = <0x0 0xf0>;
+ };
+
consumer-a {
phandle-list = <&provider1 1>,
<&provider2 2 0>,
@@ -64,7 +73,8 @@
<&provider4 4 0x100>,
<&provider4 0 0x61>,
<&provider0>,
- <&provider4 19 0x20>;
+ <&provider4 19 0x20>,
+ <&provider5 2 7>;
phandle-list-bad-phandle = <12345678 0 0>;
phandle-list-bad-args = <&provider2 1 0>,
<&provider4 0>;
diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi
index fa39611071b3..59aa2a9731a7 100644
--- a/drivers/of/unittest-data/tests-platform.dtsi
+++ b/drivers/of/unittest-data/tests-platform.dtsi
@@ -33,6 +33,34 @@
reg = <0x100>;
};
};
+
+ test-device@2 {
+ compatible = "test,rust-device";
+ reg = <0x2>;
+
+ test,u32-prop = <0xdeadbeef>;
+ test,i16-array = /bits/ 16 <1 2 (-3) (-4)>;
+
+ ref_child_0: child-0 {
+ test,ref-arg = <&ref_child_1 0x20 0x32>;
+ };
+ ref_child_1: child-1 {
+ test,ref-arg = <&ref_child_0 0x10 0x64>;
+ };
+ };
+ };
+
+ platform-tests-2 {
+ // No #address-cells or #size-cells
+ node {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ test-device@100 {
+ compatible = "test-sub-device";
+ reg = <0x100 1>;
+ };
+ };
};
};
};
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index a406a12eb208..388e9ec2cccf 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
@@ -49,6 +50,12 @@ static struct unittest_results {
failed; \
})
+#ifdef CONFIG_OF_KOBJ
+#define OF_KREF_READ(NODE) kref_read(&(NODE)->kobj.kref)
+#else
+#define OF_KREF_READ(NODE) 1
+#endif
+
/*
* Expected message may have a message level other than KERN_INFO.
* Print the expected message only if the current loglevel will allow
@@ -77,7 +84,7 @@ static void __init of_unittest_find_node_by_name(void)
np = of_find_node_by_path("/testcase-data");
name = kasprintf(GFP_KERNEL, "%pOF", np);
- unittest(np && !strcmp("/testcase-data", name),
+ unittest(np && name && !strcmp("/testcase-data", name),
"find /testcase-data failed\n");
of_node_put(np);
kfree(name);
@@ -88,14 +95,14 @@ static void __init of_unittest_find_node_by_name(void)
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
name = kasprintf(GFP_KERNEL, "%pOF", np);
- unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name),
+ unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name),
"find /testcase-data/phandle-tests/consumer-a failed\n");
of_node_put(np);
kfree(name);
np = of_find_node_by_path("testcase-alias");
name = kasprintf(GFP_KERNEL, "%pOF", np);
- unittest(np && !strcmp("/testcase-data", name),
+ unittest(np && name && !strcmp("/testcase-data", name),
"find testcase-alias failed\n");
of_node_put(np);
kfree(name);
@@ -106,7 +113,7 @@ static void __init of_unittest_find_node_by_name(void)
np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
name = kasprintf(GFP_KERNEL, "%pOF", np);
- unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name),
+ unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name),
"find testcase-alias/phandle-tests/consumer-a failed\n");
of_node_put(np);
kfree(name);
@@ -154,6 +161,15 @@ static void __init of_unittest_find_node_by_name(void)
"option alias path test, subcase #1 failed\n");
of_node_put(np);
+ np = of_find_node_opts_by_path("testcase-alias/phandle-tests/consumer-a:testaliasoption",
+ &options);
+ name = kasprintf(GFP_KERNEL, "%pOF", np);
+ unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name) &&
+ !strcmp("testaliasoption", options),
+ "option alias path test, subcase #2 failed\n");
+ of_node_put(np);
+ kfree(name);
+
np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL);
unittest(np, "NULL option alias path test failed\n");
of_node_put(np);
@@ -232,27 +248,22 @@ static void __init of_unittest_dynamic(void)
static int __init of_unittest_check_node_linkage(struct device_node *np)
{
- struct device_node *child;
int count = 0, rc;
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
if (child->parent != np) {
pr_err("Child node %pOFn links to wrong parent %pOFn\n",
child, np);
- rc = -EINVAL;
- goto put_child;
+ return -EINVAL;
}
rc = of_unittest_check_node_linkage(child);
if (rc < 0)
- goto put_child;
+ return rc;
count += rc;
}
return count + 1;
-put_child:
- of_node_put(child);
- return rc;
}
static void __init of_unittest_check_tree_linkage(void)
@@ -455,6 +466,9 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(passed, "index %i - data error on node %pOF rc=%i\n",
i, args.np, rc);
+
+ if (rc == 0)
+ of_node_put(args.np);
}
/* Check for missing list property */
@@ -544,8 +558,9 @@ static void __init of_unittest_parse_phandle_with_args(void)
static void __init of_unittest_parse_phandle_with_args_map(void)
{
- struct device_node *np, *p0, *p1, *p2, *p3;
+ struct device_node *np, *p[6] = {};
struct of_phandle_args args;
+ unsigned int prefs[6];
int i, rc;
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b");
@@ -554,34 +569,24 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
return;
}
- p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0");
- if (!p0) {
- pr_err("missing testcase data\n");
- return;
- }
-
- p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1");
- if (!p1) {
- pr_err("missing testcase data\n");
- return;
- }
-
- p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2");
- if (!p2) {
- pr_err("missing testcase data\n");
- return;
- }
-
- p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3");
- if (!p3) {
- pr_err("missing testcase data\n");
- return;
+ p[0] = of_find_node_by_path("/testcase-data/phandle-tests/provider0");
+ p[1] = of_find_node_by_path("/testcase-data/phandle-tests/provider1");
+ p[2] = of_find_node_by_path("/testcase-data/phandle-tests/provider2");
+ p[3] = of_find_node_by_path("/testcase-data/phandle-tests/provider3");
+ p[4] = of_find_node_by_path("/testcase-data/phandle-tests/provider4");
+ p[5] = of_find_node_by_path("/testcase-data/phandle-tests/provider5");
+ for (i = 0; i < ARRAY_SIZE(p); ++i) {
+ if (!p[i]) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+ prefs[i] = OF_KREF_READ(p[i]);
}
rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells");
- unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
+ unittest(rc == 8, "of_count_phandle_with_args() returned %i, expected 8\n", rc);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 9; i++) {
bool passed = true;
memset(&args, 0, sizeof(args));
@@ -592,13 +597,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
switch (i) {
case 0:
passed &= !rc;
- passed &= (args.np == p1);
+ passed &= (args.np == p[1]);
passed &= (args.args_count == 1);
passed &= (args.args[0] == 1);
break;
case 1:
passed &= !rc;
- passed &= (args.np == p3);
+ passed &= (args.np == p[3]);
passed &= (args.args_count == 3);
passed &= (args.args[0] == 2);
passed &= (args.args[1] == 5);
@@ -609,28 +614,36 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
break;
case 3:
passed &= !rc;
- passed &= (args.np == p0);
+ passed &= (args.np == p[0]);
passed &= (args.args_count == 0);
break;
case 4:
passed &= !rc;
- passed &= (args.np == p1);
+ passed &= (args.np == p[1]);
passed &= (args.args_count == 1);
passed &= (args.args[0] == 3);
break;
case 5:
passed &= !rc;
- passed &= (args.np == p0);
+ passed &= (args.np == p[0]);
passed &= (args.args_count == 0);
break;
case 6:
passed &= !rc;
- passed &= (args.np == p2);
+ passed &= (args.np == p[2]);
passed &= (args.args_count == 2);
passed &= (args.args[0] == 15);
passed &= (args.args[1] == 0x20);
break;
case 7:
+ passed &= !rc;
+ passed &= (args.np == p[3]);
+ passed &= (args.args_count == 3);
+ passed &= (args.args[0] == 2);
+ passed &= (args.args[1] == 5);
+ passed &= (args.args[2] == 3);
+ break;
+ case 8:
passed &= (rc == -ENOENT);
break;
default:
@@ -639,6 +652,9 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
unittest(passed, "index %i - data error on node %s rc=%i\n",
i, args.np->full_name, rc);
+
+ if (rc == 0)
+ of_node_put(args.np);
}
/* Check for missing list property */
@@ -664,12 +680,12 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
memset(&args, 0, sizeof(args));
EXPECT_BEGIN(KERN_INFO,
- "OF: /testcase-data/phandle-tests/consumer-b: could not find phandle");
+ "OF: /testcase-data/phandle-tests/consumer-b: could not find phandle 12345678");
rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle",
"phandle", 0, &args);
EXPECT_END(KERN_INFO,
- "OF: /testcase-data/phandle-tests/consumer-b: could not find phandle");
+ "OF: /testcase-data/phandle-tests/consumer-b: could not find phandle 12345678");
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
@@ -685,6 +701,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
"OF: /testcase-data/phandle-tests/consumer-b: #phandle-cells = 2 found 1");
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+
+ for (i = 0; i < ARRAY_SIZE(p); ++i) {
+ unittest(prefs[i] == OF_KREF_READ(p[i]),
+ "provider%d: expected:%d got:%d\n",
+ i, prefs[i], OF_KREF_READ(p[i]));
+ of_node_put(p[i]);
+ }
}
static void __init of_unittest_property_string(void)
@@ -781,29 +804,29 @@ static void __init of_unittest_property_copy(void)
new = __of_prop_dup(&p1, GFP_KERNEL);
unittest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
- kfree(new->value);
- kfree(new->name);
- kfree(new);
+ __of_prop_free(new);
new = __of_prop_dup(&p2, GFP_KERNEL);
unittest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
- kfree(new->value);
- kfree(new->name);
- kfree(new);
+ __of_prop_free(new);
#endif
}
static void __init of_unittest_changeset(void)
{
#ifdef CONFIG_OF_DYNAMIC
+ int ret;
struct property *ppadd, padd = { .name = "prop-add", .length = 1, .value = "" };
struct property *ppname_n1, pname_n1 = { .name = "name", .length = 3, .value = "n1" };
struct property *ppname_n2, pname_n2 = { .name = "name", .length = 3, .value = "n2" };
struct property *ppname_n21, pname_n21 = { .name = "name", .length = 3, .value = "n21" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove;
- struct device_node *n1, *n2, *n21, *nchangeset, *nremove, *parent, *np;
+ struct device_node *n1, *n2, *n21, *n22, *nchangeset, *nremove, *parent, *np;
+ static const char * const str_array[] = { "str1", "str2", "str3" };
+ const u32 u32_array[] = { 1, 2, 3 };
struct of_changeset chgset;
+ const char *propstr = NULL;
n1 = __of_node_dup(NULL, "n1");
unittest(n1, "testcase setup failure\n");
@@ -857,6 +880,17 @@ static void __init of_unittest_changeset(void)
unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop prop-add\n");
unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
+ n22 = of_changeset_create_node(&chgset, n2, "n22");
+ unittest(n22, "fail create n22\n");
+ unittest(!of_changeset_add_prop_string(&chgset, n22, "prop-str", "abcd"),
+ "fail add prop prop-str");
+ unittest(!of_changeset_add_prop_string_array(&chgset, n22, "prop-str-array",
+ (const char **)str_array,
+ ARRAY_SIZE(str_array)),
+ "fail add prop prop-str-array");
+ unittest(!of_changeset_add_prop_u32_array(&chgset, n22, "prop-u32-array",
+ u32_array, ARRAY_SIZE(u32_array)),
+ "fail add prop prop-u32-array");
unittest(!of_changeset_apply(&chgset), "apply failed\n");
@@ -866,14 +900,194 @@ static void __init of_unittest_changeset(void)
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%pOF' not added\n", n21);
of_node_put(np);
+ unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n22")),
+ "'%pOF' not added\n", n22);
+ of_node_put(np);
unittest(!of_changeset_revert(&chgset), "revert failed\n");
+ unittest(!of_find_node_by_path("/testcase-data/changeset/n2/n21"),
+ "'%pOF' still present after revert\n", n21);
+
+ unittest(of_property_present(parent, "prop-remove"),
+ "failed to find removed prop after revert\n");
+
+ ret = of_property_read_string(parent, "prop-update", &propstr);
+ unittest(!ret, "failed to find updated prop after revert\n");
+ if (!ret)
+ unittest(strcmp(propstr, "hello") == 0, "original value not in updated property after revert");
+
of_changeset_destroy(&chgset);
of_node_put(n1);
of_node_put(n2);
of_node_put(n21);
+ of_node_put(n22);
+#endif
+}
+
+static void __init __maybe_unused changeset_check_string(struct device_node *np,
+ const char *prop_name,
+ const char *expected_str)
+{
+ const char *str;
+ int ret;
+
+ ret = of_property_read_string(np, prop_name, &str);
+ if (unittest(ret == 0, "failed to read %s\n", prop_name))
+ return;
+
+ unittest(strcmp(str, expected_str) == 0,
+ "%s value mismatch (read '%s', exp '%s')\n",
+ prop_name, str, expected_str);
+}
+
+static void __init __maybe_unused changeset_check_string_array(struct device_node *np,
+ const char *prop_name,
+ const char * const *expected_array,
+ unsigned int count)
+{
+ const char *str;
+ unsigned int i;
+ int ret;
+ int cnt;
+
+ cnt = of_property_count_strings(np, prop_name);
+ if (unittest(cnt >= 0, "failed to get %s count\n", prop_name))
+ return;
+
+ if (unittest(cnt == count,
+ "%s count mismatch (read %d, exp %u)\n",
+ prop_name, cnt, count))
+ return;
+
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_string_index(np, prop_name, i, &str);
+ if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i))
+ continue;
+
+ unittest(strcmp(str, expected_array[i]) == 0,
+ "%s[%d] value mismatch (read '%s', exp '%s')\n",
+ prop_name, i, str, expected_array[i]);
+ }
+}
+
+static void __init __maybe_unused changeset_check_u32(struct device_node *np,
+ const char *prop_name,
+ u32 expected_u32)
+{
+ u32 val32;
+ int ret;
+
+ ret = of_property_read_u32(np, prop_name, &val32);
+ if (unittest(ret == 0, "failed to read %s\n", prop_name))
+ return;
+
+ unittest(val32 == expected_u32,
+ "%s value mismatch (read '%u', exp '%u')\n",
+ prop_name, val32, expected_u32);
+}
+
+static void __init __maybe_unused changeset_check_u32_array(struct device_node *np,
+ const char *prop_name,
+ const u32 *expected_array,
+ unsigned int count)
+{
+ unsigned int i;
+ u32 val32;
+ int ret;
+ int cnt;
+
+ cnt = of_property_count_u32_elems(np, prop_name);
+ if (unittest(cnt >= 0, "failed to get %s count\n", prop_name))
+ return;
+
+ if (unittest(cnt == count,
+ "%s count mismatch (read %d, exp %u)\n",
+ prop_name, cnt, count))
+ return;
+
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_u32_index(np, prop_name, i, &val32);
+ if (unittest(ret == 0, "failed to read %s[%d]\n", prop_name, i))
+ continue;
+
+ unittest(val32 == expected_array[i],
+ "%s[%d] value mismatch (read '%u', exp '%u')\n",
+ prop_name, i, val32, expected_array[i]);
+ }
+}
+
+static void __init __maybe_unused changeset_check_bool(struct device_node *np,
+ const char *prop_name)
+{
+ unittest(of_property_read_bool(np, prop_name),
+ "%s value mismatch (read 'false', exp 'true')\n", prop_name);
+}
+
+static void __init of_unittest_changeset_prop(void)
+{
+#ifdef CONFIG_OF_DYNAMIC
+ static const char * const str_array[] = { "abc", "defg", "hij" };
+ static const u32 u32_array[] = { 123, 4567, 89, 10, 11 };
+ struct device_node *nchangeset, *np;
+ struct of_changeset chgset;
+ int ret;
+
+ nchangeset = of_find_node_by_path("/testcase-data/changeset");
+ if (!nchangeset) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ of_changeset_init(&chgset);
+
+ np = of_changeset_create_node(&chgset, nchangeset, "test-prop");
+ if (unittest(np, "failed to create test-prop node\n"))
+ goto end_changeset_destroy;
+
+ ret = of_changeset_add_prop_string(&chgset, np, "prop-string", "abcde");
+ unittest(ret == 0, "failed to add prop-string\n");
+
+ ret = of_changeset_add_prop_string_array(&chgset, np, "prop-string-array",
+ str_array, ARRAY_SIZE(str_array));
+ unittest(ret == 0, "failed to add prop-string-array\n");
+
+ ret = of_changeset_add_prop_u32(&chgset, np, "prop-u32", 1234);
+ unittest(ret == 0, "failed to add prop-u32\n");
+
+ ret = of_changeset_add_prop_u32_array(&chgset, np, "prop-u32-array",
+ u32_array, ARRAY_SIZE(u32_array));
+ unittest(ret == 0, "failed to add prop-u32-array\n");
+
+ ret = of_changeset_add_prop_bool(&chgset, np, "prop-bool");
+ unittest(ret == 0, "failed to add prop-bool\n");
+
+ of_node_put(np);
+
+ ret = of_changeset_apply(&chgset);
+ if (unittest(ret == 0, "failed to apply changeset\n"))
+ goto end_changeset_destroy;
+
+ np = of_find_node_by_path("/testcase-data/changeset/test-prop");
+ if (unittest(np, "failed to find test-prop node\n"))
+ goto end_revert_changeset;
+
+ changeset_check_string(np, "prop-string", "abcde");
+ changeset_check_string_array(np, "prop-string-array", str_array, ARRAY_SIZE(str_array));
+ changeset_check_u32(np, "prop-u32", 1234);
+ changeset_check_u32_array(np, "prop-u32-array", u32_array, ARRAY_SIZE(u32_array));
+ changeset_check_bool(np, "prop-bool");
+
+ of_node_put(np);
+
+end_revert_changeset:
+ ret = of_changeset_revert(&chgset);
+ unittest(ret == 0, "failed to revert changeset\n");
+
+end_changeset_destroy:
+ of_changeset_destroy(&chgset);
+ of_node_put(nchangeset);
#endif
}
@@ -1008,6 +1222,44 @@ static void __init of_unittest_pci_dma_ranges(void)
of_node_put(np);
}
+static void __init of_unittest_pci_empty_dma_ranges(void)
+{
+ struct device_node *np;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+
+ if (!IS_ENABLED(CONFIG_PCI))
+ return;
+
+ np = of_find_node_by_path("/testcase-data/address-tests2/pcie@d1070000/pci@0,0/dev@0,0/local-bus@0");
+ if (!np) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ if (of_pci_dma_range_parser_init(&parser, np)) {
+ pr_err("missing dma-ranges property\n");
+ return;
+ }
+
+ /*
+ * Get the dma-ranges from the device tree
+ */
+ for_each_of_pci_range(&parser, &range) {
+ unittest(range.size == 0x10000000,
+ "for_each_of_pci_range wrong size on node %pOF size=%llx\n",
+ np, range.size);
+ unittest(range.cpu_addr == 0x00000000,
+ "for_each_of_pci_range wrong CPU addr (%llx) on node %pOF",
+ range.cpu_addr, np);
+ unittest(range.pci_addr == 0xc0000000,
+ "for_each_of_pci_range wrong DMA addr (%llx) on node %pOF",
+ range.pci_addr, np);
+ }
+
+ of_node_put(np);
+}
+
static void __init of_unittest_bus_ranges(void)
{
struct device_node *np;
@@ -1137,6 +1389,7 @@ static void __init of_unittest_bus_3cell_ranges(void)
static void __init of_unittest_reg(void)
{
struct device_node *np;
+ struct resource res;
int ret;
u64 addr, size;
@@ -1153,6 +1406,95 @@ static void __init of_unittest_reg(void)
np, addr);
of_node_put(np);
+
+ np = of_find_node_by_path("/testcase-data/platform-tests-2/node/test-device@100");
+ if (!np) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ ret = of_address_to_resource(np, 0, &res);
+ unittest(ret == -EINVAL, "of_address_to_resource(%pOF) expected error on untranslatable address\n",
+ np);
+
+ of_node_put(np);
+
+}
+
+struct of_unittest_expected_res {
+ int index;
+ struct resource res;
+};
+
+static void __init of_unittest_check_addr(const char *node_path,
+ const struct of_unittest_expected_res *tab_exp,
+ unsigned int tab_exp_count)
+{
+ const struct of_unittest_expected_res *expected;
+ struct device_node *np;
+ struct resource res;
+ unsigned int count;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_OF_ADDRESS))
+ return;
+
+ np = of_find_node_by_path(node_path);
+ if (!np) {
+ pr_err("missing testcase data (%s)\n", node_path);
+ return;
+ }
+
+ expected = tab_exp;
+ count = tab_exp_count;
+ while (count--) {
+ ret = of_address_to_resource(np, expected->index, &res);
+ unittest(!ret, "of_address_to_resource(%pOF, %d) returned error %d\n",
+ np, expected->index, ret);
+ unittest(resource_type(&res) == resource_type(&expected->res) &&
+ res.start == expected->res.start &&
+ resource_size(&res) == resource_size(&expected->res),
+ "of_address_to_resource(%pOF, %d) wrong resource %pR, expected %pR\n",
+ np, expected->index, &res, &expected->res);
+ expected++;
+ }
+
+ of_node_put(np);
+}
+
+static const struct of_unittest_expected_res of_unittest_reg_2cell_expected_res[] = {
+ {.index = 0, .res = DEFINE_RES_MEM(0xa0a01000, 0x100) },
+ {.index = 1, .res = DEFINE_RES_MEM(0xa0a02000, 0x100) },
+ {.index = 2, .res = DEFINE_RES_MEM(0xc0c01000, 0x100) },
+ {.index = 3, .res = DEFINE_RES_MEM(0xd0d01000, 0x100) },
+};
+
+static const struct of_unittest_expected_res of_unittest_reg_3cell_expected_res[] = {
+ {.index = 0, .res = DEFINE_RES_MEM(0xa0a01000, 0x100) },
+ {.index = 1, .res = DEFINE_RES_MEM(0xa0b02000, 0x100) },
+ {.index = 2, .res = DEFINE_RES_MEM(0xc0c01000, 0x100) },
+ {.index = 3, .res = DEFINE_RES_MEM(0xc0c09000, 0x100) },
+ {.index = 4, .res = DEFINE_RES_MEM(0xd0d01000, 0x100) },
+};
+
+static const struct of_unittest_expected_res of_unittest_reg_pci_expected_res[] = {
+ {.index = 0, .res = DEFINE_RES_MEM(0xe8001000, 0x1000) },
+ {.index = 1, .res = DEFINE_RES_MEM(0xea002000, 0x2000) },
+};
+
+static void __init of_unittest_translate_addr(void)
+{
+ of_unittest_check_addr("/testcase-data/address-tests2/bus-2cell@10000000/device@100000",
+ of_unittest_reg_2cell_expected_res,
+ ARRAY_SIZE(of_unittest_reg_2cell_expected_res));
+
+ of_unittest_check_addr("/testcase-data/address-tests2/bus-3cell@20000000/local-bus@100000/device@f1001000",
+ of_unittest_reg_3cell_expected_res,
+ ARRAY_SIZE(of_unittest_reg_3cell_expected_res));
+
+ of_unittest_check_addr("/testcase-data/address-tests2/pcie@d1070000/pci@0,0/dev@0,0/local-bus@0/dev@e0000000",
+ of_unittest_reg_pci_expected_res,
+ ARRAY_SIZE(of_unittest_reg_pci_expected_res));
}
static void __init of_unittest_parse_interrupts(void)
@@ -1312,6 +1654,72 @@ static void __init of_unittest_parse_interrupts_extended(void)
of_node_put(np);
}
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static void __init of_unittest_irq_refcount(void)
+{
+ struct of_phandle_args args;
+ struct device_node *intc0, *int_ext0;
+ struct device_node *int2, *intc_intmap0;
+ unsigned int ref_c0, ref_c1, ref_c2;
+ int rc;
+ bool passed;
+
+ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
+ return;
+
+ intc0 = of_find_node_by_path("/testcase-data/interrupts/intc0");
+ int_ext0 = of_find_node_by_path("/testcase-data/interrupts/interrupts-extended0");
+ intc_intmap0 = of_find_node_by_path("/testcase-data/interrupts/intc-intmap0");
+ int2 = of_find_node_by_path("/testcase-data/interrupts/interrupts2");
+ if (!intc0 || !int_ext0 || !intc_intmap0 || !int2) {
+ pr_err("missing testcase data\n");
+ goto out;
+ }
+
+ /* Test refcount for API of_irq_parse_one() */
+ passed = true;
+ ref_c0 = OF_KREF_READ(intc0);
+ ref_c1 = ref_c0 + 1;
+ memset(&args, 0, sizeof(args));
+ rc = of_irq_parse_one(int_ext0, 0, &args);
+ ref_c2 = OF_KREF_READ(intc0);
+ of_node_put(args.np);
+
+ passed &= !rc;
+ passed &= (args.np == intc0);
+ passed &= (args.args_count == 1);
+ passed &= (args.args[0] == 1);
+ passed &= (ref_c1 == ref_c2);
+ unittest(passed, "IRQ refcount case #1 failed, original(%u) expected(%u) got(%u)\n",
+ ref_c0, ref_c1, ref_c2);
+
+ /* Test refcount for API of_irq_parse_raw() */
+ passed = true;
+ ref_c0 = OF_KREF_READ(intc_intmap0);
+ ref_c1 = ref_c0 + 1;
+ memset(&args, 0, sizeof(args));
+ rc = of_irq_parse_one(int2, 0, &args);
+ ref_c2 = OF_KREF_READ(intc_intmap0);
+ of_node_put(args.np);
+
+ passed &= !rc;
+ passed &= (args.np == intc_intmap0);
+ passed &= (args.args_count == 1);
+ passed &= (args.args[0] == 2);
+ passed &= (ref_c1 == ref_c2);
+ unittest(passed, "IRQ refcount case #2 failed, original(%u) expected(%u) got(%u)\n",
+ ref_c0, ref_c1, ref_c2);
+
+out:
+ of_node_put(int2);
+ of_node_put(intc_intmap0);
+ of_node_put(int_ext0);
+ of_node_put(intc0);
+}
+#else
+static inline void __init of_unittest_irq_refcount(void) { }
+#endif
+
static const struct of_device_id match_node_table[] = {
{ .data = "A", .name = "name0", }, /* Name alone is lowest priority */
{ .data = "B", .type = "type1", }, /* followed by type alone */
@@ -1448,6 +1856,8 @@ static void __init of_unittest_platform_populate(void)
of_platform_populate(np, match, NULL, &test_bus->dev);
for_each_child_of_node(np, child) {
for_each_child_of_node(child, grandchild) {
+ if (!of_property_present(grandchild, "compatible"))
+ continue;
pdev = of_find_device_by_node(grandchild);
unittest(pdev,
"Could not create device for node '%pOFn'\n",
@@ -1533,6 +1943,8 @@ static void attach_node_and_children(struct device_node *np)
const char *full_name;
full_name = kasprintf(GFP_KERNEL, "%pOF", np);
+ if (!full_name)
+ return;
if (!strcmp(full_name, "/__local_fixups__") ||
!strcmp(full_name, "/__fixups__")) {
@@ -1578,7 +1990,7 @@ static int __init unittest_data_add(void)
struct device_node *unittest_data_node = NULL, *np;
/*
* __dtbo_testcases_begin[] and __dtbo_testcases_end[] are magically
- * created by cmd_dt_S_dtbo in scripts/Makefile.lib
+ * created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs
*/
extern uint8_t __dtbo_testcases_begin[];
extern uint8_t __dtbo_testcases_end[];
@@ -1619,24 +2031,21 @@ static int __init unittest_data_add(void)
rc = of_resolve_phandles(unittest_data_node);
if (rc) {
pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
- of_overlay_mutex_unlock();
- return -EINVAL;
+ rc = -EINVAL;
+ goto unlock;
}
+ /* attach the sub-tree to live tree */
if (!of_root) {
- of_root = unittest_data_node;
- for_each_of_allnodes(np)
- __of_attach_node_sysfs(np);
- of_aliases = of_find_node_by_path("/aliases");
- of_chosen = of_find_node_by_path("/chosen");
- of_overlay_mutex_unlock();
- return 0;
+ pr_warn("%s: no live tree to attach sub-tree\n", __func__);
+ kfree(unittest_data);
+ rc = -ENODEV;
+ goto unlock;
}
EXPECT_BEGIN(KERN_INFO,
"Duplicate name in testcase-data, renamed to \"duplicate-name#1\"");
- /* attach the sub-tree to live tree */
np = unittest_data_node->child;
while (np) {
struct device_node *next = np->sibling;
@@ -1650,9 +2059,10 @@ static int __init unittest_data_add(void)
EXPECT_END(KERN_INFO,
"Duplicate name in testcase-data, renamed to \"duplicate-name#1\"");
+unlock:
of_overlay_mutex_unlock();
- return 0;
+ return rc;
}
#ifdef CONFIG_OF_OVERLAY
@@ -1691,10 +2101,10 @@ static const struct of_device_id unittest_match[] = {
static struct platform_driver unittest_driver = {
.probe = unittest_probe,
- .remove_new = unittest_remove,
+ .remove = unittest_remove,
.driver = {
.name = "unittest",
- .of_match_table = of_match_ptr(unittest_match),
+ .of_match_table = unittest_match,
},
};
@@ -1792,10 +2202,10 @@ static const struct of_device_id unittest_gpio_id[] = {
static struct platform_driver unittest_gpio_driver = {
.probe = unittest_gpio_probe,
- .remove_new = unittest_gpio_remove,
+ .remove = unittest_gpio_remove,
.driver = {
.name = "unittest-gpio",
- .of_match_table = of_match_ptr(unittest_gpio_id),
+ .of_match_table = unittest_gpio_id,
},
};
@@ -2101,14 +2511,13 @@ static int __init of_unittest_apply_overlay(int overlay_nr, int *ovcs_id)
return 0;
}
-/* apply an overlay while checking before and after states */
-static int __init of_unittest_apply_overlay_check(int overlay_nr,
+static int __init __of_unittest_apply_overlay_check(int overlay_nr,
int unittest_nr, int before, int after,
enum overlay_type ovtype)
{
int ret, ovcs_id;
- /* unittest device must not be in before state */
+ /* unittest device must be in before state */
if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
@@ -2117,6 +2526,7 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr,
return -EINVAL;
}
+ /* apply the overlay */
ovcs_id = 0;
ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id);
if (ret != 0) {
@@ -2124,15 +2534,28 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr,
return ret;
}
- /* unittest device must be to set to after state */
+ /* unittest device must be in after state */
if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
- unittest(0, "%s failed to create @\"%s\" %s\n",
+ unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype),
!after ? "enabled" : "disabled");
return -EINVAL;
}
+ return ovcs_id;
+}
+
+/* apply an overlay while checking before and after states */
+static int __init of_unittest_apply_overlay_check(int overlay_nr,
+ int unittest_nr, int before, int after,
+ enum overlay_type ovtype)
+{
+ int ovcs_id = __of_unittest_apply_overlay_check(overlay_nr,
+ unittest_nr, before, after, ovtype);
+ if (ovcs_id < 0)
+ return ovcs_id;
+
return 0;
}
@@ -2143,32 +2566,12 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
{
int ret, ovcs_id, save_ovcs_id;
- /* unittest device must be in before state */
- if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
- unittest(0, "%s with device @\"%s\" %s\n",
- overlay_name_from_nr(overlay_nr),
- unittest_path(unittest_nr, ovtype),
- !before ? "enabled" : "disabled");
- return -EINVAL;
- }
-
- /* apply the overlay */
- ovcs_id = 0;
- ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id);
- if (ret != 0) {
- /* of_unittest_apply_overlay already called unittest() */
- return ret;
- }
-
- /* unittest device must be in after state */
- if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
- unittest(0, "%s failed to create @\"%s\" %s\n",
- overlay_name_from_nr(overlay_nr),
- unittest_path(unittest_nr, ovtype),
- !after ? "enabled" : "disabled");
- return -EINVAL;
- }
+ ovcs_id = __of_unittest_apply_overlay_check(overlay_nr, unittest_nr,
+ before, after, ovtype);
+ if (ovcs_id < 0)
+ return ovcs_id;
+ /* remove the overlay */
save_ovcs_id = ovcs_id;
ret = of_overlay_remove(&ovcs_id);
if (ret != 0) {
@@ -2180,7 +2583,7 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
of_unittest_untrack_overlay(save_ovcs_id);
/* unittest device must be again in before state */
- if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) {
+ if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "%s with device @\"%s\" %s\n",
overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype),
@@ -2619,10 +3022,10 @@ static const struct of_device_id unittest_i2c_bus_match[] = {
static struct platform_driver unittest_i2c_bus_driver = {
.probe = unittest_i2c_bus_probe,
- .remove_new = unittest_i2c_bus_remove,
+ .remove = unittest_i2c_bus_remove,
.driver = {
.name = "unittest-i2c-bus",
- .of_match_table = of_match_ptr(unittest_i2c_bus_match),
+ .of_match_table = unittest_i2c_bus_match,
},
};
@@ -2704,7 +3107,7 @@ static int unittest_i2c_mux_probe(struct i2c_client *client)
if (!muxc)
return -ENOMEM;
for (i = 0; i < nchans; i++) {
- if (i2c_mux_add_adapter(muxc, 0, i, 0)) {
+ if (i2c_mux_add_adapter(muxc, 0, i)) {
dev_err(dev, "Failed to register mux #%d\n", i);
i2c_mux_del_adapters(muxc);
return -ENODEV;
@@ -2971,12 +3374,6 @@ static void __init of_unittest_overlay_notify(void)
unittest(ovcs_id, "ovcs_id not created for overlay_17\n");
- if (ovcs_id) {
- ret = of_overlay_remove(&ovcs_id);
- unittest(!ret,
- "overlay_17 of_overlay_remove(), ret = %d\n", ret);
- }
-
/* --- overlay 18 --- */
unittest(overlay_data_apply("overlay_18", &ovcs_id),
@@ -3046,6 +3443,7 @@ static void __init of_unittest_overlay_notify(void)
static void __init of_unittest_overlay(void)
{
struct device_node *bus_np = NULL;
+ unsigned int i;
if (platform_driver_register(&unittest_driver)) {
unittest(0, "could not register unittest driver\n");
@@ -3083,7 +3481,8 @@ static void __init of_unittest_overlay(void)
of_unittest_overlay_2();
of_unittest_overlay_3();
of_unittest_overlay_4();
- of_unittest_overlay_5();
+ for (i = 0; i < 3; i++)
+ of_unittest_overlay_5();
of_unittest_overlay_6();
of_unittest_overlay_8();
@@ -3257,24 +3656,26 @@ out_skip_tests:
/*
* __dtbo_##overlay_name##_begin[] and __dtbo_##overlay_name##_end[] are
- * created by cmd_dt_S_dtbo in scripts/Makefile.lib
+ * created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs
*/
#define OVERLAY_INFO_EXTERN(overlay_name) \
extern uint8_t __dtbo_##overlay_name##_begin[]; \
extern uint8_t __dtbo_##overlay_name##_end[]
-#define OVERLAY_INFO(overlay_name, expected) \
-{ .dtbo_begin = __dtbo_##overlay_name##_begin, \
- .dtbo_end = __dtbo_##overlay_name##_end, \
- .expected_result = expected, \
- .name = #overlay_name, \
+#define OVERLAY_INFO(overlay_name, expected, expected_remove) \
+{ .dtbo_begin = __dtbo_##overlay_name##_begin, \
+ .dtbo_end = __dtbo_##overlay_name##_end, \
+ .expected_result = expected, \
+ .expected_result_remove = expected_remove, \
+ .name = #overlay_name, \
}
struct overlay_info {
uint8_t *dtbo_begin;
uint8_t *dtbo_end;
int expected_result;
+ int expected_result_remove; /* if apply failed */
int ovcs_id;
char *name;
};
@@ -3307,60 +3708,58 @@ OVERLAY_INFO_EXTERN(overlay_gpio_02b);
OVERLAY_INFO_EXTERN(overlay_gpio_03);
OVERLAY_INFO_EXTERN(overlay_gpio_04a);
OVERLAY_INFO_EXTERN(overlay_gpio_04b);
+OVERLAY_INFO_EXTERN(overlay_pci_node);
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node);
OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop);
OVERLAY_INFO_EXTERN(overlay_bad_phandle);
OVERLAY_INFO_EXTERN(overlay_bad_symbol);
+OVERLAY_INFO_EXTERN(overlay_bad_unresolved);
/* entries found by name */
static struct overlay_info overlays[] = {
- OVERLAY_INFO(overlay_base, -9999),
- OVERLAY_INFO(overlay, 0),
- OVERLAY_INFO(overlay_0, 0),
- OVERLAY_INFO(overlay_1, 0),
- OVERLAY_INFO(overlay_2, 0),
- OVERLAY_INFO(overlay_3, 0),
- OVERLAY_INFO(overlay_4, 0),
- OVERLAY_INFO(overlay_5, 0),
- OVERLAY_INFO(overlay_6, 0),
- OVERLAY_INFO(overlay_7, 0),
- OVERLAY_INFO(overlay_8, 0),
- OVERLAY_INFO(overlay_9, 0),
- OVERLAY_INFO(overlay_10, 0),
- OVERLAY_INFO(overlay_11, 0),
- OVERLAY_INFO(overlay_12, 0),
- OVERLAY_INFO(overlay_13, 0),
- OVERLAY_INFO(overlay_15, 0),
- OVERLAY_INFO(overlay_16, -EBUSY),
- OVERLAY_INFO(overlay_17, -EEXIST),
- OVERLAY_INFO(overlay_18, 0),
- OVERLAY_INFO(overlay_19, 0),
- OVERLAY_INFO(overlay_20, 0),
- OVERLAY_INFO(overlay_gpio_01, 0),
- OVERLAY_INFO(overlay_gpio_02a, 0),
- OVERLAY_INFO(overlay_gpio_02b, 0),
- OVERLAY_INFO(overlay_gpio_03, 0),
- OVERLAY_INFO(overlay_gpio_04a, 0),
- OVERLAY_INFO(overlay_gpio_04b, 0),
- OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL),
- OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL),
- OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
- OVERLAY_INFO(overlay_bad_symbol, -EINVAL),
+ OVERLAY_INFO(overlay_base, -9999, 0),
+ OVERLAY_INFO(overlay, 0, 0),
+ OVERLAY_INFO(overlay_0, 0, 0),
+ OVERLAY_INFO(overlay_1, 0, 0),
+ OVERLAY_INFO(overlay_2, 0, 0),
+ OVERLAY_INFO(overlay_3, 0, 0),
+ OVERLAY_INFO(overlay_4, 0, 0),
+ OVERLAY_INFO(overlay_5, 0, 0),
+ OVERLAY_INFO(overlay_6, 0, 0),
+ OVERLAY_INFO(overlay_7, 0, 0),
+ OVERLAY_INFO(overlay_8, 0, 0),
+ OVERLAY_INFO(overlay_9, 0, 0),
+ OVERLAY_INFO(overlay_10, 0, 0),
+ OVERLAY_INFO(overlay_11, 0, 0),
+ OVERLAY_INFO(overlay_12, 0, 0),
+ OVERLAY_INFO(overlay_13, 0, 0),
+ OVERLAY_INFO(overlay_15, 0, 0),
+ OVERLAY_INFO(overlay_16, -EBUSY, 0),
+ OVERLAY_INFO(overlay_17, -EEXIST, 0),
+ OVERLAY_INFO(overlay_18, 0, 0),
+ OVERLAY_INFO(overlay_19, 0, 0),
+ OVERLAY_INFO(overlay_20, 0, 0),
+ OVERLAY_INFO(overlay_gpio_01, 0, 0),
+ OVERLAY_INFO(overlay_gpio_02a, 0, 0),
+ OVERLAY_INFO(overlay_gpio_02b, 0, 0),
+ OVERLAY_INFO(overlay_gpio_03, 0, 0),
+ OVERLAY_INFO(overlay_gpio_04a, 0, 0),
+ OVERLAY_INFO(overlay_gpio_04b, 0, 0),
+ OVERLAY_INFO(overlay_pci_node, 0, 0),
+ OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL, -ENODEV),
+ OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL, -ENODEV),
+ OVERLAY_INFO(overlay_bad_phandle, -EINVAL, 0),
+ OVERLAY_INFO(overlay_bad_symbol, -EINVAL, -ENODEV),
+ OVERLAY_INFO(overlay_bad_unresolved, -EINVAL, 0),
/* end marker */
- {.dtbo_begin = NULL, .dtbo_end = NULL, .expected_result = 0, .name = NULL}
+ { }
};
static struct device_node *overlay_base_root;
static void * __init dt_alloc_memory(u64 size, u64 align)
{
- void *ptr = memblock_alloc(size, align);
-
- if (!ptr)
- panic("%s: Failed to allocate %llu bytes align=0x%llx\n",
- __func__, size, align);
-
- return ptr;
+ return memblock_alloc_or_panic(size, align);
}
/*
@@ -3442,8 +3841,9 @@ void __init unittest_unflatten_overlay_base(void)
static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id)
{
struct overlay_info *info;
+ int passed = 1;
int found = 0;
- int ret;
+ int ret, ret2;
u32 size;
for (info = overlays; info && info->name; info++) {
@@ -3461,7 +3861,8 @@ static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id)
if (!size)
pr_err("no overlay data for %s\n", overlay_name);
- ret = of_overlay_fdt_apply(info->dtbo_begin, size, &info->ovcs_id);
+ ret = of_overlay_fdt_apply(info->dtbo_begin, size, &info->ovcs_id,
+ NULL);
if (ovcs_id)
*ovcs_id = info->ovcs_id;
if (ret < 0)
@@ -3470,11 +3871,24 @@ static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id)
pr_debug("%s applied\n", overlay_name);
out:
- if (ret != info->expected_result)
+ if (ret != info->expected_result) {
pr_err("of_overlay_fdt_apply() expected %d, ret=%d, %s\n",
info->expected_result, ret, overlay_name);
+ passed = 0;
+ }
+
+ if (ret < 0) {
+ /* changeset may be partially applied */
+ ret2 = of_overlay_remove(&info->ovcs_id);
+ if (ret2 != info->expected_result_remove) {
+ pr_err("of_overlay_remove() expected %d, ret=%d, %s\n",
+ info->expected_result_remove, ret2,
+ overlay_name);
+ passed = 0;
+ }
+ }
- return (ret == info->expected_result);
+ return passed;
}
/*
@@ -3590,9 +4004,7 @@ static __init void of_unittest_overlay_high_level(void)
goto err_unlock;
}
if (__of_add_property(of_symbols, new_prop)) {
- kfree(new_prop->name);
- kfree(new_prop->value);
- kfree(new_prop);
+ __of_prop_free(new_prop);
/* "name" auto-generated by unflatten */
if (!strcmp(prop->name, "name"))
continue;
@@ -3613,6 +4025,8 @@ static __init void of_unittest_overlay_high_level(void)
/* now do the normal overlay usage test */
+ /* --- overlay --- */
+
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/status");
EXPECT_BEGIN(KERN_ERR,
@@ -3663,51 +4077,284 @@ static __init void of_unittest_overlay_high_level(void)
unittest(ret, "Adding overlay 'overlay' failed\n");
+ /* --- overlay_bad_add_dup_node --- */
+
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/controller");
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/controller/name");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/controller:name");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: Error reverting changeset (-19)");
unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL),
"Adding overlay 'overlay_bad_add_dup_node' failed\n");
EXPECT_END(KERN_ERR,
+ "OF: Error reverting changeset (-19)");
+ EXPECT_END(KERN_ERR,
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/controller:name");
+ EXPECT_END(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/controller/name");
EXPECT_END(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/controller");
+ /* --- overlay_bad_add_dup_prop --- */
+
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric");
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail");
EXPECT_BEGIN(KERN_ERR,
"OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/electric:name");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: Error reverting changeset (-19)");
unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL),
"Adding overlay 'overlay_bad_add_dup_prop' failed\n");
EXPECT_END(KERN_ERR,
- "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name");
+ "OF: Error reverting changeset (-19)");
EXPECT_END(KERN_ERR,
- "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail");
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/electric:name");
EXPECT_END(KERN_ERR,
- "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric");
+ "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name");
+ EXPECT_END(KERN_ERR,
+ "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail");
+ EXPECT_END(KERN_ERR,
+ "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric");
+
+ /* --- overlay_bad_phandle --- */
unittest(overlay_data_apply("overlay_bad_phandle", NULL),
"Adding overlay 'overlay_bad_phandle' failed\n");
+ /* --- overlay_bad_symbol --- */
+
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/hvac-medium-2:name");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: Error reverting changeset (-19)");
+
unittest(overlay_data_apply("overlay_bad_symbol", NULL),
"Adding overlay 'overlay_bad_symbol' failed\n");
+ EXPECT_END(KERN_ERR,
+ "OF: Error reverting changeset (-19)");
+ EXPECT_END(KERN_ERR,
+ "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/hvac-medium-2:name");
+
+ /* --- overlay_bad_unresolved --- */
+
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: resolver: node label 'this_label_does_not_exist' not found in live devicetree symbols table");
+ EXPECT_BEGIN(KERN_ERR,
+ "OF: resolver: overlay phandle fixup failed: -22");
+
+ unittest(overlay_data_apply("overlay_bad_unresolved", NULL),
+ "Adding overlay 'overlay_bad_unresolved' failed\n");
+
+ EXPECT_END(KERN_ERR,
+ "OF: resolver: overlay phandle fixup failed: -22");
+ EXPECT_END(KERN_ERR,
+ "OF: resolver: node label 'this_label_does_not_exist' not found in live devicetree symbols table");
+
return;
err_unlock:
mutex_unlock(&of_mutex);
}
+static int of_unittest_pci_dev_num;
+static int of_unittest_pci_child_num;
+
+/*
+ * PCI device tree node test driver
+ */
+static const struct pci_device_id testdrv_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x5), }, /* PCI_VENDOR_ID_REDHAT */
+ { 0, }
+};
+
+static int testdrv_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct overlay_info *info;
+ struct device_node *dn;
+ int ret, ovcs_id;
+ u32 size;
+
+ dn = pdev->dev.of_node;
+ if (!dn) {
+ dev_err(&pdev->dev, "does not find bus endpoint");
+ return -EINVAL;
+ }
+
+ for (info = overlays; info && info->name; info++) {
+ if (!strcmp(info->name, "overlay_pci_node"))
+ break;
+ }
+ if (!info || !info->name) {
+ dev_err(&pdev->dev, "no overlay data for overlay_pci_node");
+ return -ENODEV;
+ }
+
+ size = info->dtbo_end - info->dtbo_begin;
+ ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn);
+ of_node_put(dn);
+ if (ret)
+ return ret;
+
+ of_platform_default_populate(dn, NULL, &pdev->dev);
+ pci_set_drvdata(pdev, (void *)(uintptr_t)ovcs_id);
+
+ return 0;
+}
+
+static void testdrv_remove(struct pci_dev *pdev)
+{
+ int ovcs_id = (int)(uintptr_t)pci_get_drvdata(pdev);
+
+ of_platform_depopulate(&pdev->dev);
+ of_overlay_remove(&ovcs_id);
+}
+
+static struct pci_driver testdrv_driver = {
+ .name = "pci_dt_testdrv",
+ .id_table = testdrv_pci_ids,
+ .probe = testdrv_probe,
+ .remove = testdrv_remove,
+};
+
+static int unittest_pci_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev;
+ u64 exp_addr;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ dev = &pdev->dev;
+ while (dev && !dev_is_pci(dev))
+ dev = dev->parent;
+ if (!dev) {
+ pr_err("unable to find parent device\n");
+ return -ENODEV;
+ }
+
+ exp_addr = pci_resource_start(to_pci_dev(dev), 0) + 0x100;
+ unittest(res->start == exp_addr, "Incorrect translated address %llx, expected %llx\n",
+ (u64)res->start, exp_addr);
+
+ of_unittest_pci_child_num++;
+
+ return 0;
+}
+
+static const struct of_device_id unittest_pci_of_match[] = {
+ { .compatible = "unittest-pci" },
+ { }
+};
+
+static struct platform_driver unittest_pci_driver = {
+ .probe = unittest_pci_probe,
+ .driver = {
+ .name = "unittest-pci",
+ .of_match_table = unittest_pci_of_match,
+ },
+};
+
+static int of_unittest_pci_node_verify(struct pci_dev *pdev, bool add)
+{
+ struct device_node *pnp, *np = NULL;
+ struct device *child_dev;
+ char *path = NULL;
+ const __be32 *reg;
+ int rc = 0;
+
+ pnp = pdev->dev.of_node;
+ unittest(pnp, "Failed creating PCI dt node\n");
+ if (!pnp)
+ return -ENODEV;
+
+ if (add) {
+ path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0/unittest-pci@100", pnp);
+ np = of_find_node_by_path(path);
+ unittest(np, "Failed to get unittest-pci node under PCI node\n");
+ if (!np) {
+ rc = -ENODEV;
+ goto failed;
+ }
+
+ reg = of_get_property(np, "reg", NULL);
+ unittest(reg, "Failed to get reg property\n");
+ if (!reg)
+ rc = -ENODEV;
+ } else {
+ path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0", pnp);
+ np = of_find_node_by_path(path);
+ unittest(!np, "Child device tree node is not removed\n");
+ child_dev = device_find_any_child(&pdev->dev);
+ unittest(!child_dev, "Child device is not removed\n");
+ put_device(child_dev);
+ }
+
+failed:
+ kfree(path);
+ if (np)
+ of_node_put(np);
+
+ return rc;
+}
+
+static void __init of_unittest_pci_node(void)
+{
+ struct pci_dev *pdev = NULL;
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_PCI_DYNAMIC_OF_NODES))
+ return;
+
+ rc = pci_register_driver(&testdrv_driver);
+ unittest(!rc, "Failed to register pci test driver; rc = %d\n", rc);
+ if (rc)
+ return;
+
+ rc = platform_driver_register(&unittest_pci_driver);
+ if (unittest(!rc, "Failed to register unittest pci driver\n")) {
+ pci_unregister_driver(&testdrv_driver);
+ return;
+ }
+
+ while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL) {
+ of_unittest_pci_node_verify(pdev, true);
+ of_unittest_pci_dev_num++;
+ }
+ if (pdev)
+ pci_dev_put(pdev);
+
+ unittest(of_unittest_pci_dev_num,
+ "No test PCI device been found. Please run QEMU with '-device pci-testdev'\n");
+ unittest(of_unittest_pci_dev_num == of_unittest_pci_child_num,
+ "Child device number %d is not expected %d", of_unittest_pci_child_num,
+ of_unittest_pci_dev_num);
+
+ platform_driver_unregister(&unittest_pci_driver);
+ pci_unregister_driver(&testdrv_driver);
+
+ while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL)
+ of_unittest_pci_node_verify(pdev, false);
+ if (pdev)
+ pci_dev_put(pdev);
+}
#else
static inline __init void of_unittest_overlay_high_level(void) {}
+static inline __init void of_unittest_pci_node(void) { }
#endif
@@ -3722,10 +4369,6 @@ static int __init of_unittest(void)
add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
/* adding data for unittest */
-
- if (IS_ENABLED(CONFIG_UML))
- unittest_unflatten_overlay_base();
-
res = unittest_data_add();
if (res)
return res;
@@ -3749,18 +4392,23 @@ static int __init of_unittest(void)
of_unittest_property_string();
of_unittest_property_copy();
of_unittest_changeset();
+ of_unittest_changeset_prop();
of_unittest_parse_interrupts();
of_unittest_parse_interrupts_extended();
+ of_unittest_irq_refcount();
of_unittest_dma_get_max_cpu_address();
of_unittest_parse_dma_ranges();
of_unittest_pci_dma_ranges();
+ of_unittest_pci_empty_dma_ranges();
of_unittest_bus_ranges();
of_unittest_bus_3cell_ranges();
of_unittest_reg();
+ of_unittest_translate_addr();
of_unittest_match_node();
of_unittest_platform_populate();
of_unittest_overlay();
of_unittest_lifecycle();
+ of_unittest_pci_node();
/* Double check linkage after removing testcase data */
of_unittest_check_tree_linkage();