diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 10:28:47 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 10:28:47 -0700 |
| commit | 0415052db4f92b7e272fc15802ad8b8be672deea (patch) | |
| tree | 6273d2ecfa56fa21a94e1c8f632e5fefa5f3baa3 /drivers | |
| parent | 4b4704520d97b74e045154fc3b844b73ae4e7ebd (diff) | |
| parent | 33ee09cd59ce154b64f9df942dfa5456db90d5f9 (diff) | |
| download | linux-0415052db4f92b7e272fc15802ad8b8be672deea.tar.gz linux-0415052db4f92b7e272fc15802ad8b8be672deea.tar.bz2 linux-0415052db4f92b7e272fc15802ad8b8be672deea.zip | |
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
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/acpi/property.c | 26 | ||||
| -rw-r--r-- | drivers/base/core.c | 28 | ||||
| -rw-r--r-- | drivers/base/devcon.c | 26 | ||||
| -rw-r--r-- | drivers/base/property.c | 24 | ||||
| -rw-r--r-- | drivers/base/swnode.c | 324 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_cht_int33fe.c | 291 | ||||
| -rw-r--r-- | drivers/usb/roles/class.c | 2 | ||||
| -rw-r--r-- | drivers/usb/typec/bus.h | 15 | ||||
| -rw-r--r-- | drivers/usb/typec/class.c | 17 | ||||
| -rw-r--r-- | drivers/usb/typec/mux.c | 238 | ||||
| -rw-r--r-- | drivers/usb/typec/mux/pi3usb30532.c | 46 |
11 files changed, 822 insertions, 215 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index da3ced297f19..ea3d700da3ca 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -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) - if (acpi_data_node_match(child, childname)) + 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; } diff --git a/drivers/base/core.c b/drivers/base/core.c index fd7511e04e62..b4c64528f13c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -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); diff --git a/drivers/base/devcon.c b/drivers/base/devcon.c index 04db9ae235e4..f7035fc12b92 100644 --- a/drivers/base/devcon.c +++ b/drivers/base/devcon.c @@ -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); diff --git a/drivers/base/property.c b/drivers/base/property.c index 348b37e64944..81bd01ed4042 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -485,6 +485,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. * diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 7fc5a18e02ad..e7b3aa3bd55a 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -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); - ret = kobject_init_and_add(&swnode->kobj, &software_node_type, - p ? &p->kobj : NULL, "node%d", swnode->id); + if (node->name) + 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) { 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) { + 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) { - kobject_put(&swnode->kobj); + 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: diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 6fa3cced6f8e..4fbdff48a4b5 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -21,18 +21,55 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/usb/pd.h> #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 i2c_client *max17047; struct i2c_client *fusb302; 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) 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 struct property_entry max17047_props[] = { @@ -80,18 +109,196 @@ static const struct property_entry max17047_props[] = { static const struct property_entry fusb302_props[] = { 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) { struct device *dev = &pdev->dev; struct i2c_board_info board_info; struct cht_int33fe_data *data; - struct i2c_client *max17047; + struct fwnode_handle *fwnode; struct regulator *regulator; unsigned long long ptyp; acpi_status status; @@ -151,43 +358,25 @@ static int cht_int33fe_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - /* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */ - max17047 = cht_int33fe_find_max17047(); - if (max17047) { - /* Pre-existing i2c-client for the max17047, add device-props */ - ret = device_add_properties(&max17047->dev, max17047_props); - if (ret) |
