Commit 0415052d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'devprop-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

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
...@@ -600,15 +600,29 @@ static struct fwnode_handle * ...@@ -600,15 +600,29 @@ static struct fwnode_handle *
acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode, acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
const char *childname) const char *childname)
{ {
char name[ACPI_PATH_SEGMENT_LENGTH];
struct fwnode_handle *child; struct fwnode_handle *child;
struct acpi_buffer path;
acpi_status status;
/* path.length = sizeof(name);
* Find first matching named child node of this fwnode. path.pointer = name;
* For ACPI this will be a data only sub-node.
*/ fwnode_for_each_child_node(fwnode, child) {
fwnode_for_each_child_node(fwnode, child) if (is_acpi_data_node(child)) {
if (acpi_data_node_match(child, childname)) 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 child;
}
return NULL; return NULL;
} }
......
...@@ -2474,6 +2474,34 @@ struct device *device_find_child(struct device *parent, void *data, ...@@ -2474,6 +2474,34 @@ struct device *device_find_child(struct device *parent, void *data,
} }
EXPORT_SYMBOL_GPL(device_find_child); 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) int __init devices_init(void)
{ {
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
......
...@@ -38,6 +38,28 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, ...@@ -38,6 +38,28 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
return NULL; 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 * device_connection_find_match - Find physical connection to a device
* @dev: Device with the connection * @dev: Device with the connection
...@@ -65,6 +87,10 @@ void *device_connection_find_match(struct device *dev, const char *con_id, ...@@ -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); ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
if (ret) if (ret)
return ret; return ret;
ret = fwnode_devcon_match(fwnode, con_id, data, match);
if (ret)
return ret;
} }
mutex_lock(&devcon_lock); mutex_lock(&devcon_lock);
......
...@@ -484,6 +484,30 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode, ...@@ -484,6 +484,30 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
} }
EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); 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. * device_remove_properties - Remove properties from a device object.
* @dev: Device whose properties to remove. * @dev: Device whose properties to remove.
......
...@@ -11,25 +11,25 @@ ...@@ -11,25 +11,25 @@
#include <linux/property.h> #include <linux/property.h>
#include <linux/slab.h> #include <linux/slab.h>
struct software_node { struct swnode {
int id; int id;
struct kobject kobj; struct kobject kobj;
struct fwnode_handle fwnode; struct fwnode_handle fwnode;
const struct software_node *node;
/* hierarchy */ /* hierarchy */
struct ida child_ids; struct ida child_ids;
struct list_head entry; struct list_head entry;
struct list_head children; struct list_head children;
struct software_node *parent; struct swnode *parent;
/* properties */ unsigned int allocated:1;
const struct property_entry *properties;
}; };
static DEFINE_IDA(swnode_root_ids); static DEFINE_IDA(swnode_root_ids);
static struct kset *swnode_kset; 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; static const struct fwnode_operations software_node_ops;
...@@ -37,17 +37,56 @@ bool is_software_node(const struct fwnode_handle *fwnode) ...@@ -37,17 +37,56 @@ bool is_software_node(const struct fwnode_handle *fwnode)
{ {
return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops; 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) ? \ is_software_node(__to_swnode_fwnode) ? \
container_of(__to_software_node_fwnode, \ container_of(__to_swnode_fwnode, \
struct software_node, fwnode) : \ struct swnode, fwnode) : NULL; \
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 */ /* property_entry processing */
...@@ -383,6 +422,9 @@ property_entries_dup(const struct property_entry *properties) ...@@ -383,6 +422,9 @@ property_entries_dup(const struct property_entry *properties)
int i, n = 0; int i, n = 0;
int ret; int ret;
if (!properties)
return NULL;
while (properties[n].name) while (properties[n].name)
n++; n++;
...@@ -430,7 +472,7 @@ EXPORT_SYMBOL_GPL(property_entries_free); ...@@ -430,7 +472,7 @@ EXPORT_SYMBOL_GPL(property_entries_free);
static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) 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); kobject_get(&swnode->kobj);
...@@ -439,7 +481,7 @@ static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) ...@@ -439,7 +481,7 @@ static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
static void software_node_put(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); kobject_put(&swnode->kobj);
} }
...@@ -447,8 +489,9 @@ static void software_node_put(struct fwnode_handle *fwnode) ...@@ -447,8 +489,9 @@ static void software_node_put(struct fwnode_handle *fwnode)
static bool software_node_property_present(const struct fwnode_handle *fwnode, static bool software_node_property_present(const struct fwnode_handle *fwnode,
const char *propname) const char *propname)
{ {
return !!property_entry_get(to_software_node(fwnode)->properties, struct swnode *swnode = to_swnode(fwnode);
propname);
return !!property_entry_get(swnode->node->properties, propname);
} }
static int software_node_read_int_array(const struct fwnode_handle *fwnode, 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, ...@@ -456,9 +499,9 @@ static int software_node_read_int_array(const struct fwnode_handle *fwnode,
unsigned int elem_size, void *val, unsigned int elem_size, void *val,
size_t nval) 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); elem_size, val, nval);
} }
...@@ -466,27 +509,26 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode, ...@@ -466,27 +509,26 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode,
const char *propname, const char *propname,
const char **val, size_t nval) 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, return property_entry_read_string_array(swnode->node->properties,
val, nval); propname, val, nval);
} }
static struct fwnode_handle * static struct fwnode_handle *
software_node_get_parent(const struct fwnode_handle *fwnode) 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) : return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) : NULL;
NULL;
} }
static struct fwnode_handle * static struct fwnode_handle *
software_node_get_next_child(const struct fwnode_handle *fwnode, software_node_get_next_child(const struct fwnode_handle *fwnode,
struct fwnode_handle *child) struct fwnode_handle *child)
{ {
struct software_node *p = to_software_node(fwnode); struct swnode *p = to_swnode(fwnode);
struct software_node *c = to_software_node(child); struct swnode *c = to_swnode(child);
if (!p || list_empty(&p->children) || if (!p || list_empty(&p->children) ||
(c && list_is_last(&c->entry, &p->children))) (c && list_is_last(&c->entry, &p->children)))
...@@ -495,7 +537,7 @@ software_node_get_next_child(const struct fwnode_handle *fwnode, ...@@ -495,7 +537,7 @@ software_node_get_next_child(const struct fwnode_handle *fwnode,
if (c) if (c)
c = list_next_entry(c, entry); c = list_next_entry(c, entry);
else else
c = list_first_entry(&p->children, struct software_node, entry); c = list_first_entry(&p->children, struct swnode, entry);
return &c->fwnode; return &c->fwnode;
} }
...@@ -503,18 +545,14 @@ static struct fwnode_handle * ...@@ -503,18 +545,14 @@ static struct fwnode_handle *
software_node_get_named_child_node(const struct fwnode_handle *fwnode, software_node_get_named_child_node(const struct fwnode_handle *fwnode,
const char *childname) const char *childname)
{ {
struct software_node *swnode = to_software_node(fwnode); struct swnode *swnode = to_swnode(fwnode);
const struct property_entry *prop; struct swnode *child;
struct software_node *child;
if (!swnode || list_empty(&swnode->children)) if (!swnode || list_empty(&swnode->children))
return NULL; return NULL;
list_for_each_entry(child, &swnode->children, entry) { list_for_each_entry(child, &swnode->children, entry) {
prop = property_entry_get(child->properties, "name"); if (!strcmp(childname, kobject_name(&child->kobj))) {
if (!prop)
continue;
if (!strcmp(childname, prop->value.str)) {
kobject_get(&child->kobj); kobject_get(&child->kobj);
return &child->fwnode; return &child->fwnode;
} }
...@@ -522,6 +560,52 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode, ...@@ -522,6 +560,52 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode,
return NULL; 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 = { static const struct fwnode_operations software_node_ops = {
.get = software_node_get, .get = software_node_get,
.put = software_node_put, .put = software_node_put,
...@@ -531,12 +615,13 @@ static const struct fwnode_operations software_node_ops = { ...@@ -531,12 +615,13 @@ static const struct fwnode_operations software_node_ops = {
.get_parent = software_node_get_parent, .get_parent = software_node_get_parent,
.get_next_child_node = software_node_get_next_child, .get_next_child_node = software_node_get_next_child,
.get_named_child_node = software_node_get_named_child_node, .get_named_child_node = software_node_get_named_child_node,
.get_reference_args = software_node_get_reference_args
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
static int static int
software_node_register_properties(struct software_node *swnode, software_node_register_properties(struct software_node *node,
const struct property_entry *properties) const struct property_entry *properties)
{ {
struct property_entry *props; struct property_entry *props;
...@@ -545,24 +630,20 @@ software_node_register_properties(struct software_node *swnode, ...@@ -545,24 +630,20 @@ software_node_register_properties(struct software_node *swnode,
if (IS_ERR(props)) if (IS_ERR(props))
return PTR_ERR(props); return PTR_ERR(props);
swnode->properties = props; node->properties = props;
return 0; return 0;
} }
static void software_node_release(struct kobject *kobj) 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) { if (swnode->allocated) {
ida_simple_remove(&swnode->parent->child_ids, swnode->id); property_entries_free(swnode->node->properties);
list_del(&swnode->entry); kfree(swnode->node);
} else {
ida_simple_remove(&swnode_root_ids, swnode->id);
} }
ida_destroy(&swnode->child_ids); ida_destroy(&swnode->child_ids);
property_entries_free(swnode->properties);
kfree(swnode); kfree(swnode);
} }
...@@ -571,70 +652,165 @@ static struct kobj_type software_node_type = { ...@@ -571,70 +652,165 @@ static struct kobj_type software_node_type = {
.sysfs_ops = &kobj_sysfs_ops, .sysfs_ops = &kobj_sysfs_ops,
}; };
struct fwnode_handle * static struct fwnode_handle *
fwnode_create_software_node(const struct property_entry *properties, swnode_register(const struct software_node *node, struct swnode *parent,
const struct fwnode_handle *parent) unsigned int allocated)
{ {
struct software_node *p = NULL; struct swnode *swnode;
struct software_node *swnode;
int ret; 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); swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
if (!swnode) if (!swnode) {
return ERR_PTR(-ENOMEM); ret = -ENOMEM;
goto out_err;
}
ret = ida_simple_get(p ? &p->child_ids : &swnode_root_ids, 0, 0, ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
GFP_KERNEL); 0, 0, GFP_KERNEL);
if (ret < 0) { if (ret < 0) {
kfree(swnode); kfree(swnode);
return ERR_PTR(ret); goto out_err;
} }
swnode->id = ret; swnode->id = ret;
swnode->node = node;
swnode->parent = parent;
swnode->allocated = allocated;
swnode->kobj.kset = swnode_kset; swnode->kobj.kset = swnode_kset;
swnode->fwnode.ops = &software_node_ops; swnode->fwnode.ops = &software_node_ops;
ida_init(&swnode->child_ids); ida_init(&swnode->child_ids);
INIT_LIST_HEAD(&swnode->entry); INIT_LIST_HEAD(&swnode->entry);
INIT_LIST_HEAD(&swnode->children); INIT_LIST_HEAD(&swnode->children);
swnode->parent = p;
if (p)
list_add_tail(&swnode->entry, &p->children);
ret = kobject_init_and_add(&swnode->kobj, &software_node_type, if (node->name)
p ? &p->kobj : NULL, "node%d", swnode->id); ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
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) { if (ret) {
kobject_put(&swnode->kobj); kobject_put(&swnode->kobj);
return ERR_PTR(ret); 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) {
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) { if (ret) {
kobject_put(&swnode->kobj); kfree(node);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
kobject_uevent(&swnode->kobj, KOBJ_ADD); node->parent = p ? p->node : NULL;
return &swnode->fwnode;
return swnode_register(node, p, 1);
} }
EXPORT_SYMBOL_GPL(fwnode_create_software_node); EXPORT_SYMBOL_GPL(fwnode_create_software_node);
void fwnode_remove_software_node(struct fwnode_handle *fwnode) 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) if (!swnode)
return; 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); kobject_put(&swnode->kobj);
} }
EXPORT_SYMBOL_GPL(fwnode_remove_software_node); EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
...@@ -642,7 +818,7 @@ 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) int software_node_notify(struct device *dev, unsigned long action)
{ {
struct fwnode_handle *fwnode = dev_fwnode(dev); struct fwnode_handle *fwnode = dev_fwnode(dev);
struct software_node *swnode; struct swnode *swnode;
int ret; int ret;
if (!fwnode) if (!fwnode)
...@@ -653,7 +829,7 @@ int software_node_notify(struct device *dev, unsigned long action) ...@@ -653,7 +829,7 @@ int software_node_notify(struct device *dev, unsigned long action)
if (!is_software_node(fwnode)) if (!is_software_node(fwnode))
return 0; return 0;
swnode = to_software_node(fwnode); swnode = to_swnode(fwnode);
switch (action) { switch (action) {
case KOBJ_ADD: case KOBJ_ADD:
......
...@@ -21,18 +21,55 @@ ...@@ -21,18 +21,55 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb/pd.h>
#define EXPECTED_PTYPE 4 #define EXPECTED_PTYPE 4
enum {
INT33FE_NODE_FUSB302,
INT33FE_NODE_MAX17047,
INT33FE_NODE_PI3USB30532,
INT33FE_NODE_DISPLAYPORT,
INT33FE_NODE_ROLE_SWITCH,
INT33FE_NODE_USB_CONNECTOR,
INT33FE_NODE_MAX,
};
struct cht_int33fe_data { struct cht_int33fe_data {
struct i2c_client *max17047; struct i2c_client *max17047;
struct i2c_client *fusb302; struct i2c_client *fusb302;
struct i2c_client *pi3usb30532; struct i2c_client *pi3usb30532;
/* Contain a list-head must be per device */
struct device_connection connections[4]; struct fwnode_handle *dp;
struct fwnode_handle *mux;
};
static const struct software_node nodes[];
static const struct software_node_ref_args pi3usb30532_ref = {
&nodes[INT33FE_NODE_PI3USB30532]
};
static const struct software_node_ref_args dp_ref = {
&nodes[INT33FE_NODE_DISPLAYPORT]
};
static struct software_node_ref_args mux_ref;
static const struct software_node_reference usb_connector_refs[] = {
{ "orientation-switch", 1, &pi3usb30532_ref},
{ "mode-switch", 1, &pi3usb30532_ref},
{ "displayport", 1, &dp_ref},
{ }
};
static const struct software_node_reference fusb302_refs[] = {
{ "usb-role-switch", 1, &mux_ref},
{ }
}; };
/* /*
...@@ -63,14 +100,6 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data) ...@@ -63,14 +100,6 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
return 1; return 1;
} }
static struct i2c_client *cht_int33fe_find_max17047(void)
{
struct i2c_client *max17047 = NULL;
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
return max17047;
}
static const char * const max17047_suppliers[] = { "bq24190-charger" }; static const char * const max17047_suppliers[] = { "bq24190-charger" };
static const struct property_entry max17047_props[] = { static const struct property_entry max17047_props[] = {
...@@ -80,18 +109,196 @@ static const struct property_entry max17047_props[] = { ...@@ -80,18 +109,196 @@ static const struct property_entry max17047_props[] = {
static const struct property_entry fusb302_props[] = { static const struct property_entry fusb302_props[] = {
PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"), PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000),
PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000),
PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000),
{ } { }
}; };
#define PDO_FIXED_FLAGS \
(PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
static const u32 src_pdo[] = {
PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS),
};
static const u32 snk_pdo[] = {
PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
PDO_VAR(5000, 12000, 3000),
};
static const struct property_entry usb_connector_props[] = {
PROPERTY_ENTRY_STRING("data-role", "dual"),
PROPERTY_ENTRY_STRING("power-role", "dual"),
PROPERTY_ENTRY_STRING("try-power-role", "sink"),
PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
{ }
};
static const struct software_node nodes[] = {
{ "fusb302", NULL, fusb302_props, fusb302_refs },
{ "max17047", NULL, max17047_props },
{ "pi3usb30532" },
{ "displayport" },
{ "usb-role-switch" },
{ "connector", &nodes[0], usb_connector_props, usb_connector_refs },
{ }
};
static int cht_int33fe_setup_mux(struct cht_int33fe_data *data)
{
struct fwnode_handle *fwnode;
struct device *dev;
struct device *p;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_ROLE_SWITCH]);
if (!fwnode)
return -ENODEV;
/* First finding the platform device */
p = bus_find_device_by_name(&platform_bus_type, NULL,
"intel_xhci_usb_sw");
if (!p)
return -EPROBE_DEFER;
/* Then the mux child device */
dev = device_find_child_by_name(p, "intel_xhci_usb_sw-role-switch");
put_device(p);
if (!dev)
return -EPROBE_DEFER;
/* If there already is a node for the mux, using that one. */
if (dev->fwnode)
fwnode_remove_software_node(fwnode);
else
dev->fwnode = fwnode;
data->mux = fwnode_handle_get(dev->fwnode);
put_device(dev);
mux_ref.node = to_software_node(data->mux);
return 0;
}
static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
{
struct fwnode_handle *fwnode;
struct pci_dev *pdev;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]);
if (!fwnode)
return -ENODEV;
/* First let's find the GPU PCI device */
pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
if (!pdev || pdev->vendor != PCI_VENDOR_ID_INTEL) {
pci_dev_put(pdev);
return -ENODEV;
}
/* Then the DP child device node */
data->dp = device_get_named_child_node(&pdev->dev, "DD02");
pci_dev_put(pdev);
if (!data->dp)
return -ENODEV;
fwnode->secondary = ERR_PTR(-ENODEV);
data->dp->secondary = fwnode;
return 0;
}
static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
{
software_node_unregister_nodes(nodes);
if (data->mux) {
fwnode_handle_put(data->mux);
mux_ref.node = NULL;
data->mux = NULL;
}
if (data->dp) {
data->dp->secondary = NULL;
fwnode_handle_put(data->dp);
data->dp = NULL;
}
}
static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
{
int ret;
ret = software_node_register_nodes(nodes);
if (ret)
return ret;
/* The devices that are not created in this driver need extra steps. */
/*
* There is no ACPI device node for the USB role mux, so we need to find
* the mux device and assign our node directly to it. That means we
* depend on the mux driver. This function will return -PROBE_DEFER
* until the mux device is registered.
*/
ret = cht_int33fe_setup_mux(data);
if (ret)
goto err_remove_nodes;
/*
* The DP connector does have ACPI device node. In this case we can just
* find that ACPI node and assign our node as the secondary node to it.
*/
ret = cht_int33fe_setup_dp(data);
if (ret)
goto err_remove_nodes;
return 0;
err_remove_nodes:
cht_int33fe_remove_nodes(data);
return ret;
}
static int
cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
{
struct i2c_client *max17047 = NULL;
struct i2c_board_info board_info;
struct fwnode_handle *fwnode;
int ret;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]);
if (!fwnode)
return -ENODEV;
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
if (max17047) {
/* Pre-existing i2c-client for the max17047, add device-props */
fwnode->secondary = ERR_PTR(-ENODEV);
max17047->dev.fwnode->secondary = fwnode;
/* And re-probe to get the new device-props applied. */
ret = device_reprobe(&max17047->dev);
if (ret)
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
return 0;
}
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.dev_name = "max17047";
board_info.fwnode = fwnode;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
return PTR_ERR_OR_ZERO(data->max17047);
}
static int cht_int33fe_probe(struct platform_device *pdev) static int cht_int33fe_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct i2c_board_info board_info; struct i2c_board_info board_info;
struct cht_int33fe_data *data; struct cht_int33fe_data *data;
struct i2c_client *max17047; struct fwnode_handle *fwnode;
struct regulator *regulator; struct regulator *regulator;
unsigned long long ptyp; unsigned long long ptyp;
acpi_status status; acpi_status status;
...@@ -151,43 +358,25 @@ static int cht_int33fe_probe(struct platform_device *pdev) ...@@ -151,43 +358,25 @@ static int cht_int33fe_probe(struct platform_device *pdev)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
/* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */ ret = cht_int33fe_add_nodes(data);
max17047 = cht_int33fe_find_max17047(); if (ret)
if (max17047) { return ret;
/* Pre-existing i2c-client for the max17047, add device-props */
ret = device_add_properties(&max17047->dev, max17047_props);
if (ret)
return ret;
/* And re-probe to get the new device-props applied. */
ret = device_reprobe(&max17047->dev);
if (ret)
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
} else {
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
board_info.dev_name = "max17047";
board_info.properties = max17047_props;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (IS_ERR(data->max17047))
return PTR_ERR(data->max17047);
}
data->connections[0].endpoint[0] = "port0"; /* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */
data->connections[0].endpoint[1] = "i2c-pi3usb30532"; ret = cht_int33fe_register_max17047(dev, data);
data->connections[0].id = "orientation-switch"; if (ret)
data->connections[1].endpoint[0] = "port0"; goto out_remove_nodes;
data->connections[1].endpoint[1] = "i2c-pi3usb30532";
data->connections[1].id = "mode-switch";
data->connections[2].endpoint[0] = "i2c-fusb302";
data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch";
data->connections[2].id = "usb-role-switch";
device_connections_add(data->connections); fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
if (!fwnode) {
ret = -ENODEV;
goto out_unregister_max17047;
}
memset(&board_info, 0, sizeof(board_info)); memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
board_info.dev_name = "fusb302"; board_info.dev_name = "fusb302";
board_info.properties = fusb302_props; board_info.fwnode = fwnode;
board_info.irq = fusb302_irq; board_info.irq = fusb302_irq;
data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info); data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
...@@ -196,8 +385,15 @@ static int cht_int33fe_probe(struct platform_device *pdev) ...@@ -196,8 +385,15 @@ static int cht_int33fe_probe(struct platform_device *pdev)
goto out_unregister_max17047; goto out_unregister_max17047;
} }
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]);
if (!fwnode) {
ret = -ENODEV;
goto out_unregister_fusb302;
}
memset(&board_info, 0, sizeof(board_info)); memset(&board_info, 0, sizeof(board_info));
board_info.dev_name = "pi3usb30532"; board_info.dev_name = "pi3usb30532";
board_info.fwnode = fwnode;
strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE); strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info); data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
...@@ -216,7 +412,8 @@ static int cht_int33fe_probe(struct platform_device *pdev) ...@@ -216,7 +412,8 @@ static int cht_int33fe_probe(struct platform_device *pdev)
out_unregister_max17047: out_unregister_max17047:
i2c_unregister_device(data->max17047); i2c_unregister_device(data->max17047);
device_connections_remove(data->connections); out_remove_nodes:
cht_int33fe_remove_nodes(data);
return ret; return ret;
} }
...@@ -229,7 +426,7 @@ static int cht_int33fe_remove(struct platform_device *pdev) ...@@ -229,7 +426,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)
i2c_unregister_device(data->fusb302); i2c_unregister_device(data->fusb302);
i2c_unregister_device(data->max17047); i2c_unregister_device(data->max17047);
device_connections_remove(data->connections); cht_int33fe_remove_nodes(data);
return 0; return 0;
} }
......
...@@ -101,7 +101,7 @@ static void *usb_role_switch_match(struct device_connection *con, int ep, ...@@ -101,7 +101,7 @@ static void *usb_role_switch_match(struct device_connection *con, int ep,
struct device *dev; struct device *dev;
if (con->fwnode) { if (con->fwnode) {
if (!fwnode_property_present(con->fwnode, con->id)) if (con->id && !fwnode_property_present(con->fwnode, con->id))
return NULL; return NULL;
dev = class_find_device(role_class, NULL, con->fwnode, dev = class_find_device(role_class, NULL, con->fwnode,
......
...@@ -35,4 +35,19 @@ extern const struct device_type typec_port_dev_type; ...@@ -35,4 +35,19 @@ extern const struct device_type typec_port_dev_type;
#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) #define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
extern struct class typec_mux_class;
struct typec_switch {
struct device dev;
typec_switch_set_fn_t set;
};
struct typec_mux {
struct device dev;
typec_mux_set_fn_t set;
};
#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
#endif /* __USB_TYPEC_ALTMODE_H__ */ #endif /* __USB_TYPEC_ALTMODE_H__ */
...@@ -1646,13 +1646,25 @@ static int __init typec_init(void) ...@@ -1646,13 +1646,25 @@ static int __init typec_init(void)
if (ret) if (ret)
return ret; return ret;
ret = class_register(&typec_mux_class);
if (ret)
goto err_unregister_bus;
typec_class = class_create(THIS_MODULE, "typec"); typec_class = class_create(THIS_MODULE, "typec");
if (IS_ERR(typec_class)) { if (IS_ERR(typec_class)) {
bus_unregister(&typec_bus); ret = PTR_ERR(typec_class);
return PTR_ERR(typec_class); goto err_unregister_mux_class;
} }
return 0; return 0;
err_unregister_mux_class:
class_unregister(&typec_mux_class);
err_unregister_bus:
bus_unregister(&typec_bus);
return ret;
} }
subsys_initcall(typec_init); subsys_initcall(typec_init);
...@@ -1661,6 +1673,7 @@ static void __exit typec_exit(void) ...@@ -1661,6 +1673,7 @@ static void __exit typec_exit(void)
class_destroy(typec_class); class_destroy(typec_class);
ida_destroy(&typec_index_ida); ida_destroy(&typec_index_ida);
bus_unregister(&typec_bus); bus_unregister(&typec_bus);
class_unregister(&typec_mux_class);
} }
module_exit(typec_exit); module_exit(typec_exit);
......
...@@ -15,35 +15,47 @@ ...@@ -15,35 +15,47 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb/typec_mux.h> #include <linux/usb/typec_mux.h>
static DEFINE_MUTEX(switch_lock); #include "bus.h"
static DEFINE_MUTEX(mux_lock);
static LIST_HEAD(switch_list); static int name_match(struct device *dev, const void *name)
static LIST_HEAD(mux_list); {
return !strcmp((const char *)name, dev_name(dev));
}
static bool dev_name_ends_with(struct device *dev, const char *suffix)
{
const char *name = dev_name(dev);
const int name_len = strlen(name);
const int suffix_len = strlen(suffix);
if (suffix_len > name_len)
return false;
return strcmp(name + (name_len - suffix_len), suffix) == 0;
}
static int switch_fwnode_match(struct device *dev, const void *fwnode)
{
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch");
}
static void *typec_switch_match(struct device_connection *con, int ep, static void *typec_switch_match(struct device_connection *con, int ep,
void *data) void *data)
{ {
struct typec_switch *sw; struct device *dev;
if (!con->fwnode) {
list_for_each_entry(sw, &switch_list, entry)
if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
return sw;
return ERR_PTR(-EPROBE_DEFER);
}
/* if (con->fwnode) {
* With OF graph the mux node must have a boolean device property named if (con->id && !fwnode_property_present(con->fwnode, con->id))
* "orientation-switch". return NULL;
*/
if (con->id && !fwnode_property_present(con->fwnode, con->id))
return NULL;
list_for_each_entry(sw, &switch_list, entry) dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
if (dev_fwnode(sw->dev) == con->fwnode) switch_fwnode_match);
return sw; } else {
dev = class_find_device(&typec_mux_class, NULL,
con->endpoint[ep], name_match);
}
return con->id ? ERR_PTR(-EPROBE_DEFER) : NULL; return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
} }
/** /**
...@@ -59,14 +71,10 @@ struct typec_switch *typec_switch_get(struct device *dev) ...@@ -59,14 +71,10 @@ struct typec_switch *typec_switch_get(struct device *dev)
{ {
struct typec_switch *sw; struct typec_switch *sw;
mutex_lock(&switch_lock);
sw = device_connection_find_match(dev, "orientation-switch", NULL, sw = device_connection_find_match(dev, "orientation-switch", NULL,
typec_switch_match); typec_switch_match);
if (!IS_ERR_OR_NULL(sw)) { if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev->driver->owner)); WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
get_device(sw->dev);
}
mutex_unlock(&switch_lock);
return sw; return sw;
} }
...@@ -81,28 +89,64 @@ EXPORT_SYMBOL_GPL(typec_switch_get); ...@@ -81,28 +89,64 @@ EXPORT_SYMBOL_GPL(typec_switch_get);
void typec_switch_put(struct typec_switch *sw) void typec_switch_put(struct typec_switch *sw)
{ {
if (!IS_ERR_OR_NULL(sw)) { if (!IS_ERR_OR_NULL(sw)) {
module_put(sw->dev->driver->owner); module_put(sw->dev.parent->driver->owner);
put_device(sw->dev); put_device(&sw->dev);
} }
} }
EXPORT_SYMBOL_GPL(typec_switch_put); EXPORT_SYMBOL_GPL(typec_switch_put);
static void typec_switch_release(struct device *dev)
{
kfree(to_typec_switch(dev));
}
static const struct device_type typec_switch_dev_type = {
.name = "orientation_switch",
.release = typec_switch_release,
};
/** /**
* typec_switch_register - Register USB Type-C orientation switch * typec_switch_register - Register USB Type-C orientation switch
* @sw: USB Type-C orientation switch * @parent: Parent device
* @desc: Orientation switch description
* *
* This function registers a switch that can be used for routing the correct * This function registers a switch that can be used for routing the correct
* data pairs depending on the cable plug orientation from the USB Type-C * data pairs depending on the cable plug orientation from the USB Type-C
* connector to the USB controllers. USB Type-C plugs can be inserted * connector to the USB controllers. USB Type-C plugs can be inserted
* right-side-up or upside-down. * right-side-up or upside-down.
*/ */
int typec_switch_register(struct typec_switch *sw) struct typec_switch *
typec_switch_register(struct device *parent,
const struct typec_switch_desc *desc)
{ {
mutex_lock(&switch_lock); struct typec_switch *sw;
list_add_tail(&sw->entry, &switch_list); int ret;
mutex_unlock(&switch_lock);
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
sw = kzalloc(sizeof(*sw), GFP_KERNEL);
if (!sw)
return ERR_PTR(-ENOMEM);
return 0; sw->set = desc->set;
device_initialize(&sw->dev);
sw->dev.parent = parent;
sw->dev.fwnode = desc->fwnode;
sw->dev.class = &typec_mux_class;
sw->dev.type = &typec_switch_dev_type;
sw->dev.driver_data = desc->drvdata;
dev_set_name(&sw->dev, "%s-switch", dev_name(parent));
ret = device_add(&sw->dev);
if (ret) {
dev_err(parent, "failed to register switch (%d)\n", ret);
put_device(&sw->dev);
return ERR_PTR(ret);
}
return sw;
} }
EXPORT_SYMBOL_GPL(typec_switch_register); EXPORT_SYMBOL_GPL(typec_switch_register);
...@@ -114,28 +158,44 @@ EXPORT_SYMBOL_GPL(typec_switch_register); ...@@ -114,28 +158,44 @@ EXPORT_SYMBOL_GPL(typec_switch_register);
*/ */
void typec_switch_unregister(struct typec_switch *sw) void typec_switch_unregister(struct typec_switch *sw)
{ {
mutex_lock(&switch_lock); if (!IS_ERR_OR_NULL(sw))
list_del(&sw->entry); device_unregister(&sw->dev);
mutex_unlock(&switch_lock);
} }
EXPORT_SYMBOL_GPL(typec_switch_unregister); EXPORT_SYMBOL_GPL(typec_switch_unregister);
void typec_switch_set_drvdata(struct typec_switch *sw, void *data)
{
dev_set_drvdata(&sw->dev, data);
}
EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
void *typec_switch_get_drvdata(struct typec_switch *sw)
{
return dev_get_drvdata(&sw->dev);
}
EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
static int mux_fwnode_match(struct device *dev, const void *fwnode)
{
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux");
}
static void *typec_mux_match(struct device_connection *con, int ep, void *data) static void *typec_mux_match(struct device_connection *con, int ep, void *data)
{ {
const struct typec_altmode_desc *desc = data; const struct typec_altmode_desc *desc = data;
struct typec_mux *mux; struct device *dev;
int nval;
bool match; bool match;
int nval;
u16 *val; u16 *val;
int i; int i;
if (!con->fwnode) { if (!con->fwnode) {
list_for_each_entry(mux, &mux_list, entry) dev = class_find_device(&typec_mux_class, NULL,
if (!strcmp(con->endpoint[ep], dev_name(mux->dev))) con->endpoint[ep], name_match);
return mux;
return ERR_PTR(-EPROBE_DEFER); return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
} }
/* /*
...@@ -180,11 +240,10 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data) ...@@ -180,11 +240,10 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data)
return NULL; return NULL;
find_mux: find_mux:
list_for_each_entry(mux, &mux_list, entry) dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
if (dev_fwnode(mux->dev) == con->fwnode) mux_fwnode_match);
return mux;
return ERR_PTR(-EPROBE_DEFER); return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
} }
/** /**
...@@ -202,14 +261,10 @@ struct typec_mux *typec_mux_get(struct device *dev, ...@@ -202,14 +261,10 @@ struct typec_mux *typec_mux_get(struct device *dev,
{ {
struct typec_mux *mux; struct typec_mux *mux;
mutex_lock(&mux_lock);
mux = device_connection_find_match(dev, "mode-switch", (void *)desc, mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
typec_mux_match); typec_mux_match);
if (!IS_ERR_OR_NULL(mux)) { if (!IS_ERR_OR_NULL(mux))
WARN_ON(!try_module_get(mux->dev->driver->owner)); WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
get_device(mux->dev);
}
mutex_unlock(&mux_lock);
return mux; return mux;
} }
...@@ -224,28 +279,63 @@ EXPORT_SYMBOL_GPL(typec_mux_get); ...@@ -224,28 +279,63 @@ EXPORT_SYMBOL_GPL(typec_mux_get);
void typec_mux_put(struct typec_mux *mux) void typec_mux_put(struct typec_mux *mux)
{ {
if (!IS_ERR_OR_NULL(mux)) { if (!IS_ERR_OR_NULL(mux)) {
module_put(mux->dev->driver->owner); module_put(mux->dev.parent->driver->owner);
put_device(mux->dev); put_device(&mux->dev);
} }
} }
EXPORT_SYMBOL_GPL(typec_mux_put); EXPORT_SYMBOL_GPL(typec_mux_put);
static void typec_mux_release(struct device *dev)
{
kfree(to_typec_mux(dev));
}
static const struct device_type typec_mux_dev_type = {
.name = "mode_switch",
.release = typec_mux_release,
};
/** /**
* typec_mux_register - Register Multiplexer routing USB Type-C pins * typec_mux_register - Register Multiplexer routing USB Type-C pins
* @mux: USB Type-C Connector Multiplexer/DeMultiplexer * @parent: Parent device
* @desc: Multiplexer description
* *
* USB Type-C connectors can be used for alternate modes of operation besides * USB Type-C connectors can be used for alternate modes of operation besides
* USB when Accessory/Alternate Modes are supported. With some of those modes, * USB when Accessory/Alternate Modes are supported. With some of those modes,
* the pins on the connector need to be reconfigured. This function registers * the pins on the connector need to be reconfigured. This function registers
* multiplexer switches routing the pins on the connector. * multiplexer switches routing the pins on the connector.
*/ */
int typec_mux_register(struct typec_mux *mux) struct typec_mux *
typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
{ {
mutex_lock(&mux_lock); struct typec_mux *mux;
list_add_tail(&mux->entry, &mux_list); int ret;
mutex_unlock(&mux_lock);
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->set = desc->set;
device_initialize(&mux->dev);
mux->dev.parent = parent;
mux->dev.fwnode = desc->fwnode;
mux->dev.class = &typec_mux_class;
mux->dev.type = &typec_mux_dev_type;
mux->dev.driver_data = desc->drvdata;
dev_set_name(&mux->dev, "%s-mux", dev_name(parent));
ret = device_add(&mux->dev);
if (ret) {
dev_err(parent, "failed to register mux (%d)\n", ret);
put_device(&mux->dev);
return ERR_PTR(ret);
}
return 0; return mux;
} }
EXPORT_SYMBOL_GPL(typec_mux_register); EXPORT_SYMBOL_GPL(typec_mux_register);
...@@ -257,8 +347,24 @@ EXPORT_SYMBOL_GPL(typec_mux_register); ...@@ -257,8 +347,24 @@ EXPORT_SYMBOL_GPL(typec_mux_register);
*/ */
void typec_mux_unregister(struct typec_mux *mux) void typec_mux_unregister(struct typec_mux *mux)
{ {
mutex_lock(&mux_lock); if (!IS_ERR_OR_NULL(mux))
list_del(&mux->entry); device_unregister(&mux->dev);
mutex_unlock(&mux_lock);
} }
EXPORT_SYMBOL_GPL(typec_mux_unregister); EXPORT_SYMBOL_GPL(typec_mux_unregister);
void typec_mux_set_drvdata(struct typec_mux *mux, void *data)
{
dev_set_drvdata(&mux->dev, data);
}
EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
void *typec_mux_get_drvdata(struct typec_mux *mux)
{
return dev_get_drvdata(&mux->dev);
}
EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
struct class typec_mux_class = {
.name = "typec_mux",
.owner = THIS_MODULE,
};
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
struct pi3usb30532 { struct pi3usb30532 {
struct i2c_client *client; struct i2c_client *client;
struct mutex lock; /* protects the cached conf register */ struct mutex lock; /* protects the cached conf register */
struct typec_switch sw; struct typec_switch *sw;
struct typec_mux mux; struct typec_mux *mux;
u8 conf; u8 conf;
}; };
...@@ -48,7 +48,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) ...@@ -48,7 +48,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
static int pi3usb30532_sw_set(struct typec_switch *sw, static int pi3usb30532_sw_set(struct typec_switch *sw,
enum typec_orientation orientation) enum typec_orientation orientation)
{ {
struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw); struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
u8 new_conf; u8 new_conf;
int ret; int ret;
...@@ -75,7 +75,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw, ...@@ -75,7 +75,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw,
static int pi3usb30532_mux_set(struct typec_mux *mux, int state) static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
{ {
struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux); struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
u8 new_conf; u8 new_conf;
int ret; int ret;
...@@ -113,6 +113,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) ...@@ -113,6 +113,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
static int pi3usb30532_probe(struct i2c_client *client) static int pi3usb30532_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct typec_switch_desc sw_desc;
struct typec_mux_desc mux_desc;
struct pi3usb30532 *pi; struct pi3usb30532 *pi;
int ret; int ret;
...@@ -121,10 +123,6 @@ static int pi3usb30532_probe(struct i2c_client *client) ...@@ -121,10 +123,6 @@ static int pi3usb30532_probe(struct i2c_client *client)
return -ENOMEM; return -ENOMEM;
pi->client = client; pi->client = client;
pi->sw.dev = dev;
pi->sw.set = pi3usb30532_sw_set;
pi->mux.dev = dev;
pi->mux.set = pi3usb30532_mux_set;
mutex_init(&pi->lock); mutex_init(&pi->lock);
ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF); ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
...@@ -134,17 +132,27 @@ static int pi3usb30532_probe(struct i2c_client *client) ...@@ -134,17 +132,27 @@ static int pi3usb30532_probe(struct i2c_client *client)
} }
pi->conf = ret; pi->conf = ret;
ret = typec_switch_register(&pi->sw); sw_desc.drvdata = pi;
if (ret) { sw_desc.fwnode = dev->fwnode;
dev_err(dev, "Error registering typec switch: %d\n", ret); sw_desc.set = pi3usb30532_sw_set;
return ret;
pi->sw = typec_switch_register(dev, &sw_desc);
if (IS_ERR(pi->sw)) {
dev_err(dev, "Error registering typec switch: %ld\n",
PTR_ERR(pi->sw));
return PTR_ERR(pi->sw);
} }
ret = typec_mux_register(&pi->mux); mux_desc.drvdata = pi;
if (ret) { mux_desc.fwnode = dev->fwnode;
typec_switch_unregister(&pi->sw); mux_desc.set = pi3usb30532_mux_set;
dev_err(dev, "Error registering typec mux: %d\n", ret);
return ret; pi->mux = typec_mux_register(dev, &mux_desc);
if (IS_ERR(pi->mux)) {
typec_switch_unregister(pi->sw);
dev_err(dev, "Error registering typec mux: %ld\n",
PTR_ERR(pi->mux));
return PTR_ERR(pi->mux);
} }
i2c_set_clientdata(client, pi); i2c_set_clientdata(client, pi);
...@@ -155,8 +163,8 @@ static int pi3usb30532_remove(struct i2c_client *client) ...@@ -155,8 +163,8 @@ static int pi3usb30532_remove(struct i2c_client *client)
{ {
struct pi3usb30532 *pi = i2c_get_clientdata(client); struct pi3usb30532 *pi = i2c_get_clientdata(client);
typec_mux_unregister(&pi->mux); typec_mux_unregister(pi->mux);
typec_switch_unregister(&pi->sw); typec_switch_unregister(pi->sw);
return 0; return 0;
} }
......
...@@ -1255,6 +1255,8 @@ extern int device_for_each_child_reverse(struct device *dev, void *data, ...@@ -1255,6 +1255,8 @@ extern int device_for_each_child_reverse(struct device *dev, void *data,
int (*fn)(struct device *dev, void *data)); int (*fn)(struct device *dev, void *data));
extern struct device *device_find_child(struct device *dev, void *data, extern struct device *device_find_child(struct device *dev, void *data,
int (*match)(struct device *dev, void *data)); int (*match)(struct device *dev, void *data));
extern struct device *device_find_child_by_name(struct device *parent,
const char *name);
extern int device_rename(struct device *dev, const char *new_name); extern int device_rename(struct device *dev, const char *new_name);
extern int device_move(struct device *dev, struct device *new_parent, extern int device_move(struct device *dev, struct device *new_parent,
enum dpm_order dpm_order); enum dpm_order dpm_order);
......
...@@ -76,6 +76,10 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode, ...@@ -76,6 +76,10 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
unsigned int nargs, unsigned int index, unsigned int nargs, unsigned int index,
struct fwnode_reference_args *args); struct fwnode_reference_args *args);
struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
const char *name,
unsigned int index);
struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode);
struct fwnode_handle *fwnode_get_next_parent( struct fwnode_handle *fwnode_get_next_parent(
struct fwnode_handle *fwnode); struct fwnode_handle *fwnode);
...@@ -141,6 +145,26 @@ static inline int device_property_read_u64(struct device *dev, ...@@ -141,6 +145,26 @@ static inline int device_property_read_u64(struct device *dev,
return device_property_read_u64_array(dev, propname, val, 1); return device_property_read_u64_array(dev, propname, val, 1);
} }
static inline int device_property_count_u8(struct device *dev, const char *propname)
{
return device_property_read_u8_array(dev, propname, NULL, 0);
}
static inline int device_property_count_u16(struct device *dev, const char *propname)
{
return device_property_read_u16_array(dev, propname, NULL, 0);
}
static inline int device_property_count_u32(struct device *dev, const char *propname)
{
return device_property_read_u32_array(dev, propname, NULL, 0);
}
static inline int device_property_count_u64(struct device *dev, const char *propname)
{
return device_property_read_u64_array(dev, propname, NULL, 0);
}
static inline bool fwnode_property_read_bool(const struct fwnode_handle *fwnode, static inline bool fwnode_property_read_bool(const struct fwnode_handle *fwnode,
const char *propname) const char *propname)
{ {
...@@ -171,6 +195,30 @@ static inline int fwnode_property_read_u64(const struct fwnode_handle *fwnode, ...@@ -171,6 +195,30 @@ static inline int fwnode_property_read_u64(const struct fwnode_handle *fwnode,
return fwnode_property_read_u64_array(fwnode, propname, val, 1); return fwnode_property_read_u64_array(fwnode, propname, val, 1);
} }
static inline int fwnode_property_count_u8(const struct fwnode_handle *fwnode,
const char *propname)
{
return fwnode_property_read_u8_array(fwnode, propname, NULL, 0);
}
static inline int fwnode_property_count_u16(const struct fwnode_handle *fwnode,
const char *propname)
{
return fwnode_property_read_u16_array(fwnode, propname, NULL, 0);
}
static inline int fwnode_property_count_u32(const struct fwnode_handle *fwnode,
const char *propname)
{
return fwnode_property_read_u32_array(fwnode, propname, NULL, 0);
}
static inline int fwnode_property_count_u64(const struct fwnode_handle *fwnode,
const char *propname)
{
return fwnode_property_read_u64_array(fwnode, propname, NULL, 0);
}
/** /**
* struct property_entry - "Built-in" device property representation. * struct property_entry - "Built-in" device property representation.
* @name: Name of the property. * @name: Name of the property.
...@@ -329,7 +377,54 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, ...@@ -329,7 +377,54 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Software fwnode support - when HW description is incomplete or missing */ /* Software fwnode support - when HW description is incomplete or missing */
struct software_node;
/**
* struct software_node_ref_args - Reference with additional arguments
* @node: Reference to a software node
* @nargs: Number of elements in @args array
* @args: Integer arguments
*/
struct software_node_ref_args {
const struct software_node *node;
unsigned int nargs;
u64 args[NR_FWNODE_REFERENCE_ARGS];
};
/**
* struct software_node_reference - Named software node reference property
* @name: Name of the property
* @nrefs: Number of elements in @refs array
* @refs: Array of references with optional arguments
*/
struct software_node_reference {
const char *name;
unsigned int nrefs;
const struct software_node_ref_args *refs;
};
/**
* struct software_node - Software node description
* @name: Name of the software node
* @parent: Parent of the software node
* @properties: Array of device properties
* @references: Array of software node reference properties
*/
struct software_node {
const char *name;
const struct software_node *parent;
const struct property_entry *properties;
const struct software_node_reference *references;
};
bool is_software_node(const struct fwnode_handle *fwnode); bool is_software_node(const struct fwnode_handle *fwnode);
const struct software_node *to_software_node(struct fwnode_handle *fwnode);
struct fwnode_handle *software_node_fwnode(const struct software_node *node);
int software_node_register_nodes(const struct software_node *nodes);
void software_node_unregister_nodes(const struct software_node *nodes);
int software_node_register(const struct software_node *node);
int software_node_notify(struct device *dev, unsigned long action); int software_node_notify(struct device *dev, unsigned long action);
......
...@@ -3,54 +3,48 @@ ...@@ -3,54 +3,48 @@
#ifndef __USB_TYPEC_MUX #ifndef __USB_TYPEC_MUX
#define __USB_TYPEC_MUX #define __USB_TYPEC_MUX
#include <linux/list.h>
#include <linux/usb/typec.h> #include <linux/usb/typec.h>
struct device; struct device;
struct typec_mux;
struct typec_switch;
struct fwnode_handle;
/** typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw,
* struct typec_switch - USB Type-C cable orientation switch enum typec_orientation orientation);
* @dev: Switch device
* @entry: List entry
* @set: Callback to the driver for setting the orientation
*
* USB Type-C pin flipper switch routing the correct data pairs from the
* connector to the USB controller depending on the orientation of the cable
* plug.
*/
struct typec_switch {
struct device *dev;
struct list_head entry;
int (*set)(struct typec_switch *sw, enum typec_orientation orientation);
};
/** struct typec_switch_desc {
* struct typec_switch - USB Type-C connector pin mux struct fwnode_handle *fwnode;
* @dev: Mux device typec_switch_set_fn_t set;
* @entry: List entry void *drvdata;
* @set: Callback to the driver for setting the state of the mux
*
* Pin Multiplexer/DeMultiplexer switch routing the USB Type-C connector pins to
* different components depending on the requested mode of operation. Used with
* Accessory/Alternate modes.
*/
struct typec_mux {
struct device *dev;
struct list_head entry;
int (*set)(struct typec_mux *mux, int state);
}; };
struct typec_switch *typec_switch_get(struct device *dev); struct typec_switch *typec_switch_get(struct device *dev);
void typec_switch_put(struct typec_switch *sw); void typec_switch_put(struct typec_switch *sw);
int typec_switch_register(struct typec_switch *sw); struct typec_switch *
typec_switch_register(struct device *parent,
const struct typec_switch_desc *desc);
void typec_switch_unregister(struct typec_switch *sw); void typec_switch_unregister(struct typec_switch *sw);
void typec_switch_set_drvdata(struct typec_switch *sw, void *data);
void *typec_switch_get_drvdata(struct typec_switch *sw);
typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, int state);
struct typec_mux_desc {
struct fwnode_handle *fwnode;
typec_mux_set_fn_t set;
void *drvdata;
};
struct typec_mux * struct typec_mux *
typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc); typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc);
void typec_mux_put(struct typec_mux *mux); void typec_mux_put(struct typec_mux *mux);
int typec_mux_register(struct typec_mux *mux); struct typec_mux *
typec_mux_register(struct device *parent, const struct typec_mux_desc *desc);
void typec_mux_unregister(struct typec_mux *mux); void typec_mux_unregister(struct typec_mux *mux);
void typec_mux_set_drvdata(struct typec_mux *mux, void *data);
void *typec_mux_get_drvdata(struct typec_mux *mux);
#endif /* __USB_TYPEC_MUX */ #endif /* __USB_TYPEC_MUX */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment