summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig9
-rw-r--r--drivers/base/arch_numa.c4
-rw-r--r--drivers/base/attribute_container.c7
-rw-r--r--drivers/base/core.c8
-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/firmware_loader/fallback.c10
-rw-r--r--drivers/base/isa.c16
-rw-r--r--drivers/base/module.c2
-rw-r--r--drivers/base/platform.c90
-rw-r--r--drivers/base/property.c41
-rw-r--r--drivers/base/soc.c15
-rw-r--r--drivers/base/swnode.c2
-rw-r--r--drivers/base/test/Kconfig1
-rw-r--r--drivers/base/test/platform-device-test.c180
-rw-r--r--drivers/base/test/property-entry-test.c137
-rw-r--r--drivers/base/transport_class.c5
18 files changed, 493 insertions, 72 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index f7d385cbd..43f20ca95 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -222,15 +222,6 @@ config DMA_SHARED_BUFFER
APIs extension; the file's descriptor can then be passed on to other
driver.
-config DMA_FENCE_TRACE
- bool "Enable verbose DMA_FENCE_TRACE messages"
- depends on DMA_SHARED_BUFFER
- help
- Enable the DMA_FENCE_TRACE printks. This will add extra
- spam to the console log, but will make it easier to diagnose
- lockup related problems for dma-buffers shared across multiple
- devices.
-
config GENERIC_ARCH_TOPOLOGY
bool
help
diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c
index d4e426aa5..6476227b7 100644
--- a/drivers/base/arch_numa.c
+++ b/drivers/base/arch_numa.c
@@ -228,10 +228,6 @@ static int __init numa_init(int (*init_func)(void))
{
int ret;
- nodes_clear(numa_nodes_parsed);
- nodes_clear(node_possible_map);
- nodes_clear(node_online_map);
-
ret = numa_memblks_init(init_func, /* memblock_force_top_down */ false);
if (ret < 0)
goto out_free_distance;
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 4ad26b8dd..3c4563742 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -425,7 +425,12 @@ attribute_container_add_class_device(struct device *classdev)
if (error)
return error;
- return attribute_container_add_attrs(classdev);
+
+ error = attribute_container_add_attrs(classdev);
+ if (error)
+ device_del(classdev);
+
+ return error;
}
/**
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4d0266829..4c0c37399 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -127,7 +127,13 @@ static void __fwnode_link_cycle(struct fwnode_link *link)
{
pr_debug("%pfwf: cycle: depends on %pfwf\n",
link->consumer, link->supplier);
+
+ if (link->flags & FWLINK_FLAG_CYCLE)
+ return;
+
link->flags |= FWLINK_FLAG_CYCLE;
+ pr_info("%pfwf: Fixed dependency cycle(s) with %pfwf\n",
+ link->consumer, link->supplier);
}
/**
@@ -2206,8 +2212,6 @@ static int fw_devlink_create_devlink(struct device *con,
if (__fw_devlink_relax_cycles(link->consumer, sup_handle)) {
__fwnode_link_cycle(link);
pr_debug("----- cycle: end -----\n");
- pr_info("%pfwf: Fixed dependency cycle(s) with %pfwf\n",
- link->consumer, sup_handle);
}
device_links_write_unlock();
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 60c005223..f6525a7ee 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 6c067dedc..c0264b15d 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 d36befebb..1dcebe8e7 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/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
index 3ef0b312a..00d6b6e59 100644
--- a/drivers/base/firmware_loader/fallback.c
+++ b/drivers/base/firmware_loader/fallback.c
@@ -95,6 +95,16 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
retval = -EINTR;
goto out;
}
+
+ /*
+ * device_add() exposes the loading interface before pending_list is
+ * linked into pending_fw_head, so fw_state_done() may run first.
+ */
+ if (fw_state_is_done(fw_priv)) {
+ mutex_unlock(&fw_lock);
+ goto out;
+ }
+
list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
index 5887e4211..4e9f68080 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 218aaa096..6789dca48 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 fb9120b0b..66f9ec73d 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -599,19 +599,19 @@ 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);
+ device_remove_software_node(dev);
+ /*
+ * If the primary firmware node is a software node, its reference count
+ * was already decreased by the call to device_remove_software_node().
+ */
+ if (!is_software_node(dev_fwnode(dev)))
+ fwnode_handle_put(pa->pdev.dev.fwnode);
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.mfd_cell);
kfree(pa->pdev.resource);
kfree(pa);
}
-static void platform_device_release_full(struct device *dev)
-{
- device_remove_software_node(dev);
- platform_device_release(dev);
-}
-
/**
* platform_device_alloc - create a platform device
* @name: base name of the device we're adding
@@ -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,70 @@ 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. This function must not be
+ * called for a platform device that already has a software node as its primary
+ * firmware node assigned.
+ */
+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. This function must not be
+ * called for a platform device that already has a software node as its primary
+ * firmware node assigned.
+ */
+void platform_device_set_fwnode(struct platform_device *pdev,
+ struct fwnode_handle *fwnode)
+{
+ /*
+ * If we call this function for a platform device whose primary
+ * firmware node is a software node, we'll never end up calling the
+ * symmetric software_node_notify_remove(). There are no users for this
+ * right now in the tree so just disallow it.
+ */
+ WARN_ON(is_software_node(dev_fwnode(&pdev->dev)));
+ 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. This function must not be
+ * called for a platform device that already has a software node as its primary
+ * firmware node assigned.
+ */
+void platform_device_set_of_node_from_dev(struct platform_device *pdev,
+ const struct device *dev2)
+{
+ /* See platform_device_set_fwnode(). */
+ WARN_ON(is_software_node(dev_fwnode(&pdev->dev)));
+ 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 +939,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) {
@@ -890,8 +960,6 @@ struct platform_device *platform_device_register_full(const struct platform_devi
ret = device_add_software_node(&pdev->dev, pdevinfo->swnode);
if (ret)
goto err;
-
- pdev->dev.release = platform_device_release_full;
} else if (pdevinfo->properties) {
ret = device_create_managed_software_node(&pdev->dev,
pdevinfo->properties, NULL);
diff --git a/drivers/base/property.c b/drivers/base/property.c
index e08eadd66..b136c339d 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -7,10 +7,10 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
-#include <linux/kconfig.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/phy.h>
@@ -517,7 +517,6 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string);
int fwnode_property_match_string(const struct fwnode_handle *fwnode,
const char *propname, const char *string)
{
- const char **values;
int nval, ret;
nval = fwnode_property_string_array_count(fwnode, propname);
@@ -527,20 +526,18 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
if (nval == 0)
return -ENODATA;
- values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
+ const char **values __free(kfree) = kcalloc(nval, sizeof(*values), GFP_KERNEL);
if (!values)
return -ENOMEM;
ret = fwnode_property_read_string_array(fwnode, propname, values, nval);
if (ret < 0)
- goto out_free;
+ return ret;
ret = match_string(values, nval, string);
if (ret < 0)
- ret = -ENODATA;
+ return -ENODATA;
-out_free:
- kfree(values);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
@@ -807,18 +804,31 @@ struct fwnode_handle *
fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
+ const struct fwnode_handle *parent;
+ struct fwnode_handle *child_parent __free(fwnode_handle) = NULL;
struct fwnode_handle *next;
- if (IS_ERR_OR_NULL(fwnode))
+ /*
+ * If this function is in a loop and the previous iteration returned
+ * an child from fwnode->secondary, then we need to use the secondary
+ * as parent rather than @fwnode.
+ */
+ if (child) {
+ child_parent = fwnode_get_parent(child);
+ parent = child_parent;
+ } else {
+ parent = fwnode;
+ }
+ if (IS_ERR_OR_NULL(parent))
return NULL;
/* Try to find a child in primary fwnode */
- next = fwnode_call_ptr_op(fwnode, get_next_child_node, child);
+ next = fwnode_call_ptr_op(parent, get_next_child_node, child);
if (next)
return next;
/* When no more children in primary, continue with secondary */
- return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child);
+ return fwnode_get_next_child_node(parent->secondary, NULL);
}
EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
@@ -1115,8 +1125,9 @@ struct fwnode_handle *
fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
- struct fwnode_handle *ep, *port_parent = NULL;
const struct fwnode_handle *parent;
+ struct fwnode_handle *port_parent __free(fwnode_handle) = NULL;
+ struct fwnode_handle *ep;
/*
* If this function is in a loop and the previous iteration returned
@@ -1134,13 +1145,9 @@ fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
if (ep)
- goto out_put_port_parent;
-
- ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
+ return ep;
-out_put_port_parent:
- fwnode_handle_put(port_parent);
- return ep;
+ return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 65ce72d49..af7d71393 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -191,6 +191,7 @@ EXPORT_SYMBOL_GPL(soc_device_unregister);
static int __init soc_bus_register(void)
{
+ struct soc_device *soc_dev;
int ret;
ret = bus_register(&soc_bus_type);
@@ -198,10 +199,20 @@ static int __init soc_bus_register(void)
return ret;
soc_bus_registered = true;
- if (early_soc_dev_attr)
- return PTR_ERR(soc_device_register(early_soc_dev_attr));
+ if (early_soc_dev_attr) {
+ soc_dev = soc_device_register(early_soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto err_unregister_bus;
+ }
+ }
return 0;
+
+err_unregister_bus:
+ soc_bus_registered = false;
+ bus_unregister(&soc_bus_type);
+ return ret;
}
core_initcall(soc_bus_register);
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 1f2315858..84a77ffa3 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -537,7 +537,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (prop->is_inline)
return -EINVAL;
- if ((index + 1) * sizeof(*ref) > prop->length)
+ if (index >= prop->length / sizeof(*ref))
return -ENOENT;
ref_array = prop->pointer;
diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig
index 1ecf07912..542ce0753 100644
--- a/drivers/base/test/Kconfig
+++ b/drivers/base/test/Kconfig
@@ -17,6 +17,7 @@ config DM_KUNIT_TEST
config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
+ select OF
default KUNIT_ALL_TESTS
config DRIVER_SWNODE_KUNIT_TEST
diff --git a/drivers/base/test/platform-device-test.c b/drivers/base/test/platform-device-test.c
index 6355a2231..9ce563f76 100644
--- a/drivers/base/test/platform-device-test.c
+++ b/drivers/base/test/platform-device-test.c
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
+#include <kunit/fwnode.h>
#include <kunit/platform_device.h>
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/device/bus.h>
+#include <linux/fwnode.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#define DEVICE_NAME "test"
@@ -253,9 +256,186 @@ static struct kunit_suite platform_device_match_test_suite = {
.test_cases = platform_device_match_tests,
};
+static int platform_device_swnode_test_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver platform_swnode_test_driver = {
+ .probe = platform_device_swnode_test_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+};
+
+static const struct software_node platform_device_test_swnode = { };
+
+/*
+ * Check that reusing a software node works correctly. If the call to
+ * platform_device_register_full() fails after adding the secondary firmware
+ * node, the software node must be unregistered in the device's release()
+ * callback or the subsequent call to platform_device_register_full() will fail
+ * with -EBUSY due to the software node already having been registered.
+ */
+static void platform_device_swnode_add_twice(struct kunit *test)
+{
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ bool bound = false;
+ int ret;
+
+ fwnode = kunit_kzalloc(test, sizeof(*fwnode), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode_init(fwnode, NULL);
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .swnode = &platform_device_test_swnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+}
+
+/*
+ * Check that passing a software node as the primary firmware node of the
+ * platform device does not result in it being unregistered by the call to
+ * device_remove_software_node() in its release path.
+ */
+static void platform_device_swnode_as_primary(struct kunit *test)
+{
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ bool bound = false;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_software_node_register(test, &platform_device_test_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, software_node_fwnode(&platform_device_test_swnode));
+}
+
+/*
+ * Check that passing two software nodes to platform_device_register_full()
+ * fails.
+ */
+static void platform_device_two_swnodes(struct kunit *test)
+{
+ static const struct property_entry properties[] = {
+ PROPERTY_ENTRY_U32("foo", 42),
+ { }
+ };
+
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_software_node_register(test, &platform_device_test_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .swnode = &platform_device_test_swnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .swnode = &platform_device_test_swnode,
+ .properties = properties,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .properties = properties,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+}
+
+static struct kunit_case platform_device_swnode_tests[] = {
+ KUNIT_CASE(platform_device_swnode_add_twice),
+ KUNIT_CASE(platform_device_swnode_as_primary),
+ KUNIT_CASE(platform_device_two_swnodes),
+ {}
+};
+
+static struct kunit_suite platform_device_swnode_test_suite = {
+ .name = "platform-device-swnode",
+ .test_cases = platform_device_swnode_tests,
+};
+
kunit_test_suites(
&platform_device_devm_test_suite,
&platform_device_match_test_suite,
+ &platform_device_swnode_test_suite,
);
MODULE_DESCRIPTION("Test module for platform devices");
diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c
index a8657eb06..855e73b9b 100644
--- a/drivers/base/test/property-entry-test.c
+++ b/drivers/base/test/property-entry-test.c
@@ -4,6 +4,8 @@
// Copyright 2019 Google LLC.
#include <kunit/test.h>
+
+#include <linux/of.h>
#include <linux/property.h>
#include <linux/types.h>
@@ -489,6 +491,139 @@ static void pe_test_reference(struct kunit *test)
software_node_unregister_node_group(group);
}
+static struct fwnode_handle *create_device_node(struct kunit *test,
+ const char *name,
+ const char *full_name,
+ struct device_node *parent)
+{
+ struct device_node *node;
+
+ node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
+
+ node->name = kunit_kstrdup(test, name, GFP_KERNEL);
+ node->full_name = kunit_kstrdup(test, full_name, GFP_KERNEL);
+
+ if (parent) {
+ node->sibling = parent->child;
+ /* set the node as the first child of the parent */
+ parent->child = node;
+ node->parent = parent;
+ }
+
+ of_node_init(node);
+ return of_fwnode_handle(node);
+}
+
+/* Verifies that fwnode_for_each_child_node() can output correct children */
+static void pe_test_child_iteration(struct kunit *test)
+{
+ struct fwnode_handle *of_node, *of_node1;
+ struct fwnode_handle *sw_node, *sw_node1;
+ struct fwnode_handle *child;
+ int error, i, num;
+
+ static const struct software_node node = { .name = "sw" };
+ static const struct software_node node1 = { .name = "sw-1", .parent = &node};
+ static const struct software_node node2 = { .name = "sw-2", .parent = &node};
+ static const struct software_node node3 = { .name = "sw-3", .parent = &node};
+ static const struct software_node *group[] = { &node, &node1, &node2, &node3, NULL };
+
+ static const char * const of_child_array[] = { "of-1", "of-2", "of-3" };
+ static const char * const sw_child_array[] = { "sw-1", "sw-2", "sw-3" };
+ static const char * const of_sw_child_array[] = { "of-1", "of-2", "of-3",
+ "sw-1", "sw-2", "sw-3" };
+ static const char * const sw_of_child_array[] = { "sw-1", "sw-2", "sw-3",
+ "of-1", "of-2", "of-3" };
+
+ /* 1. Test OF node child iteration */
+
+ of_node = create_device_node(test, "of", "of", NULL);
+ create_device_node(test, "of", "of-3", to_of_node(of_node));
+ create_device_node(test, "of", "of-2", to_of_node(of_node));
+ of_node1 = create_device_node(test, "of", "of-1", to_of_node(of_node));
+
+ i = 0;
+ num = ARRAY_SIZE(of_child_array);
+ fwnode_for_each_child_node(of_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, of_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ /* 2. Test SW node child iteration */
+
+ error = software_node_register_node_group(group);
+ KUNIT_ASSERT_EQ(test, error, 0);
+
+ sw_node = software_node_fwnode(&node);
+
+ i = 0;
+ num = ARRAY_SIZE(sw_child_array);
+ fwnode_for_each_child_node(sw_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, sw_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ /* 3. Test OF (primary) + SW (secondary) node child iteration */
+
+ of_node->secondary = sw_node;
+ sw_node->secondary = ERR_PTR(-ENODEV);
+
+ i = 0;
+ num = ARRAY_SIZE(of_sw_child_array);
+ fwnode_for_each_child_node(of_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, of_sw_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ /* 4. Test SW (primary) + OF (secondary) node child iteration */
+
+ sw_node->secondary = of_node;
+ of_node->secondary = ERR_PTR(-ENODEV);
+
+ i = 0;
+ num = ARRAY_SIZE(sw_of_child_array);
+ fwnode_for_each_child_node(sw_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, sw_of_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ /* 5. Test OF (primary) + SW (secondary, but no children) node child iteration */
+
+ sw_node1 = software_node_fwnode(&node1);
+ of_node->secondary = sw_node1;
+ sw_node->secondary = ERR_PTR(-ENODEV);
+
+ i = 0;
+ num = ARRAY_SIZE(of_child_array);
+ fwnode_for_each_child_node(of_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, of_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ /* 6. Test SW (primary) + OF (secondary, but no children) node child iteration */
+
+ sw_node->secondary = of_node1;
+ of_node->secondary = ERR_PTR(-ENODEV);
+
+ i = 0;
+ num = ARRAY_SIZE(sw_child_array);
+ fwnode_for_each_child_node(sw_node, child) {
+ KUNIT_ASSERT_LT(test, i, num);
+ KUNIT_EXPECT_STREQ(test, sw_child_array[i++], fwnode_get_name(child));
+ }
+ KUNIT_EXPECT_PTR_EQ(test, child, NULL);
+
+ of_node->secondary = NULL;
+ sw_node->secondary = NULL;
+ software_node_unregister_node_group(group);
+}
+
static struct kunit_case property_entry_test_cases[] = {
KUNIT_CASE(pe_test_uints),
KUNIT_CASE(pe_test_uint_arrays),
@@ -497,6 +632,7 @@ static struct kunit_case property_entry_test_cases[] = {
KUNIT_CASE(pe_test_move_inline_u8),
KUNIT_CASE(pe_test_move_inline_str),
KUNIT_CASE(pe_test_reference),
+ KUNIT_CASE(pe_test_child_iteration),
{ }
};
@@ -509,4 +645,5 @@ kunit_test_suite(property_entry_test_suite);
MODULE_DESCRIPTION("Test module for the property entry API");
MODULE_AUTHOR("Dmitry Torokhov <dtor@chromium.org>");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_LICENSE("GPL");
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
index 416e9f819..351c3d3ce 100644
--- a/drivers/base/transport_class.c
+++ b/drivers/base/transport_class.c
@@ -168,11 +168,14 @@ static int transport_add_class_device(struct attribute_container *cont,
if (tcont->encryption) {
error = sysfs_create_group(&classdev->kobj, tcont->encryption);
if (error)
- goto err_del;
+ goto err_del_statistics;
}
return 0;
+err_del_statistics:
+ if (tcont->statistics)
+ sysfs_remove_group(&classdev->kobj, tcont->statistics);
err_del:
attribute_container_class_device_del(classdev);
err_remove: