summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-23 15:39:47 +0100
committerMark Brown <broonie@kernel.org>2026-07-23 15:39:47 +0100
commit83f3a44d969161d569712926dbe5df75eb1493fa (patch)
tree93adfc547c29700716dbfa8fc70d40a50c026655 /drivers
parent868aa6daa6d714648dfbe126e1d6bc692b2d06dc (diff)
parent3fadfb9362cf1b7d0a775f406f011d78800d6054 (diff)
downloadlinux-next-83f3a44d969161d569712926dbe5df75eb1493fa.tar.gz
linux-next-83f3a44d969161d569712926dbe5df75eb1493fa.zip
Merge branch 'driver-core-next' of https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
# Conflicts: # include/linux/platform_device.h
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/dd.c4
-rw-r--r--drivers/base/firmware_loader/builtin/Makefile32
-rw-r--r--drivers/base/firmware_loader/builtin/main.c2
-rw-r--r--drivers/base/isa.c16
-rw-r--r--drivers/base/module.c2
-rw-r--r--drivers/base/platform.c61
-rw-r--r--drivers/base/swnode.c79
-rw-r--r--drivers/base/test/Kconfig5
-rw-r--r--drivers/base/test/Makefile2
-rw-r--r--drivers/base/test/swnode-devlink-test.c337
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c2
-rw-r--r--drivers/gpu/nova-core/driver.rs4
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c3
-rw-r--r--drivers/iommu/fsl_pamu.c16
-rw-r--r--drivers/mfd/tps6586x.c2
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c3
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c2
-rw-r--r--drivers/of/platform.c2
-rw-r--r--drivers/platform/surface/surface_gpe.c2
-rw-r--r--drivers/pmdomain/imx/gpc.c3
-rw-r--r--drivers/pwm/pwm_th1520.rs5
-rw-r--r--drivers/reset/reset-rzg2l-usbphy-ctrl.c2
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c2
-rw-r--r--drivers/usb/chipidea/core.c2
-rw-r--r--drivers/usb/musb/jz4740.c2
25 files changed, 537 insertions, 55 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 60c005223844..f6525a7ee8c5 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -790,8 +790,8 @@ static int really_probe_debug(struct device *dev, const struct device_driver *dr
* CONFIG_DYNAMIC_DEBUG and we want a simple 'initcall_debug' on the
* kernel commandline to print this all the time at the debug level.
*/
- printk(KERN_DEBUG "probe of %s returned %d after %lld usecs\n",
- dev_name(dev), ret, ktime_us_delta(rettime, calltime));
+ printk(KERN_DEBUG "probe of %s with driver %s returned %d after %lld usecs\n",
+ dev_name(dev), drv->name, ret, ktime_us_delta(rettime, calltime));
return ret;
}
diff --git a/drivers/base/firmware_loader/builtin/Makefile b/drivers/base/firmware_loader/builtin/Makefile
index 6c067dedc01e..c0264b15d48e 100644
--- a/drivers/base/firmware_loader/builtin/Makefile
+++ b/drivers/base/firmware_loader/builtin/Makefile
@@ -16,20 +16,24 @@ ASM_ALIGN = $(if $(CONFIG_64BIT),3,2)
PROGBITS = $(if $(CONFIG_ARM),%,@)progbits
filechk_fwbin = \
- echo "/* Generated by $(src)/Makefile */" ;\
- echo " .section .rodata" ;\
- echo " .p2align 4" ;\
- echo "_fw_$(FWSTR)_bin:" ;\
- echo " .incbin \"$(fwdir)/$(FWNAME)\"" ;\
- echo "_fw_end:" ;\
- echo " .section .rodata.str,\"aMS\",$(PROGBITS),1" ;\
- echo " .p2align $(ASM_ALIGN)" ;\
- echo "_fw_$(FWSTR)_name:" ;\
- echo " .string \"$(FWNAME)\"" ;\
- echo " .section .builtin_fw,\"a\",$(PROGBITS)" ;\
- echo " .p2align $(ASM_ALIGN)" ;\
- echo " $(ASM_WORD) _fw_$(FWSTR)_name" ;\
- echo " $(ASM_WORD) _fw_$(FWSTR)_bin" ;\
+ if [ ! -s "$(fwdir)/$(FWNAME)" ]; then \
+ echo "error: empty firmware: $(fwdir)/$(FWNAME)" >&2 ;\
+ exit 1 ;\
+ fi ;\
+ echo "/* Generated by $(src)/Makefile */" ;\
+ echo " .section .rodata" ;\
+ echo " .p2align 4" ;\
+ echo "_fw_$(FWSTR)_bin:" ;\
+ echo " .incbin \"$(fwdir)/$(FWNAME)\"" ;\
+ echo "_fw_end:" ;\
+ echo " .section .rodata.str,\"aMS\",$(PROGBITS),1" ;\
+ echo " .p2align $(ASM_ALIGN)" ;\
+ echo "_fw_$(FWSTR)_name:" ;\
+ echo " .string \"$(FWNAME)\"" ;\
+ echo " .section .builtin_fw,\"a\",$(PROGBITS)" ;\
+ echo " .p2align $(ASM_ALIGN)" ;\
+ echo " $(ASM_WORD) _fw_$(FWSTR)_name" ;\
+ echo " $(ASM_WORD) _fw_$(FWSTR)_bin" ;\
echo " $(ASM_WORD) _fw_end - _fw_$(FWSTR)_bin"
$(obj)/%.gen.S: FORCE
diff --git a/drivers/base/firmware_loader/builtin/main.c b/drivers/base/firmware_loader/builtin/main.c
index d36befebb1b9..1dcebe8e7f8e 100644
--- a/drivers/base/firmware_loader/builtin/main.c
+++ b/drivers/base/firmware_loader/builtin/main.c
@@ -53,6 +53,8 @@ bool firmware_request_builtin(struct firmware *fw, const char *name)
for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
if (strcmp(name, b_fw->name) == 0) {
+ if (b_fw->size == 0)
+ return false;
fw->size = b_fw->size;
fw->data = b_fw->data;
return true;
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
index 5887e4211f80..4e9f68080f39 100644
--- a/drivers/base/isa.c
+++ b/drivers/base/isa.c
@@ -166,14 +166,16 @@ static int __init isa_bus_init(void)
int error;
error = bus_register(&isa_bus_type);
- if (!error) {
- isa_bus = root_device_register("isa");
- if (IS_ERR(isa_bus)) {
- error = PTR_ERR(isa_bus);
- bus_unregister(&isa_bus_type);
- }
+ if (error)
+ return error;
+
+ isa_bus = root_device_register("isa");
+ if (IS_ERR(isa_bus)) {
+ bus_unregister(&isa_bus_type);
+ return PTR_ERR(isa_bus);
}
- return error;
+
+ return 0;
}
postcore_initcall(isa_bus_init);
diff --git a/drivers/base/module.c b/drivers/base/module.c
index 218aaa096455..6789dca485ab 100644
--- a/drivers/base/module.c
+++ b/drivers/base/module.c
@@ -42,7 +42,7 @@ int module_add_driver(struct module *mod, const struct device_driver *drv)
if (mod)
mk = &mod->mkobj;
else if (drv->mod_name) {
- /* Lookup or create built-in module entry in /sys/modules */
+ /* Lookup or create built-in module entry in /sys/module */
mk = lookup_or_create_module_kobject(drv->mod_name);
if (mk) {
/* remember our module structure */
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index fb9120b0bcfe..a71015f1d915 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -599,7 +599,7 @@ static void platform_device_release(struct device *dev)
struct platform_object *pa = container_of(dev, struct platform_object,
pdev.dev);
- of_node_put(pa->pdev.dev.of_node);
+ fwnode_handle_put(pa->pdev.dev.fwnode);
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.mfd_cell);
kfree(pa->pdev.resource);
@@ -619,6 +619,13 @@ static void platform_device_release_full(struct device *dev)
*
* Create a platform device object which can have other objects attached
* to it, and which will have attached objects freed when it is released.
+ *
+ * The following fields of the dynamically allocated platform device must not
+ * be modified manually: resource, num_resources, dev.platform_data,
+ * dev.of_node and dev.fwnode. Users wishing to do the split platform device
+ * registration with platform_device_alloc() + platform_device_add() are
+ * required to use dedicated helpers for adding resources, platform data or
+ * assigning firmware nodes.
*/
struct platform_device *platform_device_alloc(const char *name, int id)
{
@@ -694,6 +701,55 @@ int platform_device_add_data(struct platform_device *pdev, const void *data,
EXPORT_SYMBOL_GPL(platform_device_add_data);
/**
+ * platform_device_set_of_node - assign an OF node to device
+ * @pdev: platform device to add the node for
+ * @np: new device node
+ *
+ * Assign an OF node to this platform device. Internally keep track of the
+ * reference count. Devices created with platform_device_alloc() must use this
+ * function instead of assigning the node manually.
+ */
+void platform_device_set_of_node(struct platform_device *pdev,
+ struct device_node *np)
+{
+ platform_device_set_fwnode(pdev, of_fwnode_handle(np));
+}
+EXPORT_SYMBOL_GPL(platform_device_set_of_node);
+
+/**
+ * platform_device_set_fwnode - assign a firmware node to device
+ * @pdev: platform device to set the node for
+ * @fwnode: new firmware node
+ *
+ * Assign a firmware node to this platform device. Internally keep track of the
+ * reference count. Devices created with platform_device_alloc() must use this
+ * function instead of assigning the node manually.
+ */
+void platform_device_set_fwnode(struct platform_device *pdev,
+ struct fwnode_handle *fwnode)
+{
+ fwnode_handle_put(pdev->dev.fwnode);
+ device_set_node(&pdev->dev, fwnode_handle_get(fwnode));
+}
+EXPORT_SYMBOL_GPL(platform_device_set_fwnode);
+
+/**
+ * platform_device_set_of_node_from_dev - reuse OF node of another device
+ * @pdev: platform device to set the node for
+ * @dev2: device whose OF node to reuse
+ *
+ * Reuses the OF node of another device in this platform device while
+ * internally keeping track of reference counting.
+ */
+void platform_device_set_of_node_from_dev(struct platform_device *pdev,
+ const struct device *dev2)
+{
+ device_set_of_node_from_dev(&pdev->dev, dev2);
+ pdev->dev.fwnode = of_fwnode_handle(pdev->dev.of_node);
+}
+EXPORT_SYMBOL_GPL(platform_device_set_of_node_from_dev);
+
+/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
@@ -868,8 +924,7 @@ struct platform_device *platform_device_register_full(const struct platform_devi
return ERR_PTR(-ENOMEM);
pdev->dev.parent = pdevinfo->parent;
- pdev->dev.fwnode = pdevinfo->fwnode;
- pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode));
+ device_set_node(&pdev->dev, fwnode_handle_get(pdevinfo->fwnode));
dev_assign_of_node_reused(&pdev->dev, pdevinfo->of_node_reused);
if (pdevinfo->dma_mask) {
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 869228a65cb3..1f2315858cc3 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -699,6 +699,62 @@ software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
return 0;
}
+static int software_node_add_links(struct fwnode_handle *fwnode)
+{
+ const struct software_node_ref_args *ref, *ref_array;
+ struct swnode *swnode = to_swnode(fwnode);
+ const struct property_entry *prop;
+ struct fwnode_handle *refnode;
+ unsigned int count;
+
+ if (!swnode || !swnode->node->properties)
+ return 0;
+
+ /*
+ * Unlike Device Tree, where phandles appear in many non-supplier
+ * contexts and a curated allowlist is required, a software node only
+ * carries a DEV_PROP_REF property when the author explicitly describes
+ * a reference to another node. Every such reference is therefore an
+ * intentional supplier dependency, so we create fwnode links for all
+ * of them.
+ */
+ for (prop = swnode->node->properties; prop->name; prop++) {
+ if (prop->type != DEV_PROP_REF || prop->is_inline)
+ continue;
+
+ /*
+ * TODO: Graph "remote-endpoint" references go both ways
+ * between endpoint child nodes and would create endpoint
+ * cycles. Let's leave it out for now until we have potential
+ * users.
+ */
+ if (!strcmp(prop->name, "remote-endpoint"))
+ continue;
+
+ ref_array = prop->pointer;
+ count = prop->length / sizeof(*ref_array);
+
+ for (unsigned int i = 0; i < count; i++) {
+ ref = &ref_array[i];
+
+ if (ref->swnode)
+ refnode = software_node_fwnode(ref->swnode);
+ else if (ref->fwnode)
+ refnode = ref->fwnode;
+ else
+ continue;
+
+ /* Supplier not registered yet, or self-reference. */
+ if (!refnode || refnode == &swnode->fwnode)
+ continue;
+
+ fwnode_link_add(&swnode->fwnode, refnode, 0);
+ }
+ }
+
+ return 0;
+}
+
static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
@@ -716,6 +772,7 @@ static const struct fwnode_operations software_node_ops = {
.graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
.graph_get_port_parent = software_node_graph_get_port_parent,
.graph_parse_endpoint = software_node_graph_parse_endpoint,
+ .add_links = software_node_add_links,
};
/* -------------------------------------------------------------------------- */
@@ -787,6 +844,8 @@ static void software_node_release(struct kobject *kobj)
{
struct swnode *swnode = kobj_to_swnode(kobj);
+ fwnode_links_purge(&swnode->fwnode);
+
if (swnode->parent) {
ida_free(&swnode->parent->child_ids, swnode->id);
list_del(&swnode->entry);
@@ -1105,6 +1164,17 @@ void software_node_notify(struct device *dev)
if (!swnode)
return;
+ /*
+ * When the software node is the device's secondary firmware node,
+ * the core only records the owning device on the primary fwnode
+ * (see device_add()). fw_devlink resolves a supplier device through
+ * fwnode->dev, so without this a consumer referencing the software
+ * node could never find the supplier device and would defer forever.
+ * Make fwnode.dev point to its owner in that case.
+ */
+ if (!device_match_fwnode(dev, &swnode->fwnode) && !swnode->fwnode.dev)
+ swnode->fwnode.dev = dev;
+
swnode_get(swnode);
ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node");
if (ret)
@@ -1127,6 +1197,15 @@ void software_node_notify_remove(struct device *dev)
sysfs_remove_link(&swnode->kobj, dev_name(dev));
sysfs_remove_link(&dev->kobj, "software_node");
+
+ /*
+ * Drop the device pointer mirrored onto a secondary software node in
+ * software_node_notify(). For a primary software node the core owns
+ * fwnode->dev and clears it in device_del().
+ */
+ if (!device_match_fwnode(dev, &swnode->fwnode) && swnode->fwnode.dev == dev)
+ swnode->fwnode.dev = NULL;
+
swnode_put(swnode);
if (swnode->managed) {
diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 2756870615cc..1ecf0791241a 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -18,3 +18,8 @@ config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+
+config DRIVER_SWNODE_KUNIT_TEST
+ tristate "KUnit Tests for software node fw_devlink links" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile
index e321dfc7e922..9ced7bbd569f 100644
--- a/drivers/base/test/Makefile
+++ b/drivers/base/test/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
+
+obj-$(CONFIG_DRIVER_SWNODE_KUNIT_TEST) += swnode-devlink-test.o
diff --git a/drivers/base/test/swnode-devlink-test.c b/drivers/base/test/swnode-devlink-test.c
new file mode 100644
index 000000000000..f53549e92611
--- /dev/null
+++ b/drivers/base/test/swnode-devlink-test.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
+ */
+
+#include <linux/array_size.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <kunit/fwnode.h>
+#include <kunit/platform_device.h>
+#include <kunit/test.h>
+
+static int swnode_count_suppliers(struct fwnode_handle *fwnode)
+{
+ struct fwnode_link *link;
+ unsigned int count = 0;
+
+ /*
+ * The suppliers and consumers lists should typically only be accessed
+ * with the fwnode_link_lock taken but it's private to the driver core.
+ *
+ * These are tests and at this point nobody should be modifying them so
+ * let's just access the list.
+ */
+ list_for_each_entry(link, &fwnode->suppliers, c_hook)
+ count++;
+
+ return count;
+}
+
+/* True if a supplier link con->sup exists, checked from both list ends. */
+static bool swnode_has_link(struct fwnode_handle *consumer,
+ struct fwnode_handle *supplier)
+{
+ bool from_con = false, from_sup = false;
+ struct fwnode_link *link;
+
+ list_for_each_entry(link, &consumer->suppliers, c_hook) {
+ if (link->supplier == supplier && link->consumer == consumer)
+ from_con = true;
+ }
+
+ list_for_each_entry(link, &supplier->consumers, s_hook) {
+ if (link->supplier == supplier && link->consumer == consumer)
+ from_sup = true;
+ }
+
+ return from_con && from_sup;
+}
+
+/* A single reference creates exactly one supplier link, on both list ends. */
+static void swnode_devlink_test_single_ref(struct kunit *test)
+{
+ static const struct software_node supp_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ struct fwnode_handle *cons_fwnode, *supp_fwnode;
+ int ret;
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("supplier", &supp_swnode),
+ { }
+ };
+
+ supp_fwnode = kunit_software_node_register(test, &supp_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
+
+ cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
+
+ ret = fwnode_call_int_op(cons_fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode));
+}
+
+/* Multiple distinct references create multiple supplier links. */
+static void swnode_devlink_test_multiple_refs(struct kunit *test)
+{
+ static const struct software_node supp1_swnode = {
+ .name = "swnode-devlink-test-supplier-1",
+ };
+ static const struct software_node supp2_swnode = {
+ .name = "swnode-devlink-test-supplier-2",
+ };
+ static const struct software_node *supp_nodes[] = {
+ &supp1_swnode, &supp2_swnode, NULL
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("foo", &supp1_swnode),
+ PROPERTY_ENTRY_REF("bar", &supp2_swnode),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_software_node_register_node_group(test, supp_nodes);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
+}
+
+/* A reference to an unregistered node creates no link (graceful skip). */
+static void swnode_devlink_test_unregistered_ref(struct kunit *test)
+{
+ static const struct software_node supp_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("supplier", &supp_swnode),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0);
+}
+
+/* Graph "remote-endpoint" references are excluded. */
+static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test)
+{
+ static const struct software_node ep_swnode = {
+ .name = "swnode-devlink-test-end-point"
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode),
+ { }
+ };
+
+ struct fwnode_handle *cons_fwnode, *supp_fwnode;
+ int ret;
+
+ supp_fwnode = kunit_software_node_register(test, &ep_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
+
+ cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
+
+ ret = fwnode_call_int_op(cons_fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0);
+}
+
+/* A reference array creates one link per registered element. */
+static void swnode_devlink_test_ref_array(struct kunit *test)
+{
+ static const struct software_node supp1_swnode = {
+ .name = "swnode-devlink-test-supplier-1",
+ };
+ static const struct software_node supp2_swnode = {
+ .name = "swnode-devlink-test-supplier-2",
+ };
+ static const struct software_node *supp_nodes[] = {
+ &supp1_swnode, &supp2_swnode, NULL
+ };
+ static const struct software_node_ref_args refs[] = {
+ SOFTWARE_NODE_REFERENCE(&supp1_swnode),
+ SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2),
+ };
+
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_REF_ARRAY("suppliers", refs),
+ { }
+ };
+
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_software_node_register_node_group(test, supp_nodes);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_fwnode_create_software_node(test, props, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = fwnode_call_int_op(fwnode, add_links);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
+ KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
+}
+
+/*
+ * End-to-end test: fw_devlink must defer a consumer's probe until its
+ * supplier has probed.
+ *
+ * The reference created by software_node_add_links() is only useful if the
+ * driver core promotes it to a real device_link and uses it to order probing.
+ * This test drives actual probing through the platform bus and asserts the
+ * supplier binds before the consumer.
+ */
+
+#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier"
+#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer"
+#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC)
+
+struct swnode_test_probe_order {
+ /* Names in the order their drivers' .probe ran. */
+ const char *probed[2];
+ unsigned int count;
+ wait_queue_head_t wq;
+};
+
+static int swnode_test_record_probe(struct platform_device *pdev)
+{
+ struct swnode_test_probe_order *order = platform_get_drvdata(pdev);
+
+ if (order && order->count < ARRAY_SIZE(order->probed)) {
+ order->probed[order->count++] = dev_name(&pdev->dev);
+ wake_up_interruptible(&order->wq);
+ }
+
+ return 0;
+}
+
+static struct platform_driver swnode_test_supplier_driver = {
+ .probe = swnode_test_record_probe,
+ .driver = {
+ .name = SWNODE_DEVLINK_TEST_SUPPLIER,
+ },
+};
+
+static struct platform_driver swnode_test_consumer_driver = {
+ .probe = swnode_test_record_probe,
+ .driver = {
+ .name = SWNODE_DEVLINK_TEST_CONSUMER,
+ },
+};
+
+static void swnode_devlink_test_probe_order(struct kunit *test)
+{
+ static const struct software_node supplier_swnode = {
+ .name = "swnode-devlink-test-supplier",
+ };
+
+ const struct property_entry consumer_props[] = {
+ PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode),
+ { }
+ };
+
+ struct platform_device *supplier, *consumer;
+ struct swnode_test_probe_order *order;
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order);
+ init_waitqueue_head(&order->wq);
+
+ fwnode = kunit_software_node_register(test, &supplier_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
+ consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer);
+
+ platform_set_drvdata(supplier, order);
+ platform_set_drvdata(consumer, order);
+
+ ret = kunit_device_add_software_node(test, &supplier->dev, &supplier_swnode);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = device_create_managed_software_node(&consumer->dev,
+ consumer_props, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_platform_device_add(test, consumer);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ ret = kunit_platform_device_add(test, supplier);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = wait_event_interruptible_timeout(order->wq,
+ order->count == 2,
+ msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER);
+ KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER);
+
+ /* Tear down the consumer (and its device link) before the supplier. */
+ kunit_platform_device_unregister(test, consumer);
+}
+
+static struct kunit_case swnode_test_cases[] = {
+ KUNIT_CASE(swnode_devlink_test_single_ref),
+ KUNIT_CASE(swnode_devlink_test_multiple_refs),
+ KUNIT_CASE(swnode_devlink_test_unregistered_ref),
+ KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded),
+ KUNIT_CASE(swnode_devlink_test_ref_array),
+ KUNIT_CASE(swnode_devlink_test_probe_order),
+ { }
+};
+
+static struct kunit_suite swnode_test_suite = {
+ .name = "software-node-links",
+ .test_cases = swnode_test_cases,
+};
+
+kunit_test_suite(swnode_test_suite);
+
+MODULE_DESCRIPTION("Test module for software node fw_devlink support");
+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index a26c38bb17a1..1812273d40da 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -126,7 +126,7 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
}
pdev->dev.parent = i2c->drm_dev;
- pdev->dev.fwnode = fwnode;
+ platform_device_set_fwnode(pdev, fwnode);
i2c->adapter_node = fwnode;
i2c->pdev = pdev;
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 5738d4ac521b..48380ac15f68 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -15,7 +15,7 @@ use kernel::{
Atomic,
Relaxed, //
},
- types::ForLt,
+ types::CovariantForLt,
};
use crate::gpu::Gpu;
@@ -29,7 +29,7 @@ pub(crate) struct NovaCore<'bound> {
pub(crate) gpu: Gpu<'bound>,
bar: pci::Bar<'bound, BAR0_SIZE>,
#[allow(clippy::type_complexity)]
- _reg: auxiliary::Registration<'bound, ForLt!(())>,
+ _reg: auxiliary::Registration<'bound, CovariantForLt!(())>,
}
pub(crate) struct NovaCoreDriver;
diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index dbd542300f80..92a0647f08c6 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -76,7 +76,8 @@ static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar)
goto out;
}
pdev->dev.parent = &dev->dev;
- pdev->dev.of_node = child;
+
+ platform_device_set_of_node(pdev, child);
ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret)
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
index 25aa477a95a9..c83bbc3faad5 100644
--- a/drivers/iommu/fsl_pamu.c
+++ b/drivers/iommu/fsl_pamu.c
@@ -8,6 +8,7 @@
#include "fsl_pamu.h"
+#include <linux/cleanup.h>
#include <linux/fsl/guts.h>
#include <linux/interrupt.h>
#include <linux/genalloc.h>
@@ -933,7 +934,6 @@ static struct platform_driver fsl_of_pamu_driver = {
static __init int fsl_pamu_init(void)
{
struct platform_device *pdev = NULL;
- struct device_node *np;
int ret;
/*
@@ -955,7 +955,8 @@ static __init int fsl_pamu_init(void)
* PAMU node would require significant changes to a lot of code.
*/
- np = of_find_compatible_node(NULL, NULL, "fsl,pamu");
+ struct device_node *np __free(device_node) =
+ of_find_compatible_node(NULL, NULL, "fsl,pamu");
if (!np) {
pr_err("could not find a PAMU node\n");
return -ENODEV;
@@ -964,7 +965,7 @@ static __init int fsl_pamu_init(void)
ret = platform_driver_register(&fsl_of_pamu_driver);
if (ret) {
pr_err("could not register driver (err=%i)\n", ret);
- goto error_driver_register;
+ return ret;
}
pdev = platform_device_alloc("fsl-of-pamu", 0);
@@ -973,7 +974,8 @@ static __init int fsl_pamu_init(void)
ret = -ENOMEM;
goto error_device_alloc;
}
- pdev->dev.of_node = of_node_get(np);
+
+ platform_device_set_of_node(pdev, np);
ret = pamu_domain_init();
if (ret)
@@ -988,17 +990,11 @@ static __init int fsl_pamu_init(void)
return 0;
error_device_add:
- of_node_put(pdev->dev.of_node);
- pdev->dev.of_node = NULL;
-
platform_device_put(pdev);
error_device_alloc:
platform_driver_unregister(&fsl_of_pamu_driver);
-error_driver_register:
- of_node_put(np);
-
return ret;
}
arch_initcall(fsl_pamu_init);
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index f5f805446603..3cfd2f02b62f 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
pdev->dev.parent = tps6586x->dev;
pdev->dev.platform_data = subdev->platform_data;
- pdev->dev.of_node = of_node_get(subdev->of_node);
+ platform_device_set_of_node(pdev, subdev->of_node);
ret = platform_device_add(pdev);
if (ret) {
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index a4e0d5a68268..0f0dbabfaabb 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -490,8 +490,9 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
+
if (dn)
- ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
+ platform_device_set_of_node(ppdev, bcmgenet_mii_of_find_mdio(priv));
else
ppd.phy_mask = ~0;
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 1881583be5ce..9caa1e47c174 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
goto put_err;
}
ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- ppdev->dev.of_node = of_node_get(pnp);
+ platform_device_set_of_node(ppdev, pnp);
ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 53bca8c6f781..8b1e76407782 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -126,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(of_node_get(np)));
+ platform_device_set_of_node(dev, np);
dev->dev.parent = parent ? : &platform_bus;
if (bus_id)
diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
index b359413903b1..40896a8544b0 100644
--- a/drivers/platform/surface/surface_gpe.c
+++ b/drivers/platform/surface/surface_gpe.c
@@ -317,7 +317,7 @@ static int __init surface_gpe_init(void)
goto err_alloc;
}
- pdev->dev.fwnode = fwnode;
+ platform_device_set_fwnode(pdev, fwnode);
status = platform_device_add(pdev);
if (status)
diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c
index 42e50c9b4fb9..abca5f449a22 100644
--- a/drivers/pmdomain/imx/gpc.c
+++ b/drivers/pmdomain/imx/gpc.c
@@ -487,8 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev)
domain->ipg_rate_mhz = ipg_rate_mhz;
pd_pdev->dev.parent = &pdev->dev;
- pd_pdev->dev.of_node = of_node_get(np);
- pd_pdev->dev.fwnode = of_fwnode_handle(np);
+ platform_device_set_of_node(pd_pdev, np);
ret = platform_device_add(pd_pdev);
if (ret) {
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
index 022338d17218..9372cd3e6a6d 100644
--- a/drivers/pwm/pwm_th1520.rs
+++ b/drivers/pwm/pwm_th1520.rs
@@ -23,9 +23,8 @@
use kernel::{
clk::Clk,
device::{Bound, Core, Device},
- devres,
io::{
- mem::IoMem,
+ mem::DevresIoMem,
Io, //
},
of, platform,
@@ -85,7 +84,7 @@ struct Th1520WfHw {
#[pin_data(PinnedDrop)]
struct Th1520PwmDriverData {
#[pin]
- iomem: devres::Devres<IoMem<'static, TH1520_PWM_REG_SIZE>>,
+ iomem: DevresIoMem<TH1520_PWM_REG_SIZE>,
clk: Clk,
}
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
index fd75d9601a3b..f003b360629c 100644
--- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -249,7 +249,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
vdev->dev.parent = dev;
priv->vdev = vdev;
- device_set_of_node_from_dev(&vdev->dev, dev);
+ platform_device_set_of_node_from_dev(vdev, dev);
error = platform_device_add(vdev);
if (error)
goto err_device_put;
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index 3071e46d03be..6e89415712ce 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -1539,7 +1539,7 @@ static int of_qcom_slim_ngd_register(struct device *parent,
kfree(ngd);
return ret;
}
- ngd->pdev->dev.of_node = of_node_get(node);
+ platform_device_set_of_node(ngd->pdev, node);
ctrl->ngd = ngd;
ret = platform_device_add(ngd->pdev);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index b53672f3ea63..3461dd763a6e 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -879,7 +879,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
}
pdev->dev.parent = dev;
- device_set_of_node_from_dev(&pdev->dev, dev);
+ platform_device_set_of_node_from_dev(pdev, dev);
ret = platform_device_add_resources(pdev, res, nres);
if (ret)
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index df56c972986f..c770ba576f05 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -273,7 +273,7 @@ static int jz4740_probe(struct platform_device *pdev)
musb->dev.parent = dev;
musb->dev.dma_mask = &musb->dev.coherent_dma_mask;
musb->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- device_set_of_node_from_dev(&musb->dev, dev);
+ platform_device_set_of_node_from_dev(musb, dev);
glue->pdev = musb;
glue->clk = clk;