summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-03-29 11:23:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-03-29 11:23:16 -0700
commit3b9ea5b5ed7e07c47932bbc40ef633de51b3752f (patch)
tree043a8a78ef71abbaf68e8cee73a430860612a812 /drivers
parent092e335082f22880207384ad736729c67d784665 (diff)
parent314655d41e650b3d72c60aa80a449e0ab22e2ffd (diff)
Merge tag 'devicetree-for-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
Pull devicetree updates from Rob Herring: "DT core: - Fix ref counting errors in interrupt parsing code - Allow "nonposted-mmio" property per device and on non-Apple h/w - Use typed accessors in platform driver code - Fix mismatch between DT MAX_PHANDLE_ARGS and NR_FWNODE_REFERENCE_ARGS and increase the maximum number args - Rework of_resolve_phandles() to use __free() cleanup and fix ref count error - Use of_prop_cmp() in a few more places - Improve make_fit.py script error handling DT bindings: - Update DT property ordering rules for properties within groups (i.e. common suffix) - Update DT submitting-patches doc to cover sending .dts patches and SoC maintainer rules on being warning free against linux-next - Add ti,tps53681, ti,tps53681, Maxim max15301, max15303, and max20751 to trivial devices - Add Renesas RZ/V2H(P) and Allwinner H616 support to Arm Mali Bifrost GPU. Add Samsung exynos7870 support to Arm Mail Midgard. - Rework qcom,ebi2 and samsung,exynos4210-sram memory controller bindings to split child node properties. Fix the LAN9115 binding to use the child node schema so all properties are documented. - Convert nxp,lpc3220-mic and Altera ECC manager bindings to schema - Fix some issues with LVDS display panels causing validation warnings - Drop some obsolete parts of Xilinx bindings" * tag 'devicetree-for-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (48 commits) scripts/make_fit: Print DT name before libfdt errors dt-bindings: edac: altera: socfpga: Convert to YAML dt-bindings: pps: gpio: Correct indentation and style in DTS example media: dt-bindings: mediatek,vcodec-encoder: Drop assigned-clock properties of: address: Allow to specify nonposted-mmio per-device of: address: Expand nonposted-mmio to non-Apple Silicon platforms docs: dt-bindings: Specify ordering for properties within groups dt-bindings: gpu: arm,mali-midgard: add exynos7870-mali compatible of: Move of_prop_val_eq() next to the single user of/platform: Use typed accessors rather than of_get_property() dt-bindings: trivial-devices: Add Maxim max15301, max15303, and max20751 dt-bindings: fsi: ibm,p9-scom: Add "ibm,fsi2pib" compatible dt-bindings: memory-controllers: qcom,ebi2: Enforce child props dt-bindings: memory-controllers: samsung,exynos4210-srom: Enforce child props dt-bindings: display: mitsubishi,aa104xd12: Adjust allowed and required properties dt-bindings: display: mitsubishi,aa104xd12: Allow jeida-18 for data-mapping dt-bindings: interrupt-controller: Convert nxp,lpc3220-mic.txt to yaml format docs: process: maintainer-soc-clean-dts: linux-next is decisive docs: dt: submitting-patches: Document sending DTS patches of: Align macro MAX_PHANDLE_ARGS with NR_FWNODE_REFERENCE_ARGS ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/of/address.c13
-rw-r--r--drivers/of/base.c6
-rw-r--r--drivers/of/irq.c84
-rw-r--r--drivers/of/of_private.h7
-rw-r--r--drivers/of/overlay.c10
-rw-r--r--drivers/of/platform.c8
-rw-r--r--drivers/of/resolver.c41
-rw-r--r--drivers/of/unittest-data/tests-interrupts.dtsi13
-rw-r--r--drivers/of/unittest.c67
9 files changed, 166 insertions, 83 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c
index d177a2b9edaf..f0f8f0dd191c 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -1031,20 +1031,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(const struct device_node *np)
{
- if (!IS_ENABLED(CONFIG_ARCH_APPLE))
- return false;
-
struct device_node *parent __free(device_node) = of_get_parent(np);
- if (!parent)
- return false;
- return of_property_read_bool(parent, "nonposted-mmio");
+ if (of_property_read_bool(np, "nonposted-mmio"))
+ return true;
+
+ return parent && of_property_read_bool(parent, "nonposted-mmio");
}
static int __of_address_to_resource(struct device_node *dev, int index, int bar_no,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index e37b088f1fad..7043acd971a0 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1678,7 +1678,7 @@ int __of_add_property(struct device_node *np, struct property *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 */
rc = -EEXIST;
goto out_unlock;
@@ -1882,9 +1882,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);
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 6c843d54ebb1..f8ad79b9b1c9 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>
@@ -38,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);
@@ -50,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)
{
@@ -165,6 +170,8 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph
* 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)
{
@@ -310,6 +317,12 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
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;
}
@@ -339,10 +352,12 @@ 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, addr_len;
@@ -367,41 +382,33 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar
/* 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_buf, out_irq);
-
- /* Look for the interrupt parent. */
- p = of_irq_find_parent(device);
- if (p == NULL)
- return -EINVAL;
+ 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;
+ }
- /* Get size of interrupt specifier */
- if (of_property_read_u32(p, "#interrupt-cells", &intsize)) {
- res = -EINVAL;
- goto out;
+ pr_debug(" intspec=%d\n", *out_irq->args);
}
- 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)
- goto out;
- }
-
- pr_debug(" intspec=%d\n", *out_irq->args);
-
-
/* Check if there are any interrupt-map translations to process */
- res = of_irq_parse_raw(addr_buf, 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);
@@ -505,8 +512,10 @@ 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;
}
@@ -623,6 +632,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;
}
@@ -653,6 +664,7 @@ 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);
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 1bdc7ceef3c5..df0bb00349e0 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -208,6 +208,13 @@ static void __maybe_unused of_dump_addr(const char *s, const __be32 *addr, int n
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
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 434f6dd6a86c..1af6f52d0708 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -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
@@ -304,9 +310,7 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
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)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index c6d8afb284e8..f77cb19973a5 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -334,7 +334,7 @@ static int of_platform_bus_create(struct device_node *bus,
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;
@@ -536,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);
@@ -551,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))
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index 779db058c42f..86424e145919 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -161,9 +161,7 @@ static int adjust_local_phandle_references(const struct device_node *local_fixup
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)
@@ -249,25 +247,22 @@ static int adjust_local_phandle_references(const struct device_node *local_fixup
*/
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;
@@ -279,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;
@@ -288,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) {
@@ -311,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);
@@ -328,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/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi
index 7c9f31cc131b..4ccb54f91c30 100644
--- a/drivers/of/unittest-data/tests-interrupts.dtsi
+++ b/drivers/of/unittest-data/tests-interrupts.dtsi
@@ -50,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>;
@@ -60,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.c b/drivers/of/unittest.c
index f88ddb1cf5d7..64d301893af7 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -1654,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 */
@@ -4324,6 +4390,7 @@ static int __init of_unittest(void)
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();