Software fwnode references
Subsystems (7)
Heikki Krogerus
maintainercover Software fwnode references
Hi,
This is the second version of this series. In this version I'm introducing a new helper device_find_child_by_name() as proposed by Andy. Andy requested also another helper that could be used for chaining the fwnodes, but I decided not to add that now. I would like to still think about how we should handle exceptions like if there already is a secondary node assigned for a node.
The v1 commit message:
This series adds support for software fwnode reference handling. In practice it means making fwnode_property_get_reference_args() function usable in the drivers also with software nodes. I send the series originally as RFC [1].
As the first user for the software node references, I'm converting intel_cht_int33fe.c to use them as part of the series.
[1] https://lkml.org/lkml/2019/3/15/457
thanks,
Heikki Krogerus (12):
software node: Allow node creation without properties software node: Simplify software_node_release() function software node: Add support for references
software node: Implement .get_reference_args fwnode operation driver core: Add helper device_find_child_by_name()
ACPI / property: Don't limit named child node matching to data nodes device connection: Find connections also by checking the references
usb: typec: Registering real device entries for the muxes platform/x86: intel_cht_int33fe: Provide software nodes for the
devices
platform/x86: intel_cht_int33fe: Provide fwnode for the USB connector platform/x86: intel_cht_int33fe: Link with external dependencies using
fwnodes
platform/x86: intel_cht_int33fe: Replacing the old connections with
references
drivers/acpi/property.c | 26 +- drivers/base/core.c | 31 +++ drivers/base/devcon.c | 28 ++ drivers/base/swnode.c | 180 +++++++++++- drivers/platform/x86/intel_cht_int33fe.c | 339 +++++++++++++++++++---- drivers/usb/typec/bus.h | 15 + drivers/usb/typec/class.c | 17 +- drivers/usb/typec/mux.c | 221 ++++++++++----- drivers/usb/typec/mux/pi3usb30532.c | 46 +-- include/linux/device.h | 2 + include/linux/property.h | 8 + include/linux/usb/typec_mux.h | 62 ++--- 12 files changed, 791 insertions(+), 184 deletions(-)
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 1/12 software node: Allow node creation without properties
Software nodes are not forced to have device properties. Adding check to property_entries_dup() to make it possible to create software nodes that don't have any properties.
--- drivers/base/swnode.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/base/swnode.c +3 -0
@@ -383,6 +383,9 @@ property_entries_dup(const struct property_entry *properties)int i, n =0 ;int ret; +if (!properties) +return NULL ; +while (properties[n].name) n++;
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 2/12 software node: Simplify software_node_release() function
In reply to Heikki KrogerusIt's possible to release the node ID immediately when fwnode_remove_software_node() is called, no need to wait for software_node_release() with that. This change has no functional effect.
--- drivers/base/swnode.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
drivers/base/swnode.c +10 -10
@@ -557,13 +557,6 @@ static void software_node_release(struct kobject *kobj) {struct software_node *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); - } -ida_destroy (&swnode->child_ids);property_entries_free (swnode->properties);kfree (swnode);
@@ -610,9 +603,6 @@ fwnode_create_software_node(const struct property_entry *properties,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 (ret) {
@@ -626,6 +616,9 @@ fwnode_create_software_node(const struct property_entry *properties,return ERR_PTR (ret); } +if (p) +list_add_tail (&swnode->entry, &p->children); +kobject_uevent (&swnode->kobj,KOBJ_ADD );return &swnode->fwnode; }
@@ -638,6 +631,13 @@ void fwnode_remove_software_node(struct fwnode_handle *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);
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 3/12 software node: Add support for references
In reply to Heikki KrogerusIntroducing functions that can be used for adding and removing references to other software nodes. The goal is to support fwnode_property_get_reference_args() also with software nodes, however, get_reference_args fwnode operation callback is not yet implemented in this commit for the software nodes. This commit will only add support for reference addition/removal.
The next example shows how to add references to GPIOs using the format described in Documentation/acpi/gpio-properties.txt:
/* Array with two GPIOs */
static struct fwnode_reference_args gpioargs[] __initdata = {
{
.nargs = 3,
.args[0]= 19, /* index */
.args[1]= 0, /* pin */
.args[2]= 0, /* active_low */
},
{
.nargs = 3,
.args[0]= 20, /* index */
.args[1]= 0, /* pin */
.args[2]= 0, /* active_low */
},
{ }
};static int myinit(void)
{
struct software_node_reference *ref;
struct fwnode_handle *gpio_node;
struct fwnode_handle *my_node;
/* Creating the nodes */
gpio_node = fwnode_create_software_node(gpio_props, NULL);
...
my_node = fwnode_create_software_node(my_props, NULL);
...
/* gpio_node is associated with a GPIO/Pin controller in this example */
...
/* Assigning the actual node references */
gpioargs[0].fwnode = gpio_node;
gpioargs[1].fwnode = gpio_node;
/* my_node will now have a named ("gpios") reference to the two GPIOs */
ref = fwnode_create_software_node_reference(my_node, "gpios", gpioargs);
...
return 0;
}--- drivers/base/swnode.c | 101 +++++++++++++++++++++++++++++++++++++++ include/linux/property.h | 8 ++++ 2 files changed, 109 insertions(+)
drivers/base/swnode.c +101 -0
@@ -11,10 +11,19 @@#include <linux/property.h> #include <linux/slab.h> +struct software_node_reference { +struct list_head list; +const char *name; +int nrefs; +struct fwnode_reference_args *args; +}; +struct software_node {int id;struct kobject kobj;struct fwnode_handle fwnode; +struct list_head references; +struct mutex lock;/* node lock */ /* hierarchy */ struct ida child_ids;
@@ -598,9 +607,11 @@ fwnode_create_software_node(const struct property_entry *properties, swnode->kobj.kset = swnode_kset; swnode->fwnode.ops = &software_node_ops; +mutex_init (&swnode->lock);ida_init (&swnode->child_ids);INIT_LIST_HEAD (&swnode->entry);INIT_LIST_HEAD (&swnode->children); +INIT_LIST_HEAD (&swnode->references); swnode->parent = p; ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
@@ -631,6 +642,11 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode)if (!swnode)return ; +mutex_lock (&swnode->lock); +WARN (!list_empty (&swnode->references), +"\"%s\" has still references" ,kobject_name (&swnode->kobj)); +mutex_unlock (&swnode->lock); +if (swnode->parent) {ida_simple_remove (&swnode->parent->child_ids, swnode->id);list_del (&swnode->entry);
@@ -642,6 +658,91 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode) }EXPORT_SYMBOL_GPL (fwnode_remove_software_node); +/** +* fwnode_create_software_node_reference - Create named reference description +* @fwnode: The software node to have the references +* @name: Name given to reference description +* @args: Zero terminated array of software node references with arguments +* +* Associates software nodes listed in @args with @fwnode. The association is +* named @name. The reference count is incremented for the nodes in @args. +* +* Returns pointer to software node reference description on success, or ERR_PTR +* on failure. +*/ +struct software_node_reference * +fwnode_create_software_node_reference (const struct fwnode_handle *fwnode, +const char *name, +const struct fwnode_reference_args *args) +{ +struct software_node *swnode =to_software_node (fwnode); +struct software_node_reference *ref; +int n; + +if (!swnode) +return ERR_PTR (-EINVAL ); + +for (n =0 ; args[n].fwnode; n++) +if (!is_software_node (args[n].fwnode)) +return ERR_PTR (-EINVAL ); + + ref =kzalloc (sizeof (*ref),GFP_KERNEL ); +if (!ref) +return ERR_PTR (-ENOMEM ); + + ref->nrefs = n; + + ref->name =kstrdup (name,GFP_KERNEL ); +if (!ref->name) { +kfree (ref); +return ERR_PTR (-ENOMEM ); + } + + ref->args =kcalloc (ref->nrefs,sizeof (*ref->args),GFP_KERNEL ); +if (!ref->args) { +kfree (ref->name); +kfree (ref); +return ERR_PTR (-ENOMEM ); + } + +for (n =0 ; n < ref->nrefs; n++) { + ref->args[n] = args[n]; +software_node_get (ref->args[n].fwnode); + } + +mutex_lock (&swnode->lock); +list_add_tail (&ref->list, &swnode->references); +mutex_unlock (&swnode->lock); + +return ref; +} +EXPORT_SYMBOL_GPL (fwnode_create_software_node_reference); + +/** +* fwnode_remove_software_node_reference - Remove named reference description +* @ref: Software node reference description +* +* Remove named reference @ref. Decrements the software node reference count of +* each node in @ref, and removes the association that was created in +* fwnode_create_software_node_reference(). +*/ +void fwnode_remove_software_node_reference (struct software_node_reference *ref) +{ +int n; + +if (IS_ERR_OR_NULL (ref)) +return ; + +for (n =0 ; n < ref->nrefs; n++) +kobject_put (&to_software_node (ref->args[n].fwnode)->kobj); + +list_del (&ref->list); +kfree (ref->args); +kfree (ref->name); +kfree (ref); +} +EXPORT_SYMBOL_GPL (fwnode_remove_software_node_reference); +int software_node_notify (struct device *dev,unsigned long action) {struct fwnode_handle *fwnode =dev_fwnode (dev);
include/linux/property.h +8 -0
@@ -314,6 +314,8 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,/* -------------------------------------------------------------------------- */ /* Software fwnode support - when HW description is incomplete or missing */ +struct sofware_node_reference ; +bool is_software_node (const struct fwnode_handle *fwnode);int software_node_notify (struct device *dev,unsigned long action);
@@ -323,4 +325,10 @@ fwnode_create_software_node(const struct property_entry *properties, const struct fwnode_handle *parent);void fwnode_remove_software_node (struct fwnode_handle *fwnode); +struct software_node_reference * +fwnode_create_software_node_reference (const struct fwnode_handle *fwnode, +const char *name, +const struct fwnode_reference_args *args); +void fwnode_remove_software_node_reference (struct software_node_reference *ref); +#endif /* _LINUX_PROPERTY_H_ */
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 4/12 software node: Implement .get_reference_args fwnode operation
In reply to Heikki KrogerusThis makes it possible to support drivers that use fwnode_property_get_reference_args() function.
--- drivers/base/swnode.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
drivers/base/swnode.c +56 -0
@@ -534,6 +534,61 @@ 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 software_node *swnode =to_software_node (fwnode); +struct software_node_reference *ref; +const struct property_entry *prop; +int ret = -ENOENT ; +int i; + +mutex_lock (&swnode->lock); + +if (!swnode ||list_empty (&swnode->references)) + goto err_unlock; + +if (nargs_prop) { + prop =property_entry_get (swnode->properties, nargs_prop); +if (!prop) { + ret = -EINVAL ; + goto err_unlock; + } + + nargs = prop->value.u32_data; + } + +if (nargs >NR_FWNODE_REFERENCE_ARGS ) { + ret = -EINVAL ; + goto err_unlock; + } + +list_for_each_entry (ref, &swnode->references, list) { +if (strcmp (ref->name, propname)) +continue ; + +if (index > (ref->nrefs -1 )) +break ; + + args->nargs = nargs; + args->fwnode =software_node_get (ref->args[index].fwnode); + +for (i =0 ; i < nargs; i++) + args->args[i] = ref->args[index].args[i]; + + ret =0 ; +break ; + } + +err_unlock: +mutex_unlock (&swnode->lock); + +return ret; +} +static const struct fwnode_operations software_node_ops = { .get = software_node_get, .put = software_node_put,
@@ -543,6 +598,7 @@ 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, };/* -------------------------------------------------------------------------- */
Signature · 1 line
2.20.1
Rafael J. Wysocki
maintainerRe: [PATCH v2 04/12] software node: Implement .get_reference_args fwnode operation
Quoted 51 lines · Heikki Krogerus
This makes it possible to support drivers that use
fwnode_property_get_reference_args()function.Signed-off-by Heikki Krogerus <[email protected]>--- drivers/base/swnode.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)drivers/base/swnode.c +34
-0@@ -534,6 +534,61 @@ 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 software_node *swnode =to_software_node (fwnode); +struct software_node_reference *ref; +const struct property_entry *prop; +int ret = -ENOENT ; +int i; + +mutex_lock (&swnode->lock); + +if (!swnode ||list_empty (&swnode->references)) + goto err_unlock; + +if (nargs_prop) { + prop =property_entry_get (swnode->properties, nargs_prop); +if (!prop) { + ret = -EINVAL ; + goto err_unlock; + } + + nargs = prop->value.u32_data; + } + +if (nargs >NR_FWNODE_REFERENCE_ARGS ) { + ret = -EINVAL ; + goto err_unlock; + } + +list_for_each_entry (ref, &swnode->references, list) { +if (strcmp (ref->name, propname))
drivers/base/swnode.c software_node_get_named_child_node(const struct fwnode_handle *fwnode, · line 580
Quoted 10 lines
drivers/base/swnode.c +10
-0+continue ; + +if (index > (ref->nrefs -1 )) +break ; + + args->nargs = nargs; + args->fwnode =software_node_get (ref->args[index].fwnode); + +for (i =0 ; i < nargs; i++) + args->args[i] = ref->args[index].args[i];
If nargs is provided by the caller, can't it be greater than the number of args[index].args[] entries?
Quoted 22 lines
drivers/base/swnode.c +12
-0+ + ret =0 ; +break ; + } + +err_unlock: +mutex_unlock (&swnode->lock); + +return ret; +} +static const struct fwnode_operations software_node_ops = { .get = software_node_get, .put = software_node_put,@@ -543,6 +598,7 @@ 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, };/* -------------------------------------------------------------------------- */
Heikki Krogerus
maintainerRe: [PATCH v2 04/12] software node: Implement .get_reference_args fwnode operation
Quoted 39 lines · Rafael J. Wysocki
Quoted 37 lines · Heikki Krogerus
This makes it possible to support drivers that use
fwnode_property_get_reference_args()function.Signed-off-by Heikki Krogerus <[email protected]>--- drivers/base/swnode.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)drivers/base/swnode.c +20
-0@@ -534,6 +534,61 @@ 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 software_node *swnode =to_software_node (fwnode); +struct software_node_reference *ref; +const struct property_entry *prop; +int ret = -ENOENT ; +int i; + +mutex_lock (&swnode->lock); + +if (!swnode ||list_empty (&swnode->references)) + goto err_unlock; + +if (nargs_prop) { + prop =property_entry_get (swnode->properties, nargs_prop); +if (!prop) {
Quoted 8 lines
drivers/base/swnode.c software_node_get_named_child_node(const struct fwnode_handle *fwnode, · line 564
Quoted 8 lines
drivers/base/swnode.c +8
-0+ ret = -EINVAL ; + goto err_unlock; + } + + nargs = prop->value.u32_data; + } + +if (nargs >NR_FWNODE_REFERENCE_ARGS ) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note
Quoted 11 lines
Quoted 11 lines
drivers/base/swnode.c +11
-0+ ret = -EINVAL ; + goto err_unlock; + } + +list_for_each_entry (ref, &swnode->references, list) { +if (strcmp (ref->name, propname)) +continue ; + +if (index > (ref->nrefs -1 )) +break ; +
Quoted 8 lines
drivers/base/swnode.c software_node_get_named_child_node(const struct fwnode_handle *fwnode, · line 580
Quoted 5 lines
drivers/base/swnode.c +5
-0+ args->nargs = nargs; + args->fwnode =software_node_get (ref->args[index].fwnode); + +for (i =0 ; i < nargs; i++) + args->args[i] = ref->args[index].args[i];If nargs is provided by the caller, can't it be greater than the number of args[index].args[] entries?
No. They have NR_FWNODE_REREFECE_ARGS elements, and we check above that nargs is not greater then that.
thanks,
Signature · 1 line
heikki
Heikki Krogerus
maintainerPATCH v2 5/12 driver core: Add helper device_find_child_by_name()
In reply to Heikki KrogerusIt looks like the child device is often matched with a name. This introduces a helper that does it automatically.
--- drivers/base/core.c | 31 +++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 33 insertions(+)
drivers/base/core.c +31 -0
@@ -2469,6 +2469,37 @@ 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. +* +* The callback should return NULL if the child is not found and a reference to +* the child device if it is found. +* +* 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 );
include/linux/device.h +2 -0
@@ -1250,6 +1250,8 @@ extern int device_for_each_child_reverse(struct device *dev, void *data, int (*fn)(struct device *dev, void *data));extern struct device *device_find_child (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_move (struct device *dev,struct device *new_parent,enum dpm_order dpm_order);
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 6/12 ACPI / property: Don't limit named child node matching to data nodes
In reply to Heikki KrogerusThere is no reason why we should limit the use offwnode_get_named_child_node() to data nodes only.
--- drivers/acpi/property.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
drivers/acpi/property.c +20 -6
@@ -602,15 +602,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_NAME_SIZE ))return child; + }return NULL ; }
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerRe: [PATCH v2 06/12] ACPI / property: Don't limit named child node matching to data nodes
Hi Rafael,
Quoted 33 lines · Heikki Krogerus
There is no reason why we should limit the use of
fwnode_get_named_child_node()to data nodes only.Signed-off-by Heikki Krogerus <[email protected]>--- drivers/acpi/property.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)drivers/acpi/property.c +9
-6@@ -602,15 +602,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))
drivers/acpi/property.c static struct fwnode_handle * · line 625
Quoted 10 lines
drivers/acpi/property.c +10
-0+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_NAME_SIZE ))
I rebased these on of todays linux-next, and noticed that Bob renamed ACPI_NAME_SIZE to ACPI_NAMESEG_SIZE in commit 3278675567df ("ACPICA:
Rename nameseg length macro/define for clarity").
I'll fix that and resend these tomorrow.
thanks,
Signature · 1 line
heikki
Heikki Krogerus
maintainerPATCH v2 7/12 device connection: Find connections also by checking the references
In reply to Heikki KrogerusWe can also use this API to find named references that the device nodes have by using fwnode_property_get_reference_args() function.
--- drivers/base/devcon.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
drivers/base/devcon.c +28 -0
@@ -38,6 +38,30 @@ 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 = { }; +struct fwnode_reference_args args; +void *ret; +int i; + +for (i =0 ; ; i++) { +if (fwnode_property_get_reference_args (fwnode, con_id,NULL ,0 , + i, &args)) +break ; + + con.fwnode = args.fwnode; + ret =match (&con,-1 , data); +fwnode_handle_put (args.fwnode); +if (ret) +return ret; + } + +return NULL ; +} +/** * device_connection_find_match - Find physical connection to a device * @dev: Device with the connection
@@ -65,6 +89,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);
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 8/12 usb: typec: Registering real device entries for the muxes
In reply to Heikki KrogerusRegistering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit 3e3b81965cbf ("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.
--- drivers/platform/x86/intel_cht_int33fe.c | 4 +- drivers/usb/typec/bus.h | 15 ++ drivers/usb/typec/class.c | 17 +- drivers/usb/typec/mux.c | 221 ++++++++++++++++------- drivers/usb/typec/mux/pi3usb30532.c | 46 +++-- include/linux/usb/typec_mux.h | 62 +++---- 6 files changed, 242 insertions(+), 123 deletions(-)
drivers/platform/x86/intel_cht_int33fe.c +2 -2
@@ -173,10 +173,10 @@ static int cht_int33fe_probe(struct platform_device *pdev) } data->connections[0 ].endpoint[0 ] ="port0" ; - data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532" ; + data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ; data->connections[0 ].id ="orientation-switch" ; data->connections[1 ].endpoint[0 ] ="port0" ; - data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532" ; + data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532-mux" ; data->connections[1 ].id ="mode-switch" ; data->connections[2 ].endpoint[0 ] ="i2c-fusb302" ; data->connections[2 ].endpoint[1 ] ="intel_xhci_usb_sw-role-switch" ;
drivers/usb/typec/bus.h +15 -0
@@ -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_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__ */
drivers/usb/typec/class.c +15 -2
@@ -1646,13 +1646,25 @@ static int __init typec_init(void)if (ret)return ret; + ret =class_register (&typec_mux_class); +if (ret) + goto err_unregister_bus; + typec_class =class_create (THIS_MODULE ,"typec" );if (IS_ERR (typec_class)) { -bus_unregister (&typec_bus); -return PTR_ERR (typec_class); + ret =PTR_ERR (typec_class); + goto err_unregister_mux_class; }return 0 ; + +err_unregister_mux_class: +class_unregister (&typec_mux_class); + +err_unregister_bus: +bus_unregister (&typec_bus); + +return ret; }subsys_initcall (typec_init);
@@ -1661,6 +1673,7 @@ static void __exit typec_exit(void)class_destroy (typec_class);ida_destroy (&typec_index_ida);bus_unregister (&typec_bus); +class_unregister (&typec_mux_class); }module_exit (typec_exit);
drivers/usb/typec/mux.c +155 -66
321 lines not on this page · raw message
drivers/usb/typec/mux/pi3usb30532.c +27 -19
90 lines not on this page · raw message
include/linux/usb/typec_mux.h +28 -34
82 lines not on this page · raw message
Signature · 1 line
2.20.1
Re: [PATCH v2 08/12] usb: typec: Registering real device entries for the muxes
Quoted 29 lines · Heikki Krogerus
Registering real device entries (struct device) for the mode muxes as well as for the orientation switches.
The Type-C mux code was deliberately attempting to avoid creation of separate device entries for the orientation switch and the mode switch (alternate modes) because they are not physical devices. They are functions of a single physical multiplexer/demultiplexer switch device.
Unfortunately because of the dependency we still have on the underlying mux device driver, we had to put in hacks like the one in the commit
3e3b81965cbf("usb: typec: mux: Take care of driver module reference counting") to make sure the driver does not disappear from underneath us. Even with those hacks we were still left with a potential NUll pointer dereference scenario, so just creating the device entries, and letting the core take care of the dependencies. No more hacks needed.drivers/platform/x86/intel_cht_int33fe.c | 4 +-drivers/platform/x86/intel_cht_int33fe.c +0
-0@@ -173,10 +173,10 @@ static int cht_int33fe_probe(struct platform_device *pdev) }
drivers/platform/x86/intel_cht_int33fe.c static int cht_int33fe_probe(struct platform_device *pdev) · line 182
Quoted 10 lines
drivers/platform/x86/intel_cht_int33fe.c +2
-2data->connections[0 ].endpoint[0 ] ="port0" ; - data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532" ; + data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ; data->connections[0 ].id ="orientation-switch" ; data->connections[1 ].endpoint[0 ] ="port0" ; - data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532" ; + data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532-mux" ; data->connections[1 ].id ="mode-switch" ; data->connections[2 ].endpoint[0 ] ="i2c-fusb302" ; data->connections[2 ].endpoint[1 ] ="intel_xhci_usb_sw-role-switch" ;
Signature · 2 lines
With Best Regards, Andy Shevchenko
Heikki Krogerus
maintainerPATCH v2 9/12 platform/x86: intel_cht_int33fe: Provide software nodes for the devices
In reply to Heikki KrogerusSoftware nodes provide two features that we will need later.
1) Software nodes can have references to other software nodes.
2) Software nodes can exist before a device entry is created.
--- drivers/platform/x86/intel_cht_int33fe.c | 124 +++++++++++++++++------ 1 file changed, 93 insertions(+), 31 deletions(-)
drivers/platform/x86/intel_cht_int33fe.c +93 -31
@@ -21,18 +21,28 @@#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> #define EXPECTED_PTYPE 4 +enum { +INT33FE_NODE_FUSB302 , +INT33FE_NODE_MAX17047 , +INT33FE_NODE_PI3USB30532 , +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 *node[INT33FE_NODE_MAX ]; };/*
@@ -63,14 +73,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[] = {
@@ -78,6 +80,36 @@ static const struct property_entry max17047_props[] = { { } }; +static int +cht_int33fe_find_max17047 (struct device *dev,struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_MAX17047 ]; +struct i2c_client *max17047 =NULL ; +struct i2c_board_info board_info; +int ret; + +i2c_for_each_dev (&max17047, cht_int33fe_check_for_max17047); +if (max17047) { +/* Pre-existing i2c-client for the max17047, add device-props */ + 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); +if (IS_ERR (data->max17047)) +return PTR_ERR (data->max17047); + +return 0 ; +} +static const struct property_entry fusb302_props[] = {PROPERTY_ENTRY_STRING ("linux,extcon-name" ,"cht_wcove_pwrsrc" ),PROPERTY_ENTRY_U32 ("fcs,max-sink-microvolt" ,12000000 ),
@@ -86,12 +118,50 @@ static const struct property_entry fusb302_props[] = { { } }; +static const struct property_entry *props[] = { + [INT33FE_NODE_FUSB302 ] = fusb302_props, + [INT33FE_NODE_MAX17047 ] = max17047_props, + [INT33FE_NODE_PI3USB30532 ] =NULL , +}; + +static void cht_int33fe_remove_nodes (struct cht_int33fe_data *data) +{ +int i; + +for (i =0 ; i <INT33FE_NODE_MAX ; i++) { +fwnode_remove_software_node (data->node[i]); + data->node[i] =NULL ; + } +} + +static int cht_int33fe_add_nodes (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode; +int ret; +int i; + +for (i =0 ; i <ARRAY_SIZE (props); i++) { + fwnode =fwnode_create_software_node (props[i],NULL ); +if (IS_ERR (fwnode)) { + ret =PTR_ERR (fwnode); + goto err_remove_nodes; + } + data->node[i] = fwnode; + } + +return 0 ; + +err_remove_nodes: +cht_int33fe_remove_nodes (data); + +return ret; +} +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 regulator *regulator;unsigned long long ptyp;acpi_status status;
@@ -151,26 +221,14 @@ 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) -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); - } + ret =cht_int33fe_add_nodes (data); +if (ret) +return ret; + +/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ + ret =cht_int33fe_find_max17047 (dev, data); +if (ret) + goto out_remove_nodes; data->connections[0 ].endpoint[0 ] ="port0" ; data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ;
@@ -187,7 +245,7 @@ static int cht_int33fe_probe(struct platform_device *pdev)memset (&board_info,0 ,sizeof (board_info));strlcpy (board_info.type,"typec_fusb302" ,I2C_NAME_SIZE ); board_info.dev_name ="fusb302" ; - board_info.properties = fusb302_props; + board_info.fwnode = data->node[INT33FE_NODE_FUSB302 ]; board_info.irq = fusb302_irq; data->fusb302 =i2c_acpi_new_device (dev,2 , &board_info);
@@ -198,6 +256,7 @@ static int cht_int33fe_probe(struct platform_device *pdev)memset (&board_info,0 ,sizeof (board_info)); board_info.dev_name ="pi3usb30532" ; + board_info.fwnode = data->node[INT33FE_NODE_PI3USB30532 ];strlcpy (board_info.type,"pi3usb30532" ,I2C_NAME_SIZE ); data->pi3usb30532 =i2c_acpi_new_device (dev,3 , &board_info);
@@ -214,9 +273,11 @@ static int cht_int33fe_probe(struct platform_device *pdev)i2c_unregister_device (data->fusb302); out_unregister_max17047: +device_connections_remove (data->connections);i2c_unregister_device (data->max17047); -device_connections_remove (data->connections); +out_remove_nodes: +cht_int33fe_remove_nodes (data);return ret; }
@@ -230,6 +291,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)i2c_unregister_device (data->max17047);device_connections_remove (data->connections); +cht_int33fe_remove_nodes (data);return 0 ; }
Signature · 1 line
2.20.1
Re: [PATCH v2 09/12] platform/x86: intel_cht_int33fe: Provide software nodes for the devices
Quoted 5 lines · Heikki Krogerus
Software nodes provide two features that we will need later.
1) Software nodes can have references to other software nodes.
2) Software nodes can exist before a device entry is created.
It seems does two things in one patch, and the first one is not mentioned in the commit message.
I would suggest to create a preparation patch which extracts finding and registering max17047 device followed by what you do here.
Quoted 218 lines
Signed-off-by Heikki Krogerus <[email protected]>--- drivers/platform/x86/intel_cht_int33fe.c | 124 +++++++++++++++++------ 1 file changed, 93 insertions(+), 31 deletions(-)drivers/platform/x86/intel_cht_int33fe.c +93
-32@@ -21,18 +21,28 @@#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> #define EXPECTED_PTYPE 4 +enum { +INT33FE_NODE_FUSB302 , +INT33FE_NODE_MAX17047 , +INT33FE_NODE_PI3USB30532 , +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 *node[INT33FE_NODE_MAX ]; };/* @@ -63,14 +73,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[] = {@@ -78,6 +80,36 @@ static const struct property_entry max17047_props[] = { { } }; +static int +cht_int33fe_find_max17047 (struct device *dev,struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_MAX17047 ]; +struct i2c_client *max17047 =NULL ; +struct i2c_board_info board_info; +int ret; + +i2c_for_each_dev (&max17047, cht_int33fe_check_for_max17047); +if (max17047) { +/* Pre-existing i2c-client for the max17047, add device-props */ + 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); +if (IS_ERR (data->max17047)) +return PTR_ERR (data->max17047); + +return 0 ; +} +static const struct property_entry fusb302_props[] = {PROPERTY_ENTRY_STRING ("linux,extcon-name" ,"cht_wcove_pwrsrc" ),PROPERTY_ENTRY_U32 ("fcs,max-sink-microvolt" ,12000000 ),@@ -86,12 +118,50 @@ static const struct property_entry fusb302_props[] = { { } }; +static const struct property_entry *props[] = { + [INT33FE_NODE_FUSB302 ] = fusb302_props, + [INT33FE_NODE_MAX17047 ] = max17047_props, + [INT33FE_NODE_PI3USB30532 ] =NULL , +}; + +static void cht_int33fe_remove_nodes (struct cht_int33fe_data *data) +{ +int i; + +for (i =0 ; i <INT33FE_NODE_MAX ; i++) { +fwnode_remove_software_node (data->node[i]); + data->node[i] =NULL ; + } +} + +static int cht_int33fe_add_nodes (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode; +int ret; +int i; + +for (i =0 ; i <ARRAY_SIZE (props); i++) { + fwnode =fwnode_create_software_node (props[i],NULL ); +if (IS_ERR (fwnode)) { + ret =PTR_ERR (fwnode); + goto err_remove_nodes; + } + data->node[i] = fwnode; + } + +return 0 ; + +err_remove_nodes: +cht_int33fe_remove_nodes (data); + +return ret; +} +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 regulator *regulator;unsigned long long ptyp;acpi_status status;@@ -151,26 +221,14 @@ 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) -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); - } + ret =cht_int33fe_add_nodes (data); +if (ret) +return ret; + +/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ + ret =cht_int33fe_find_max17047 (dev, data); +if (ret) + goto out_remove_nodes; data->connections[0 ].endpoint[0 ] ="port0" ; data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ;@@ -187,7 +245,7 @@ static int cht_int33fe_probe(struct platform_device *pdev)memset (&board_info,0 ,sizeof (board_info));strlcpy (board_info.type,"typec_fusb302" ,I2C_NAME_SIZE ); board_info.dev_name ="fusb302" ; - board_info.properties = fusb302_props; + board_info.fwnode = data->node[INT33FE_NODE_FUSB302 ]; board_info.irq = fusb302_irq; data->fusb302 =i2c_acpi_new_device (dev,2 , &board_info);@@ -198,6 +256,7 @@ static int cht_int33fe_probe(struct platform_device *pdev)memset (&board_info,0 ,sizeof (board_info)); board_info.dev_name ="pi3usb30532" ; + board_info.fwnode = data->node[INT33FE_NODE_PI3USB30532 ];strlcpy (board_info.type,"pi3usb30532" ,I2C_NAME_SIZE ); data->pi3usb30532 =i2c_acpi_new_device (dev,3 , &board_info);@@ -214,9 +273,11 @@ static int cht_int33fe_probe(struct platform_device *pdev)i2c_unregister_device (data->fusb302); out_unregister_max17047: +device_connections_remove (data->connections);i2c_unregister_device (data->max17047); -device_connections_remove (data->connections); +out_remove_nodes: +cht_int33fe_remove_nodes (data);return ret; }@@ -230,6 +291,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)i2c_unregister_device (data->max17047);device_connections_remove (data->connections); +cht_int33fe_remove_nodes (data);return 0 ; } --2.20.1
Signature · 2 lines
With Best Regards, Andy Shevchenko
Heikki Krogerus
maintainerRe: [PATCH v2 09/12] platform/x86: intel_cht_int33fe: Provide software nodes for the devices
Quoted 8 lines · Andy Shevchenko
Quoted 5 lines · Heikki Krogerus
Software nodes provide two features that we will need later.
1) Software nodes can have references to other software nodes.
2) Software nodes can exist before a device entry is created.
Quoted 4 lines
It seems does two things in one patch, and the first one is not mentioned in the commit message.
I would suggest to create a preparation patch which extracts finding and registering max17047 device followed by what you do here.
OK, I'll split the patch.
thanks,
Signature · 1 line
heikki
Heikki Krogerus
maintainerPATCH v2 10/12 platform/x86: intel_cht_int33fe: Provide fwnode for the USB connector
In reply to Heikki KrogerusIn ACPI, and now also in DT, the USB connectors usually have their own device nodes. In case of USB Type-C, those connector (port) nodes are child nodes of the controller or PHY device, in our case the fusb302. The software fwnodes allow us to create a similar child node for fusb302 that represents the connector also on Intel CHT.
This makes it possible replace the fusb302 specific device properties which were deprecated with the common USB connector properties that tcpm.c is able to use directly.
--- drivers/platform/x86/intel_cht_int33fe.c | 37 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-)
drivers/platform/x86/intel_cht_int33fe.c +34 -3
@@ -25,6 +25,7 @@#include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/usb/pd.h> #define EXPECTED_PTYPE 4
@@ -32,6 +33,7 @@ enum {INT33FE_NODE_FUSB302 ,INT33FE_NODE_MAX17047 ,INT33FE_NODE_PI3USB30532 , +INT33FE_NODE_USB_CONNECTOR , INT33FE_NODE_MAX, };
@@ -112,9 +114,29 @@ cht_int33fe_find_max17047(struct device *dev, struct cht_int33fe_data *data)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 ("name" ,"connector" ), +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" ,36000000 ), { } };
@@ -149,6 +171,15 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data) data->node[i] = fwnode; } +/* Node for the USB connector (FUSB302 is the parent) */ + fwnode =fwnode_create_software_node (usb_connector_props, + data->node[INT33FE_NODE_FUSB302 ]); +if (IS_ERR (fwnode)) { + ret =PTR_ERR (fwnode); + goto err_remove_nodes; + } + data->node[INT33FE_NODE_USB_CONNECTOR ] = fwnode; +return 0 ; err_remove_nodes:
Signature · 1 line
2.20.1
Heikki Krogerus
maintainerPATCH v2 11/12 platform/x86: intel_cht_int33fe: Link with external dependencies using fwnodes
In reply to Heikki KrogerusSupplying also external devices - the DisplayPort connector and the USB role switch - software fwnodes. After this the driver has access to all the components tied to the USB Type-C connector and can start creating software node references to actually associate them with the USB Type-C connector device.
--- drivers/platform/x86/intel_cht_int33fe.c | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
drivers/platform/x86/intel_cht_int33fe.c +92 -0
@@ -33,6 +33,8 @@ enum {INT33FE_NODE_FUSB302 ,INT33FE_NODE_MAX17047 ,INT33FE_NODE_PI3USB30532 , +INT33FE_NODE_DISPLAYPORT , +INT33FE_NODE_ROLE_SWITCH ,INT33FE_NODE_USB_CONNECTOR , INT33FE_NODE_MAX, };
@@ -44,6 +46,7 @@ struct cht_int33fe_data {/* Contain a list-head must be per device */ struct device_connection connections[4 ]; +struct fwnode_handle *dp;struct fwnode_handle *node[INT33FE_NODE_MAX ]; };
@@ -144,8 +147,71 @@ static const struct property_entry *props[] = { [INT33FE_NODE_FUSB302] = fusb302_props, [INT33FE_NODE_MAX17047] = max17047_props, [INT33FE_NODE_PI3USB30532] = NULL, + [INT33FE_NODE_DISPLAYPORT] = NULL, + [INT33FE_NODE_ROLE_SWITCH] = NULL, }; +static int cht_int33fe_setup_mux (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_ROLE_SWITCH ]; +struct pci_dev *pdev; +struct device *dev; +struct device *p; + +/* First let's find xHCI PCI device */ + pdev =pci_get_class (PCI_CLASS_SERIAL_USB_XHCI ,NULL ); +if (!pdev || (pdev->vendor !=PCI_VENDOR_ID_INTEL )) +return -ENODEV ; + +/* Then the child platform device */ + p =device_find_child_by_name (&pdev->dev,"intel_xhci_usb_sw" ); +pci_dev_put (pdev); +if (!p) +return -EPROBE_DEFER ; + +/* Finally the mux 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_handle_get (dev->fwnode); +fwnode_remove_software_node (fwnode); + data->node[INT33FE_NODE_ROLE_SWITCH ] = dev->fwnode; + }else { +/* The node can be tied to the lifetime of the device. */ + dev->fwnode =fwnode_handle_get (dev->fwnode); + } + +put_device (dev); + +return 0 ; +} + +static int cht_int33fe_setup_dp (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_DISPLAYPORT ]; +struct pci_dev *pdev; + +/* 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 )) +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) {int i;
@@ -154,6 +220,12 @@ static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)fwnode_remove_software_node (data->node[i]); data->node[i] =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)
@@ -180,6 +252,26 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data) } data->node[INT33FE_NODE_USB_CONNECTOR ] = fwnode; +/* 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 assing 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:
Signature · 1 line
2.20.1
Re: [PATCH v2 11/12] platform/x86: intel_cht_int33fe: Link with external dependencies using fwnodes
Quoted 8 lines · Heikki Krogerus
Supplying also external devices - the DisplayPort connector and the USB role switch - software fwnodes. After this the driver has access to all the components tied to the USB Type-C connector and can start creating software node references to actually associate them with the USB Type-C connector device.
Quoted 139 lines
Signed-off-by Heikki Krogerus <[email protected]>--- drivers/platform/x86/intel_cht_int33fe.c | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+)drivers/platform/x86/intel_cht_int33fe.c +92
-1@@ -33,6 +33,8 @@ enum {INT33FE_NODE_FUSB302 ,INT33FE_NODE_MAX17047 ,INT33FE_NODE_PI3USB30532 , +INT33FE_NODE_DISPLAYPORT , +INT33FE_NODE_ROLE_SWITCH ,INT33FE_NODE_USB_CONNECTOR , INT33FE_NODE_MAX, };@@ -44,6 +46,7 @@ struct cht_int33fe_data {/* Contain a list-head must be per device */ struct device_connection connections[4 ]; +struct fwnode_handle *dp;struct fwnode_handle *node[INT33FE_NODE_MAX ]; };@@ -144,8 +147,71 @@ static const struct property_entry *props[] = { [INT33FE_NODE_FUSB302] = fusb302_props, [INT33FE_NODE_MAX17047] = max17047_props, [INT33FE_NODE_PI3USB30532] = NULL, + [INT33FE_NODE_DISPLAYPORT] = NULL, + [INT33FE_NODE_ROLE_SWITCH] = NULL, }; +static int cht_int33fe_setup_mux (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_ROLE_SWITCH ]; +struct pci_dev *pdev; +struct device *dev; +struct device *p; + +/* First let's find xHCI PCI device */ + pdev =pci_get_class (PCI_CLASS_SERIAL_USB_XHCI ,NULL ); +if (!pdev || (pdev->vendor !=PCI_VENDOR_ID_INTEL )) +return -ENODEV ; + +/* Then the child platform device */ + p =device_find_child_by_name (&pdev->dev,"intel_xhci_usb_sw" ); +pci_dev_put (pdev); +if (!p) +return -EPROBE_DEFER ; + +/* Finally the mux 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_handle_get (dev->fwnode); +fwnode_remove_software_node (fwnode); + data->node[INT33FE_NODE_ROLE_SWITCH ] = dev->fwnode; + }else { +/* The node can be tied to the lifetime of the device. */ + dev->fwnode =fwnode_handle_get (dev->fwnode); + } + +put_device (dev); + +return 0 ; +} + +static int cht_int33fe_setup_dp (struct cht_int33fe_data *data) +{ +struct fwnode_handle *fwnode = data->node[INT33FE_NODE_DISPLAYPORT ]; +struct pci_dev *pdev; + +/* 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 )) +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) {int i;@@ -154,6 +220,12 @@ static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)fwnode_remove_software_node (data->node[i]); data->node[i] =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)@@ -180,6 +252,26 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data) } data->node[INT33FE_NODE_USB_CONNECTOR ] = fwnode; +/* 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 assing 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: --
Quoted 2 lines
2.20.1
--
With Best Regards,
Andy Shevchenko
Heikki Krogerus
maintainerPATCH v2 12/12 platform/x86: intel_cht_int33fe: Replacing the old connections with references
In reply to Heikki KrogerusNow that the software nodes support references, and the device connection API support parsing fwnode references, replacing the old connection descriptions with software node references. Relying on device names when matching the connection would not have been possible to link the USB Type-C connector and the DisplayPort connector together, but with real references it's not problem.
The DisplayPort ACPI node is dag up, and the drivers own software node for the DisplayPort is set as the secondary node for it. The USB Type-C connector refers the software node, but it is now tied to the ACPI node, and therefore any device entry (struct drm_connector in practice) that the node combo is assigned to.
The USB role switch device does not have ACPI node, so we have to wait for the device to appear. Then we can simply assign our software node for the to the device.
--- drivers/platform/x86/intel_cht_int33fe.c | 94 +++++++++++++++++++----- 1 file changed, 77 insertions(+), 17 deletions(-)
drivers/platform/x86/intel_cht_int33fe.c +77 -17
@@ -39,15 +39,22 @@ enum { INT33FE_NODE_MAX, }; +enum { +INT33FE_REF_ORIENTATION , +INT33FE_REF_MODE_MUX , +INT33FE_REF_DISPLAYPORT , +INT33FE_REF_ROLE_SWITCH , +INT33FE_REF_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 *node[INT33FE_NODE_MAX ]; +struct software_node_reference *ref[INT33FE_REF_MAX ]; };/*
@@ -280,6 +287,65 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)return ret; } +static void cht_int33fe_remove_references (struct cht_int33fe_data *data) +{ +int i; + +for (i =0 ; i <INT33FE_REF_MAX ; i++) +fwnode_remove_software_node_reference (data->ref[i]); +} + +static int cht_int33fe_add_references (struct cht_int33fe_data *data) +{ +struct fwnode_reference_args args[2 ] = { }; +struct software_node_reference *ref; +struct fwnode_handle *con; + + con = data->node[INT33FE_NODE_USB_CONNECTOR ]; + +/* USB Type-C muxes */ + args[0 ].fwnode = data->node[INT33FE_NODE_PI3USB30532 ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"orientation-switch" , + args); +if (IS_ERR (ref)) +return PTR_ERR (ref); + data->ref[INT33FE_REF_ORIENTATION ] = ref; + + ref =fwnode_create_software_node_reference (con,"mode-switch" , args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_MODE_MUX ] = ref; + +/* USB role switch */ + args[0 ].fwnode = data->node[INT33FE_NODE_ROLE_SWITCH ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"usb-role-switch" , + args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_ROLE_SWITCH ] = ref; + +/* DisplayPort */ + args[0 ].fwnode = data->node[INT33FE_NODE_DISPLAYPORT ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"displayport" , args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_DISPLAYPORT ] = ref; + +return 0 ; +} +static int cht_int33fe_probe (struct platform_device *pdev) {struct device *dev = &pdev->dev;
@@ -348,22 +414,14 @@ static int cht_int33fe_probe(struct platform_device *pdev)if (ret)return ret; -/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ - ret =cht_int33fe_find_max17047 (dev, data); + ret =cht_int33fe_add_references (data);if (ret) goto out_remove_nodes; - data->connections[0 ].endpoint[0 ] ="port0" ; - data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ; - data->connections[0 ].id ="orientation-switch" ; - data->connections[1 ].endpoint[0 ] ="port0" ; - data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532-mux" ; - 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); +/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ + ret =cht_int33fe_find_max17047 (dev, data); +if (ret) + goto out_remove_references;memset (&board_info,0 ,sizeof (board_info));strlcpy (board_info.type,"typec_fusb302" ,I2C_NAME_SIZE );
@@ -396,9 +454,11 @@ static int cht_int33fe_probe(struct platform_device *pdev)i2c_unregister_device (data->fusb302); out_unregister_max17047: -device_connections_remove (data->connections);i2c_unregister_device (data->max17047); +out_remove_references: +cht_int33fe_remove_references (data); + out_remove_nodes:cht_int33fe_remove_nodes (data);
@@ -413,7 +473,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)i2c_unregister_device (data->fusb302);i2c_unregister_device (data->max17047); -device_connections_remove (data->connections); +cht_int33fe_remove_references (data);cht_int33fe_remove_nodes (data);return 0 ;
Signature · 1 line
2.20.1
Re: [PATCH v2 12/12] platform/x86: intel_cht_int33fe: Replacing the old connections with references
Quoted 16 lines · Heikki Krogerus
Now that the software nodes support references, and the device connection API support parsing fwnode references, replacing the old connection descriptions with software node references. Relying on device names when matching the connection would not have been possible to link the USB Type-C connector and the DisplayPort connector together, but with real references it's not problem.
The DisplayPort ACPI node is dag up, and the drivers own software node for the DisplayPort is set as the secondary node for it. The USB Type-C connector refers the software node, but it is now tied to the ACPI node, and therefore any device entry (struct
drm_connectorin practice) that the node combo is assigned to.
Quoted 4 lines
The USB role switch device does not have ACPI node, so we have to wait for the device to appear. Then we can simply assign our software node for the to the device.
Quoted 153 lines
Signed-off-by Heikki Krogerus <[email protected]>--- drivers/platform/x86/intel_cht_int33fe.c | 94 +++++++++++++++++++----- 1 file changed, 77 insertions(+), 17 deletions(-)drivers/platform/x86/intel_cht_int33fe.c +77
-18@@ -39,15 +39,22 @@ enum { INT33FE_NODE_MAX, }; +enum { +INT33FE_REF_ORIENTATION , +INT33FE_REF_MODE_MUX , +INT33FE_REF_DISPLAYPORT , +INT33FE_REF_ROLE_SWITCH , +INT33FE_REF_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 *node[INT33FE_NODE_MAX ]; +struct software_node_reference *ref[INT33FE_REF_MAX ]; };/* @@ -280,6 +287,65 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)return ret; } +static void cht_int33fe_remove_references (struct cht_int33fe_data *data) +{ +int i; + +for (i =0 ; i <INT33FE_REF_MAX ; i++) +fwnode_remove_software_node_reference (data->ref[i]); +} + +static int cht_int33fe_add_references (struct cht_int33fe_data *data) +{ +struct fwnode_reference_args args[2 ] = { }; +struct software_node_reference *ref; +struct fwnode_handle *con; + + con = data->node[INT33FE_NODE_USB_CONNECTOR ]; + +/* USB Type-C muxes */ + args[0 ].fwnode = data->node[INT33FE_NODE_PI3USB30532 ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"orientation-switch" , + args); +if (IS_ERR (ref)) +return PTR_ERR (ref); + data->ref[INT33FE_REF_ORIENTATION ] = ref; + + ref =fwnode_create_software_node_reference (con,"mode-switch" , args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_MODE_MUX ] = ref; + +/* USB role switch */ + args[0 ].fwnode = data->node[INT33FE_NODE_ROLE_SWITCH ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"usb-role-switch" , + args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_ROLE_SWITCH ] = ref; + +/* DisplayPort */ + args[0 ].fwnode = data->node[INT33FE_NODE_DISPLAYPORT ]; + args[1 ].fwnode =NULL ; + + ref =fwnode_create_software_node_reference (con,"displayport" , args); +if (IS_ERR (ref)) { +cht_int33fe_remove_references (data); +return PTR_ERR (ref); + } + data->ref[INT33FE_REF_DISPLAYPORT ] = ref; + +return 0 ; +} +static int cht_int33fe_probe (struct platform_device *pdev) {struct device *dev = &pdev->dev;@@ -348,22 +414,14 @@ static int cht_int33fe_probe(struct platform_device *pdev)if (ret)return ret; -/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ - ret =cht_int33fe_find_max17047 (dev, data); + ret =cht_int33fe_add_references (data);if (ret) goto out_remove_nodes; - data->connections[0 ].endpoint[0 ] ="port0" ; - data->connections[0 ].endpoint[1 ] ="i2c-pi3usb30532-switch" ; - data->connections[0 ].id ="orientation-switch" ; - data->connections[1 ].endpoint[0 ] ="port0" ; - data->connections[1 ].endpoint[1 ] ="i2c-pi3usb30532-mux" ; - 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); +/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */ + ret =cht_int33fe_find_max17047 (dev, data); +if (ret) + goto out_remove_references;memset (&board_info,0 ,sizeof (board_info));strlcpy (board_info.type,"typec_fusb302" ,I2C_NAME_SIZE );@@ -396,9 +454,11 @@ static int cht_int33fe_probe(struct platform_device *pdev)i2c_unregister_device (data->fusb302); out_unregister_max17047: -device_connections_remove (data->connections);i2c_unregister_device (data->max17047); +out_remove_references: +cht_int33fe_remove_references (data); + out_remove_nodes:cht_int33fe_remove_nodes (data);@@ -413,7 +473,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)i2c_unregister_device (data->fusb302);i2c_unregister_device (data->max17047); -device_connections_remove (data->connections); +cht_int33fe_remove_references (data);cht_int33fe_remove_nodes (data);return 0 ; --2.20.1
Signature · 2 lines
With Best Regards, Andy Shevchenko