When a single DT node has a graph connected to more than one usb-c-connector node we can't differentiate which typec switch registered for the device is associated with the USB connector because the devcon matcher code assumes a 1:1 relationship between remote node and typec switch. Furthermore, we don't have a #typec-switch-cells property so there can only be one node per typec switch. Support multiple USB typec switches exposed by one node by passing the remote endpoint node in addition to the remote node to the devcon matcher function (devcon_match_fn_t). With this change, typec switch drivers can register switches with the device node pointer for a graph endpoint so that they can support more than one typec switch if necessary. Either way, a DT property like 'mode-switch' is always in the graph's parent node and not in the endpoint node. Cc: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> Cc: Daniel Scally <djrscally@xxxxxxxxx> Cc: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> Cc: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> Cc: Vinod Koul <vkoul@xxxxxxxxxx> Cc: "Rafael J. Wysocki" <rafael.j.wysocki@xxxxxxxxx> Cc: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> Cc: Alexandre Belloni <alexandre.belloni@xxxxxxxxxxx> Cc: Ivan Orlov <ivan.orlov0322@xxxxxxxxx> Cc: Rob Herring <robh+dt@xxxxxxxxxx> Cc: Krzysztof Kozlowski <krzysztof.kozlowski+dt@xxxxxxxxxx> Cc: Conor Dooley <conor+dt@xxxxxxxxxx> Cc: <devicetree@xxxxxxxxxxxxxxx> Cc: <linux-usb@xxxxxxxxxxxxxxx> Cc: <linux-acpi@xxxxxxxxxxxxxxx> Cc: Pin-yen Lin <treapking@xxxxxxxxxxxx> Signed-off-by: Stephen Boyd <swboyd@xxxxxxxxxxxx> --- drivers/base/property.c | 7 +++++-- drivers/usb/roles/class.c | 4 ++-- drivers/usb/typec/mux.c | 8 ++++++++ drivers/usb/typec/retimer.c | 7 ++++++- include/linux/property.h | 5 +++-- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 8c40abed7852..cae81ed4e298 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1289,6 +1289,7 @@ static unsigned int fwnode_graph_devcon_matches(const struct fwnode_handle *fwno { struct fwnode_handle *node; struct fwnode_handle *ep; + struct fwnode_handle *remote_ep; unsigned int count = 0; void *ret; @@ -1304,7 +1305,9 @@ static unsigned int fwnode_graph_devcon_matches(const struct fwnode_handle *fwno continue; } - ret = match(node, con_id, data); + remote_ep = fwnode_graph_get_remote_endpoint(ep); + ret = match(node, remote_ep, con_id, data); + fwnode_handle_put(remote_ep); fwnode_handle_put(node); if (ret) { if (matches) @@ -1334,7 +1337,7 @@ static unsigned int fwnode_devcon_matches(const struct fwnode_handle *fwnode, if (IS_ERR(node)) break; - ret = match(node, NULL, data); + ret = match(node, NULL, NULL, data); fwnode_handle_put(node); if (ret) { if (matches) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index ae41578bd014..9a0ef5fa0a19 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -89,8 +89,8 @@ enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) } EXPORT_SYMBOL_GPL(usb_role_switch_get_role); -static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const char *id, - void *data) +static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const struct fwnode_handle *endpoint, + const char *id, void *data) { struct device *dev; diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 80dd91938d96..3eabd0d62f47 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -33,6 +33,7 @@ static int switch_fwnode_match(struct device *dev, const void *fwnode) } static void *typec_switch_match(const struct fwnode_handle *fwnode, + const struct fwnode_handle *endpoint, const char *id, void *data) { struct device *dev; @@ -55,6 +56,9 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, */ dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); + if (!dev) + dev = class_find_device(&typec_mux_class, NULL, endpoint, + switch_fwnode_match); return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -263,6 +267,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) } static void *typec_mux_match(const struct fwnode_handle *fwnode, + const struct fwnode_handle *endpoint, const char *id, void *data) { struct device *dev; @@ -280,6 +285,9 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); + if (!dev) + dev = class_find_device(&typec_mux_class, NULL, endpoint, + mux_fwnode_match); return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c index 4a7d1b5c4d86..eb74abee6619 100644 --- a/drivers/usb/typec/retimer.c +++ b/drivers/usb/typec/retimer.c @@ -22,7 +22,9 @@ static int retimer_fwnode_match(struct device *dev, const void *fwnode) return is_typec_retimer(dev) && device_match_fwnode(dev, fwnode); } -static void *typec_retimer_match(const struct fwnode_handle *fwnode, const char *id, void *data) +static void *typec_retimer_match(const struct fwnode_handle *fwnode, + const struct fwnode_handle *endpoint, + const char *id, void *data) { struct device *dev; @@ -31,6 +33,9 @@ static void *typec_retimer_match(const struct fwnode_handle *fwnode, const char dev = class_find_device(&retimer_class, NULL, fwnode, retimer_fwnode_match); + if (!dev) + dev = class_find_device(&retimer_class, NULL, endpoint, + retimer_fwnode_match); return dev ? to_typec_retimer(dev) : ERR_PTR(-EPROBE_DEFER); } diff --git a/include/linux/property.h b/include/linux/property.h index 9f2585d705a8..0f20df1f0a49 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -455,8 +455,9 @@ unsigned int fwnode_graph_get_endpoint_count(const struct fwnode_handle *fwnode, int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint); -typedef void *(*devcon_match_fn_t)(const struct fwnode_handle *fwnode, const char *id, - void *data); +typedef void *(*devcon_match_fn_t)(const struct fwnode_handle *fwnode, + const struct fwnode_handle *endpoint, + const char *id, void *data); void *fwnode_connection_find_match(const struct fwnode_handle *fwnode, const char *con_id, void *data, -- https://chromeos.dev