Commit 0415052d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull device properties framework updates from Rafael Wysocki:
 "These add helpers for counting items in a property array and extend
  the "software nodes" support to be more convenient for representing
  device properties supplied by drivers and make the intel_cht_int33fe
  driver use that.

  Specifics:

   - Add helpers to count items in a property array (Andy Shevchenko).

   - Extend "software nodes" support to be more convenient for
     representing device properties supplied by drivers (Heikki
     Krogerus).

   - Add device_find_child_by_name() helper to the driver core (Heikki
     Krogerus).

   - Extend device connection code to also look for references provided
     via fwnode pointers (Heikki Krogerus).

   - Start to register proper struct device objects for USB Type-C muxes
     and orientation switches (Heikki Krogerus).

   - Update the intel_cht_int33fe driver to describe devices in a more
     general way with the help of "software nodes" (Heikki Krogerus)"

* tag 'devprop-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  device property: Add helpers to count items in an array
  platform/x86: intel_cht_int33fe: Replacing the old connections with references
  platform/x86: intel_cht_int33fe: Supply fwnodes for the external dependencies
  platform/x86: intel_cht_int33fe: Provide fwnode for the USB connector
  platform/x86: intel_cht_int33fe: Provide software nodes for the devices
  platform/x86: intel_cht_int33fe: Remove unused fusb302 device property
  platform/x86: intel_cht_int33fe: Register max17047 in its own function
  usb: typec: Registering real device entries for the muxes
  device connection: Find connections also by checking the references
  device property: Introduce fwnode_find_reference()
  ACPI / property: Don't limit named child node matching to data nodes
  driver core: Add helper device_find_child_by_name()
  software node: Add software_node_get_reference_args()
  software node: Use kobject name when finding child nodes by name
  software node: Add support for static node descriptors
  software node: Simplify software_node_release() function
  software node: Allow node creation without properties
parents 4b470452 33ee09cd
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -600,15 +600,29 @@ static struct fwnode_handle *
acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
				 const char *childname)
{
	char name[ACPI_PATH_SEGMENT_LENGTH];
	struct fwnode_handle *child;
	struct acpi_buffer path;
	acpi_status status;

	/*
	 * Find first matching named child node of this fwnode.
	 * For ACPI this will be a data only sub-node.
	 */
	fwnode_for_each_child_node(fwnode, child)
	path.length = sizeof(name);
	path.pointer = name;

	fwnode_for_each_child_node(fwnode, child) {
		if (is_acpi_data_node(child)) {
			if (acpi_data_node_match(child, childname))
				return child;
			continue;
		}

		status = acpi_get_name(ACPI_HANDLE_FWNODE(child),
				       ACPI_SINGLE_NAME, &path);
		if (ACPI_FAILURE(status))
			break;

		if (!strncmp(name, childname, ACPI_NAMESEG_SIZE))
			return child;
	}

	return NULL;
}
+28 −0
Original line number Diff line number Diff line
@@ -2474,6 +2474,34 @@ struct device *device_find_child(struct device *parent, void *data,
}
EXPORT_SYMBOL_GPL(device_find_child);

/**
 * device_find_child_by_name - device iterator for locating a child device.
 * @parent: parent struct device
 * @name: name of the child device
 *
 * This is similar to the device_find_child() function above, but it
 * returns a reference to a device that has the name @name.
 *
 * NOTE: you will need to drop the reference with put_device() after use.
 */
struct device *device_find_child_by_name(struct device *parent,
					 const char *name)
{
	struct klist_iter i;
	struct device *child;

	if (!parent)
		return NULL;

	klist_iter_init(&parent->p->klist_children, &i);
	while ((child = next_device(&i)))
		if (!strcmp(dev_name(child), name) && get_device(child))
			break;
	klist_iter_exit(&i);
	return child;
}
EXPORT_SYMBOL_GPL(device_find_child_by_name);

int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
+26 −0
Original line number Diff line number Diff line
@@ -38,6 +38,28 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
	return NULL;
}

static void *
fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
		    void *data, devcon_match_fn_t match)
{
	struct device_connection con = { };
	void *ret;
	int i;

	for (i = 0; ; i++) {
		con.fwnode = fwnode_find_reference(fwnode, con_id, i);
		if (IS_ERR(con.fwnode))
			break;

		ret = match(&con, -1, data);
		fwnode_handle_put(con.fwnode);
		if (ret)
			return ret;
	}

	return NULL;
}

/**
 * device_connection_find_match - Find physical connection to a device
 * @dev: Device with the connection
@@ -65,6 +87,10 @@ void *device_connection_find_match(struct device *dev, const char *con_id,
		ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
		if (ret)
			return ret;

		ret = fwnode_devcon_match(fwnode, con_id, data, match);
		if (ret)
			return ret;
	}

	mutex_lock(&devcon_lock);
+24 −0
Original line number Diff line number Diff line
@@ -484,6 +484,30 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
}
EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);

/**
 * fwnode_find_reference - Find named reference to a fwnode_handle
 * @fwnode: Firmware node where to look for the reference
 * @name: The name of the reference
 * @index: Index of the reference
 *
 * @index can be used when the named reference holds a table of references.
 *
 * Returns pointer to the reference fwnode, or ERR_PTR. Caller is responsible to
 * call fwnode_handle_put() on the returned fwnode pointer.
 */
struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
					    const char *name,
					    unsigned int index)
{
	struct fwnode_reference_args args;
	int ret;

	ret = fwnode_property_get_reference_args(fwnode, name, NULL, 0, index,
						 &args);
	return ret ? ERR_PTR(ret) : args.fwnode;
}
EXPORT_SYMBOL_GPL(fwnode_find_reference);

/**
 * device_remove_properties - Remove properties from a device object.
 * @dev: Device whose properties to remove.
+250 −74
Original line number Diff line number Diff line
@@ -11,25 +11,25 @@
#include <linux/property.h>
#include <linux/slab.h>

struct software_node {
struct swnode {
	int id;
	struct kobject kobj;
	struct fwnode_handle fwnode;
	const struct software_node *node;

	/* hierarchy */
	struct ida child_ids;
	struct list_head entry;
	struct list_head children;
	struct software_node *parent;
	struct swnode *parent;

	/* properties */
	const struct property_entry *properties;
	unsigned int allocated:1;
};

static DEFINE_IDA(swnode_root_ids);
static struct kset *swnode_kset;

#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct software_node, kobj)
#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)

static const struct fwnode_operations software_node_ops;

@@ -37,17 +37,56 @@ bool is_software_node(const struct fwnode_handle *fwnode)
{
	return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;
}
EXPORT_SYMBOL_GPL(is_software_node);

#define to_software_node(__fwnode)					\
#define to_swnode(__fwnode)						\
	({								\
		typeof(__fwnode) __to_software_node_fwnode = __fwnode;	\
		typeof(__fwnode) __to_swnode_fwnode = __fwnode;		\
									\
		is_software_node(__to_software_node_fwnode) ?		\
			container_of(__to_software_node_fwnode,		\
				     struct software_node, fwnode) :	\
			NULL;						\
		is_software_node(__to_swnode_fwnode) ?			\
			container_of(__to_swnode_fwnode,		\
				     struct swnode, fwnode) : NULL;	\
	})

static struct swnode *
software_node_to_swnode(const struct software_node *node)
{
	struct swnode *swnode;
	struct kobject *k;

	if (!node)
		return NULL;

	spin_lock(&swnode_kset->list_lock);

	list_for_each_entry(k, &swnode_kset->list, entry) {
		swnode = kobj_to_swnode(k);
		if (swnode->node == node)
			break;
		swnode = NULL;
	}

	spin_unlock(&swnode_kset->list_lock);

	return swnode;
}

const struct software_node *to_software_node(struct fwnode_handle *fwnode)
{
	struct swnode *swnode = to_swnode(fwnode);

	return swnode ? swnode->node : NULL;
}
EXPORT_SYMBOL_GPL(to_software_node);

struct fwnode_handle *software_node_fwnode(const struct software_node *node)
{
	struct swnode *swnode = software_node_to_swnode(node);

	return swnode ? &swnode->fwnode : NULL;
}
EXPORT_SYMBOL_GPL(software_node_fwnode);

/* -------------------------------------------------------------------------- */
/* property_entry processing */

@@ -383,6 +422,9 @@ property_entries_dup(const struct property_entry *properties)
	int i, n = 0;
	int ret;

	if (!properties)
		return NULL;

	while (properties[n].name)
		n++;

@@ -430,7 +472,7 @@ EXPORT_SYMBOL_GPL(property_entries_free);

static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	kobject_get(&swnode->kobj);

@@ -439,7 +481,7 @@ static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)

static void software_node_put(struct fwnode_handle *fwnode)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	kobject_put(&swnode->kobj);
}
@@ -447,8 +489,9 @@ static void software_node_put(struct fwnode_handle *fwnode)
static bool software_node_property_present(const struct fwnode_handle *fwnode,
					   const char *propname)
{
	return !!property_entry_get(to_software_node(fwnode)->properties,
				    propname);
	struct swnode *swnode = to_swnode(fwnode);

	return !!property_entry_get(swnode->node->properties, propname);
}

static int software_node_read_int_array(const struct fwnode_handle *fwnode,
@@ -456,9 +499,9 @@ static int software_node_read_int_array(const struct fwnode_handle *fwnode,
					unsigned int elem_size, void *val,
					size_t nval)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	return property_entry_read_int_array(swnode->properties, propname,
	return property_entry_read_int_array(swnode->node->properties, propname,
					     elem_size, val, nval);
}

@@ -466,27 +509,26 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode,
					   const char *propname,
					   const char **val, size_t nval)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	return property_entry_read_string_array(swnode->properties, propname,
						val, nval);
	return property_entry_read_string_array(swnode->node->properties,
						propname, val, nval);
}

static struct fwnode_handle *
software_node_get_parent(const struct fwnode_handle *fwnode)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) :
			NULL;
	return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) : NULL;
}

static struct fwnode_handle *
software_node_get_next_child(const struct fwnode_handle *fwnode,
			     struct fwnode_handle *child)
{
	struct software_node *p = to_software_node(fwnode);
	struct software_node *c = to_software_node(child);
	struct swnode *p = to_swnode(fwnode);
	struct swnode *c = to_swnode(child);

	if (!p || list_empty(&p->children) ||
	    (c && list_is_last(&c->entry, &p->children)))
@@ -495,7 +537,7 @@ software_node_get_next_child(const struct fwnode_handle *fwnode,
	if (c)
		c = list_next_entry(c, entry);
	else
		c = list_first_entry(&p->children, struct software_node, entry);
		c = list_first_entry(&p->children, struct swnode, entry);
	return &c->fwnode;
}

@@ -503,18 +545,14 @@ static struct fwnode_handle *
software_node_get_named_child_node(const struct fwnode_handle *fwnode,
				   const char *childname)
{
	struct software_node *swnode = to_software_node(fwnode);
	const struct property_entry *prop;
	struct software_node *child;
	struct swnode *swnode = to_swnode(fwnode);
	struct swnode *child;

	if (!swnode || list_empty(&swnode->children))
		return NULL;

	list_for_each_entry(child, &swnode->children, entry) {
		prop = property_entry_get(child->properties, "name");
		if (!prop)
			continue;
		if (!strcmp(childname, prop->value.str)) {
		if (!strcmp(childname, kobject_name(&child->kobj))) {
			kobject_get(&child->kobj);
			return &child->fwnode;
		}
@@ -522,6 +560,52 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode,
	return NULL;
}

static int
software_node_get_reference_args(const struct fwnode_handle *fwnode,
				 const char *propname, const char *nargs_prop,
				 unsigned int nargs, unsigned int index,
				 struct fwnode_reference_args *args)
{
	struct swnode *swnode = to_swnode(fwnode);
	const struct software_node_reference *ref;
	const struct property_entry *prop;
	struct fwnode_handle *refnode;
	int i;

	if (!swnode || !swnode->node->references)
		return -ENOENT;

	for (ref = swnode->node->references; ref->name; ref++)
		if (!strcmp(ref->name, propname))
			break;

	if (!ref->name || index > (ref->nrefs - 1))
		return -ENOENT;

	refnode = software_node_fwnode(ref->refs[index].node);
	if (!refnode)
		return -ENOENT;

	if (nargs_prop) {
		prop = property_entry_get(swnode->node->properties, nargs_prop);
		if (!prop)
			return -EINVAL;

		nargs = prop->value.u32_data;
	}

	if (nargs > NR_FWNODE_REFERENCE_ARGS)
		return -EINVAL;

	args->fwnode = software_node_get(refnode);
	args->nargs = nargs;

	for (i = 0; i < nargs; i++)
		args->args[i] = ref->refs[index].args[i];

	return 0;
}

static const struct fwnode_operations software_node_ops = {
	.get = software_node_get,
	.put = software_node_put,
@@ -531,12 +615,13 @@ static const struct fwnode_operations software_node_ops = {
	.get_parent = software_node_get_parent,
	.get_next_child_node = software_node_get_next_child,
	.get_named_child_node = software_node_get_named_child_node,
	.get_reference_args = software_node_get_reference_args
};

/* -------------------------------------------------------------------------- */

static int
software_node_register_properties(struct software_node *swnode,
software_node_register_properties(struct software_node *node,
				  const struct property_entry *properties)
{
	struct property_entry *props;
@@ -545,24 +630,20 @@ software_node_register_properties(struct software_node *swnode,
	if (IS_ERR(props))
		return PTR_ERR(props);

	swnode->properties = props;
	node->properties = props;

	return 0;
}

static void software_node_release(struct kobject *kobj)
{
	struct software_node *swnode = kobj_to_swnode(kobj);
	struct swnode *swnode = kobj_to_swnode(kobj);

	if (swnode->parent) {
		ida_simple_remove(&swnode->parent->child_ids, swnode->id);
		list_del(&swnode->entry);
	} else {
		ida_simple_remove(&swnode_root_ids, swnode->id);
	if (swnode->allocated) {
		property_entries_free(swnode->node->properties);
		kfree(swnode->node);
	}

	ida_destroy(&swnode->child_ids);
	property_entries_free(swnode->properties);
	kfree(swnode);
}

@@ -571,70 +652,165 @@ static struct kobj_type software_node_type = {
	.sysfs_ops = &kobj_sysfs_ops,
};

struct fwnode_handle *
fwnode_create_software_node(const struct property_entry *properties,
			    const struct fwnode_handle *parent)
static struct fwnode_handle *
swnode_register(const struct software_node *node, struct swnode *parent,
		unsigned int allocated)
{
	struct software_node *p = NULL;
	struct software_node *swnode;
	struct swnode *swnode;
	int ret;

	if (parent) {
		if (IS_ERR(parent))
			return ERR_CAST(parent);
		if (!is_software_node(parent))
			return ERR_PTR(-EINVAL);
		p = to_software_node(parent);
	}

	swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
	if (!swnode)
		return ERR_PTR(-ENOMEM);
	if (!swnode) {
		ret = -ENOMEM;
		goto out_err;
	}

	ret = ida_simple_get(p ? &p->child_ids : &swnode_root_ids, 0, 0,
			     GFP_KERNEL);
	ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
			     0, 0, GFP_KERNEL);
	if (ret < 0) {
		kfree(swnode);
		return ERR_PTR(ret);
		goto out_err;
	}

	swnode->id = ret;
	swnode->node = node;
	swnode->parent = parent;
	swnode->allocated = allocated;
	swnode->kobj.kset = swnode_kset;
	swnode->fwnode.ops = &software_node_ops;

	ida_init(&swnode->child_ids);
	INIT_LIST_HEAD(&swnode->entry);
	INIT_LIST_HEAD(&swnode->children);
	swnode->parent = p;

	if (p)
		list_add_tail(&swnode->entry, &p->children);

	if (node->name)
		ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
				   p ? &p->kobj : NULL, "node%d", swnode->id);
					   parent ? &parent->kobj : NULL,
					   "%s", node->name);
	else
		ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
					   parent ? &parent->kobj : NULL,
					   "node%d", swnode->id);
	if (ret) {
		kobject_put(&swnode->kobj);
		return ERR_PTR(ret);
	}

	ret = software_node_register_properties(swnode, properties);
	if (parent)
		list_add_tail(&swnode->entry, &parent->children);

	kobject_uevent(&swnode->kobj, KOBJ_ADD);
	return &swnode->fwnode;

out_err:
	if (allocated)
		property_entries_free(node->properties);
	return ERR_PTR(ret);
}

/**
 * software_node_register_nodes - Register an array of software nodes
 * @nodes: Zero terminated array of software nodes to be registered
 *
 * Register multiple software nodes at once.
 */
int software_node_register_nodes(const struct software_node *nodes)
{
	int ret;
	int i;

	for (i = 0; nodes[i].name; i++) {
		ret = software_node_register(&nodes[i]);
		if (ret) {
		kobject_put(&swnode->kobj);
			software_node_unregister_nodes(nodes);
			return ret;
		}
	}

	return 0;
}
EXPORT_SYMBOL_GPL(software_node_register_nodes);

/**
 * software_node_unregister_nodes - Unregister an array of software nodes
 * @nodes: Zero terminated array of software nodes to be unregistered
 *
 * Unregister multiple software nodes at once.
 */
void software_node_unregister_nodes(const struct software_node *nodes)
{
	struct swnode *swnode;
	int i;

	for (i = 0; nodes[i].name; i++) {
		swnode = software_node_to_swnode(&nodes[i]);
		if (swnode)
			fwnode_remove_software_node(&swnode->fwnode);
	}
}
EXPORT_SYMBOL_GPL(software_node_unregister_nodes);

/**
 * software_node_register - Register static software node
 * @node: The software node to be registered
 */
int software_node_register(const struct software_node *node)
{
	struct swnode *parent = software_node_to_swnode(node->parent);

	if (software_node_to_swnode(node))
		return -EEXIST;

	return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
}
EXPORT_SYMBOL_GPL(software_node_register);

struct fwnode_handle *
fwnode_create_software_node(const struct property_entry *properties,
			    const struct fwnode_handle *parent)
{
	struct software_node *node;
	struct swnode *p = NULL;
	int ret;

	if (parent) {
		if (IS_ERR(parent))
			return ERR_CAST(parent);
		if (!is_software_node(parent))
			return ERR_PTR(-EINVAL);
		p = to_swnode(parent);
	}

	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (!node)
		return ERR_PTR(-ENOMEM);

	ret = software_node_register_properties(node, properties);
	if (ret) {
		kfree(node);
		return ERR_PTR(ret);
	}

	kobject_uevent(&swnode->kobj, KOBJ_ADD);
	return &swnode->fwnode;
	node->parent = p ? p->node : NULL;

	return swnode_register(node, p, 1);
}
EXPORT_SYMBOL_GPL(fwnode_create_software_node);

void fwnode_remove_software_node(struct fwnode_handle *fwnode)
{
	struct software_node *swnode = to_software_node(fwnode);
	struct swnode *swnode = to_swnode(fwnode);

	if (!swnode)
		return;

	if (swnode->parent) {
		ida_simple_remove(&swnode->parent->child_ids, swnode->id);
		list_del(&swnode->entry);
	} else {
		ida_simple_remove(&swnode_root_ids, swnode->id);
	}

	kobject_put(&swnode->kobj);
}
EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
@@ -642,7 +818,7 @@ EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
int software_node_notify(struct device *dev, unsigned long action)
{
	struct fwnode_handle *fwnode = dev_fwnode(dev);
	struct software_node *swnode;
	struct swnode *swnode;
	int ret;

	if (!fwnode)
@@ -653,7 +829,7 @@ int software_node_notify(struct device *dev, unsigned long action)
	if (!is_software_node(fwnode))
		return 0;

	swnode = to_software_node(fwnode);
	swnode = to_swnode(fwnode);

	switch (action) {
	case KOBJ_ADD:
Loading