[PATCH v2 3/7] of: unittest: Test fw_devlink with GPIO nexus maps
From: Ernest Van Hoecke
Date: Thu Sep 10 2026 - 11:13:09 EST
From: Ernest Van Hoecke <ernest.vanhoecke@xxxxxxxxxxx>
Add OF unit tests that call the fw_devlink add_links operation for GPIOs
routed through nexus maps. Verify that each mapped consumer has exactly
one link to the final GPIO provider rather than to the nexus.
Also cover direct references and all GPIO property spellings recognized
by fw_devlink, including the deprecated singular and unprefixed forms.
Signed-off-by: Ernest Van Hoecke <ernest.vanhoecke@xxxxxxxxxxx>
---
drivers/of/unittest-data/tests-phandle.dtsi | 33 +++++++++++++
drivers/of/unittest.c | 75 +++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi
index 554a996b2ef1..c3727ed15bde 100644
--- a/drivers/of/unittest-data/tests-phandle.dtsi
+++ b/drivers/of/unittest-data/tests-phandle.dtsi
@@ -79,6 +79,39 @@ consumer-b {
phandle-list-bad-args = <&provider2 1 0>,
<&provider4 0>;
};
+
+ fw-devlink-tests {
+ gpio_provider: gpio-controller {
+ #gpio-cells = <2>;
+ };
+
+ fwdevlink_nexus: nexus {
+ #gpio-cells = <2>;
+ gpio-map = <0 0 &gpio_provider 1 0>;
+ gpio-map-mask = <0xffffffff 0>;
+ gpio-map-pass-thru = <0 0xffffffff>;
+ };
+
+ gpio-compat-consumer {
+ gpios = <&fwdevlink_nexus 0 0>;
+ };
+
+ gpio-compat-singular-consumer {
+ gpio = <&fwdevlink_nexus 0 0>;
+ };
+
+ gpio-consumer {
+ test-gpios = <&fwdevlink_nexus 0 0>;
+ };
+
+ gpio-direct-consumer {
+ test-gpios = <&gpio_provider 1 0>;
+ };
+
+ gpio-singular-consumer {
+ test-gpio = <&fwdevlink_nexus 0 0>;
+ };
+ };
};
};
};
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index e255f54f4d76..dfa3c885d142 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -10,6 +10,7 @@
#include <linux/dma-direct.h> /* to test phys_to_dma/dma_to_phys */
#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/fwnode.h>
#include <linux/hashtable.h>
#include <linux/libfdt.h>
#include <linux/of.h>
@@ -710,6 +711,79 @@ static void __init of_unittest_parse_phandle_with_args_map(void)
}
}
+static void __init of_unittest_fw_devlink_supplier(struct device_node *tests,
+ const char *consumer_name,
+ const char *supplier_name)
+{
+ struct device_node *consumer, *supplier;
+ struct fwnode_handle *consumer_fwnode;
+ struct fwnode_link *link;
+ unsigned int link_count = 0;
+ bool found = false;
+ int rc;
+
+ consumer = of_get_child_by_name(tests, consumer_name);
+ supplier = of_get_child_by_name(tests, supplier_name);
+ if (unittest(consumer && supplier, "missing consumer %s or supplier %s\n",
+ consumer_name, supplier_name))
+ goto put_nodes;
+
+ consumer_fwnode = of_fwnode_handle(consumer);
+ fwnode_links_purge(consumer_fwnode);
+ rc = fwnode_call_int_op(consumer_fwnode, add_links);
+ if (rc == -EOPNOTSUPP) {
+ pr_info_once("fw_devlink tests skipped: OF supplier links are not supported\n");
+ goto purge_links;
+ }
+ if (unittest(!rc, "failed to add links for %pOF: %d\n", consumer, rc))
+ goto purge_links;
+
+ /*
+ * The suppliers list should normally be accessed with fwnode_link_lock
+ * held, but that lock is private to the driver core. These test nodes
+ * are isolated and have no concurrent link updates.
+ */
+ list_for_each_entry(link, &consumer_fwnode->suppliers, c_hook) {
+ link_count++;
+ if (link->supplier == of_fwnode_handle(supplier))
+ found = true;
+ }
+
+ unittest(link_count == 1, "%pOF has %u suppliers, expected 1\n", consumer, link_count);
+ unittest(found, "%pOF is not linked to supplier %pOF\n", consumer, supplier);
+
+purge_links:
+ fwnode_links_purge(consumer_fwnode);
+put_nodes:
+ of_node_put(consumer);
+ of_node_put(supplier);
+}
+
+static void __init of_unittest_fw_devlink(void)
+{
+ static const struct {
+ const char *consumer;
+ const char *supplier;
+ } cases[] = {
+ { "gpio-compat-consumer", "gpio-controller" },
+ { "gpio-compat-singular-consumer", "gpio-controller" },
+ { "gpio-consumer", "gpio-controller" },
+ { "gpio-direct-consumer", "gpio-controller" },
+ { "gpio-singular-consumer", "gpio-controller" },
+ };
+ struct device_node *tests;
+ int i;
+
+ tests = of_find_node_by_path("/testcase-data/phandle-tests/fw-devlink-tests");
+ if (unittest(tests, "missing fw_devlink test data\n"))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(cases); i++)
+ of_unittest_fw_devlink_supplier(tests, cases[i].consumer, cases[i].supplier);
+
+ of_node_put(tests);
+}
+
static void __init of_unittest_property_string(void)
{
const char *strings[4];
@@ -4533,6 +4607,7 @@ static int __init of_unittest(void)
of_unittest_dynamic();
of_unittest_parse_phandle_with_args();
of_unittest_parse_phandle_with_args_map();
+ of_unittest_fw_devlink();
of_unittest_printf();
of_unittest_property_string();
of_unittest_property_copy();
--
2.43.0