@@ -11,10 +11,19 @@
#include <linux/property.h>
#include <linux/slab.h>
+struct software_node_reference {
+ struct list_head list;
+ const char *name;
+ int nrefs;
+ struct fwnode_reference_args *args;
+};
+
struct software_node {
int id;
struct kobject kobj;
struct fwnode_handle fwnode;
+ struct list_head references;
+ struct mutex lock; /* node lock */
/* hierarchy */
struct ida child_ids;
@@ -598,9 +607,11 @@ fwnode_create_software_node(const struct property_entry *properties,
swnode->kobj.kset = swnode_kset;
swnode->fwnode.ops = &software_node_ops;
+ mutex_init(&swnode->lock);
ida_init(&swnode->child_ids);
INIT_LIST_HEAD(&swnode->entry);
INIT_LIST_HEAD(&swnode->children);
+ INIT_LIST_HEAD(&swnode->references);
swnode->parent = p;
ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
@@ -631,6 +642,11 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode)
if (!swnode)
return;
+ mutex_lock(&swnode->lock);
+ WARN(!list_empty(&swnode->references),
+ "\"%s\" has still references", kobject_name(&swnode->kobj));
+ mutex_unlock(&swnode->lock);
+
if (swnode->parent) {
ida_simple_remove(&swnode->parent->child_ids, swnode->id);
list_del(&swnode->entry);
@@ -642,6 +658,91 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
+/**
+ * fwnode_create_software_node_reference - Create named reference description
+ * @fwnode: The software node to have the references
+ * @name: Name given to reference description
+ * @args: Zero terminated array of software node references with arguments
+ *
+ * Associates software nodes listed in @args with @fwnode. The association is
+ * named @name. The reference count is incremented for the nodes in @args.
+ *
+ * Returns pointer to software node reference description on success, or ERR_PTR
+ * on failure.
+ */
+struct software_node_reference *
+fwnode_create_software_node_reference(const struct fwnode_handle *fwnode,
+ const char *name,
+ const struct fwnode_reference_args *args)
+{
+ struct software_node *swnode = to_software_node(fwnode);
+ struct software_node_reference *ref;
+ int n;
+
+ if (!swnode)
+ return ERR_PTR(-EINVAL);
+
+ for (n = 0; args[n].fwnode; n++)
+ if (!is_software_node(args[n].fwnode))
+ return ERR_PTR(-EINVAL);
+
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return ERR_PTR(-ENOMEM);
+
+ ref->nrefs = n;
+
+ ref->name = kstrdup(name, GFP_KERNEL);
+ if (!ref->name) {
+ kfree(ref);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ref->args = kcalloc(ref->nrefs, sizeof(*ref->args), GFP_KERNEL);
+ if (!ref->args) {
+ kfree(ref->name);
+ kfree(ref);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ for (n = 0; n < ref->nrefs; n++) {
+ ref->args[n] = args[n];
+ software_node_get(ref->args[n].fwnode);
+ }
+
+ mutex_lock(&swnode->lock);
+ list_add_tail(&ref->list, &swnode->references);
+ mutex_unlock(&swnode->lock);
+
+ return ref;
+}
+EXPORT_SYMBOL_GPL(fwnode_create_software_node_reference);
+
+/**
+ * fwnode_remove_software_node_reference - Remove named reference description
+ * @ref: Software node reference description
+ *
+ * Remove named reference @ref. Decrements the software node reference count of
+ * each node in @ref, and removes the association that was created in
+ * fwnode_create_software_node_reference().
+ */
+void fwnode_remove_software_node_reference(struct software_node_reference *ref)
+{
+ int n;
+
+ if (IS_ERR_OR_NULL(ref))
+ return;
+
+ for (n = 0; n < ref->nrefs; n++)
+ kobject_put(&to_software_node(ref->args[n].fwnode)->kobj);
+
+ list_del(&ref->list);
+ kfree(ref->args);
+ kfree(ref->name);
+ kfree(ref);
+}
+EXPORT_SYMBOL_GPL(fwnode_remove_software_node_reference);
+
int software_node_notify(struct device *dev, unsigned long action)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
@@ -314,6 +314,8 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
/* -------------------------------------------------------------------------- */
/* Software fwnode support - when HW description is incomplete or missing */
+struct sofware_node_reference;
+
bool is_software_node(const struct fwnode_handle *fwnode);
int software_node_notify(struct device *dev, unsigned long action);
@@ -323,4 +325,10 @@ fwnode_create_software_node(const struct property_entry *properties,
const struct fwnode_handle *parent);
void fwnode_remove_software_node(struct fwnode_handle *fwnode);
+struct software_node_reference *
+fwnode_create_software_node_reference(const struct fwnode_handle *fwnode,
+ const char *name,
+ const struct fwnode_reference_args *args);
+void fwnode_remove_software_node_reference(struct software_node_reference *ref);
+
#endif /* _LINUX_PROPERTY_H_ */
Introducing functions that can be used for adding and removing references to other software nodes. The goal is to support fwnode_property_get_reference_args() also with software nodes, however, get_reference_args fwnode operation callback is not yet implemented in this commit for the software nodes. This commit will only add support for reference addition/removal. The next example shows how to add references to GPIOs using the format described in Documentation/acpi/gpio-properties.txt: /* Array with two GPIOs */ static struct fwnode_reference_args gpioargs[] __initdata = { { .nargs = 3, .args[0]= 19, /* index */ .args[1]= 0, /* pin */ .args[2]= 0, /* active_low */ }, { .nargs = 3, .args[0]= 20, /* index */ .args[1]= 0, /* pin */ .args[2]= 0, /* active_low */ }, { } }; static int myinit(void) { struct software_node_reference *ref; struct fwnode_handle *gpio_node; struct fwnode_handle *my_node; /* Creating the nodes */ gpio_node = fwnode_create_software_node(gpio_props, NULL); ... my_node = fwnode_create_software_node(my_props, NULL); ... /* gpio_node is associated with a GPIO/Pin controller in this example */ ... /* Assigning the actual node references */ gpioargs[0].fwnode = gpio_node; gpioargs[1].fwnode = gpio_node; /* my_node will now have a named ("gpios") reference to the two GPIOs */ ref = fwnode_create_software_node_reference(my_node, "gpios", gpioargs); ... return 0; } Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> --- drivers/base/swnode.c | 101 +++++++++++++++++++++++++++++++++++++++ include/linux/property.h | 8 ++++ 2 files changed, 109 insertions(+)