From c1686d3304bf7dd443e85eeda74db1f95eafa5d4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:02 -0300 Subject: [PATCH 001/142] thunderbolt: Use correct type in tb_port_is_clx_enabled() prototype Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d31137619776f9c173a46a79bc7733a2b106061f commit d31137619776f9c173a46a79bc7733a2b106061f Author: "Jiri Slaby (SUSE)" Date: Mon, 12 Dec 2022 11:29:36 +0100 tb_port_is_clx_enabled() generates a valid warning with gcc-13: drivers/thunderbolt/switch.c:1286:6: error: conflicting types for 'tb_port_is_clx_enabled' due to enum/integer mismatch; have 'bool(struct tb_port *, unsigned int)' ... drivers/thunderbolt/tb.h:1050:6: note: previous declaration of 'tb_port_is_clx_enabled' with type 'bool(struct tb_port *, enum tb_clx)' ... I.e. the type of the 2nd parameter of tb_port_is_clx_enabled() in the declaration is unsigned int, while the definition spells enum tb_clx. Synchronize them to the former as the parameter is in fact a mask of the enum values. Signed-off-by: Jiri Slaby (SUSE) Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index f9786976f5ec..6c4a26b1c37c 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1047,7 +1047,7 @@ void tb_port_lane_bonding_disable(struct tb_port *port); int tb_port_wait_for_link_width(struct tb_port *port, int width, int timeout_msec); int tb_port_update_credits(struct tb_port *port); -bool tb_port_is_clx_enabled(struct tb_port *port, enum tb_clx clx); +bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); From 7367f8631344372fbacb73270dacc7a5ed02a373 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:03 -0300 Subject: [PATCH 002/142] thunderbolt: Refactor tb_acpi_add_link() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=953ff25fc9fb831a675259ce1e738c94fb6202b6 commit 953ff25fc9fb831a675259ce1e738c94fb6202b6 Author: Andy Shevchenko Date: Mon, 2 Jan 2023 21:24:04 +0200 Convert while loop into do-while with only a single call to acpi_get_first_physical_node(). No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/acpi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c index 317e4f5fdb97..628225deb8fe 100644 --- a/drivers/thunderbolt/acpi.c +++ b/drivers/thunderbolt/acpi.c @@ -36,16 +36,13 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, * We need to do this because the xHCI driver might not yet be * bound so the USB3 SuperSpeed ports are not yet created. */ - dev = acpi_get_first_physical_node(adev); - while (!dev) { - adev = acpi_dev_parent(adev); - if (!adev) - break; + do { dev = acpi_get_first_physical_node(adev); - } + if (dev) + break; - if (!dev) - goto out_put; + adev = acpi_dev_parent(adev); + } while (adev); /* * Check that the device is PCIe. This is because USB3 From abea77fcc4d9f46c4cfaf8ba2783385a347bc032 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:03 -0300 Subject: [PATCH 003/142] usb: typec: altmodes/displayport: Add hpd sysfs attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=001b0c780eac328bc48b70b8437f202a4ed785e4 commit 001b0c780eac328bc48b70b8437f202a4ed785e4 Author: Badhri Jagan Sridharan Date: Sun, 11 Dec 2022 11:37:55 -0800 Exporsing HotPlugDetect(HPD) helps userspace to infer HPD state as defined by VESA DisplayPort Alt Mode on USB Type-C Standard. This allows userspace to notify users for self help, for instance, to hint user that the display port cable is probably detached (or) the display port sink (viz., monitors ect.,) is un-powered. Also helps to debug issues reported from field. This change adds an additional attribute "hpd" to the existing "displayport" attributes. VESA DisplayPort Alt Mode on USB Type-C Standard defines how HotPlugDetect(HPD) shall be supported on the USB-C connector when operating in DisplayPort Alt Mode. This is a read only node which reflects the current state of HPD. Valid values: - 1 when HPD’s logical state is high (HPD_High) - 0 when HPD’s logical state is low (HPD_Low) Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20221211193755.1392128-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../ABI/testing/sysfs-driver-typec-displayport | 15 +++++++++++++++ drivers/usb/typec/altmodes/displayport.c | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-typec-displayport b/Documentation/ABI/testing/sysfs-driver-typec-displayport index 231471ad0d4b..256c87c5219a 100644 --- a/Documentation/ABI/testing/sysfs-driver-typec-displayport +++ b/Documentation/ABI/testing/sysfs-driver-typec-displayport @@ -47,3 +47,18 @@ Description: USB SuperSpeed protocol. From user perspective pin assignments C and E are equal, where all channels on the connector are used for carrying DisplayPort protocol (allowing higher resolutions). + +What: /sys/bus/typec/devices/.../displayport/hpd +Date: Dec 2022 +Contact: Badhri Jagan Sridharan +Description: + VESA DisplayPort Alt Mode on USB Type-C Standard defines how + HotPlugDetect(HPD) shall be supported on the USB-C connector when + operating in DisplayPort Alt Mode. This is a read only node which + reflects the current state of HPD. + + Valid values: + - 1: when HPD’s logical state is high (HPD_High) as defined + by VESA DisplayPort Alt Mode on USB Type-C Standard. + - 0 when HPD’s logical state is low (HPD_Low) as defined by + VESA DisplayPort Alt Mode on USB Type-C Standard. diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 50b24096eb7f..d2e72230613a 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -146,6 +146,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp) if (dp->hpd != hpd) { drm_connector_oob_hotplug_event(dp->connector_fwnode); dp->hpd = hpd; + sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); } } @@ -514,9 +515,18 @@ static ssize_t pin_assignment_show(struct device *dev, } static DEVICE_ATTR_RW(pin_assignment); +static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", dp->hpd); +} +static DEVICE_ATTR_RO(hpd); + static struct attribute *dp_altmode_attrs[] = { &dev_attr_configuration.attr, &dev_attr_pin_assignment.attr, + &dev_attr_hpd.attr, NULL }; From dedb549deaa3851b71b8a9f209f1dad2dd192e3c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:04 -0300 Subject: [PATCH 004/142] usb: chipidea: imx: Drop empty platform remove function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9aa1afc8f62263ed064dc5d94fa7a7ee6054e2ed commit 9aa1afc8f62263ed064dc5d94fa7a7ee6054e2ed Author: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 12 Dec 2022 22:27:17 +0100 A remove callback just returning 0 is equivalent to no remove callback at all. So drop the useless function. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20221212212717.3774606-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/chipidea/usbmisc_imx.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index acdb13316cd0..c57c1a71a513 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1263,14 +1263,8 @@ static int usbmisc_imx_probe(struct platform_device *pdev) return 0; } -static int usbmisc_imx_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver usbmisc_imx_driver = { .probe = usbmisc_imx_probe, - .remove = usbmisc_imx_remove, .driver = { .name = "usbmisc_imx", .of_match_table = usbmisc_imx_dt_ids, From c4780f5552fbeb7481e0cd81146571a8b98a286e Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:04 -0300 Subject: [PATCH 005/142] usb: typec: ucsi: Register USB Power Delivery Capabilities Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b04e1747fbcc6bf4a93a95b5c2505bf2a6467ee8 Conflicts: * Considering already applied commits ("usb: ucsi: Ensure connector delayed work items are flushed") and ("Merge 6.2-rc5 into usb-next") commit b04e1747fbcc6bf4a93a95b5c2505bf2a6467ee8 Author: Saranya Gopal Date: Mon, 2 Jan 2023 11:51:08 +0530 UCSI allows the USB PD capabilities to be read with the GET_PDO command. This will register those capabilities and make them visible to user space. Reviewed-by: Heikki Krogerus Co-developed-by: Rajaram Regupathy Signed-off-by: Rajaram Regupathy Signed-off-by: Saranya Gopal Link: https://lore.kernel.org/r/20230102062108.838423-1-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/ucsi/ucsi.c | 174 ++++++++++++++++++++++++++++++---- drivers/usb/typec/ucsi/ucsi.h | 8 ++ 2 files changed, 164 insertions(+), 18 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 1cf8947c6d66..f632350f6dcb 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -567,8 +567,9 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) } } -static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner, - u32 *pdos, int offset, int num_pdos) +static int ucsi_read_pdos(struct ucsi_connector *con, + enum typec_role role, int is_partner, + u32 *pdos, int offset, int num_pdos) { struct ucsi *ucsi = con->ucsi; u64 command; @@ -578,7 +579,7 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner, command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner); command |= UCSI_GET_PDOS_PDO_OFFSET(offset); command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1); - command |= UCSI_GET_PDOS_SRC_PDOS; + command |= is_source(role) ? UCSI_GET_PDOS_SRC_PDOS : 0; ret = ucsi_send_command(ucsi, command, pdos + offset, num_pdos * sizeof(u32)); if (ret < 0 && ret != -ETIMEDOUT) @@ -587,30 +588,43 @@ static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner, return ret; } +static int ucsi_get_pdos(struct ucsi_connector *con, enum typec_role role, + int is_partner, u32 *pdos) +{ + u8 num_pdos; + int ret; + + /* UCSI max payload means only getting at most 4 PDOs at a time */ + ret = ucsi_read_pdos(con, role, is_partner, pdos, 0, UCSI_MAX_PDOS); + if (ret < 0) + return ret; + + num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */ + if (num_pdos < UCSI_MAX_PDOS) + return num_pdos; + + /* get the remaining PDOs, if any */ + ret = ucsi_read_pdos(con, role, is_partner, pdos, UCSI_MAX_PDOS, + PDO_MAX_OBJECTS - UCSI_MAX_PDOS); + if (ret < 0) + return ret; + + return ret / sizeof(u32) + num_pdos; +} + static int ucsi_get_src_pdos(struct ucsi_connector *con) { int ret; - /* UCSI max payload means only getting at most 4 PDOs at a time */ - ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS); + ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, con->src_pdos); if (ret < 0) return ret; - con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */ - if (con->num_pdos < UCSI_MAX_PDOS) - return 0; - - /* get the remaining PDOs, if any */ - ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS, - PDO_MAX_OBJECTS - UCSI_MAX_PDOS); - if (ret < 0) - return ret; - - con->num_pdos += ret / sizeof(u32); + con->num_pdos = ret; ucsi_port_psy_changed(con); - return 0; + return ret; } static int ucsi_check_altmodes(struct ucsi_connector *con) @@ -635,6 +649,72 @@ static int ucsi_check_altmodes(struct ucsi_connector *con) return ret; } +static int ucsi_register_partner_pdos(struct ucsi_connector *con) +{ + struct usb_power_delivery_desc desc = { con->ucsi->cap.pd_version }; + struct usb_power_delivery_capabilities_desc caps; + struct usb_power_delivery_capabilities *cap; + int ret; + + if (con->partner_pd) + return 0; + + con->partner_pd = usb_power_delivery_register(NULL, &desc); + if (IS_ERR(con->partner_pd)) + return PTR_ERR(con->partner_pd); + + ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, caps.pdo); + if (ret > 0) { + if (ret < PDO_MAX_OBJECTS) + caps.pdo[ret] = 0; + + caps.role = TYPEC_SOURCE; + cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); + + con->partner_source_caps = cap; + + ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd); + if (ret) { + usb_power_delivery_unregister_capabilities(con->partner_source_caps); + return ret; + } + } + + ret = ucsi_get_pdos(con, TYPEC_SINK, 1, caps.pdo); + if (ret > 0) { + if (ret < PDO_MAX_OBJECTS) + caps.pdo[ret] = 0; + + caps.role = TYPEC_SINK; + + cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); + + con->partner_sink_caps = cap; + + ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd); + if (ret) { + usb_power_delivery_unregister_capabilities(con->partner_sink_caps); + return ret; + } + } + + return 0; +} + +static void ucsi_unregister_partner_pdos(struct ucsi_connector *con) +{ + usb_power_delivery_unregister_capabilities(con->partner_sink_caps); + con->partner_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(con->partner_source_caps); + con->partner_source_caps = NULL; + usb_power_delivery_unregister(con->partner_pd); + con->partner_pd = NULL; +} + static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { @@ -643,6 +723,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0); ucsi_partner_task(con, ucsi_check_altmodes, 30, 0); + ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ); break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: con->rdo = 0; @@ -701,6 +782,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con) if (!con->partner) return; + ucsi_unregister_partner_pdos(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP); typec_unregister_partner(con->partner); con->partner = NULL; @@ -805,6 +887,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->status.flags & UCSI_CONSTAT_CONNECTED) { ucsi_register_partner(con); ucsi_partner_task(con, ucsi_check_connection, 1, HZ); + + if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == + UCSI_CONSTAT_PWR_OPMODE_PD) + ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ); } else { ucsi_unregister_partner(con); } @@ -1041,6 +1127,9 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) static int ucsi_register_port(struct ucsi *ucsi, int index) { + struct usb_power_delivery_desc desc = { ucsi->cap.pd_version}; + struct usb_power_delivery_capabilities_desc pd_caps; + struct usb_power_delivery_capabilities *pd_cap; struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; @@ -1120,6 +1209,41 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) goto out; } + con->pd = usb_power_delivery_register(ucsi->dev, &desc); + + ret = ucsi_get_pdos(con, TYPEC_SOURCE, 0, pd_caps.pdo); + if (ret > 0) { + if (ret < PDO_MAX_OBJECTS) + pd_caps.pdo[ret] = 0; + + pd_caps.role = TYPEC_SOURCE; + pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps); + if (IS_ERR(pd_cap)) { + ret = PTR_ERR(pd_cap); + goto out; + } + + con->port_source_caps = pd_cap; + typec_port_set_usb_power_delivery(con->port, con->pd); + } + + memset(&pd_caps, 0, sizeof(pd_caps)); + ret = ucsi_get_pdos(con, TYPEC_SINK, 0, pd_caps.pdo); + if (ret > 0) { + if (ret < PDO_MAX_OBJECTS) + pd_caps.pdo[ret] = 0; + + pd_caps.role = TYPEC_SINK; + pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps); + if (IS_ERR(pd_cap)) { + ret = PTR_ERR(pd_cap); + goto out; + } + + con->port_sink_caps = pd_cap; + typec_port_set_usb_power_delivery(con->port, con->pd); + } + /* Alternate modes */ ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON); if (ret) { @@ -1158,8 +1282,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) if (con->status.flags & UCSI_CONSTAT_CONNECTED) { typec_set_pwr_role(con->port, !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); - ucsi_pwr_opmode_change(con); ucsi_register_partner(con); + ucsi_pwr_opmode_change(con); ucsi_port_psy_changed(con); } @@ -1265,6 +1389,13 @@ err_unregister: ucsi_unregister_port_psy(con); if (con->wq) destroy_workqueue(con->wq); + + usb_power_delivery_unregister_capabilities(con->port_sink_caps); + con->port_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(con->port_source_caps); + con->port_source_caps = NULL; + usb_power_delivery_unregister(con->pd); + con->pd = NULL; typec_unregister_port(con->port); con->port = NULL; } @@ -1447,6 +1578,13 @@ void ucsi_unregister(struct ucsi *ucsi) mutex_unlock(&ucsi->connector[i].lock); destroy_workqueue(ucsi->connector[i].wq); } + + usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); + ucsi->connector[i].port_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); + ucsi->connector[i].port_source_caps = NULL; + usb_power_delivery_unregister(ucsi->connector[i].pd); + ucsi->connector[i].pd = NULL; typec_unregister_port(ucsi->connector[i].port); } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 60ce9fb6e745..c09af859f573 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -340,6 +340,14 @@ struct ucsi_connector { u32 src_pdos[PDO_MAX_OBJECTS]; int num_pdos; + /* USB PD objects */ + struct usb_power_delivery *pd; + struct usb_power_delivery_capabilities *port_source_caps; + struct usb_power_delivery_capabilities *port_sink_caps; + struct usb_power_delivery *partner_pd; + struct usb_power_delivery_capabilities *partner_source_caps; + struct usb_power_delivery_capabilities *partner_sink_caps; + struct usb_role_switch *usb_role_sw; }; From 10f9c9478ab83f952c542010f08739765f824ad3 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:05 -0300 Subject: [PATCH 006/142] xhci: Convert to use list_count_nodes() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5220cb493bf418cc4ce5f3ba961dbd0207441731 commit 5220cb493bf418cc4ce5f3ba961dbd0207441731 Author: Andy Shevchenko Date: Wed, 30 Nov 2022 15:48:38 +0200 The list API provides the list_count_nodes() to help with counting existing nodes in the list. Utilise it. Acked-by: Mathias Nyman Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221130134838.23805-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-ring.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f5b0e1ce22af..5f1ecdee2c1c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2531,7 +2531,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, union xhci_trb *ep_trb; int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; - struct list_head *tmp; u32 trb_comp_code; int td_num = 0; bool handling_skipped_tds = false; @@ -2585,10 +2584,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, } /* Count current td numbers if ep->skip is set */ - if (ep->skip) { - list_for_each(tmp, &ep_ring->td_list) - td_num++; - } + if (ep->skip) + td_num += list_count_nodes(&ep_ring->td_list); /* Look for common error cases */ switch (trb_comp_code) { From 477f533d9d370f16c0f1ad51acc7264c1a03b359 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:05 -0300 Subject: [PATCH 007/142] net: thunderbolt: Move into own directory Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0d0950a968009fd6e5c2a3763b2c6e81b3805b05 commit 0d0950a968009fd6e5c2a3763b2c6e81b3805b05 Author: Mika Westerberg Date: Wed, 11 Jan 2023 08:26:31 +0200 We will be adding tracepoints to the driver so instead of littering the main network driver directory, move the driver into its own directory. While there, rename the module to thunderbolt_net (with underscore) to match with the thunderbolt_dma_test convention. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Jakub Kicinski Signed-off-by: Desnes Nunes --- MAINTAINERS | 2 +- drivers/net/Kconfig | 13 +------------ drivers/net/Makefile | 4 +--- drivers/net/thunderbolt/Kconfig | 12 ++++++++++++ drivers/net/thunderbolt/Makefile | 3 +++ drivers/net/{thunderbolt.c => thunderbolt/main.c} | 0 6 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 drivers/net/thunderbolt/Kconfig create mode 100644 drivers/net/thunderbolt/Makefile rename drivers/net/{thunderbolt.c => thunderbolt/main.c} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 921d6e42a92f..583f791ecf31 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18932,7 +18932,7 @@ M: Mika Westerberg M: Yehezkel Bernat L: netdev@vger.kernel.org S: Maintained -F: drivers/net/thunderbolt.c +F: drivers/net/thunderbolt/ THUNDERX GPIO DRIVER M: Robert Richter diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index cbf27df5a362..2bd6ce320129 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -568,18 +568,7 @@ config FUJITSU_ES This driver provides support for Extended Socket network device on Extended Partitioning of FUJITSU PRIMEQUEST 2000 E2 series. -config USB4_NET - tristate "Networking over USB4 and Thunderbolt cables" - depends on USB4 && INET - help - Select this if you want to create network between two computers - over a USB4 and Thunderbolt cables. The driver supports Apple - ThunderboltIP protocol and allows communication with any host - supporting the same protocol including Windows and macOS. - - To compile this driver a module, choose M here. The module will be - called thunderbolt-net. - +source "drivers/net/thunderbolt/Kconfig" source "drivers/net/hyperv/Kconfig" config NETDEVSIM diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3c8dd75e2e1a..66be35aeb3d6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -81,8 +81,6 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/ obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o obj-$(CONFIG_FUJITSU_ES) += fjes/ - -thunderbolt-net-y += thunderbolt.o -obj-$(CONFIG_USB4_NET) += thunderbolt-net.o +obj-$(CONFIG_USB4_NET) += thunderbolt/ obj-$(CONFIG_NETDEVSIM) += netdevsim/ obj-$(CONFIG_NET_FAILOVER) += net_failover.o diff --git a/drivers/net/thunderbolt/Kconfig b/drivers/net/thunderbolt/Kconfig new file mode 100644 index 000000000000..e127848c8cbd --- /dev/null +++ b/drivers/net/thunderbolt/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB4_NET + tristate "Networking over USB4 and Thunderbolt cables" + depends on USB4 && INET + help + Select this if you want to create network between two computers + over a USB4 and Thunderbolt cables. The driver supports Apple + ThunderboltIP protocol and allows communication with any host + supporting the same protocol including Windows and macOS. + + To compile this driver a module, choose M here. The module will be + called thunderbolt_net. diff --git a/drivers/net/thunderbolt/Makefile b/drivers/net/thunderbolt/Makefile new file mode 100644 index 000000000000..dd644c8775d9 --- /dev/null +++ b/drivers/net/thunderbolt/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_USB4_NET) := thunderbolt_net.o +thunderbolt_net-objs := main.o diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt/main.c similarity index 100% rename from drivers/net/thunderbolt.c rename to drivers/net/thunderbolt/main.c From 19687c05661620ed78214047b7fdbdc8729cc33e Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:06 -0300 Subject: [PATCH 008/142] net: thunderbolt: Add debugging when sending/receiving control packets Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7b3502c1598ae9dbac4f91af702e4e0d30337705 commit 7b3502c1598ae9dbac4f91af702e4e0d30337705 Author: Mika Westerberg Date: Wed, 11 Jan 2023 08:26:32 +0200 These can be useful when debugging possible issues around USB4NET control packet exchange. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Jakub Kicinski Signed-off-by: Desnes Nunes --- drivers/net/thunderbolt/main.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 4e8e07c42614..9b738920c4fd 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -305,6 +305,8 @@ static int tbnet_logout_request(struct tbnet *net) static void start_login(struct tbnet *net) { + netdev_dbg(net->dev, "login started\n"); + mutex_lock(&net->connection_lock); net->login_sent = false; net->login_received = false; @@ -318,6 +320,8 @@ static void stop_login(struct tbnet *net) { cancel_delayed_work_sync(&net->login_work); cancel_work_sync(&net->connected_work); + + netdev_dbg(net->dev, "login stopped\n"); } static inline unsigned int tbnet_frame_size(const struct tbnet_frame *tf) @@ -374,6 +378,8 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) int ret, retries = TBNET_LOGOUT_RETRIES; while (send_logout && retries-- > 0) { + netdev_dbg(net->dev, "sending logout request %u\n", + retries); ret = tbnet_logout_request(net); if (ret != -ETIMEDOUT) break; @@ -400,6 +406,8 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) net->login_sent = false; net->login_received = false; + netdev_dbg(net->dev, "network traffic stopped\n"); + mutex_unlock(&net->connection_lock); } @@ -431,12 +439,15 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data) switch (pkg->hdr.type) { case TBIP_LOGIN: + netdev_dbg(net->dev, "remote login request received\n"); if (!netif_running(net->dev)) break; ret = tbnet_login_response(net, route, sequence, pkg->hdr.command_id); if (!ret) { + netdev_dbg(net->dev, "remote login response sent\n"); + mutex_lock(&net->connection_lock); net->login_received = true; net->remote_transmit_path = pkg->transmit_path; @@ -458,9 +469,12 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data) break; case TBIP_LOGOUT: + netdev_dbg(net->dev, "remote logout request received\n"); ret = tbnet_logout_response(net, route, sequence, command_id); - if (!ret) + if (!ret) { + netdev_dbg(net->dev, "remote logout response sent\n"); queue_work(system_long_wq, &net->disconnect_work); + } break; default: @@ -612,6 +626,8 @@ static void tbnet_connected_work(struct work_struct *work) if (!connected) return; + netdev_dbg(net->dev, "login successful, enabling paths\n"); + ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path); if (ret != net->remote_transmit_path) { netdev_err(net->dev, "failed to allocate Rx HopID\n"); @@ -647,6 +663,8 @@ static void tbnet_connected_work(struct work_struct *work) netif_carrier_on(net->dev); netif_start_queue(net->dev); + + netdev_dbg(net->dev, "network traffic started\n"); return; err_free_tx_buffers: @@ -668,8 +686,13 @@ static void tbnet_login_work(struct work_struct *work) if (netif_carrier_ok(net->dev)) return; + netdev_dbg(net->dev, "sending login request, retries=%u\n", + net->login_retries); + ret = tbnet_login_request(net, net->login_retries % 4); if (ret) { + netdev_dbg(net->dev, "sending login request failed, ret=%d\n", + ret); if (net->login_retries++ < TBNET_LOGIN_RETRIES) { queue_delayed_work(system_long_wq, &net->login_work, delay); @@ -677,6 +700,8 @@ static void tbnet_login_work(struct work_struct *work) netdev_info(net->dev, "ThunderboltIP login timed out\n"); } } else { + netdev_dbg(net->dev, "received login reply\n"); + net->login_retries = 0; mutex_lock(&net->connection_lock); From a59629d0de019090134d99a094ae6ea7ef9768f4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:07 -0300 Subject: [PATCH 009/142] net: thunderbolt: Add tracepoints Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f758652703803a1701d470de88158d5b8c70c9a4 commit f758652703803a1701d470de88158d5b8c70c9a4 Author: Mika Westerberg Date: Wed, 11 Jan 2023 08:26:33 +0200 These are useful when debugging various performance issues. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Jakub Kicinski Signed-off-by: Desnes Nunes --- drivers/net/thunderbolt/Makefile | 5 +- drivers/net/thunderbolt/main.c | 21 +++++ drivers/net/thunderbolt/trace.c | 10 +++ drivers/net/thunderbolt/trace.h | 141 +++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 drivers/net/thunderbolt/trace.c create mode 100644 drivers/net/thunderbolt/trace.h diff --git a/drivers/net/thunderbolt/Makefile b/drivers/net/thunderbolt/Makefile index dd644c8775d9..e81c2a4849f0 100644 --- a/drivers/net/thunderbolt/Makefile +++ b/drivers/net/thunderbolt/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_USB4_NET) := thunderbolt_net.o -thunderbolt_net-objs := main.o +thunderbolt_net-objs := main.o trace.o + +# Tracepoints need to know where to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 9b738920c4fd..87af1927541f 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -23,6 +23,8 @@ #include +#include "trace.h" + /* Protocol timeouts in ms */ #define TBNET_LOGIN_DELAY 4500 #define TBNET_LOGIN_TIMEOUT 500 @@ -353,6 +355,8 @@ static void tbnet_free_buffers(struct tbnet_ring *ring) size = TBNET_RX_PAGE_SIZE; } + trace_tbnet_free_frame(i, tf->page, tf->frame.buffer_phy, dir); + if (tf->frame.buffer_phy) dma_unmap_page(dma_dev, tf->frame.buffer_phy, size, dir); @@ -526,6 +530,9 @@ static int tbnet_alloc_rx_buffers(struct tbnet *net, unsigned int nbuffers) tf->frame.buffer_phy = dma_addr; tf->dev = net->dev; + trace_tbnet_alloc_rx_frame(index, tf->page, dma_addr, + DMA_FROM_DEVICE); + tb_ring_rx(ring->ring, &tf->frame); ring->prod++; @@ -602,6 +609,8 @@ static int tbnet_alloc_tx_buffers(struct tbnet *net) tf->frame.callback = tbnet_tx_callback; tf->frame.sof = TBIP_PDF_FRAME_START; tf->frame.eof = TBIP_PDF_FRAME_END; + + trace_tbnet_alloc_tx_frame(i, tf->page, dma_addr, DMA_TO_DEVICE); } ring->cons = 0; @@ -832,12 +841,16 @@ static int tbnet_poll(struct napi_struct *napi, int budget) hdr = page_address(page); if (!tbnet_check_frame(net, tf, hdr)) { + trace_tbnet_invalid_rx_ip_frame(hdr->frame_size, + hdr->frame_id, hdr->frame_index, hdr->frame_count); __free_pages(page, TBNET_RX_PAGE_ORDER); dev_kfree_skb_any(net->skb); net->skb = NULL; continue; } + trace_tbnet_rx_ip_frame(hdr->frame_size, hdr->frame_id, + hdr->frame_index, hdr->frame_count); frame_size = le32_to_cpu(hdr->frame_size); skb = net->skb; @@ -871,6 +884,7 @@ static int tbnet_poll(struct napi_struct *napi, int budget) if (last) { skb->protocol = eth_type_trans(skb, net->dev); + trace_tbnet_rx_skb(skb); napi_gro_receive(&net->napi, skb); net->skb = NULL; } @@ -990,6 +1004,8 @@ static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb, for (i = 0; i < frame_count; i++) { hdr = page_address(frames[i]->page); hdr->frame_count = cpu_to_le32(frame_count); + trace_tbnet_tx_ip_frame(hdr->frame_size, hdr->frame_id, + hdr->frame_index, hdr->frame_count); dma_sync_single_for_device(dma_dev, frames[i]->frame.buffer_phy, tbnet_frame_size(frames[i]), DMA_TO_DEVICE); @@ -1054,6 +1070,8 @@ static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb, len = le32_to_cpu(hdr->frame_size) - offset; wsum = csum_partial(dest, len, wsum); hdr->frame_count = cpu_to_le32(frame_count); + trace_tbnet_tx_ip_frame(hdr->frame_size, hdr->frame_id, + hdr->frame_index, hdr->frame_count); offset = 0; } @@ -1096,6 +1114,8 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, bool unmap = false; void *dest; + trace_tbnet_tx_skb(skb); + nframes = DIV_ROUND_UP(data_len, TBNET_MAX_PAYLOAD_SIZE); if (tbnet_available_buffers(&net->tx_ring) < nframes) { netif_stop_queue(net->dev); @@ -1202,6 +1222,7 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb, net->stats.tx_packets++; net->stats.tx_bytes += skb->len; + trace_tbnet_consume_skb(skb); dev_consume_skb_any(skb); return NETDEV_TX_OK; diff --git a/drivers/net/thunderbolt/trace.c b/drivers/net/thunderbolt/trace.c new file mode 100644 index 000000000000..1b1499520a44 --- /dev/null +++ b/drivers/net/thunderbolt/trace.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tracepoints for Thunderbolt/USB4 networking driver + * + * Copyright (C) 2023, Intel Corporation + * Author: Mika Westerberg + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/thunderbolt/trace.h b/drivers/net/thunderbolt/trace.h new file mode 100644 index 000000000000..9626eadaebb9 --- /dev/null +++ b/drivers/net/thunderbolt/trace.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Tracepoints for Thunderbolt/USB4 networking driver + * + * Copyright (C) 2023, Intel Corporation + * Author: Mika Westerberg + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM thunderbolt_net + +#if !defined(__TRACE_THUNDERBOLT_NET_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_THUNDERBOLT_NET_H + +#include +#include +#include + +#define DMA_DATA_DIRECTION_NAMES \ + { DMA_BIDIRECTIONAL, "DMA_BIDIRECTIONAL" }, \ + { DMA_TO_DEVICE, "DMA_TO_DEVICE" }, \ + { DMA_FROM_DEVICE, "DMA_FROM_DEVICE" }, \ + { DMA_NONE, "DMA_NONE" } + +DECLARE_EVENT_CLASS(tbnet_frame, + TP_PROTO(unsigned int index, const void *page, dma_addr_t phys, + enum dma_data_direction dir), + TP_ARGS(index, page, phys, dir), + TP_STRUCT__entry( + __field(unsigned int, index) + __field(const void *, page) + __field(dma_addr_t, phys) + __field(enum dma_data_direction, dir) + ), + TP_fast_assign( + __entry->index = index; + __entry->page = page; + __entry->phys = phys; + __entry->dir = dir; + ), + TP_printk("index=%u page=%p phys=%pad dir=%s", + __entry->index, __entry->page, &__entry->phys, + __print_symbolic(__entry->dir, DMA_DATA_DIRECTION_NAMES)) +); + +DEFINE_EVENT(tbnet_frame, tbnet_alloc_rx_frame, + TP_PROTO(unsigned int index, const void *page, dma_addr_t phys, + enum dma_data_direction dir), + TP_ARGS(index, page, phys, dir) +); + +DEFINE_EVENT(tbnet_frame, tbnet_alloc_tx_frame, + TP_PROTO(unsigned int index, const void *page, dma_addr_t phys, + enum dma_data_direction dir), + TP_ARGS(index, page, phys, dir) +); + +DEFINE_EVENT(tbnet_frame, tbnet_free_frame, + TP_PROTO(unsigned int index, const void *page, dma_addr_t phys, + enum dma_data_direction dir), + TP_ARGS(index, page, phys, dir) +); + +DECLARE_EVENT_CLASS(tbnet_ip_frame, + TP_PROTO(__le32 size, __le16 id, __le16 index, __le32 count), + TP_ARGS(size, id, index, count), + TP_STRUCT__entry( + __field(u32, size) + __field(u16, id) + __field(u16, index) + __field(u32, count) + ), + TP_fast_assign( + __entry->size = le32_to_cpu(size); + __entry->id = le16_to_cpu(id); + __entry->index = le16_to_cpu(index); + __entry->count = le32_to_cpu(count); + ), + TP_printk("id=%u size=%u index=%u count=%u", + __entry->id, __entry->size, __entry->index, __entry->count) +); + +DEFINE_EVENT(tbnet_ip_frame, tbnet_rx_ip_frame, + TP_PROTO(__le32 size, __le16 id, __le16 index, __le32 count), + TP_ARGS(size, id, index, count) +); + +DEFINE_EVENT(tbnet_ip_frame, tbnet_invalid_rx_ip_frame, + TP_PROTO(__le32 size, __le16 id, __le16 index, __le32 count), + TP_ARGS(size, id, index, count) +); + +DEFINE_EVENT(tbnet_ip_frame, tbnet_tx_ip_frame, + TP_PROTO(__le32 size, __le16 id, __le16 index, __le32 count), + TP_ARGS(size, id, index, count) +); + +DECLARE_EVENT_CLASS(tbnet_skb, + TP_PROTO(const struct sk_buff *skb), + TP_ARGS(skb), + TP_STRUCT__entry( + __field(const void *, addr) + __field(unsigned int, len) + __field(unsigned int, data_len) + __field(unsigned int, nr_frags) + ), + TP_fast_assign( + __entry->addr = skb; + __entry->len = skb->len; + __entry->data_len = skb->data_len; + __entry->nr_frags = skb_shinfo(skb)->nr_frags; + ), + TP_printk("skb=%p len=%u data_len=%u nr_frags=%u", + __entry->addr, __entry->len, __entry->data_len, + __entry->nr_frags) +); + +DEFINE_EVENT(tbnet_skb, tbnet_rx_skb, + TP_PROTO(const struct sk_buff *skb), + TP_ARGS(skb) +); + +DEFINE_EVENT(tbnet_skb, tbnet_tx_skb, + TP_PROTO(const struct sk_buff *skb), + TP_ARGS(skb) +); + +DEFINE_EVENT(tbnet_skb, tbnet_consume_skb, + TP_PROTO(const struct sk_buff *skb), + TP_ARGS(skb) +); + +#endif /* _TRACE_THUNDERBOLT_NET_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include From 7bd74e84d83aada956603a79615ef31b1dabb2e6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:14 -0300 Subject: [PATCH 010/142] media: uvcvideo: Remove format descriptions Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=50459f103edfe47c9a599d766a850ef6014936c5 commit 50459f103edfe47c9a599d766a850ef6014936c5 Author: Laurent Pinchart Date: Tue, 15 Nov 2016 18:44:29 +0200 The V4L2 core overwrites format descriptions in v4l_fill_fmtdesc(), there's no need to manually set the descriptions in the driver. This prepares for removal of the format descriptions from the uvc_fmts table. Unlike V4L2, UVC makes a distinction between the SD-DV, SDL-DV and HD-DV formats. It also indicates whether the DV format uses 50Hz or 60Hz. This information is parsed by the driver to construct a format name string that is printed in a debug message, but serves no other purpose as V4L2 has a single V4L2_PIX_FMT_DV pixel format that covers all those cases. As the information is available in the UVC descriptors, and thus accessible to users with lsusb if they really care, don't log it in a debug message and drop the format name string to simplify the code. Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Reviewed-by: Michael Grzeschik Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 24 ++---------------------- drivers/media/usb/uvc/uvc_v4l2.c | 2 -- drivers/media/usb/uvc/uvcvideo.h | 2 -- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index e4bcb5011360..0a35d7c0d0a0 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -251,14 +251,10 @@ static int uvc_parse_format(struct uvc_device *dev, fmtdesc = uvc_format_by_guid(&buffer[5]); if (fmtdesc != NULL) { - strscpy(format->name, fmtdesc->name, - sizeof(format->name)); format->fcc = fmtdesc->fcc; } else { dev_info(&streaming->intf->dev, "Unknown video format %pUl\n", &buffer[5]); - snprintf(format->name, sizeof(format->name), "%pUl\n", - &buffer[5]); format->fcc = 0; } @@ -270,8 +266,6 @@ static int uvc_parse_format(struct uvc_device *dev, */ if (dev->quirks & UVC_QUIRK_FORCE_Y8) { if (format->fcc == V4L2_PIX_FMT_YUYV) { - strscpy(format->name, "Greyscale 8-bit (Y8 )", - sizeof(format->name)); format->fcc = V4L2_PIX_FMT_GREY; format->bpp = 8; width_multiplier = 2; @@ -312,7 +306,6 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - strscpy(format->name, "MJPEG", sizeof(format->name)); format->fcc = V4L2_PIX_FMT_MJPEG; format->flags = UVC_FMT_FLAG_COMPRESSED; format->bpp = 0; @@ -328,17 +321,7 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - switch (buffer[8] & 0x7f) { - case 0: - strscpy(format->name, "SD-DV", sizeof(format->name)); - break; - case 1: - strscpy(format->name, "SDL-DV", sizeof(format->name)); - break; - case 2: - strscpy(format->name, "HD-DV", sizeof(format->name)); - break; - default: + if ((buffer[8] & 0x7f) > 2) { uvc_dbg(dev, DESCR, "device %d videostreaming interface %d: unknown DV format %u\n", dev->udev->devnum, @@ -346,9 +329,6 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", - sizeof(format->name)); - format->fcc = V4L2_PIX_FMT_DV; format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; format->bpp = 0; @@ -375,7 +355,7 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - uvc_dbg(dev, DESCR, "Found format %s\n", format->name); + uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc); buflen -= buffer[0]; buffer += buffer[0]; diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index f4d4c33b6dfb..dcd178d249b6 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -660,8 +660,6 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - strscpy(fmt->description, format->name, sizeof(fmt->description)); - fmt->description[sizeof(fmt->description) - 1] = 0; fmt->pixelformat = format->fcc; return 0; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index df93db259312..b60e4ae95e81 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -264,8 +264,6 @@ struct uvc_format { u32 fcc; u32 flags; - char name[32]; - unsigned int nframes; struct uvc_frame *frame; }; From fcc90387009b69aa0a8dc58d114d755c8b957308 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:14 -0300 Subject: [PATCH 011/142] media: uvcvideo: Handle cameras with invalid descriptors Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=41ddb251c68ac75c101d3a50a68c4629c9055e4c commit 41ddb251c68ac75c101d3a50a68c4629c9055e4c Author: Ricardo Ribalda Date: Tue, 20 Sep 2022 16:04:55 +0200 If the source entity does not contain any pads, do not create a link. Reported-by: syzbot Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_entity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index 7c4d2f93d351..cc68dd24eb42 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -37,7 +37,7 @@ static int uvc_mc_create_links(struct uvc_video_chain *chain, continue; remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); - if (remote == NULL) + if (remote == NULL || remote->num_pads == 0) return -EINVAL; source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) From bdb81fe1fed7807b34639187bccadb4f1eb94815 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:15 -0300 Subject: [PATCH 012/142] media: uvcvideo: Only create input devs if hw supports it Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3bc22dc66a4f386393119118169ed0b78c389898 commit 3bc22dc66a4f386393119118169ed0b78c389898 Author: Ricardo Ribalda Date: Tue, 20 Sep 2022 16:09:50 +0200 Examine the stream headers to figure out if the device has a button and can be used as an input. Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_status.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 7518ffce22ed..cb90aff344bc 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -18,11 +18,34 @@ * Input device */ #ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV + +static bool uvc_input_has_button(struct uvc_device *dev) +{ + struct uvc_streaming *stream; + + /* + * The device has button events if both bTriggerSupport and + * bTriggerUsage are one. Otherwise the camera button does not + * exist or is handled automatically by the camera without host + * driver or client application intervention. + */ + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTriggerSupport == 1 && + stream->header.bTriggerUsage == 1) + return true; + } + + return false; +} + static int uvc_input_init(struct uvc_device *dev) { struct input_dev *input; int ret; + if (!uvc_input_has_button(dev)) + return 0; + input = input_allocate_device(); if (input == NULL) return -ENOMEM; From ba89380a2d0bb87a6753094da3c6e29f0c6f9423 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:16 -0300 Subject: [PATCH 013/142] media: uvcvideo: Handle errors from calls to usb_string Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4867bb590ae445bcfaa711a86b603c97e94574b3 commit 4867bb590ae445bcfaa711a86b603c97e94574b3 Author: Guenter Roeck Date: Tue, 25 Oct 2022 16:41:01 +0200 On a Webcam from Quanta, we see the following error. usb 3-5: New USB device found, idVendor=0408, idProduct=30d2, bcdDevice= 0.03 usb 3-5: New USB device strings: Mfr=3, Product=1, SerialNumber=2 usb 3-5: Product: USB2.0 HD UVC WebCam usb 3-5: Manufacturer: Quanta usb 3-5: SerialNumber: 0x0001 ... uvcvideo: Found UVC 1.10 device USB2.0 HD UVC WebCam (0408:30d2) uvcvideo: Failed to initialize entity for entity 5 uvcvideo: Failed to register entities (-22). The Webcam reports an entity of type UVC_VC_EXTENSION_UNIT. It reports a string index of '7' associated with that entity. The attempt to read that string from the camera fails with error -32 (-EPIPE). usb_string() returns that error, but it is ignored. As result, the entity name is empty. This later causes v4l2_device_register_subdev() to return -EINVAL, and no entities are registered as result. While this appears to be a firmware problem with the camera, the kernel should still handle the situation gracefully. To do that, check the return value from usb_string(). If it reports an error, assign the entity's default name. Signed-off-by: Guenter Roeck Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 48 ++++++++++++------------------ 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 0a35d7c0d0a0..4c372965517d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -859,10 +859,8 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); - if (buffer[24+p+2*n] != 0) - usb_string(udev, buffer[24+p+2*n], unit->name, - sizeof(unit->name)); - else + if (buffer[24+p+2*n] == 0 || + usb_string(udev, buffer[24+p+2*n], unit->name, sizeof(unit->name)) < 0) sprintf(unit->name, "Extension %u", buffer[3]); list_add_tail(&unit->list, &dev->entities); @@ -986,15 +984,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(term->media.bmTransportModes, &buffer[10+n], p); } - if (buffer[7] != 0) - usb_string(udev, buffer[7], term->name, - sizeof(term->name)); - else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) - sprintf(term->name, "Camera %u", buffer[3]); - else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) - sprintf(term->name, "Media %u", buffer[3]); - else - sprintf(term->name, "Input %u", buffer[3]); + if (buffer[7] == 0 || + usb_string(udev, buffer[7], term->name, sizeof(term->name)) < 0) { + if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) + sprintf(term->name, "Camera %u", buffer[3]); + if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) + sprintf(term->name, "Media %u", buffer[3]); + else + sprintf(term->name, "Input %u", buffer[3]); + } list_add_tail(&term->list, &dev->entities); break; @@ -1027,10 +1025,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(term->baSourceID, &buffer[7], 1); - if (buffer[8] != 0) - usb_string(udev, buffer[8], term->name, - sizeof(term->name)); - else + if (buffer[8] == 0 || + usb_string(udev, buffer[8], term->name, sizeof(term->name)) < 0) sprintf(term->name, "Output %u", buffer[3]); list_add_tail(&term->list, &dev->entities); @@ -1052,10 +1048,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(unit->baSourceID, &buffer[5], p); - if (buffer[5+p] != 0) - usb_string(udev, buffer[5+p], unit->name, - sizeof(unit->name)); - else + if (buffer[5+p] == 0 || + usb_string(udev, buffer[5+p], unit->name, sizeof(unit->name)) < 0) sprintf(unit->name, "Selector %u", buffer[3]); list_add_tail(&unit->list, &dev->entities); @@ -1085,10 +1079,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (dev->uvc_version >= 0x0110) unit->processing.bmVideoStandards = buffer[9+n]; - if (buffer[8+n] != 0) - usb_string(udev, buffer[8+n], unit->name, - sizeof(unit->name)); - else + if (buffer[8+n] == 0 || + usb_string(udev, buffer[8+n], unit->name, sizeof(unit->name)) < 0) sprintf(unit->name, "Processing %u", buffer[3]); list_add_tail(&unit->list, &dev->entities); @@ -1116,10 +1108,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->extension.bmControls = (u8 *)unit + sizeof(*unit); memcpy(unit->extension.bmControls, &buffer[23+p], n); - if (buffer[23+p+n] != 0) - usb_string(udev, buffer[23+p+n], unit->name, - sizeof(unit->name)); - else + if (buffer[23+p+n] == 0 || + usb_string(udev, buffer[23+p+n], unit->name, sizeof(unit->name)) < 0) sprintf(unit->name, "Extension %u", buffer[3]); list_add_tail(&unit->list, &dev->entities); From aeb3176278ef44d03bf679e5036278788543338a Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:17 -0300 Subject: [PATCH 014/142] media: uvcvideo: Fix missing newline after declarations Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a8c1952ed4bc85f64174def8c00c71c264dc3a2 commit 2a8c1952ed4bc85f64174def8c00c71c264dc3a2 Author: Pedro Guilherme Siqueira Moreira Date: Tue, 25 Oct 2022 02:04:48 -0300 Fixes 'Missing a blank line after declarations' warning issued by scripts/checkpatch.pl on drivers/media/usb/uvc/uvc_driver.c Signed-off-by: Pedro Guilherme Siqueira Moreira Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 4c372965517d..8f649fa9ab22 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -712,6 +712,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; + alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, streaming->header.bEndpointAddress); @@ -1826,12 +1827,14 @@ static void uvc_delete(struct kref *kref) list_for_each_safe(p, n, &dev->chains) { struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); kfree(chain); } list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); #ifdef CONFIG_MEDIA_CONTROLLER uvc_mc_cleanup_entity(entity); @@ -1841,6 +1844,7 @@ static void uvc_delete(struct kref *kref) list_for_each_safe(p, n, &dev->streams) { struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); usb_driver_release_interface(&uvc_driver.driver, streaming->intf); From 5d78578fe812df3f246368e71d97a3c0dabadee7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:18 -0300 Subject: [PATCH 015/142] media: uvcvideo: Fix assignment inside if condition Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7b78a8464d5701796a4e146b3973422350e56977 commit 7b78a8464d5701796a4e146b3973422350e56977 Author: Pedro Guilherme Siqueira Moreira Date: Tue, 25 Oct 2022 02:04:49 -0300 Fixes 'do not use assignment in if condition' errors issued by scripts/checkpatch.pl on drivers/media/usb/uvc/uvc_driver.c Signed-off-by: Pedro Guilherme Siqueira Moreira Reviewed-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 8f649fa9ab22..d84e46fdb1b9 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1144,7 +1144,8 @@ static int uvc_parse_control(struct uvc_device *dev) buffer[1] != USB_DT_CS_INTERFACE) goto next_descriptor; - if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) + ret = uvc_parse_standard_control(dev, buffer, buflen); + if (ret < 0) return ret; next_descriptor: @@ -2180,7 +2181,8 @@ static int uvc_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); /* Initialize the interrupt URB. */ - if ((ret = uvc_status_init(dev)) < 0) { + ret = uvc_status_init(dev); + if (ret < 0) { dev_info(&dev->udev->dev, "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", ret); From 908e6e77da11b04ef100652e5f85b1f5f32ef047 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:18 -0300 Subject: [PATCH 016/142] media: uvcvideo: Fix usage of symbolic permissions to octal Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ce75d5ecd9edaa027c0c2a7df0edcdf59ea7738 commit 0ce75d5ecd9edaa027c0c2a7df0edcdf59ea7738 Author: Pedro Guilherme Siqueira Moreira Date: Tue, 25 Oct 2022 02:04:50 -0300 Change symbolic permissions to octal equivalents as recommended by scripts/checkpatch.pl on drivers/media/usb/uvc/uvc_driver.c. Signed-off-by: Pedro Guilherme Siqueira Moreira Reviewed-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index d84e46fdb1b9..4f59583bf516 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2329,17 +2329,17 @@ static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) } module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, - &uvc_clock_param, S_IRUGO|S_IWUSR); + &uvc_clock_param, 0644); MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); -module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR); +module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); -module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +module_param_named(nodrop, uvc_no_drop_param, uint, 0644); MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); -module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); +module_param_named(quirks, uvc_quirks_param, uint, 0644); MODULE_PARM_DESC(quirks, "Forced device quirks"); -module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR); +module_param_named(trace, uvc_dbg_param, uint, 0644); MODULE_PARM_DESC(trace, "Trace level bitmask"); -module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +module_param_named(timeout, uvc_timeout_param, uint, 0644); MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); /* ------------------------------------------------------------------------ From ee5a70995d0f507da5c3d9c1f6fed1a6281958d1 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:19 -0300 Subject: [PATCH 017/142] media: uvcvideo: Remove void casting for the status endpoint Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=adfd3910c27fbc0959ae5f1dafaec563f7d0bdd2 commit adfd3910c27fbc0959ae5f1dafaec563f7d0bdd2 Author: Ricardo Ribalda Date: Tue, 20 Dec 2022 23:56:44 +0100 Make the code more resilient, by replacing the castings with proper structure definitions and using offsetof() instead of open coding the location of the data. Suggested-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Andrzej Pietrasiewicz Reviewed-by: Laurent Pinchart Reviewed-by: Sergey Senozhatsky Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_status.c | 65 ++++++++++-------------------- drivers/media/usb/uvc/uvcvideo.h | 25 ++++++++++-- 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index cb90aff344bc..602830a8023e 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -96,38 +96,23 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, /* -------------------------------------------------------------------------- * Status interrupt endpoint */ -struct uvc_streaming_status { - u8 bStatusType; - u8 bOriginator; - u8 bEvent; - u8 bValue[]; -} __packed; - -struct uvc_control_status { - u8 bStatusType; - u8 bOriginator; - u8 bEvent; - u8 bSelector; - u8 bAttribute; - u8 bValue[]; -} __packed; - static void uvc_event_streaming(struct uvc_device *dev, - struct uvc_streaming_status *status, int len) + struct uvc_status *status, int len) { - if (len < 3) { + if (len <= offsetof(struct uvc_status, bEvent)) { uvc_dbg(dev, STATUS, "Invalid streaming status event received\n"); return; } if (status->bEvent == 0) { - if (len < 4) + if (len <= offsetof(struct uvc_status, streaming)) return; + uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n", status->bOriginator, - status->bValue[0] ? "pressed" : "released", len); - uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); + status->streaming.button ? "pressed" : "released", len); + uvc_input_report_key(dev, KEY_CAMERA, status->streaming.button); } else { uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n", status->bOriginator, status->bEvent, len); @@ -154,7 +139,7 @@ static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity, } static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, - const struct uvc_control_status *status, + const struct uvc_status *status, struct uvc_video_chain **chain) { list_for_each_entry((*chain), &dev->chains, list) { @@ -166,7 +151,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, continue; ctrl = uvc_event_entity_find_ctrl(entity, - status->bSelector); + status->control.bSelector); if (ctrl) return ctrl; } @@ -176,7 +161,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, } static bool uvc_event_control(struct urb *urb, - const struct uvc_control_status *status, int len) + const struct uvc_status *status, int len) { static const char *attrs[] = { "value", "info", "failure", "min", "max" }; struct uvc_device *dev = urb->context; @@ -184,24 +169,24 @@ static bool uvc_event_control(struct urb *urb, struct uvc_control *ctrl; if (len < 6 || status->bEvent != 0 || - status->bAttribute >= ARRAY_SIZE(attrs)) { + status->control.bAttribute >= ARRAY_SIZE(attrs)) { uvc_dbg(dev, STATUS, "Invalid control status event received\n"); return false; } uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n", - status->bOriginator, status->bSelector, - attrs[status->bAttribute], len); + status->bOriginator, status->control.bSelector, + attrs[status->control.bAttribute], len); /* Find the control. */ ctrl = uvc_event_find_ctrl(dev, status, &chain); if (!ctrl) return false; - switch (status->bAttribute) { + switch (status->control.bAttribute) { case UVC_CTRL_VALUE_CHANGE: return uvc_ctrl_status_event_async(urb, chain, ctrl, - status->bValue); + status->control.bValue); case UVC_CTRL_INFO_CHANGE: case UVC_CTRL_FAILURE_CHANGE: @@ -237,28 +222,22 @@ static void uvc_status_complete(struct urb *urb) len = urb->actual_length; if (len > 0) { - switch (dev->status[0] & 0x0f) { + switch (dev->status->bStatusType & 0x0f) { case UVC_STATUS_TYPE_CONTROL: { - struct uvc_control_status *status = - (struct uvc_control_status *)dev->status; - - if (uvc_event_control(urb, status, len)) + if (uvc_event_control(urb, dev->status, len)) /* The URB will be resubmitted in work context. */ return; break; } case UVC_STATUS_TYPE_STREAMING: { - struct uvc_streaming_status *status = - (struct uvc_streaming_status *)dev->status; - - uvc_event_streaming(dev, status, len); + uvc_event_streaming(dev, dev->status, len); break; } default: uvc_dbg(dev, STATUS, "Unknown status event type %u\n", - dev->status[0]); + dev->status->bStatusType); break; } } @@ -282,12 +261,12 @@ int uvc_status_init(struct uvc_device *dev) uvc_input_init(dev); - dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); - if (dev->status == NULL) + dev->status = kzalloc(sizeof(*dev->status), GFP_KERNEL); + if (!dev->status) return -ENOMEM; dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); - if (dev->int_urb == NULL) { + if (!dev->int_urb) { kfree(dev->status); return -ENOMEM; } @@ -304,7 +283,7 @@ int uvc_status_init(struct uvc_device *dev) interval = fls(interval) - 1; usb_fill_int_urb(dev->int_urb, dev->udev, pipe, - dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete, + dev->status, sizeof(*dev->status), uvc_status_complete, dev, interval); return 0; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index b60e4ae95e81..cd5861cae3b0 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -51,8 +51,6 @@ #define UVC_URBS 5 /* Maximum number of packets per URB. */ #define UVC_MAX_PACKETS 32 -/* Maximum status buffer size in bytes of interrupt URB. */ -#define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 5000 #define UVC_CTRL_STREAMING_TIMEOUT 5000 @@ -525,6 +523,26 @@ struct uvc_device_info { const struct uvc_control_mapping **mappings; }; +struct uvc_status_streaming { + u8 button; +} __packed; + +struct uvc_status_control { + u8 bSelector; + u8 bAttribute; + u8 bValue[11]; +} __packed; + +struct uvc_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + union { + struct uvc_status_control control; + struct uvc_status_streaming streaming; + }; +} __packed; + struct uvc_device { struct usb_device *udev; struct usb_interface *intf; @@ -557,7 +575,8 @@ struct uvc_device { /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; struct urb *int_urb; - u8 *status; + struct uvc_status *status; + struct input_dev *input; char input_phys[64]; From 4b5f8e241086cc485af674609f3c0e97d76f4d70 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:20 -0300 Subject: [PATCH 018/142] media: uvcvideo: Recover stalled ElGato devices Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=16045708a3ff0266b1b8cca6198ea50eb80fff6a commit 16045708a3ff0266b1b8cca6198ea50eb80fff6a Author: Ricardo Ribalda Date: Tue, 6 Dec 2022 00:15:04 +0100 Elgato Cam Link 4k can be in a stalled state if the resolution of the external source has changed while the firmware initializes. Once in this state, the device is useless until it receives a USB reset. It has even been observed that the stalled state will continue even after unplugging the device. lsusb -v Bus 002 Device 002: ID 0fd9:0066 Elgato Systems GmbH Cam Link 4K Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 3.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 9 idVendor 0x0fd9 Elgato Systems GmbH idProduct 0x0066 bcdDevice 0.00 iManufacturer 1 Elgato iProduct 2 Cam Link 4K iSerial 4 0005AC52FE000 bNumConfigurations 1 Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index d2eb9066e4dc..503ef29211ca 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -129,12 +129,13 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, return -EPIPE; } +static const struct usb_device_id elgato_cam_link_4k = { + USB_DEVICE(0x0fd9, 0x0066) +}; + static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { - static const struct usb_device_id elgato_cam_link_4k = { - USB_DEVICE(0x0fd9, 0x0066) - }; struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; @@ -297,7 +298,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, dev_err(&stream->intf->dev, "Failed to query (%u) UVC %s control : %d (exp. %u).\n", query, probe ? "probe" : "commit", ret, size); - ret = -EIO; + ret = (ret == -EPROTO) ? -EPROTO : -EIO; goto out; } @@ -2121,6 +2122,21 @@ int uvc_video_init(struct uvc_streaming *stream) * request on the probe control, as required by the UVC specification. */ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + + /* + * Elgato Cam Link 4k can be in a stalled state if the resolution of + * the external source has changed while the firmware initializes. + * Once in this state, the device is useless until it receives a + * USB reset. It has even been observed that the stalled state will + * continue even after unplugging the device. + */ + if (ret == -EPROTO && + usb_match_one_id(stream->dev->intf, &elgato_cam_link_4k)) { + dev_err(&stream->intf->dev, "Elgato Cam Link 4K firmware crash detected\n"); + dev_err(&stream->intf->dev, "Resetting the device, unplug and replug to recover\n"); + usb_reset_device(stream->dev->udev); + } + if (ret < 0) return ret; From 698d94f584c7905372e71f9d66f1a8770a732ff1 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:20 -0300 Subject: [PATCH 019/142] media: uvcvideo: Limit power line control for Acer EasyCamera Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81e78a6fc320693c269990fb1af37b8c2c382cf2 commit 81e78a6fc320693c269990fb1af37b8c2c382cf2 Author: Ricardo Ribalda Date: Fri, 2 Dec 2022 17:45:06 +0100 The device does not implement the power line control correctly. Add a corresponding control mapping override. Bus 003 Device 002: ID 5986:1180 Acer, Inc EasyCamera Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x5986 Acer, Inc idProduct 0x1180 bcdDevice 56.04 iManufacturer 3 Bison iProduct 1 EasyCamera iSerial 2 bNumConfigurations 1 Signed-off-by: Ricardo Ribalda Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 4f59583bf516..1de17db3356b 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2967,6 +2967,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Acer EasyCamera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x1180, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Intel RealSense D4M */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, From 4c754fee7fe58540eac3036a3ccdf7111d74eaf7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:21 -0300 Subject: [PATCH 020/142] media: uvcvideo: Factor out usb_string() calls Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5f0e659d2a2ac6172d495915f2775fa592c7ab17 commit 5f0e659d2a2ac6172d495915f2775fa592c7ab17 Author: Laurent Pinchart Date: Tue, 25 Oct 2022 21:42:21 +0300 When parsing UVC descriptors to instantiate entities, the driver calls usb_string() to retrieve the entity name from the device, and falls back to a default name if the string can't be retrieved. This code pattern occurs multiple times. Factor it out to a separate helper function. Signed-off-by: Laurent Pinchart Reviewed-by: Guenter Roeck Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 59 ++++++++++++++++++------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 1de17db3356b..5f6e0da0ef90 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -794,6 +794,27 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, return entity; } +static void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity, + const char *type_name, u8 string_id) +{ + int ret; + + /* + * First attempt to read the entity name from the device. If the entity + * has no associated string, or if reading the string fails (most + * likely due to a buggy firmware), fall back to default names based on + * the entity type. + */ + if (string_id) { + ret = usb_string(dev->udev, string_id, entity->name, + sizeof(entity->name)); + if (!ret) + return; + } + + sprintf(entity->name, "%s %u", type_name, entity->id); +} + /* Parse vendor-specific extensions. */ static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen) @@ -860,9 +881,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); - if (buffer[24+p+2*n] == 0 || - usb_string(udev, buffer[24+p+2*n], unit->name, sizeof(unit->name)) < 0) - sprintf(unit->name, "Extension %u", buffer[3]); + uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]); list_add_tail(&unit->list, &dev->entities); handled = 1; @@ -880,6 +899,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, struct usb_interface *intf; struct usb_host_interface *alts = dev->intf->cur_altsetting; unsigned int i, n, p, len; + const char *type_name; u16 type; switch (buffer[2]) { @@ -985,15 +1005,14 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(term->media.bmTransportModes, &buffer[10+n], p); } - if (buffer[7] == 0 || - usb_string(udev, buffer[7], term->name, sizeof(term->name)) < 0) { - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) - sprintf(term->name, "Camera %u", buffer[3]); - if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) - sprintf(term->name, "Media %u", buffer[3]); - else - sprintf(term->name, "Input %u", buffer[3]); - } + if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) + type_name = "Camera"; + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) + type_name = "Media"; + else + type_name = "Input"; + + uvc_entity_set_name(dev, term, type_name, buffer[7]); list_add_tail(&term->list, &dev->entities); break; @@ -1026,9 +1045,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(term->baSourceID, &buffer[7], 1); - if (buffer[8] == 0 || - usb_string(udev, buffer[8], term->name, sizeof(term->name)) < 0) - sprintf(term->name, "Output %u", buffer[3]); + uvc_entity_set_name(dev, term, "Output", buffer[8]); list_add_tail(&term->list, &dev->entities); break; @@ -1049,9 +1066,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, memcpy(unit->baSourceID, &buffer[5], p); - if (buffer[5+p] == 0 || - usb_string(udev, buffer[5+p], unit->name, sizeof(unit->name)) < 0) - sprintf(unit->name, "Selector %u", buffer[3]); + uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]); list_add_tail(&unit->list, &dev->entities); break; @@ -1080,9 +1095,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (dev->uvc_version >= 0x0110) unit->processing.bmVideoStandards = buffer[9+n]; - if (buffer[8+n] == 0 || - usb_string(udev, buffer[8+n], unit->name, sizeof(unit->name)) < 0) - sprintf(unit->name, "Processing %u", buffer[3]); + uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]); list_add_tail(&unit->list, &dev->entities); break; @@ -1109,9 +1122,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->extension.bmControls = (u8 *)unit + sizeof(*unit); memcpy(unit->extension.bmControls, &buffer[23+p], n); - if (buffer[23+p+n] == 0 || - usb_string(udev, buffer[23+p+n], unit->name, sizeof(unit->name)) < 0) - sprintf(unit->name, "Extension %u", buffer[3]); + uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]); list_add_tail(&unit->list, &dev->entities); break; From 8c76e4040c522efa00435de05c6e90bb148d8b06 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:21 -0300 Subject: [PATCH 021/142] media: uvcvideo: Check for INACTIVE in uvc_ctrl_is_accessible() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9f582f0418ed1c18f92c9e4628075d6ec9a7d9fb commit 9f582f0418ed1c18f92c9e4628075d6ec9a7d9fb Author: Hans Verkuil Date: Tue, 3 Jan 2023 15:36:19 +0100 Check for inactive controls in uvc_ctrl_is_accessible(). Use the new value for the master_id controls if present, otherwise use the existing value to determine if it is OK to set the control. Doing this here avoids attempting to set an inactive control, which will return an error from the USB device, which returns an invalid errorcode. This fixes: warn: v4l2-test-controls.cpp(483): s_ctrl returned EIO   warn: v4l2-test-controls.cpp(483): s_ctrl returned EIO test VIDIOC_G/S_CTRL: OK   warn: v4l2-test-controls.cpp(739): s_ext_ctrls returned EIO   warn: v4l2-test-controls.cpp(739): s_ext_ctrls returned EIO   warn: v4l2-test-controls.cpp(816): s_ext_ctrls returned EIO test VIDIOC_G/S/TRY_EXT_CTRLS: OK Tested with: v4l2-ctl -c auto_exposure=1 OK v4l2-ctl -c exposure_time_absolute=251 OK v4l2-ctl -c auto_exposure=3 OK v4l2-ctl -c exposure_time_absolute=251 VIDIOC_S_EXT_CTRLS: failed: Input/output error exposure_time_absolute: Input/output error ERROR v4l2-ctl -c auto_exposure=3,exposure_time_absolute=251,auto_exposure=1 v4l2-ctl -C auto_exposure,exposure_time_absolute   auto_exposure: 1 exposure_time_absolute: 251 Reviewed-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 42 +++++++++++++++++++++++++++++++- drivers/media/usb/uvc/uvc_v4l2.c | 3 +-- drivers/media/usb/uvc/uvcvideo.h | 3 ++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index c95a2229f4fa..6f5aaaf09ee0 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1085,11 +1085,28 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id, return 0; } +/* + * Check if control @v4l2_id can be accessed by the given control @ioctl + * (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS). + * + * For set operations on slave controls, check if the master's value is set to + * manual, either in the others controls set in the same ioctl call, or from + * the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set + * both the master and slave control, such as for instance setting + * auto_exposure=1, exposure_time_absolute=251. + */ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, - bool read) + const struct v4l2_ext_controls *ctrls, + unsigned long ioctl) { + struct uvc_control_mapping *master_map = NULL; + struct uvc_control *master_ctrl = NULL; struct uvc_control_mapping *mapping; struct uvc_control *ctrl; + bool read = ioctl == VIDIOC_G_EXT_CTRLS; + s32 val; + int ret; + int i; if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0) return -EACCES; @@ -1104,6 +1121,29 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read) return -EACCES; + if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id) + return 0; + + /* + * Iterate backwards in cases where the master control is accessed + * multiple times in the same ioctl. We want the last value. + */ + for (i = ctrls->count - 1; i >= 0; i--) { + if (ctrls->controls[i].id == mapping->master_id) + return ctrls->controls[i].value == + mapping->master_manual ? 0 : -EACCES; + } + + __uvc_find_control(ctrl->entity, mapping->master_id, &master_map, + &master_ctrl, 0); + + if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) + return 0; + + ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); + if (ret >= 0 && val != mapping->master_manual) + return -EACCES; + return 0; } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index dcd178d249b6..4187e59680bb 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -1018,8 +1018,7 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain, int ret = 0; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_is_accessible(chain, ctrl->id, - ioctl == VIDIOC_G_EXT_CTRLS); + ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl); if (ret) break; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index cd5861cae3b0..8c0df94374c9 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -778,7 +778,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle) int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl); int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, - bool read); + const struct v4l2_ext_controls *ctrls, + unsigned long ioctl); int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); From 256eb290e85d3d1d988b30c05e47f74845b3e34c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:22 -0300 Subject: [PATCH 022/142] media: uvcvideo: Improve error logging in uvc_query_ctrl() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=67a655be5748426a9bda7845fc264ccf71a76ea3 commit 67a655be5748426a9bda7845fc264ccf71a76ea3 Author: Hans Verkuil Date: Tue, 3 Jan 2023 15:36:20 +0100 Standard use of the driver may result in error messages on the kernel log. This can hide other more important messages, and alert unnecessarily the user. Let's keep dev_err() for the important occasions. If __uvc_query_ctrl() failed with a non -EPIPE error, then report that with dev_err. If an error code is obtained, then report that with dev_dbg. Reviewed-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 503ef29211ca..f35674fb8912 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -79,13 +79,14 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (likely(ret == size)) return 0; - dev_err(&dev->udev->dev, - "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", - uvc_query_name(query), cs, unit, ret, size); - - if (ret != -EPIPE) + if (ret != -EPIPE) { + dev_err(&dev->udev->dev, + "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", + uvc_query_name(query), cs, unit, ret, size); return ret; + } + /* Reuse data[0] to request the error code. */ tmp = *(u8 *)data; ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum, From 6ba6ea91c4e2043f5713ba2c885045be9647a9b5 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:23 -0300 Subject: [PATCH 023/142] media: uvcvideo: Return -EACCES for Wrong state error Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=44cdba4130ecdb59ff94c617b6cd8dc22b4f3c97 commit 44cdba4130ecdb59ff94c617b6cd8dc22b4f3c97 Author: Ricardo Ribalda Date: Tue, 3 Jan 2023 15:36:21 +0100 Error 2 is defined by UVC as: Wrong State: The device is in a state that disallows the specific request. The device will remain in this state until a specific action from the host or the user is completed. This is documented as happening when attempting to set the value of a manual control when the device is in auto mode. While V4L2 allows this, the closest error code defined by VIDIOC_S_CTRL is EACCES: EACCES: Attempt to set a read-only control or to get a write-only control. Or if there is an attempt to set an inactive control and the driver is not capable of caching the new value until the control is active again. Replace EILSEQ with EACCES. Reviewed-by: Laurent Pinchart Suggested-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f35674fb8912..3d3753a4c1f8 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -108,7 +108,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, case 1: /* Not ready */ return -EBUSY; case 2: /* Wrong state */ - return -EILSEQ; + return -EACCES; case 3: /* Power */ return -EREMOTE; case 4: /* Out of range */ From 834a22a184925046af66e8119d8d051003d85644 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:23 -0300 Subject: [PATCH 024/142] media: uvcvideo: Do not return positive errors in uvc_query_ctrl() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a763b9fb58be869e252a7d33acb0a6390b01c801 commit a763b9fb58be869e252a7d33acb0a6390b01c801 Author: Ricardo Ribalda Date: Tue, 3 Jan 2023 15:36:22 +0100 If the returned size of the query does not match the expected size or it is zero, return -EPIPE instead of 0 or a positive value. This will avoid confusing the caller (and ultimately userspace) that doesn't expect a positive or zero value. Reviewed-by: Laurent Pinchart Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 3d3753a4c1f8..e4b71df08216 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -83,7 +83,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, dev_err(&dev->udev->dev, "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, unit, ret, size); - return ret; + return ret < 0 ? ret : -EPIPE; } /* Reuse data[0] to request the error code. */ From 9f41bf6b7c19fd9148befdd03ad9492e255cbe52 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:24 -0300 Subject: [PATCH 025/142] media: uvcvideo: Fix handling on Bitmask controls Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7faf8ae4277156da32eada72c07f5edeb82cd9e4 commit 7faf8ae4277156da32eada72c07f5edeb82cd9e4 Author: Ricardo Ribalda Date: Tue, 3 Jan 2023 15:36:23 +0100 Minimum and step values for V4L2_CTRL_TYPE_BITMASK controls should be 0. There is no need to query the camera firmware about this and maybe get invalid results. Also value should be masked to the max value advertised by the hardware. Finally, handle UVC 1.5 mask controls that use MAX instead of RES to describe the valid bits. Fixes v4l2-compliane: Control ioctls (Input 0): fail: v4l2-test-controls.cpp(97): minimum must be 0 for a bitmask control test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: FAIL Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 52 ++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 6f5aaaf09ee0..65d41946ef1a 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1161,6 +1161,25 @@ static const char *uvc_map_get_name(const struct uvc_control_mapping *map) return "Unknown Control"; } +static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl, + struct uvc_control_mapping *mapping) +{ + /* + * Some controls, like CT_AE_MODE_CONTROL, use GET_RES to represent + * the number of bits supported. Those controls do not list GET_MAX + * as supported. + */ + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) + return mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) + return mapping->get(mapping, UVC_GET_MAX, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + + return ~0; +} + static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl, struct uvc_control_mapping *mapping, @@ -1235,6 +1254,12 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->step = 0; return 0; + case V4L2_CTRL_TYPE_BITMASK: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = uvc_get_ctrl_bitmap(ctrl, mapping); + v4l2_ctrl->step = 0; + return 0; + default: break; } @@ -1336,19 +1361,14 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, menu_info = &mapping->menu_info[query_menu->index]; - if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && - (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { - s32 bitmap; - + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) { if (!ctrl->cached) { ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) goto done; } - bitmap = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - if (!(bitmap & menu_info->value)) { + if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & menu_info->value)) { ret = -EINVAL; goto done; } @@ -1831,6 +1851,17 @@ int uvc_ctrl_set(struct uvc_fh *handle, value = xctrl->value; break; + case V4L2_CTRL_TYPE_BITMASK: + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + xctrl->value &= uvc_get_ctrl_bitmap(ctrl, mapping); + value = xctrl->value; + break; + case V4L2_CTRL_TYPE_BOOLEAN: xctrl->value = clamp(xctrl->value, 0, 1); value = xctrl->value; @@ -1845,17 +1876,14 @@ int uvc_ctrl_set(struct uvc_fh *handle, * Valid menu indices are reported by the GET_RES request for * UVC controls that support it. */ - if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && - (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) { + if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) { if (!ctrl->cached) { ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) return ret; } - step = mapping->get(mapping, UVC_GET_RES, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); - if (!(step & value)) + if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & value)) return -EINVAL; } From 6ed1b6491d5cd474e0a79ffb208451b061c63597 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:24 -0300 Subject: [PATCH 026/142] media: uvcvideo: Refactor __uvc_ctrl_add_mapping Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=252d50da337ba97019574b1e830879d5dd5121d2 commit 252d50da337ba97019574b1e830879d5dd5121d2 Author: Ricardo Ribalda Date: Tue, 3 Jan 2023 15:36:25 +0100 Simplify the exit code with a common error tag freeing all the memory. Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 65d41946ef1a..ffa0e2654264 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2286,32 +2286,30 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, unsigned int i; /* - * Most mappings come from static kernel data and need to be duplicated. + * Most mappings come from static kernel data, and need to be duplicated. * Mappings that come from userspace will be unnecessarily duplicated, * this could be optimized. */ map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL); - if (map == NULL) + if (!map) return -ENOMEM; + map->name = NULL; + map->menu_info = NULL; + /* For UVCIOC_CTRL_MAP custom control */ if (mapping->name) { map->name = kstrdup(mapping->name, GFP_KERNEL); - if (!map->name) { - kfree(map); - return -ENOMEM; - } + if (!map->name) + goto err_nomem; } INIT_LIST_HEAD(&map->ev_subs); size = sizeof(*mapping->menu_info) * mapping->menu_count; map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); - if (map->menu_info == NULL) { - kfree(map->name); - kfree(map); - return -ENOMEM; - } + if (!map->menu_info) + goto err_nomem; if (map->get == NULL) map->get = uvc_get_le_value; @@ -2332,6 +2330,12 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, ctrl->info.selector); return 0; + +err_nomem: + kfree(map->menu_info); + kfree(map->name); + kfree(map); + return -ENOMEM; } int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, From 8fdc17431ab0851d526716945da2a916b3a718cc Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:25 -0300 Subject: [PATCH 027/142] media: uvcvideo: Extend documentation of uvc_video_clock_decode() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=70fcaf92a39e6cb7542de0fc0ad4562f42f7f54a commit 70fcaf92a39e6cb7542de0fc0ad4562f42f7f54a Author: Ricardo Ribalda Date: Wed, 4 Jan 2023 11:45:19 +0100 Make an explicit reference to UVC 1.5, explaining how the algorithm supports the different behaviour of UVC 1.1 and 1.5. Reviewed-by: Laurent Pinchart Tested-by: HungNien Chen Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index e4b71df08216..b1f7416858b7 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -518,7 +518,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, /* * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. */ dev_sof = get_unaligned_le16(&data[header_size - 2]); if (dev_sof == stream->clock.last_sof) From e6124c08a3b4001c1a818d53b0c0a83505c58eaf Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:25 -0300 Subject: [PATCH 028/142] media: uvcvideo: Implement mask for V4L2_CTRL_TYPE_MENU Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=40140eda661ea4be219ef194a4f43b7b5e3bbd27 commit 40140eda661ea4be219ef194a4f43b7b5e3bbd27 Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 14:52:54 +0100 Replace the count with a mask field that lets us choose not only the max value, but also the minimum value and what values are valid in between. Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 33 +++++++++++++++++++++--------- drivers/media/usb/uvc/uvc_driver.c | 4 +++- drivers/media/usb/uvc/uvc_v4l2.c | 3 ++- drivers/media/usb/uvc/uvcvideo.h | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index ffa0e2654264..305cce26f567 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -6,6 +6,7 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include #include #include #include @@ -525,7 +526,8 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_BITMASK, .menu_info = exposure_auto_controls, - .menu_count = ARRAY_SIZE(exposure_auto_controls), + .menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY, + V4L2_EXPOSURE_AUTO), .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, }, { @@ -731,7 +733,8 @@ static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = { .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, .menu_info = power_line_frequency_controls, - .menu_count = ARRAY_SIZE(power_line_frequency_controls) - 1, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), }, }; @@ -745,7 +748,8 @@ static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = { .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, .menu_info = power_line_frequency_controls, - .menu_count = ARRAY_SIZE(power_line_frequency_controls), + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), }, }; @@ -975,7 +979,9 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, const struct uvc_menu_info *menu = mapping->menu_info; unsigned int i; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { + for (i = 0; BIT(i) <= mapping->menu_mask; ++i, ++menu) { + if (!test_bit(i, &mapping->menu_mask)) + continue; if (menu->value == value) { value = i; break; @@ -1228,12 +1234,14 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, switch (mapping->v4l2_type) { case V4L2_CTRL_TYPE_MENU: - v4l2_ctrl->minimum = 0; - v4l2_ctrl->maximum = mapping->menu_count - 1; + v4l2_ctrl->minimum = ffs(mapping->menu_mask) - 1; + v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1; v4l2_ctrl->step = 1; menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { + for (i = 0; BIT(i) <= mapping->menu_mask; ++i, ++menu) { + if (!test_bit(i, &mapping->menu_mask)) + continue; if (menu->value == v4l2_ctrl->default_value) { v4l2_ctrl->default_value = i; break; @@ -1354,7 +1362,7 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, goto done; } - if (query_menu->index >= mapping->menu_count) { + if (!test_bit(query_menu->index, &mapping->menu_mask)) { ret = -EINVAL; goto done; } @@ -1868,8 +1876,13 @@ int uvc_ctrl_set(struct uvc_fh *handle, break; case V4L2_CTRL_TYPE_MENU: - if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) + if (xctrl->value < (ffs(mapping->menu_mask) - 1) || + xctrl->value > (fls(mapping->menu_mask) - 1)) return -ERANGE; + + if (!test_bit(xctrl->value, &mapping->menu_mask)) + return -EINVAL; + value = mapping->menu_info[xctrl->value].value; /* @@ -2306,7 +2319,7 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, INIT_LIST_HEAD(&map->ev_subs); - size = sizeof(*mapping->menu_info) * mapping->menu_count; + size = sizeof(*mapping->menu_info) * fls(mapping->menu_mask); map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); if (!map->menu_info) goto err_nomem; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 5f6e0da0ef90..cadd154df9cb 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -2371,7 +2372,8 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, .menu_info = power_line_frequency_controls_limited, - .menu_count = ARRAY_SIZE(power_line_frequency_controls_limited), + .menu_mask = + GENMASK(ARRAY_SIZE(power_line_frequency_controls_limited) - 1, 0), }; static const struct uvc_device_info uvc_ctrl_power_line_limited = { diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 4187e59680bb..950b42d78a10 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -6,6 +6,7 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include #include #include #include @@ -80,7 +81,7 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, goto free_map; } - map->menu_count = xmap->menu_count; + map->menu_mask = GENMASK(xmap->menu_count - 1, 0); break; default: diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 8c0df94374c9..e8f277e380a5 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -115,7 +115,7 @@ struct uvc_control_mapping { u32 data_type; const struct uvc_menu_info *menu_info; - u32 menu_count; + unsigned long menu_mask; u32 master_id; s32 master_manual; From d2275d1b4b11be16a7ba7bfab37da930ad439cd1 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:26 -0300 Subject: [PATCH 029/142] media: uvcvideo: Refactor uvc_ctrl_mappings_uvcXX Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=96a160b068e09b4ed5a32e2179e5219fc3545eca commit 96a160b068e09b4ed5a32e2179e5219fc3545eca Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 14:52:55 +0100 Convert the array of structs into an array of pointers, that way the mappings can be reused. Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 74 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 305cce26f567..af765d4cae90 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -723,34 +723,40 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { }, }; -static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, - .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), - }, +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), }; -static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = { - { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, - .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), - }, +static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = { + &uvc_ctrl_power_line_mapping_uvc11, + NULL, /* Sentinel */ +}; + +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), +}; + +static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = { + &uvc_ctrl_power_line_mapping_uvc15, + NULL, /* Sentinel */ }; /* ------------------------------------------------------------------------ @@ -2506,8 +2512,7 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl) { - const struct uvc_control_mapping *mappings; - unsigned int num_mappings; + const struct uvc_control_mapping **mappings; unsigned int i; /* @@ -2574,16 +2579,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, } /* Finally process version-specific mappings. */ - if (chain->dev->uvc_version < 0x0150) { - mappings = uvc_ctrl_mappings_uvc11; - num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11); - } else { - mappings = uvc_ctrl_mappings_uvc15; - num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15); - } + mappings = chain->dev->uvc_version < 0x0150 + ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15; - for (i = 0; i < num_mappings; ++i) { - const struct uvc_control_mapping *mapping = &mappings[i]; + for (i = 0; mappings[i]; ++i) { + const struct uvc_control_mapping *mapping = mappings[i]; if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) From 429fb521197e49377acb3c178165f3cca7bcaa5a Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:26 -0300 Subject: [PATCH 030/142] media: uvcvideo: Refactor power_line_frequency_controls_limited Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3aa8628eb78a63d0bf93e159846e9c92bccc8f69 commit 3aa8628eb78a63d0bf93e159846e9c92bccc8f69 Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 14:52:56 +0100 Move the control mapping to uvc_ctrl.c. This way we do not have references to UVC controls or V4L2 controls in uvc_driver.c. This also fixes a bug introduced in commit 382075604a68 ("media: uvcvideo: Limit power line control for Quanta UVC Webcam"). The offending commit caused the power line control menu entries to have incorrect indices compared to the V4L2_CID_POWER_LINE_FREQUENCY_* enumeration. Now that the limited mapping reuses the correct menu_info array, the indices correctly map to the V4L2 control specification. Fixes: 382075604a68 ("media: uvcvideo: Limit power line control for Quanta UVC Webcam") Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 13 +++++++++++++ drivers/media/usb/uvc/uvc_driver.c | 18 ------------------ drivers/media/usb/uvc/uvcvideo.h | 1 + 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index af765d4cae90..c15c38427cc3 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -723,6 +723,19 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { }, }; +const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ), +}; + static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .entity = UVC_GUID_UVC_PROCESSING, diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index cadd154df9cb..17547aebc15a 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2358,24 +2358,6 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); * Driver initialization and cleanup */ -static const struct uvc_menu_info power_line_frequency_controls_limited[] = { - { 1, "50 Hz" }, - { 2, "60 Hz" }, -}; - -static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls_limited, - .menu_mask = - GENMASK(ARRAY_SIZE(power_line_frequency_controls_limited) - 1, 0), -}; - static const struct uvc_device_info uvc_ctrl_power_line_limited = { .mappings = (const struct uvc_control_mapping *[]) { &uvc_ctrl_power_line_mapping_limited, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index e8f277e380a5..6573df1e0a47 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -745,6 +745,7 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags); void uvc_status_stop(struct uvc_device *dev); /* Controls */ +extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited; extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, From 2337781afba4d8049b6ff0459dc37a165de279db Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:27 -0300 Subject: [PATCH 031/142] media: uvcvideo: Fix power line control for Lenovo Integrated Camera Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7c28150af42798263a30bedbe46f201b46fb486 commit a7c28150af42798263a30bedbe46f201b46fb486 Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 14:52:57 +0100 The device does not implement the power line frequenyc control correctly. It is a UVC 1.5 device, but implements the control as a UVC 1.1 device. Add the corresponding control mapping override. Bus 003 Device 002: ID 30c9:0093 Lenovo Integrated Camera Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.01 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x30c9 idProduct 0x0093 bcdDevice 0.07 iManufacturer 3 Lenovo iProduct 1 Integrated Camera iSerial 2 8SSC21J75356V1SR2830069 bNumConfigurations 1 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 2 +- drivers/media/usb/uvc/uvc_driver.c | 16 ++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index c15c38427cc3..6354efffe9b0 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -736,7 +736,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { V4L2_CID_POWER_LINE_FREQUENCY_50HZ), }; -static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { +const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 17547aebc15a..9f7acd9bb61d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2365,6 +2365,13 @@ static const struct uvc_device_info uvc_ctrl_power_line_limited = { }, }; +static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = { + .mappings = (const struct uvc_control_mapping *[]) { + &uvc_ctrl_power_line_mapping_uvc11, + NULL, /* Sentinel */ + }, +}; + static const struct uvc_device_info uvc_quirk_probe_minmax = { .quirks = UVC_QUIRK_PROBE_MINMAX, }; @@ -2944,6 +2951,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, + /* Lenovo Integrated Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x30c9, + .idProduct = 0x0093, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = UVC_PC_PROTOCOL_15, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, /* Sonix Technology USB 2.0 Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6573df1e0a47..178729467967 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -746,6 +746,7 @@ void uvc_status_stop(struct uvc_device *dev); /* Controls */ extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited; +extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11; extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, From d52867f77f67413894fb51b132b124c01154b4bd Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:28 -0300 Subject: [PATCH 032/142] media: uvcvideo: Use standard names for menus Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=716c330433e3ad6074d057092b98c09a989e17d8 commit 716c330433e3ad6074d057092b98c09a989e17d8 Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 14:52:58 +0100 Instead of duplicating the menu info, use the one from the core. Also, do not use extra memory for 1:1 mappings. Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 129 ++++++++++++++++++++++--------- drivers/media/usb/uvc/uvc_v4l2.c | 105 ++++++++++++++++++------- drivers/media/usb/uvc/uvcvideo.h | 3 +- include/uapi/linux/uvcvideo.h | 4 +- 4 files changed, 176 insertions(+), 65 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 6354efffe9b0..ee58f0db2763 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -364,19 +364,45 @@ static const u32 uvc_control_classes[] = { V4L2_CID_USER_CLASS, }; -static const struct uvc_menu_info power_line_frequency_controls[] = { - { 0, "Disabled" }, - { 1, "50 Hz" }, - { 2, "60 Hz" }, - { 3, "Auto" }, -}; +static const int exposure_auto_mapping[] = { 2, 1, 4, 8 }; -static const struct uvc_menu_info exposure_auto_controls[] = { - { 2, "Auto Mode" }, - { 1, "Manual Mode" }, - { 4, "Shutter Priority Mode" }, - { 8, "Aperture Priority Mode" }, -}; +/* + * This function translates the V4L2 menu index @idx, as exposed to userspace as + * the V4L2 control value, to the corresponding UVC control value used by the + * device. The custom menu_mapping in the control @mapping is used when + * available, otherwise the function assumes that the V4L2 and UVC values are + * identical. + * + * For controls of type UVC_CTRL_DATA_TYPE_BITMASK, the UVC control value is + * expressed as a bitmask and is thus guaranteed to have a single bit set. + * + * The function returns -EINVAL if the V4L2 menu index @idx isn't valid for the + * control, which includes all controls whose type isn't UVC_CTRL_DATA_TYPE_ENUM + * or UVC_CTRL_DATA_TYPE_BITMASK. + */ +static int uvc_mapping_get_menu_value(const struct uvc_control_mapping *mapping, + u32 idx) +{ + if (!test_bit(idx, &mapping->menu_mask)) + return -EINVAL; + + if (mapping->menu_mapping) + return mapping->menu_mapping[idx]; + + return idx; +} + +static const char * +uvc_mapping_get_menu_name(const struct uvc_control_mapping *mapping, u32 idx) +{ + if (!test_bit(idx, &mapping->menu_mask)) + return NULL; + + if (mapping->menu_names) + return mapping->menu_names[idx]; + + return v4l2_ctrl_get_menu(mapping->id)[idx]; +} static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, u8 query, const u8 *data) @@ -525,7 +551,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_BITMASK, - .menu_info = exposure_auto_controls, + .menu_mapping = exposure_auto_mapping, .menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY, V4L2_EXPOSURE_AUTO), .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, @@ -731,7 +757,6 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, V4L2_CID_POWER_LINE_FREQUENCY_50HZ), }; @@ -744,7 +769,6 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), }; @@ -762,7 +786,6 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), }; @@ -995,13 +1018,17 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, s32 value = mapping->get(mapping, UVC_GET_CUR, data); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - const struct uvc_menu_info *menu = mapping->menu_info; unsigned int i; - for (i = 0; BIT(i) <= mapping->menu_mask; ++i, ++menu) { + for (i = 0; BIT(i) <= mapping->menu_mask; ++i) { + u32 menu_value; + if (!test_bit(i, &mapping->menu_mask)) continue; - if (menu->value == value) { + + menu_value = uvc_mapping_get_menu_value(mapping, i); + + if (menu_value == value) { value = i; break; } @@ -1212,7 +1239,6 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, { struct uvc_control_mapping *master_map = NULL; struct uvc_control *master_ctrl = NULL; - const struct uvc_menu_info *menu; unsigned int i; memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl)); @@ -1257,11 +1283,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1; v4l2_ctrl->step = 1; - menu = mapping->menu_info; - for (i = 0; BIT(i) <= mapping->menu_mask; ++i, ++menu) { + for (i = 0; BIT(i) <= mapping->menu_mask; ++i) { + u32 menu_value; + if (!test_bit(i, &mapping->menu_mask)) continue; - if (menu->value == v4l2_ctrl->default_value) { + + menu_value = uvc_mapping_get_menu_value(mapping, i); + + if (menu_value == v4l2_ctrl->default_value) { v4l2_ctrl->default_value = i; break; } @@ -1360,11 +1390,11 @@ done: int uvc_query_v4l2_menu(struct uvc_video_chain *chain, struct v4l2_querymenu *query_menu) { - const struct uvc_menu_info *menu_info; struct uvc_control_mapping *mapping; struct uvc_control *ctrl; u32 index = query_menu->index; u32 id = query_menu->id; + const char *name; int ret; memset(query_menu, 0, sizeof(*query_menu)); @@ -1386,22 +1416,34 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, goto done; } - menu_info = &mapping->menu_info[query_menu->index]; - if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) { + int mask; + if (!ctrl->cached) { ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) goto done; } - if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & menu_info->value)) { + mask = uvc_mapping_get_menu_value(mapping, query_menu->index); + if (mask < 0) { + ret = mask; + goto done; + } + + if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & mask)) { ret = -EINVAL; goto done; } } - strscpy(query_menu->name, menu_info->name, sizeof(query_menu->name)); + name = uvc_mapping_get_menu_name(mapping, query_menu->index); + if (!name) { + ret = -EINVAL; + goto done; + } + + strscpy(query_menu->name, name, sizeof(query_menu->name)); done: mutex_unlock(&chain->ctrl_mutex); @@ -1902,7 +1944,7 @@ int uvc_ctrl_set(struct uvc_fh *handle, if (!test_bit(xctrl->value, &mapping->menu_mask)) return -EINVAL; - value = mapping->menu_info[xctrl->value].value; + value = uvc_mapping_get_menu_value(mapping, xctrl->value); /* * Valid menu indices are reported by the GET_RES request for @@ -2327,7 +2369,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, return -ENOMEM; map->name = NULL; - map->menu_info = NULL; + map->menu_names = NULL; + map->menu_mapping = NULL; /* For UVCIOC_CTRL_MAP custom control */ if (mapping->name) { @@ -2338,10 +2381,22 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, INIT_LIST_HEAD(&map->ev_subs); - size = sizeof(*mapping->menu_info) * fls(mapping->menu_mask); - map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); - if (!map->menu_info) - goto err_nomem; + if (mapping->menu_mapping && mapping->menu_mask) { + size = sizeof(mapping->menu_mapping[0]) + * fls(mapping->menu_mask); + map->menu_mapping = kmemdup(mapping->menu_mapping, size, + GFP_KERNEL); + if (!map->menu_mapping) + goto err_nomem; + } + if (mapping->menu_names && mapping->menu_mask) { + size = sizeof(mapping->menu_names[0]) + * fls(mapping->menu_mask); + map->menu_names = kmemdup(mapping->menu_names, size, + GFP_KERNEL); + if (!map->menu_names) + goto err_nomem; + } if (map->get == NULL) map->get = uvc_get_le_value; @@ -2364,7 +2419,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, return 0; err_nomem: - kfree(map->menu_info); + kfree(map->menu_names); + kfree(map->menu_mapping); kfree(map->name); kfree(map); return -ENOMEM; @@ -2689,7 +2745,8 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev, list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) { list_del(&mapping->list); - kfree(mapping->menu_info); + kfree(mapping->menu_names); + kfree(mapping->menu_mapping); kfree(mapping->name); kfree(mapping); } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 950b42d78a10..35453f81c1d9 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -26,14 +26,84 @@ #include "uvcvideo.h" +static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain, + struct uvc_control_mapping *map, + const struct uvc_xu_control_mapping *xmap) +{ + unsigned int i; + size_t size; + int ret; + + /* + * Prevent excessive memory consumption, as well as integer + * overflows. + */ + if (xmap->menu_count == 0 || + xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) + return -EINVAL; + + map->menu_names = NULL; + map->menu_mapping = NULL; + + map->menu_mask = BIT_MASK(xmap->menu_count); + + size = xmap->menu_count * sizeof(*map->menu_mapping); + map->menu_mapping = kzalloc(size, GFP_KERNEL); + if (!map->menu_mapping) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < xmap->menu_count ; i++) { + if (copy_from_user((u32 *)&map->menu_mapping[i], + &xmap->menu_info[i].value, + sizeof(map->menu_mapping[i]))) { + ret = -EACCES; + goto done; + } + } + + /* + * Always use the standard naming if available, otherwise copy the + * names supplied by userspace. + */ + if (!v4l2_ctrl_get_menu(map->id)) { + size = xmap->menu_count * sizeof(map->menu_names[0]); + map->menu_names = kzalloc(size, GFP_KERNEL); + if (!map->menu_names) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < xmap->menu_count ; i++) { + /* sizeof(names[i]) - 1: to take care of \0 */ + if (copy_from_user((char *)map->menu_names[i], + xmap->menu_info[i].name, + sizeof(map->menu_names[i]) - 1)) { + ret = -EACCES; + goto done; + } + } + } + + ret = uvc_ctrl_add_mapping(chain, map); + +done: + kfree(map->menu_names); + map->menu_names = NULL; + kfree(map->menu_mapping); + map->menu_mapping = NULL; + + return ret; +} + /* ------------------------------------------------------------------------ * UVC ioctls */ -static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, - struct uvc_xu_control_mapping *xmap) +static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain, + struct uvc_xu_control_mapping *xmap) { struct uvc_control_mapping *map; - unsigned int size; int ret; map = kzalloc(sizeof(*map), GFP_KERNEL); @@ -61,39 +131,20 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_BUTTON: + ret = uvc_ctrl_add_mapping(chain, map); break; case V4L2_CTRL_TYPE_MENU: - /* - * Prevent excessive memory consumption, as well as integer - * overflows. - */ - if (xmap->menu_count == 0 || - xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { - ret = -EINVAL; - goto free_map; - } - - size = xmap->menu_count * sizeof(*map->menu_info); - map->menu_info = memdup_user(xmap->menu_info, size); - if (IS_ERR(map->menu_info)) { - ret = PTR_ERR(map->menu_info); - goto free_map; - } - - map->menu_mask = GENMASK(xmap->menu_count - 1, 0); + ret = uvc_control_add_xu_mapping(chain, map, xmap); break; default: uvc_dbg(chain->dev, CONTROL, "Unsupported V4L2 control type %u\n", xmap->v4l2_type); ret = -ENOTTY; - goto free_map; + break; } - ret = uvc_ctrl_add_mapping(chain, map); - - kfree(map->menu_info); free_map: kfree(map); @@ -1314,7 +1365,7 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio, switch (cmd) { /* Dynamic controls. */ case UVCIOC_CTRL_MAP: - return uvc_ioctl_ctrl_map(chain, arg); + return uvc_ioctl_xu_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: return uvc_xu_ctrl_query(chain, arg); @@ -1427,7 +1478,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); if (ret) return ret; - ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap); + ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap); if (ret) return ret; ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 178729467967..e85df8deb965 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -114,7 +114,8 @@ struct uvc_control_mapping { enum v4l2_ctrl_type v4l2_type; u32 data_type; - const struct uvc_menu_info *menu_info; + const u32 *menu_mapping; + const char (*menu_names)[UVC_MENU_NAME_LEN]; unsigned long menu_mask; u32 master_id; diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index 8288137387c0..d45d0c2ea252 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -36,9 +36,11 @@ UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \ UVC_CTRL_FLAG_GET_DEF) +#define UVC_MENU_NAME_LEN 32 + struct uvc_menu_info { __u32 value; - __u8 name[32]; + __u8 name[UVC_MENU_NAME_LEN]; }; struct uvc_xu_control_mapping { From d6a5054ffe5f505efeceab04364447e27aaf236c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:28 -0300 Subject: [PATCH 033/142] media: uvcvideo: Fix race condition with usb_kill_urb Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=619d9b710cf06f7a00a17120ca92333684ac45a8 commit 619d9b710cf06f7a00a17120ca92333684ac45a8 Author: Ricardo Ribalda Date: Thu, 5 Jan 2023 15:31:29 +0100 usb_kill_urb warranties that all the handlers are finished when it returns, but does not protect against threads that might be handling asynchronously the urb. For UVC, the function uvc_ctrl_status_event_async() takes care of control changes asynchronously. If the code is executed in the following order: CPU 0 CPU 1 ===== ===== uvc_status_complete() uvc_status_stop() uvc_ctrl_status_event_work() uvc_status_start() -> FAIL Then uvc_status_start will keep failing and this error will be shown: <4>[ 5.540139] URB 0000000000000000 submitted while active drivers/usb/core/urb.c:378 usb_submit_urb+0x4c3/0x528 Let's improve the current situation, by not re-submiting the urb if we are stopping the status event. Also process the queued work (if any) during stop. CPU 0 CPU 1 ===== ===== uvc_status_complete() uvc_status_stop() uvc_status_start() uvc_ctrl_status_event_work() -> FAIL Hopefully, with the usb layer protection this should be enough to cover all the cases. Cc: stable@vger.kernel.org Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives") Reviewed-by: Yunke Cao Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 5 ++++ drivers/media/usb/uvc/uvc_status.c | 37 ++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 3 files changed, 43 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index ee58f0db2763..c3aeba3fe31b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -6,6 +6,7 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include #include #include #include @@ -1571,6 +1572,10 @@ static void uvc_ctrl_status_event_work(struct work_struct *work) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); + /* The barrier is needed to synchronize with uvc_status_stop(). */ + if (smp_load_acquire(&dev->flush_status)) + return; + /* Resubmit the URB. */ w->urb->interval = dev->int_ep->desc.bInterval; ret = usb_submit_urb(w->urb, GFP_KERNEL); diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 602830a8023e..a78a88c710e2 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -6,6 +6,7 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ +#include #include #include #include @@ -311,5 +312,41 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags) void uvc_status_stop(struct uvc_device *dev) { + struct uvc_ctrl_work *w = &dev->async_ctrl; + + /* + * Prevent the asynchronous control handler from requeing the URB. The + * barrier is needed so the flush_status change is visible to other + * CPUs running the asynchronous handler before usb_kill_urb() is + * called below. + */ + smp_store_release(&dev->flush_status, true); + + /* + * Cancel any pending asynchronous work. If any status event was queued, + * process it synchronously. + */ + if (cancel_work_sync(&w->work)) + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); + + /* Kill the urb. */ usb_kill_urb(dev->int_urb); + + /* + * The URB completion handler may have queued asynchronous work. This + * won't resubmit the URB as flush_status is set, but it needs to be + * cancelled before returning or it could then race with a future + * uvc_status_start() call. + */ + if (cancel_work_sync(&w->work)) + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); + + /* + * From this point, there are no events on the queue and the status URB + * is dead. No events will be queued until uvc_status_start() is called. + * The barrier is needed to make sure that flush_status is visible to + * uvc_ctrl_status_event_work() when uvc_status_start() will be called + * again. + */ + smp_store_release(&dev->flush_status, false); } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index e85df8deb965..c0e706fcd2cb 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -577,6 +577,7 @@ struct uvc_device { struct usb_host_endpoint *int_ep; struct urb *int_urb; struct uvc_status *status; + bool flush_status; struct input_dev *input; char input_phys[64]; From 61124174f656fae25f30460b06b81d17ebccbd67 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:29 -0300 Subject: [PATCH 034/142] media: uvcvideo: Quirk for autosuspend in Logitech B910 and C910 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=136effa754b57632f99574fc4a3433e0cfc031d9 commit 136effa754b57632f99574fc4a3433e0cfc031d9 Author: Ricardo Ribalda Date: Wed, 4 Jan 2023 11:45:23 +0100 Logitech B910 and C910 firmware are unable to recover from a USB autosuspend. When it resumes, the device is in a state where it only produces invalid frames. Eg: $ echo 0xFFFF > /sys/module/uvcvideo/parameters/trace # enable verbose log $ yavta -c1 -n1 --file='frame#.jpg' --format MJPEG --size=1920x1080 /dev/video1 [350438.435219] uvcvideo: uvc_v4l2_open [350438.529794] uvcvideo: Resuming interface 2 [350438.529801] uvcvideo: Resuming interface 3 [350438.529991] uvcvideo: Trying format 0x47504a4d (MJPG): 1920x1080. [350438.529996] uvcvideo: Using default frame interval 33333.3 us (30.0 fps). [350438.551496] uvcvideo: uvc_v4l2_mmap [350438.555890] uvcvideo: Device requested 3060 B/frame bandwidth. [350438.555896] uvcvideo: Selecting alternate setting 11 (3060 B/frame bandwidth). [350438.556362] uvcvideo: Allocated 5 URB buffers of 32x3060 bytes each. [350439.316468] uvcvideo: Marking buffer as bad (error bit set). [350439.316475] uvcvideo: Frame complete (EOF found). [350439.316477] uvcvideo: EOF in empty payload. [350439.316484] uvcvideo: frame 1 stats: 149/261/417 packets, 1/149/417 pts (early initial), 416/417 scr, last pts/stc/sof 2976325734/2978107243/249 [350439.384510] uvcvideo: Marking buffer as bad (error bit set). [350439.384516] uvcvideo: Frame complete (EOF found). [350439.384518] uvcvideo: EOF in empty payload. [350439.384525] uvcvideo: frame 2 stats: 265/379/533 packets, 1/265/533 pts (early initial), 532/533 scr, last pts/stc/sof 2979524454/2981305193/316 [350439.448472] uvcvideo: Marking buffer as bad (error bit set). [350439.448478] uvcvideo: Frame complete (EOF found). [350439.448480] uvcvideo: EOF in empty payload. [350439.448487] uvcvideo: frame 3 stats: 265/377/533 packets, 1/265/533 pts (early initial), 532/533 scr, last pts/stc/sof 2982723174/2984503144/382 ...(loop)... The devices can leave this invalid state if the alternate setting of the streaming interface is toggled. This patch adds a quirk for this device so it can be autosuspended properly. lsusb -v: Bus 001 Device 049: ID 046d:0821 Logitech, Inc. HD Webcam C910 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x046d Logitech, Inc. idProduct 0x0821 HD Webcam C910 bcdDevice 0.10 iManufacturer 0 iProduct 0 iSerial 1 390022B0 bNumConfigurations 1 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 18 ++++++++++++++++++ drivers/media/usb/uvc/uvc_video.c | 11 +++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 3 files changed, 30 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 9f7acd9bb61d..b9f3787fdbab 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2474,6 +2474,24 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Logitech, Webcam C910 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x0821, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, + /* Logitech, Webcam B910 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x0823, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, /* Logitech Quickcam Fusion */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index b1f7416858b7..98d34d20afe3 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1969,6 +1969,17 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, "Selecting alternate setting %u (%u B/frame bandwidth)\n", altsetting, best_psize); + /* + * Some devices, namely the Logitech C910 and B910, are unable + * to recover from a USB autosuspend, unless the alternate + * setting of the streaming interface is toggled. + */ + if (stream->dev->quirks & UVC_QUIRK_WAKE_AUTOSUSPEND) { + usb_set_interface(stream->dev->udev, intfnum, + altsetting); + usb_set_interface(stream->dev->udev, intfnum, 0); + } + ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); if (ret < 0) return ret; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index c0e706fcd2cb..9a596c8d894a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -72,6 +72,7 @@ #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400 #define UVC_QUIRK_FORCE_Y8 0x00000800 #define UVC_QUIRK_FORCE_BPP 0x00001000 +#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 From 97ef3ff914a52ebb91b9f595bd0c9a47af11dc6e Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:29 -0300 Subject: [PATCH 035/142] media: uvcvideo: Silence memcpy() run-time false positive warnings Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b839212988575c701aab4d3d9ca15e44c87e383c commit b839212988575c701aab4d3d9ca15e44c87e383c Author: Kees Cook Date: Thu, 5 Jan 2023 22:17:04 -0800 The memcpy() in uvc_video_decode_meta() intentionally copies across the length and flags members and into the trailing buf flexible array. Split the copy so that the compiler can better reason about (the lack of) buffer overflows here. Avoid the run-time false positive warning: memcpy: detected field-spanning write (size 12) of single field "&meta->length" at drivers/media/usb/uvc/uvc_video.c:1355 (size 1) Additionally fix a typo in the documentation for struct uvc_meta_buf. Reported-by: ionut_n2001@yahoo.com Link: https://bugzilla.kernel.org/show_bug.cgi?id=216810 Signed-off-by: Kees Cook Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_video.c | 4 +++- include/uapi/linux/uvcvideo.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 98d34d20afe3..d4b023d4de7c 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1356,7 +1356,9 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, if (has_scr) memcpy(stream->clock.last_scr, scr, 6); - memcpy(&meta->length, mem, length); + meta->length = mem[0]; + meta->flags = mem[1]; + memcpy(meta->buf, &mem[2], length - 2); meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof); uvc_dbg(stream->dev, FRAME, diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index d45d0c2ea252..f86185456dc5 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -88,7 +88,7 @@ struct uvc_xu_control_query { * struct. The first two fields are added by the driver, they can be used for * clock synchronisation. The rest is an exact copy of a UVC payload header. * Only complete objects with complete buffers are included. Therefore it's - * always sizeof(meta->ts) + sizeof(meta->sof) + meta->length bytes large. + * always sizeof(meta->ns) + sizeof(meta->sof) + meta->length bytes large. */ struct uvc_meta_buf { __u64 ns; From a4b6069fcff9ac847bd910c0a73f0f774db750e5 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:31 -0300 Subject: [PATCH 036/142] thunderbolt: Use decimal port number in control and tunnel logs too Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=49f2b350f330cf600cf0563fa3e55ba1c1799440 commit 49f2b350f330cf600cf0563fa3e55ba1c1799440 Author: Mika Westerberg Date: Thu, 5 May 2022 13:58:04 +0300 Use decimal number instead of hex in port numbers as we have been doing with other logging functions too. This makes the output more consistent. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/ctl.c | 2 +- drivers/thunderbolt/tunnel.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 0c661a706160..25f7868257de 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -754,7 +754,7 @@ int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug) .pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG : TB_CFG_ERROR_PG_HOT_PLUG, }; - tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n", + tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%u\n", unplug ? "un" : "", route, port); return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR); } diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 1fc3c29b24f8..de584d7e3991 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -49,7 +49,7 @@ static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ do { \ struct tb_tunnel *__tunnel = (tunnel); \ - level(__tunnel->tb, "%llx:%x <-> %llx:%x (%s): " fmt, \ + level(__tunnel->tb, "%llx:%u <-> %llx:%u (%s): " fmt, \ tb_route(__tunnel->src_port->sw), \ __tunnel->src_port->port, \ tb_route(__tunnel->dst_port->sw), \ From c39464d7799010819bd5d735236d70fe555a7aae Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:32 -0300 Subject: [PATCH 037/142] thunderbolt: Log DP adapter type Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b0ef48fc95cc2ce042fd5ad85d193e8a57502094 commit b0ef48fc95cc2ce042fd5ad85d193e8a57502094 Author: Mika Westerberg Date: Thu, 2 Jun 2022 12:50:20 +0300 This makes it easier to see from the debug logs what type of DisplayPort adapter is in use or available. No functional changes. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 3f1ab30c4fb1..db9dade7b4b0 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -892,7 +892,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in) continue; if (tb_port_is_enabled(port)) { - tb_port_dbg(port, "in use\n"); + tb_port_dbg(port, "DP OUT in use\n"); continue; } @@ -941,7 +941,7 @@ static void tb_tunnel_dp(struct tb *tb) continue; if (tb_port_is_enabled(port)) { - tb_port_dbg(port, "in use\n"); + tb_port_dbg(port, "DP IN in use\n"); continue; } From 7805d1b1b9b4191a086c58f16e00ea2aef906de4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:33 -0300 Subject: [PATCH 038/142] thunderbolt: Improve debug logging in tb_available_bandwidth() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2426fdf77afb4d78316585531a4069905a5accc7 commit 2426fdf77afb4d78316585531a4069905a5accc7 Author: Mika Westerberg Date: Thu, 5 May 2022 13:59:21 +0300 This makes it easier to see what is going on when bandwidth is being allocated for tunneling. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index db9dade7b4b0..cebbb3660c68 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -350,7 +350,9 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, struct tb_tunnel *tunnel; struct tb_port *port; - tb_port_dbg(dst_port, "calculating available bandwidth\n"); + tb_dbg(tb, "calculating available bandwidth between %llx:%u <-> %llx:%u\n", + tb_route(src_port->sw), src_port->port, tb_route(dst_port->sw), + dst_port->port); tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port); if (tunnel) { @@ -387,7 +389,8 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, up_bw -= up_bw / 10; down_bw = up_bw; - tb_port_dbg(port, "link total bandwidth %d Mb/s\n", up_bw); + tb_port_dbg(port, "link total bandwidth %d/%d Mb/s\n", up_bw, + down_bw); /* * Find all DP tunnels that cross the port and reduce From eac65e4f99c880bbecc7647a128ffdc32d0f6642 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:35 -0300 Subject: [PATCH 039/142] thunderbolt: Take CL states into account when waiting for link to come up Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e70a8f36987da50b9f443173f8800795f70266da commit e70a8f36987da50b9f443173f8800795f70266da Author: Mika Westerberg Date: Wed, 23 Mar 2022 16:13:32 +0200 If CL states are enabled for the link it may be in these states too when reading the lane adapter state but it will enter CL0 as soon as there is traffic in the high-speed lanes. Upon discovery we want to make sure that is accounted as the link being up, otherwise we end up tearing down the topology with no good reason. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/switch.c | 42 +++++++++++++++++++++-------------- drivers/thunderbolt/tb_regs.h | 4 ++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 363d712aa364..e1ad1b437291 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -513,36 +513,44 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) while (retries--) { state = tb_port_state(port); - if (state < 0) - return state; - if (state == TB_PORT_DISABLED) { + switch (state) { + case TB_PORT_DISABLED: tb_port_dbg(port, "is disabled (state: 0)\n"); return 0; - } - if (state == TB_PORT_UNPLUGGED) { + + case TB_PORT_UNPLUGGED: if (wait_if_unplugged) { /* used during resume */ tb_port_dbg(port, "is unplugged (state: 7), retrying...\n"); msleep(100); - continue; + break; } tb_port_dbg(port, "is unplugged (state: 7)\n"); return 0; - } - if (state == TB_PORT_UP) { - tb_port_dbg(port, "is connected, link is up (state: 2)\n"); + + case TB_PORT_UP: + case TB_PORT_TX_CL0S: + case TB_PORT_RX_CL0S: + case TB_PORT_CL1: + case TB_PORT_CL2: + tb_port_dbg(port, "is connected, link is up (state: %d)\n", state); return 1; + + default: + if (state < 0) + return state; + + /* + * After plug-in the state is TB_PORT_CONNECTING. Give it some + * time. + */ + tb_port_dbg(port, + "is connected, link is not up (state: %d), retrying...\n", + state); + msleep(100); } - /* - * After plug-in the state is TB_PORT_CONNECTING. Give it some - * time. - */ - tb_port_dbg(port, - "is connected, link is not up (state: %d), retrying...\n", - state); - msleep(100); } tb_port_warn(port, "failed to reach state TB_PORT_UP. Ignoring port...\n"); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 3c38b0cb8f74..f4a194cc0d63 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -50,6 +50,10 @@ enum tb_port_state { TB_PORT_DISABLED = 0, /* tb_cap_phy.disable == 1 */ TB_PORT_CONNECTING = 1, /* retry */ TB_PORT_UP = 2, + TB_PORT_TX_CL0S = 3, + TB_PORT_RX_CL0S = 4, + TB_PORT_CL1 = 5, + TB_PORT_CL2 = 6, TB_PORT_UNPLUGGED = 7, }; From 324d1da05e85119f7348bb8a0b5145c0c4ed109e Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:37 -0300 Subject: [PATCH 040/142] thunderbolt: Add functions to support DisplayPort bandwidth allocation mode Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e327380133d96a7a71baca65a809bf65609a1a69 commit e327380133d96a7a71baca65a809bf65609a1a69 Author: Mika Westerberg Date: Wed, 23 Mar 2022 16:18:28 +0200 USB4 spec defines an additional feature that DP IN adapters can implement (alongside with the graphics DPCD register set) to support more dynamic bandwidth management for DisplayPort tunnels. For the connection manager the communication happens through the DP IN adapter using a set of registers in the adapter config space allocated for this. Add functions that export this functionality for the rest of the driver. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tb.h | 15 + drivers/thunderbolt/tb_regs.h | 32 ++ drivers/thunderbolt/usb4.c | 571 ++++++++++++++++++++++++++++++++++ 3 files changed, 618 insertions(+) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 6c4a26b1c37c..cd6c9eeaf049 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1238,6 +1238,21 @@ int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw, int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, int *downstream_bw); +int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id); +bool usb4_dp_port_bw_mode_supported(struct tb_port *port); +bool usb4_dp_port_bw_mode_enabled(struct tb_port *port); +int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported); +int usb4_dp_port_group_id(struct tb_port *port); +int usb4_dp_port_set_group_id(struct tb_port *port, int group_id); +int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes); +int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes); +int usb4_dp_port_granularity(struct tb_port *port); +int usb4_dp_port_set_granularity(struct tb_port *port, int granularity); +int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw); +int usb4_dp_port_allocated_bw(struct tb_port *port); +int usb4_dp_port_allocate_bw(struct tb_port *port, int bw); +int usb4_dp_port_requested_bw(struct tb_port *port); + static inline bool tb_is_usb4_port_device(const struct device *dev) { return dev->type == &usb4_port_device_type; diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index f4a194cc0d63..2636423748cd 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -385,15 +385,42 @@ struct tb_regs_port_header { #define ADP_DP_CS_1_AUX_RX_HOPID_MASK GENMASK(21, 11) #define ADP_DP_CS_1_AUX_RX_HOPID_SHIFT 11 #define ADP_DP_CS_2 0x02 +#define ADP_DP_CS_2_NRD_MLC_MASK GENMASK(2, 0) #define ADP_DP_CS_2_HDP BIT(6) +#define ADP_DP_CS_2_NRD_MLR_MASK GENMASK(9, 7) +#define ADP_DP_CS_2_NRD_MLR_SHIFT 7 +#define ADP_DP_CS_2_CA BIT(10) +#define ADP_DP_CS_2_GR_MASK GENMASK(12, 11) +#define ADP_DP_CS_2_GR_SHIFT 11 +#define ADP_DP_CS_2_GR_0_25G 0x0 +#define ADP_DP_CS_2_GR_0_5G 0x1 +#define ADP_DP_CS_2_GR_1G 0x2 +#define ADP_DP_CS_2_GROUP_ID_MASK GENMASK(15, 13) +#define ADP_DP_CS_2_GROUP_ID_SHIFT 13 +#define ADP_DP_CS_2_CM_ID_MASK GENMASK(19, 16) +#define ADP_DP_CS_2_CM_ID_SHIFT 16 +#define ADP_DP_CS_2_CMMS BIT(20) +#define ADP_DP_CS_2_ESTIMATED_BW_MASK GENMASK(31, 24) +#define ADP_DP_CS_2_ESTIMATED_BW_SHIFT 24 #define ADP_DP_CS_3 0x03 #define ADP_DP_CS_3_HDPC BIT(9) #define DP_LOCAL_CAP 0x04 #define DP_REMOTE_CAP 0x05 +/* For DP IN adapter */ +#define DP_STATUS 0x06 +#define DP_STATUS_ALLOCATED_BW_MASK GENMASK(31, 24) +#define DP_STATUS_ALLOCATED_BW_SHIFT 24 +/* For DP OUT adapter */ #define DP_STATUS_CTRL 0x06 #define DP_STATUS_CTRL_CMHS BIT(25) #define DP_STATUS_CTRL_UF BIT(26) #define DP_COMMON_CAP 0x07 +/* Only if DP IN supports BW allocation mode */ +#define ADP_DP_CS_8 0x08 +#define ADP_DP_CS_8_REQUESTED_BW_MASK GENMASK(7, 0) +#define ADP_DP_CS_8_DPME BIT(30) +#define ADP_DP_CS_8_DR BIT(31) + /* * DP_COMMON_CAP offsets work also for DP_LOCAL_CAP and DP_REMOTE_CAP * with exception of DPRX done. @@ -410,7 +437,12 @@ struct tb_regs_port_header { #define DP_COMMON_CAP_2_LANES 0x1 #define DP_COMMON_CAP_4_LANES 0x2 #define DP_COMMON_CAP_LTTPR_NS BIT(27) +#define DP_COMMON_CAP_BW_MODE BIT(28) #define DP_COMMON_CAP_DPRX_DONE BIT(31) +/* Only present if DP IN supports BW allocation mode */ +#define ADP_DP_CS_8 0x08 +#define ADP_DP_CS_8_DPME BIT(30) +#define ADP_DP_CS_8_DR BIT(31) /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 2ed50fcbcca7..2a9266fb5c0f 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -2186,3 +2186,574 @@ err_request: usb4_usb3_port_clear_cm_request(port); return ret; } + +static bool is_usb4_dpin(const struct tb_port *port) +{ + if (!tb_port_is_dpin(port)) + return false; + if (!tb_switch_is_usb4(port->sw)) + return false; + return true; +} + +/** + * usb4_dp_port_set_cm_id() - Assign CM ID to the DP IN adapter + * @port: DP IN adapter + * @cm_id: CM ID to assign + * + * Sets CM ID for the @port. Returns %0 on success and negative errno + * otherwise. Speficially returns %-EOPNOTSUPP if the @port does not + * support this. + */ +int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_CM_ID_MASK; + val |= cm_id << ADP_DP_CS_2_CM_ID_SHIFT; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_bw_mode_supported() - Is the bandwidth allocation mode supported + * @port: DP IN adapter to check + * + * Can be called to any DP IN adapter. Returns true if the adapter + * supports USB4 bandwidth allocation mode, false otherwise. + */ +bool usb4_dp_port_bw_mode_supported(struct tb_port *port) +{ + int ret; + u32 val; + + if (!is_usb4_dpin(port)) + return false; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + DP_LOCAL_CAP, 1); + if (ret) + return false; + + return !!(val & DP_COMMON_CAP_BW_MODE); +} + +/** + * usb4_dp_port_bw_mode_enabled() - Is the bandwidth allocation mode enabled + * @port: DP IN adapter to check + * + * Can be called to any DP IN adapter. Returns true if the bandwidth + * allocation mode has been enabled, false otherwise. + */ +bool usb4_dp_port_bw_mode_enabled(struct tb_port *port) +{ + int ret; + u32 val; + + if (!is_usb4_dpin(port)) + return false; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_8, 1); + if (ret) + return false; + + return !!(val & ADP_DP_CS_8_DPME); +} + +/** + * usb4_dp_port_set_cm_bw_mode_supported() - Set/clear CM support for bandwidth allocation mode + * @port: DP IN adapter + * @supported: Does the CM support bandwidth allocation mode + * + * Can be called to any DP IN adapter. Sets or clears the CM support bit + * of the DP IN adapter. Returns %0 in success and negative errno + * otherwise. Specifically returns %-OPNOTSUPP if the passed in adapter + * does not support this. + */ +int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + if (supported) + val |= ADP_DP_CS_2_CMMS; + else + val &= ~ADP_DP_CS_2_CMMS; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_group_id() - Return Group ID assigned for the adapter + * @port: DP IN adapter + * + * Reads bandwidth allocation Group ID from the DP IN adapter and + * returns it. If the adapter does not support setting Group_ID + * %-EOPNOTSUPP is returned. + */ +int usb4_dp_port_group_id(struct tb_port *port) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + return (val & ADP_DP_CS_2_GROUP_ID_MASK) >> ADP_DP_CS_2_GROUP_ID_SHIFT; +} + +/** + * usb4_dp_port_set_group_id() - Set adapter Group ID + * @port: DP IN adapter + * @group_id: Group ID for the adapter + * + * Sets bandwidth allocation mode Group ID for the DP IN adapter. + * Returns %0 in case of success and negative errno otherwise. + * Specifically returns %-EOPNOTSUPP if the adapter does not support + * this. + */ +int usb4_dp_port_set_group_id(struct tb_port *port, int group_id) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_GROUP_ID_MASK; + val |= group_id << ADP_DP_CS_2_GROUP_ID_SHIFT; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_nrd() - Read non-reduced rate and lanes + * @port: DP IN adapter + * @rate: Non-reduced rate in Mb/s is placed here + * @lanes: Non-reduced lanes are placed here + * + * Reads the non-reduced rate and lanes from the DP IN adapter. Returns + * %0 in success and negative errno otherwise. Specifically returns + * %-EOPNOTSUPP if the adapter does not support this. + */ +int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes) +{ + u32 val, tmp; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + tmp = (val & ADP_DP_CS_2_NRD_MLR_MASK) >> ADP_DP_CS_2_NRD_MLR_SHIFT; + switch (tmp) { + case DP_COMMON_CAP_RATE_RBR: + *rate = 1620; + break; + case DP_COMMON_CAP_RATE_HBR: + *rate = 2700; + break; + case DP_COMMON_CAP_RATE_HBR2: + *rate = 5400; + break; + case DP_COMMON_CAP_RATE_HBR3: + *rate = 8100; + break; + } + + tmp = val & ADP_DP_CS_2_NRD_MLC_MASK; + switch (tmp) { + case DP_COMMON_CAP_1_LANE: + *lanes = 1; + break; + case DP_COMMON_CAP_2_LANES: + *lanes = 2; + break; + case DP_COMMON_CAP_4_LANES: + *lanes = 4; + break; + } + + return 0; +} + +/** + * usb4_dp_port_set_nrd() - Set non-reduced rate and lanes + * @port: DP IN adapter + * @rate: Non-reduced rate in Mb/s + * @lanes: Non-reduced lanes + * + * Before the capabilities reduction this function can be used to set + * the non-reduced values for the DP IN adapter. Returns %0 in success + * and negative errno otherwise. If the adapter does not support this + * %-EOPNOTSUPP is returned. + */ +int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_NRD_MLR_MASK; + + switch (rate) { + case 1620: + break; + case 2700: + val |= (DP_COMMON_CAP_RATE_HBR << ADP_DP_CS_2_NRD_MLR_SHIFT) + & ADP_DP_CS_2_NRD_MLR_MASK; + break; + case 5400: + val |= (DP_COMMON_CAP_RATE_HBR2 << ADP_DP_CS_2_NRD_MLR_SHIFT) + & ADP_DP_CS_2_NRD_MLR_MASK; + break; + case 8100: + val |= (DP_COMMON_CAP_RATE_HBR3 << ADP_DP_CS_2_NRD_MLR_SHIFT) + & ADP_DP_CS_2_NRD_MLR_MASK; + break; + default: + return -EINVAL; + } + + val &= ~ADP_DP_CS_2_NRD_MLC_MASK; + + switch (lanes) { + case 1: + break; + case 2: + val |= DP_COMMON_CAP_2_LANES; + break; + case 4: + val |= DP_COMMON_CAP_4_LANES; + break; + default: + return -EINVAL; + } + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_granularity() - Return granularity for the bandwidth values + * @port: DP IN adapter + * + * Reads the programmed granularity from @port. If the DP IN adapter does + * not support bandwidth allocation mode returns %-EOPNOTSUPP and negative + * errno in other error cases. + */ +int usb4_dp_port_granularity(struct tb_port *port) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ADP_DP_CS_2_GR_MASK; + val >>= ADP_DP_CS_2_GR_SHIFT; + + switch (val) { + case ADP_DP_CS_2_GR_0_25G: + return 250; + case ADP_DP_CS_2_GR_0_5G: + return 500; + case ADP_DP_CS_2_GR_1G: + return 1000; + } + + return -EINVAL; +} + +/** + * usb4_dp_port_set_granularity() - Set granularity for the bandwidth values + * @port: DP IN adapter + * @granularity: Granularity in Mb/s. Supported values: 1000, 500 and 250. + * + * Sets the granularity used with the estimated, allocated and requested + * bandwidth. Returns %0 in success and negative errno otherwise. If the + * adapter does not support this %-EOPNOTSUPP is returned. + */ +int usb4_dp_port_set_granularity(struct tb_port *port, int granularity) +{ + u32 val; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_GR_MASK; + + switch (granularity) { + case 250: + val |= ADP_DP_CS_2_GR_0_25G << ADP_DP_CS_2_GR_SHIFT; + break; + case 500: + val |= ADP_DP_CS_2_GR_0_5G << ADP_DP_CS_2_GR_SHIFT; + break; + case 1000: + val |= ADP_DP_CS_2_GR_1G << ADP_DP_CS_2_GR_SHIFT; + break; + default: + return -EINVAL; + } + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_set_estimated_bw() - Set estimated bandwidth + * @port: DP IN adapter + * @bw: Estimated bandwidth in Mb/s. + * + * Sets the estimated bandwidth to @bw. Set the granularity by calling + * usb4_dp_port_set_granularity() before calling this. The @bw is round + * down to the closest granularity multiplier. Returns %0 in success + * and negative errno otherwise. Specifically returns %-EOPNOTSUPP if + * the adapter does not support this. + */ +int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw) +{ + u32 val, granularity; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = usb4_dp_port_granularity(port); + if (ret < 0) + return ret; + granularity = ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_ESTIMATED_BW_MASK; + val |= (bw / granularity) << ADP_DP_CS_2_ESTIMATED_BW_SHIFT; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_allocated_bw() - Return allocated bandwidth + * @port: DP IN adapter + * + * Reads and returns allocated bandwidth for @port in Mb/s (taking into + * account the programmed granularity). Returns negative errno in case + * of error. + */ +int usb4_dp_port_allocated_bw(struct tb_port *port) +{ + u32 val, granularity; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = usb4_dp_port_granularity(port); + if (ret < 0) + return ret; + granularity = ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + DP_STATUS, 1); + if (ret) + return ret; + + val &= DP_STATUS_ALLOCATED_BW_MASK; + val >>= DP_STATUS_ALLOCATED_BW_SHIFT; + + return val * granularity; +} + +static int __usb4_dp_port_set_cm_ack(struct tb_port *port, bool ack) +{ + u32 val; + int ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + if (ack) + val |= ADP_DP_CS_2_CA; + else + val &= ~ADP_DP_CS_2_CA; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +static inline int usb4_dp_port_set_cm_ack(struct tb_port *port) +{ + return __usb4_dp_port_set_cm_ack(port, true); +} + +static int usb4_dp_port_wait_and_clear_cm_ack(struct tb_port *port, + int timeout_msec) +{ + ktime_t end; + u32 val; + int ret; + + ret = __usb4_dp_port_set_cm_ack(port, false); + if (ret) + return ret; + + end = ktime_add_ms(ktime_get(), timeout_msec); + do { + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_8, 1); + if (ret) + return ret; + + if (!(val & ADP_DP_CS_8_DR)) + break; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), end)); + + if (val & ADP_DP_CS_8_DR) + return -ETIMEDOUT; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); + if (ret) + return ret; + + val &= ~ADP_DP_CS_2_CA; + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); +} + +/** + * usb4_dp_port_allocate_bw() - Set allocated bandwidth + * @port: DP IN adapter + * @bw: New allocated bandwidth in Mb/s + * + * Communicates the new allocated bandwidth with the DPCD (graphics + * driver). Takes into account the programmed granularity. Returns %0 in + * success and negative errno in case of error. + */ +int usb4_dp_port_allocate_bw(struct tb_port *port, int bw) +{ + u32 val, granularity; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = usb4_dp_port_granularity(port); + if (ret < 0) + return ret; + granularity = ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + DP_STATUS, 1); + if (ret) + return ret; + + val &= ~DP_STATUS_ALLOCATED_BW_MASK; + val |= (bw / granularity) << DP_STATUS_ALLOCATED_BW_SHIFT; + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + DP_STATUS, 1); + if (ret) + return ret; + + ret = usb4_dp_port_set_cm_ack(port); + if (ret) + return ret; + + return usb4_dp_port_wait_and_clear_cm_ack(port, 500); +} + +/** + * usb4_dp_port_requested_bw() - Read requested bandwidth + * @port: DP IN adapter + * + * Reads the DPCD (graphics driver) requested bandwidth and returns it + * in Mb/s. Takes the programmed granularity into account. In case of + * error returns negative errno. Specifically returns %-EOPNOTSUPP if + * the adapter does not support bandwidth allocation mode. + */ +int usb4_dp_port_requested_bw(struct tb_port *port) +{ + u32 val, granularity; + int ret; + + if (!is_usb4_dpin(port)) + return -EOPNOTSUPP; + + ret = usb4_dp_port_granularity(port); + if (ret < 0) + return ret; + granularity = ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_8, 1); + if (ret) + return 0; + + if (!(val & ADP_DP_CS_8_DR)) + return 0; + + return (val & ADP_DP_CS_8_REQUESTED_BW_MASK) * granularity; +} From 480c1c809f6e5c3cb5265fa4828e70ef0698c026 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:39 -0300 Subject: [PATCH 041/142] thunderbolt: Include the additional DP IN double word in debugfs dump Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=630f211be7c0a8cf693fef2b6d77d0ac357041e0 commit 630f211be7c0a8cf693fef2b6d77d0ac357041e0 Author: Mika Westerberg Date: Fri, 29 Apr 2022 17:14:57 +0300 When DisplayPort bandwidth allocation mode is supported by the DP IN adapter it has an extra double word in the adapter config space. Include this in the debugfs register dump. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/debugfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 834bcad42e9f..4339e706cc3a 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -1159,7 +1159,10 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s, if (tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) { length = PORT_CAP_PCIE_LEN; } else if (tb_port_is_dpin(port) || tb_port_is_dpout(port)) { - length = PORT_CAP_DP_LEN; + if (usb4_dp_port_bw_mode_supported(port)) + length = PORT_CAP_DP_LEN + 1; + else + length = PORT_CAP_DP_LEN; } else if (tb_port_is_usb3_down(port) || tb_port_is_usb3_up(port)) { length = PORT_CAP_USB3_LEN; From 447eec25509a4bdc037a261ebbb5e59a04ff38b8 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:41 -0300 Subject: [PATCH 042/142] thunderbolt: Add support for DisplayPort bandwidth allocation mode Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6ce3563520be90a155706bafc186fc264a13850e commit 6ce3563520be90a155706bafc186fc264a13850e Author: Mika Westerberg Date: Wed, 23 Mar 2022 16:45:39 +0200 The USB4 spec defines an optional feature that allows the connection manager to negotiate with the graphics through DPCD registers changes in the bandwidth allocation dynamically. This is referred as "bandwidth allocation mode" in the spec. The connection manager uses DP IN adapters registers to communicate with the graphics, and also gets notifications from these adapters when the graphics wants to change the bandwidth allocation. Both the connection manager and the graphics driver needs to support this. We check if the DP IN adapter supports this and if it does enable it before establishing a DP tunnel. Then we react on DP_BW notifications coming from the DP IN adapter and update the bandwidth allocation accordingly (within the maximum common capabilities the DP IN/OUT support). Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/ctl.c | 50 +++- drivers/thunderbolt/ctl.h | 2 + drivers/thunderbolt/tb.c | 493 +++++++++++++++++++++++++++++++++- drivers/thunderbolt/tb.h | 22 ++ drivers/thunderbolt/tb_msgs.h | 11 +- drivers/thunderbolt/tunnel.c | 483 ++++++++++++++++++++++++++++++--- drivers/thunderbolt/tunnel.h | 18 ++ 7 files changed, 1023 insertions(+), 56 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 25f7868257de..6e7d28e8d81a 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -230,7 +230,6 @@ static int check_config_address(struct tb_cfg_address addr, static struct tb_cfg_result decode_error(const struct ctl_pkg *response) { struct cfg_error_pkg *pkg = response->buffer; - struct tb_ctl *ctl = response->ctl; struct tb_cfg_result res = { 0 }; res.response_route = tb_cfg_get_route(&pkg->header); res.response_port = 0; @@ -239,13 +238,6 @@ static struct tb_cfg_result decode_error(const struct ctl_pkg *response) if (res.err) return res; - if (pkg->zero1) - tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1); - if (pkg->zero2) - tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2); - if (pkg->zero3) - tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3); - res.err = 1; res.tb_error = pkg->error; res.response_port = pkg->port; @@ -416,6 +408,7 @@ static int tb_async_error(const struct ctl_pkg *pkg) case TB_CFG_ERROR_LINK_ERROR: case TB_CFG_ERROR_HEC_ERROR_DETECTED: case TB_CFG_ERROR_FLOW_CONTROL_ERROR: + case TB_CFG_ERROR_DP_BW: return true; default: @@ -735,6 +728,47 @@ void tb_ctl_stop(struct tb_ctl *ctl) /* public interface, commands */ +/** + * tb_cfg_ack_notification() - Ack notification + * @ctl: Control channel to use + * @route: Router that originated the event + * @error: Pointer to the notification package + * + * Call this as response for non-plug notification to ack it. Returns + * %0 on success or an error code on failure. + */ +int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route, + const struct cfg_error_pkg *error) +{ + struct cfg_ack_pkg pkg = { + .header = tb_cfg_make_header(route), + }; + const char *name; + + switch (error->error) { + case TB_CFG_ERROR_LINK_ERROR: + name = "link error"; + break; + case TB_CFG_ERROR_HEC_ERROR_DETECTED: + name = "HEC error"; + break; + case TB_CFG_ERROR_FLOW_CONTROL_ERROR: + name = "flow control error"; + break; + case TB_CFG_ERROR_DP_BW: + name = "DP_BW"; + break; + default: + name = "unknown"; + break; + } + + tb_ctl_dbg(ctl, "acking %s (%#x) notification on %llx\n", name, + error->error, route); + + return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_NOTIFY_ACK); +} + /** * tb_cfg_ack_plug() - Ack hot plug/unplug event * @ctl: Control channel to use diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index 7c7d80f96c0c..eec5c953c743 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -122,6 +122,8 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route) return header; } +int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route, + const struct cfg_error_pkg *error); int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug); struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route); struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index cebbb3660c68..0b891d749a96 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -16,7 +16,8 @@ #include "tb_regs.h" #include "tunnel.h" -#define TB_TIMEOUT 100 /* ms */ +#define TB_TIMEOUT 100 /* ms */ +#define MAX_GROUPS 7 /* max Group_ID is 7 */ /** * struct tb_cm - Simple Thunderbolt connection manager @@ -28,12 +29,14 @@ * after cfg has been paused. * @remove_work: Work used to remove any unplugged routers after * runtime resume + * @groups: Bandwidth groups used in this domain. */ struct tb_cm { struct list_head tunnel_list; struct list_head dp_resources; bool hotplug_active; struct delayed_work remove_work; + struct tb_bandwidth_group groups[MAX_GROUPS]; }; static inline struct tb *tcm_to_tb(struct tb_cm *tcm) @@ -49,6 +52,112 @@ struct tb_hotplug_event { bool unplug; }; +static void tb_init_bandwidth_groups(struct tb_cm *tcm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) { + struct tb_bandwidth_group *group = &tcm->groups[i]; + + group->tb = tcm_to_tb(tcm); + group->index = i + 1; + INIT_LIST_HEAD(&group->ports); + } +} + +static void tb_bandwidth_group_attach_port(struct tb_bandwidth_group *group, + struct tb_port *in) +{ + if (!group || WARN_ON(in->group)) + return; + + in->group = group; + list_add_tail(&in->group_list, &group->ports); + + tb_port_dbg(in, "attached to bandwidth group %d\n", group->index); +} + +static struct tb_bandwidth_group *tb_find_free_bandwidth_group(struct tb_cm *tcm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) { + struct tb_bandwidth_group *group = &tcm->groups[i]; + + if (list_empty(&group->ports)) + return group; + } + + return NULL; +} + +static struct tb_bandwidth_group * +tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in, + struct tb_port *out) +{ + struct tb_bandwidth_group *group; + struct tb_tunnel *tunnel; + + /* + * Find all DP tunnels that go through all the same USB4 links + * as this one. Because we always setup tunnels the same way we + * can just check for the routers at both ends of the tunnels + * and if they are the same we have a match. + */ + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (!tb_tunnel_is_dp(tunnel)) + continue; + + if (tunnel->src_port->sw == in->sw && + tunnel->dst_port->sw == out->sw) { + group = tunnel->src_port->group; + if (group) { + tb_bandwidth_group_attach_port(group, in); + return group; + } + } + } + + /* Pick up next available group then */ + group = tb_find_free_bandwidth_group(tcm); + if (group) + tb_bandwidth_group_attach_port(group, in); + else + tb_port_warn(in, "no available bandwidth groups\n"); + + return group; +} + +static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in, + struct tb_port *out) +{ + if (usb4_dp_port_bw_mode_enabled(in)) { + int index, i; + + index = usb4_dp_port_group_id(in); + for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) { + if (tcm->groups[i].index == index) { + tb_bandwidth_group_attach_port(&tcm->groups[i], in); + return; + } + } + } + + tb_attach_bandwidth_group(tcm, in, out); +} + +static void tb_detach_bandwidth_group(struct tb_port *in) +{ + struct tb_bandwidth_group *group = in->group; + + if (group) { + in->group = NULL; + list_del_init(&in->group_list); + + tb_port_dbg(in, "detached from bandwidth group %d\n", group->index); + } +} + static void tb_handle_hotplug(struct work_struct *work); static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) @@ -193,9 +302,14 @@ static void tb_discover_tunnels(struct tb *tb) parent = tb_switch_parent(parent); } } else if (tb_tunnel_is_dp(tunnel)) { + struct tb_port *in = tunnel->src_port; + struct tb_port *out = tunnel->dst_port; + /* Keep the domain from powering down */ - pm_runtime_get_sync(&tunnel->src_port->sw->dev); - pm_runtime_get_sync(&tunnel->dst_port->sw->dev); + pm_runtime_get_sync(&in->sw->dev); + pm_runtime_get_sync(&out->sw->dev); + + tb_discover_bandwidth_group(tcm, in, out); } } } @@ -355,7 +469,8 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, dst_port->port); tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port); - if (tunnel) { + if (tunnel && tunnel->src_port != src_port && + tunnel->dst_port != dst_port) { ret = tb_tunnel_consumed_bandwidth(tunnel, &usb3_consumed_up, &usb3_consumed_down); if (ret) @@ -399,12 +514,24 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, list_for_each_entry(tunnel, &tcm->tunnel_list, list) { int dp_consumed_up, dp_consumed_down; + if (tb_tunnel_is_invalid(tunnel)) + continue; + if (!tb_tunnel_is_dp(tunnel)) continue; if (!tb_tunnel_port_on_path(tunnel, port)) continue; + /* + * Ignore the DP tunnel between src_port and + * dst_port because it is the same tunnel and we + * may be re-calculating estimated bandwidth. + */ + if (tunnel->src_port == src_port && + tunnel->dst_port == dst_port) + continue; + ret = tb_tunnel_consumed_bandwidth(tunnel, &dp_consumed_up, &dp_consumed_down); @@ -765,6 +892,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel) switch (tunnel->type) { case TB_TUNNEL_DP: + tb_detach_bandwidth_group(src_port); /* * In case of DP tunnel make sure the DP IN resource is * deallocated properly. @@ -882,6 +1010,99 @@ out: return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN); } +static void +tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group) +{ + struct tb_tunnel *first_tunnel; + struct tb *tb = group->tb; + struct tb_port *in; + int ret; + + tb_dbg(tb, "re-calculating bandwidth estimation for group %u\n", + group->index); + + first_tunnel = NULL; + list_for_each_entry(in, &group->ports, group_list) { + int estimated_bw, estimated_up, estimated_down; + struct tb_tunnel *tunnel; + struct tb_port *out; + + if (!usb4_dp_port_bw_mode_enabled(in)) + continue; + + tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL); + if (WARN_ON(!tunnel)) + break; + + if (!first_tunnel) { + /* + * Since USB3 bandwidth is shared by all DP + * tunnels under the host router USB4 port, even + * if they do not begin from the host router, we + * can release USB3 bandwidth just once and not + * for each tunnel separately. + */ + first_tunnel = tunnel; + ret = tb_release_unused_usb3_bandwidth(tb, + first_tunnel->src_port, first_tunnel->dst_port); + if (ret) { + tb_port_warn(in, + "failed to release unused bandwidth\n"); + break; + } + } + + out = tunnel->dst_port; + ret = tb_available_bandwidth(tb, in, out, &estimated_up, + &estimated_down); + if (ret) { + tb_port_warn(in, + "failed to re-calculate estimated bandwidth\n"); + break; + } + + /* + * Estimated bandwidth includes: + * - already allocated bandwidth for the DP tunnel + * - available bandwidth along the path + * - bandwidth allocated for USB 3.x but not used. + */ + tb_port_dbg(in, "re-calculated estimated bandwidth %u/%u Mb/s\n", + estimated_up, estimated_down); + + if (in->sw->config.depth < out->sw->config.depth) + estimated_bw = estimated_down; + else + estimated_bw = estimated_up; + + if (usb4_dp_port_set_estimated_bw(in, estimated_bw)) + tb_port_warn(in, "failed to update estimated bandwidth\n"); + } + + if (first_tunnel) + tb_reclaim_usb3_bandwidth(tb, first_tunnel->src_port, + first_tunnel->dst_port); + + tb_dbg(tb, "bandwidth estimation for group %u done\n", group->index); +} + +static void tb_recalc_estimated_bandwidth(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + int i; + + tb_dbg(tb, "bandwidth consumption changed, re-calculating estimated bandwidth\n"); + + for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) { + struct tb_bandwidth_group *group = &tcm->groups[i]; + + if (!list_empty(&group->ports)) + tb_recalc_estimated_bandwidth_for_group(group); + } + + tb_dbg(tb, "bandwidth re-calculation done\n"); +} + static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in) { struct tb_port *host_port, *port; @@ -996,17 +1217,19 @@ static void tb_tunnel_dp(struct tb *tb) goto err_rpm_put; } + if (!tb_attach_bandwidth_group(tcm, in, out)) + goto err_dealloc_dp; + /* Make all unused USB3 bandwidth available for the new DP tunnel */ ret = tb_release_unused_usb3_bandwidth(tb, in, out); if (ret) { tb_warn(tb, "failed to release unused bandwidth\n"); - goto err_dealloc_dp; + goto err_detach_group; } - ret = tb_available_bandwidth(tb, in, out, &available_up, - &available_down); + ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down); if (ret) - goto err_reclaim; + goto err_reclaim_usb; tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n", available_up, available_down); @@ -1015,7 +1238,7 @@ static void tb_tunnel_dp(struct tb *tb) available_down); if (!tunnel) { tb_port_dbg(out, "could not allocate DP tunnel\n"); - goto err_reclaim; + goto err_reclaim_usb; } if (tb_tunnel_activate(tunnel)) { @@ -1025,6 +1248,10 @@ static void tb_tunnel_dp(struct tb *tb) list_add_tail(&tunnel->list, &tcm->tunnel_list); tb_reclaim_usb3_bandwidth(tb, in, out); + + /* Update the domain with the new bandwidth estimation */ + tb_recalc_estimated_bandwidth(tb); + /* * In case of DP tunnel exists, change host router's 1st children * TMU mode to HiFi for CL0s to work. @@ -1035,8 +1262,10 @@ static void tb_tunnel_dp(struct tb *tb) err_free: tb_tunnel_free(tunnel); -err_reclaim: +err_reclaim_usb: tb_reclaim_usb3_bandwidth(tb, in, out); +err_detach_group: + tb_detach_bandwidth_group(in); err_dealloc_dp: tb_switch_dealloc_dp_resource(in->sw, in); err_rpm_put: @@ -1069,6 +1298,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port) * See if there is another DP OUT port that can be used for * to create another tunnel. */ + tb_recalc_estimated_bandwidth(tb); tb_tunnel_dp(tb); } @@ -1316,6 +1546,7 @@ static void tb_handle_hotplug(struct work_struct *work) if (port->dual_link_port) port->dual_link_port->remote = NULL; /* Maybe we can create another DP tunnel */ + tb_recalc_estimated_bandwidth(tb); tb_tunnel_dp(tb); } else if (port->xdomain) { struct tb_xdomain *xd = tb_xdomain_get(port->xdomain); @@ -1373,6 +1604,235 @@ out: kfree(ev); } +static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, + int *requested_down) +{ + int allocated_up, allocated_down, available_up, available_down, ret; + int requested_up_corrected, requested_down_corrected, granularity; + int max_up, max_down, max_up_rounded, max_down_rounded; + struct tb *tb = tunnel->tb; + struct tb_port *in, *out; + + ret = tb_tunnel_allocated_bandwidth(tunnel, &allocated_up, &allocated_down); + if (ret) + return ret; + + in = tunnel->src_port; + out = tunnel->dst_port; + + tb_port_dbg(in, "bandwidth allocated currently %d/%d Mb/s\n", + allocated_up, allocated_down); + + /* + * If we get rounded up request from graphics side, say HBR2 x 4 + * that is 17500 instead of 17280 (this is because of the + * granularity), we allow it too. Here the graphics has already + * negotiated with the DPRX the maximum possible rates (which is + * 17280 in this case). + * + * Since the link cannot go higher than 17280 we use that in our + * calculations but the DP IN adapter Allocated BW write must be + * the same value (17500) otherwise the adapter will mark it as + * failed for graphics. + */ + ret = tb_tunnel_maximum_bandwidth(tunnel, &max_up, &max_down); + if (ret) + return ret; + + ret = usb4_dp_port_granularity(in); + if (ret < 0) + return ret; + granularity = ret; + + max_up_rounded = roundup(max_up, granularity); + max_down_rounded = roundup(max_down, granularity); + + /* + * This will "fix" the request down to the maximum supported + * rate * lanes if it is at the maximum rounded up level. + */ + requested_up_corrected = *requested_up; + if (requested_up_corrected == max_up_rounded) + requested_up_corrected = max_up; + else if (requested_up_corrected < 0) + requested_up_corrected = 0; + requested_down_corrected = *requested_down; + if (requested_down_corrected == max_down_rounded) + requested_down_corrected = max_down; + else if (requested_down_corrected < 0) + requested_down_corrected = 0; + + tb_port_dbg(in, "corrected bandwidth request %d/%d Mb/s\n", + requested_up_corrected, requested_down_corrected); + + if ((*requested_up >= 0 && requested_up_corrected > max_up_rounded) || + (*requested_down >= 0 && requested_down_corrected > max_down_rounded)) { + tb_port_dbg(in, "bandwidth request too high (%d/%d Mb/s > %d/%d Mb/s)\n", + requested_up_corrected, requested_down_corrected, + max_up_rounded, max_down_rounded); + return -ENOBUFS; + } + + if ((*requested_up >= 0 && requested_up_corrected <= allocated_up) || + (*requested_down >= 0 && requested_down_corrected <= allocated_down)) { + /* + * If requested bandwidth is less or equal than what is + * currently allocated to that tunnel we simply change + * the reservation of the tunnel. Since all the tunnels + * going out from the same USB4 port are in the same + * group the released bandwidth will be taken into + * account for the other tunnels automatically below. + */ + return tb_tunnel_alloc_bandwidth(tunnel, requested_up, + requested_down); + } + + /* + * More bandwidth is requested. Release all the potential + * bandwidth from USB3 first. + */ + ret = tb_release_unused_usb3_bandwidth(tb, in, out); + if (ret) + return ret; + + /* + * Then go over all tunnels that cross the same USB4 ports (they + * are also in the same group but we use the same function here + * that we use with the normal bandwidth allocation). + */ + ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down); + if (ret) + goto reclaim; + + tb_port_dbg(in, "bandwidth available for allocation %d/%d Mb/s\n", + available_up, available_down); + + if ((*requested_up >= 0 && available_up >= requested_up_corrected) || + (*requested_down >= 0 && available_down >= requested_down_corrected)) { + ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up, + requested_down); + } else { + ret = -ENOBUFS; + } + +reclaim: + tb_reclaim_usb3_bandwidth(tb, in, out); + return ret; +} + +static void tb_handle_dp_bandwidth_request(struct work_struct *work) +{ + struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); + int requested_bw, requested_up, requested_down, ret; + struct tb_port *in, *out; + struct tb_tunnel *tunnel; + struct tb *tb = ev->tb; + struct tb_cm *tcm = tb_priv(tb); + struct tb_switch *sw; + + pm_runtime_get_sync(&tb->dev); + + mutex_lock(&tb->lock); + if (!tcm->hotplug_active) + goto unlock; + + sw = tb_switch_find_by_route(tb, ev->route); + if (!sw) { + tb_warn(tb, "bandwidth request from non-existent router %llx\n", + ev->route); + goto unlock; + } + + in = &sw->ports[ev->port]; + if (!tb_port_is_dpin(in)) { + tb_port_warn(in, "bandwidth request to non-DP IN adapter\n"); + goto unlock; + } + + tb_port_dbg(in, "handling bandwidth allocation request\n"); + + if (!usb4_dp_port_bw_mode_enabled(in)) { + tb_port_warn(in, "bandwidth allocation mode not enabled\n"); + goto unlock; + } + + requested_bw = usb4_dp_port_requested_bw(in); + if (requested_bw < 0) { + tb_port_dbg(in, "no bandwidth request active\n"); + goto unlock; + } + + tb_port_dbg(in, "requested bandwidth %d Mb/s\n", requested_bw); + + tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL); + if (!tunnel) { + tb_port_warn(in, "failed to find tunnel\n"); + goto unlock; + } + + out = tunnel->dst_port; + + if (in->sw->config.depth < out->sw->config.depth) { + requested_up = -1; + requested_down = requested_bw; + } else { + requested_up = requested_bw; + requested_down = -1; + } + + ret = tb_alloc_dp_bandwidth(tunnel, &requested_up, &requested_down); + if (ret) { + if (ret == -ENOBUFS) + tb_port_warn(in, "not enough bandwidth available\n"); + else + tb_port_warn(in, "failed to change bandwidth allocation\n"); + } else { + tb_port_dbg(in, "bandwidth allocation changed to %d/%d Mb/s\n", + requested_up, requested_down); + + /* Update other clients about the allocation change */ + tb_recalc_estimated_bandwidth(tb); + } + +unlock: + mutex_unlock(&tb->lock); + + pm_runtime_mark_last_busy(&tb->dev); + pm_runtime_put_autosuspend(&tb->dev); +} + +static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port) +{ + struct tb_hotplug_event *ev; + + ev = kmalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return; + + ev->tb = tb; + ev->route = route; + ev->port = port; + INIT_WORK(&ev->work, tb_handle_dp_bandwidth_request); + queue_work(tb->wq, &ev->work); +} + +static void tb_handle_notification(struct tb *tb, u64 route, + const struct cfg_error_pkg *error) +{ + if (tb_cfg_ack_notification(tb->ctl, route, error)) + tb_warn(tb, "could not ack notification on %llx\n", route); + + switch (error->error) { + case TB_CFG_ERROR_DP_BW: + tb_queue_dp_bandwidth_request(tb, route, error->port); + break; + + default: + /* Ack is enough */ + return; + } +} + /* * tb_schedule_hotplug_handler() - callback function for the control channel * @@ -1382,15 +1842,19 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, const void *buf, size_t size) { const struct cfg_event_pkg *pkg = buf; - u64 route; + u64 route = tb_cfg_get_route(&pkg->header); - if (type != TB_CFG_PKG_EVENT) { + switch (type) { + case TB_CFG_PKG_ERROR: + tb_handle_notification(tb, route, (const struct cfg_error_pkg *)buf); + return; + case TB_CFG_PKG_EVENT: + break; + default: tb_warn(tb, "unexpected event %#x, ignoring\n", type); return; } - route = tb_cfg_get_route(&pkg->header); - if (tb_cfg_ack_plug(tb->ctl, route, pkg->port, pkg->unplug)) { tb_warn(tb, "could not ack plug event on %llx:%x\n", route, pkg->port); @@ -1820,6 +2284,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) INIT_LIST_HEAD(&tcm->tunnel_list); INIT_LIST_HEAD(&tcm->dp_resources); INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work); + tb_init_bandwidth_groups(tcm); tb_dbg(tb, "using software connection manager\n"); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index cd6c9eeaf049..2953cf38c29e 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -223,6 +223,23 @@ struct tb_switch { enum tb_clx clx; }; +/** + * struct tb_bandwidth_group - Bandwidth management group + * @tb: Pointer to the domain the group belongs to + * @index: Index of the group (aka Group_ID). Valid values %1-%7 + * @ports: DP IN adapters belonging to this group are linked here + * + * Any tunnel that requires isochronous bandwidth (that's DP for now) is + * attached to a bandwidth group. All tunnels going through the same + * USB4 links share the same group and can dynamically distribute the + * bandwidth within the group. + */ +struct tb_bandwidth_group { + struct tb *tb; + int index; + struct list_head ports; +}; + /** * struct tb_port - a thunderbolt port, part of a tb_switch * @config: Cached port configuration read from registers @@ -247,6 +264,9 @@ struct tb_switch { * @ctl_credits: Buffers reserved for control path * @dma_credits: Number of credits allocated for DMA tunneling for all * DMA paths through this port. + * @group: Bandwidth allocation group the adapter is assigned to. Only + * used for DP IN adapters for now. + * @group_list: The adapter is linked to the group's list of ports through this * * In USB4 terminology this structure represents an adapter (protocol or * lane adapter). @@ -272,6 +292,8 @@ struct tb_port { unsigned int total_credits; unsigned int ctl_credits; unsigned int dma_credits; + struct tb_bandwidth_group *group; + struct list_head group_list; }; /** diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 33c4c7aed56d..3234bff07899 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -29,6 +29,7 @@ enum tb_cfg_error { TB_CFG_ERROR_HEC_ERROR_DETECTED = 12, TB_CFG_ERROR_FLOW_CONTROL_ERROR = 13, TB_CFG_ERROR_LOCK = 15, + TB_CFG_ERROR_DP_BW = 32, }; /* common header */ @@ -64,14 +65,16 @@ struct cfg_write_pkg { /* TB_CFG_PKG_ERROR */ struct cfg_error_pkg { struct tb_cfg_header header; - enum tb_cfg_error error:4; - u32 zero1:4; + enum tb_cfg_error error:8; u32 port:6; - u32 zero2:2; /* Both should be zero, still they are different fields. */ - u32 zero3:14; + u32 reserved:16; u32 pg:2; } __packed; +struct cfg_ack_pkg { + struct tb_cfg_header header; +}; + #define TB_CFG_ERROR_PG_HOT_PLUG 0x2 #define TB_CFG_ERROR_PG_HOT_UNPLUG 0x3 diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index de584d7e3991..e143369b9c0f 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "tunnel.h" #include "tb.h" @@ -44,6 +45,11 @@ /* Minimum number of credits for DMA path */ #define TB_MIN_DMA_CREDITS 1U +static bool bw_alloc_mode = true; +module_param(bw_alloc_mode, bool, 0444); +MODULE_PARM_DESC(bw_alloc_mode, + "enable bandwidth allocation mode if supported (default: true)"); + static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ @@ -597,6 +603,133 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) in->cap_adap + DP_REMOTE_CAP, 1); } +static int tb_dp_bw_alloc_mode_enable(struct tb_tunnel *tunnel) +{ + int ret, estimated_bw, granularity, tmp; + struct tb_port *out = tunnel->dst_port; + struct tb_port *in = tunnel->src_port; + u32 out_dp_cap, out_rate, out_lanes; + u32 in_dp_cap, in_rate, in_lanes; + u32 rate, lanes; + + if (!bw_alloc_mode) + return 0; + + ret = usb4_dp_port_set_cm_bw_mode_supported(in, true); + if (ret) + return ret; + + ret = usb4_dp_port_set_group_id(in, in->group->index); + if (ret) + return ret; + + /* + * Get the non-reduced rate and lanes based on the lowest + * capability of both adapters. + */ + ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT, + in->cap_adap + DP_LOCAL_CAP, 1); + if (ret) + return ret; + + ret = tb_port_read(out, &out_dp_cap, TB_CFG_PORT, + out->cap_adap + DP_LOCAL_CAP, 1); + if (ret) + return ret; + + in_rate = tb_dp_cap_get_rate(in_dp_cap); + in_lanes = tb_dp_cap_get_lanes(in_dp_cap); + out_rate = tb_dp_cap_get_rate(out_dp_cap); + out_lanes = tb_dp_cap_get_lanes(out_dp_cap); + + rate = min(in_rate, out_rate); + lanes = min(in_lanes, out_lanes); + tmp = tb_dp_bandwidth(rate, lanes); + + tb_port_dbg(in, "non-reduced bandwidth %u Mb/s x%u = %u Mb/s\n", rate, + lanes, tmp); + + ret = usb4_dp_port_set_nrd(in, rate, lanes); + if (ret) + return ret; + + for (granularity = 250; tmp / granularity > 255 && granularity <= 1000; + granularity *= 2) + ; + + tb_port_dbg(in, "granularity %d Mb/s\n", granularity); + + /* + * Returns -EINVAL if granularity above is outside of the + * accepted ranges. + */ + ret = usb4_dp_port_set_granularity(in, granularity); + if (ret) + return ret; + + /* + * Bandwidth estimation is pretty much what we have in + * max_up/down fields. For discovery we just read what the + * estimation was set to. + */ + if (in->sw->config.depth < out->sw->config.depth) + estimated_bw = tunnel->max_down; + else + estimated_bw = tunnel->max_up; + + tb_port_dbg(in, "estimated bandwidth %d Mb/s\n", estimated_bw); + + ret = usb4_dp_port_set_estimated_bw(in, estimated_bw); + if (ret) + return ret; + + /* Initial allocation should be 0 according the spec */ + ret = usb4_dp_port_allocate_bw(in, 0); + if (ret) + return ret; + + tb_port_dbg(in, "bandwidth allocation mode enabled\n"); + return 0; +} + +static int tb_dp_init(struct tb_tunnel *tunnel) +{ + struct tb_port *in = tunnel->src_port; + struct tb_switch *sw = in->sw; + struct tb *tb = in->sw->tb; + int ret; + + ret = tb_dp_xchg_caps(tunnel); + if (ret) + return ret; + + if (!tb_switch_is_usb4(sw)) + return 0; + + if (!usb4_dp_port_bw_mode_supported(in)) + return 0; + + tb_port_dbg(in, "bandwidth allocation mode supported\n"); + + ret = usb4_dp_port_set_cm_id(in, tb->index); + if (ret) + return ret; + + return tb_dp_bw_alloc_mode_enable(tunnel); +} + +static void tb_dp_deinit(struct tb_tunnel *tunnel) +{ + struct tb_port *in = tunnel->src_port; + + if (!usb4_dp_port_bw_mode_supported(in)) + return; + if (usb4_dp_port_bw_mode_enabled(in)) { + usb4_dp_port_set_cm_bw_mode_supported(in, false); + tb_port_dbg(in, "bandwidth allocation mode disabled\n"); + } +} + static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) { int ret; @@ -634,49 +767,275 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) return 0; } +/* max_bw is rounded up to next granularity */ +static int tb_dp_nrd_bandwidth(struct tb_tunnel *tunnel, int *max_bw) +{ + struct tb_port *in = tunnel->src_port; + int ret, rate, lanes, nrd_bw; + + ret = usb4_dp_port_nrd(in, &rate, &lanes); + if (ret) + return ret; + + nrd_bw = tb_dp_bandwidth(rate, lanes); + + if (max_bw) { + ret = usb4_dp_port_granularity(in); + if (ret < 0) + return ret; + *max_bw = roundup(nrd_bw, ret); + } + + return nrd_bw; +} + +static int tb_dp_bw_mode_consumed_bandwidth(struct tb_tunnel *tunnel, + int *consumed_up, int *consumed_down) +{ + struct tb_port *out = tunnel->dst_port; + struct tb_port *in = tunnel->src_port; + int ret, allocated_bw, max_bw; + + if (!usb4_dp_port_bw_mode_enabled(in)) + return -EOPNOTSUPP; + + if (!tunnel->bw_mode) + return -EOPNOTSUPP; + + /* Read what was allocated previously if any */ + ret = usb4_dp_port_allocated_bw(in); + if (ret < 0) + return ret; + allocated_bw = ret; + + ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + if (ret < 0) + return ret; + if (allocated_bw == max_bw) + allocated_bw = ret; + + tb_port_dbg(in, "consumed bandwidth through allocation mode %d Mb/s\n", + allocated_bw); + + if (in->sw->config.depth < out->sw->config.depth) { + *consumed_up = 0; + *consumed_down = allocated_bw; + } else { + *consumed_up = allocated_bw; + *consumed_down = 0; + } + + return 0; +} + +static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up, + int *allocated_down) +{ + struct tb_port *out = tunnel->dst_port; + struct tb_port *in = tunnel->src_port; + + /* + * If we have already set the allocated bandwidth then use that. + * Otherwise we read it from the DPRX. + */ + if (usb4_dp_port_bw_mode_enabled(in) && tunnel->bw_mode) { + int ret, allocated_bw, max_bw; + + ret = usb4_dp_port_allocated_bw(in); + if (ret < 0) + return ret; + allocated_bw = ret; + + ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + if (ret < 0) + return ret; + if (allocated_bw == max_bw) + allocated_bw = ret; + + if (in->sw->config.depth < out->sw->config.depth) { + *allocated_up = 0; + *allocated_down = allocated_bw; + } else { + *allocated_up = allocated_bw; + *allocated_down = 0; + } + return 0; + } + + return tunnel->consumed_bandwidth(tunnel, allocated_up, + allocated_down); +} + +static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, + int *alloc_down) +{ + struct tb_port *out = tunnel->dst_port; + struct tb_port *in = tunnel->src_port; + int max_bw, ret, tmp; + + if (!usb4_dp_port_bw_mode_enabled(in)) + return -EOPNOTSUPP; + + ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + if (ret < 0) + return ret; + + if (in->sw->config.depth < out->sw->config.depth) { + tmp = min(*alloc_down, max_bw); + ret = usb4_dp_port_allocate_bw(in, tmp); + if (ret) + return ret; + *alloc_down = tmp; + *alloc_up = 0; + } else { + tmp = min(*alloc_up, max_bw); + ret = usb4_dp_port_allocate_bw(in, tmp); + if (ret) + return ret; + *alloc_down = 0; + *alloc_up = tmp; + } + + /* Now we can use BW mode registers to figure out the bandwidth */ + /* TODO: need to handle discovery too */ + tunnel->bw_mode = true; + return 0; +} + +static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes, + int timeout_msec) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); + struct tb_port *in = tunnel->src_port; + + /* + * Wait for DPRX done. Normally it should be already set for + * active tunnel. + */ + do { + u32 val; + int ret; + + ret = tb_port_read(in, &val, TB_CFG_PORT, + in->cap_adap + DP_COMMON_CAP, 1); + if (ret) + return ret; + + if (val & DP_COMMON_CAP_DPRX_DONE) { + *rate = tb_dp_cap_get_rate(val); + *lanes = tb_dp_cap_get_lanes(val); + + tb_port_dbg(in, "consumed bandwidth through DPRX %d Mb/s\n", + tb_dp_bandwidth(*rate, *lanes)); + return 0; + } + usleep_range(100, 150); + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +/* Read cap from tunnel DP IN */ +static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate, + u32 *lanes) +{ + struct tb_port *in = tunnel->src_port; + u32 val; + int ret; + + switch (cap) { + case DP_LOCAL_CAP: + case DP_REMOTE_CAP: + break; + + default: + tb_tunnel_WARN(tunnel, "invalid capability index %#x\n", cap); + return -EINVAL; + } + + /* + * Read from the copied remote cap so that we take into account + * if capabilities were reduced during exchange. + */ + ret = tb_port_read(in, &val, TB_CFG_PORT, in->cap_adap + cap, 1); + if (ret) + return ret; + + *rate = tb_dp_cap_get_rate(val); + *lanes = tb_dp_cap_get_lanes(val); + + tb_port_dbg(in, "bandwidth from %#x capability %d Mb/s\n", cap, + tb_dp_bandwidth(*rate, *lanes)); + return 0; +} + +static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, + int *max_down) +{ + struct tb_port *in = tunnel->src_port; + u32 rate, lanes; + int ret; + + /* + * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX read + * parameter values so this so we can use this to determine the + * maximum possible bandwidth over this link. + */ + ret = tb_dp_read_cap(tunnel, DP_LOCAL_CAP, &rate, &lanes); + if (ret) + return ret; + + if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) { + *max_up = 0; + *max_down = tb_dp_bandwidth(rate, lanes); + } else { + *max_up = tb_dp_bandwidth(rate, lanes); + *max_down = 0; + } + + return 0; +} + static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down) { struct tb_port *in = tunnel->src_port; const struct tb_switch *sw = in->sw; - u32 val, rate = 0, lanes = 0; + u32 rate = 0, lanes = 0; int ret; if (tb_dp_is_usb4(sw)) { - int timeout = 20; - /* - * Wait for DPRX done. Normally it should be already set - * for active tunnel. + * On USB4 routers check if the bandwidth allocation + * mode is enabled first and then read the bandwidth + * through those registers. */ - do { - ret = tb_port_read(in, &val, TB_CFG_PORT, - in->cap_adap + DP_COMMON_CAP, 1); + ret = tb_dp_bw_mode_consumed_bandwidth(tunnel, consumed_up, + consumed_down); + if (ret < 0) { + if (ret != -EOPNOTSUPP) + return ret; + } else if (!ret) { + return 0; + } + /* + * Then see if the DPRX negotiation is ready and if yes + * return that bandwidth (it may be smaller than the + * reduced one). Otherwise return the remote (possibly + * reduced) caps. + */ + ret = tb_dp_read_dprx(tunnel, &rate, &lanes, 150); + if (ret) { + if (ret == -ETIMEDOUT) + ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, + &rate, &lanes); if (ret) return ret; - - if (val & DP_COMMON_CAP_DPRX_DONE) { - rate = tb_dp_cap_get_rate(val); - lanes = tb_dp_cap_get_lanes(val); - break; - } - msleep(250); - } while (timeout--); - - if (!timeout) - return -ETIMEDOUT; + } } else if (sw->generation >= 2) { - /* - * Read from the copied remote cap so that we take into - * account if capabilities were reduced during exchange. - */ - ret = tb_port_read(in, &val, TB_CFG_PORT, - in->cap_adap + DP_REMOTE_CAP, 1); + ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes); if (ret) return ret; - - rate = tb_dp_cap_get_rate(val); - lanes = tb_dp_cap_get_lanes(val); } else { /* No bandwidth management for legacy devices */ *consumed_up = 0; @@ -798,8 +1157,12 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, if (!tunnel) return NULL; - tunnel->init = tb_dp_xchg_caps; + tunnel->init = tb_dp_init; + tunnel->deinit = tb_dp_deinit; tunnel->activate = tb_dp_activate; + tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth; + tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth; + tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth; tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; tunnel->src_port = in; @@ -887,8 +1250,12 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, if (!tunnel) return NULL; - tunnel->init = tb_dp_xchg_caps; + tunnel->init = tb_dp_init; + tunnel->deinit = tb_dp_deinit; tunnel->activate = tb_dp_activate; + tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth; + tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth; + tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth; tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; tunnel->src_port = in; tunnel->dst_port = out; @@ -1713,6 +2080,62 @@ static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel) return true; } +int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, + int *max_down) +{ + if (!tb_tunnel_is_active(tunnel)) + return -EINVAL; + + if (tunnel->maximum_bandwidth) + return tunnel->maximum_bandwidth(tunnel, max_up, max_down); + return -EOPNOTSUPP; +} + +/** + * tb_tunnel_allocated_bandwidth() - Return bandwidth allocated for the tunnel + * @tunnel: Tunnel to check + * @allocated_up: Currently allocated upstream bandwidth in Mb/s is stored here + * @allocated_down: Currently allocated downstream bandwidth in Mb/s is + * stored here + * + * Returns the bandwidth allocated for the tunnel. This may be higher + * than what the tunnel actually consumes. + */ +int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up, + int *allocated_down) +{ + if (!tb_tunnel_is_active(tunnel)) + return -EINVAL; + + if (tunnel->allocated_bandwidth) + return tunnel->allocated_bandwidth(tunnel, allocated_up, + allocated_down); + return -EOPNOTSUPP; +} + +/** + * tb_tunnel_alloc_bandwidth() - Change tunnel bandwidth allocation + * @tunnel: Tunnel whose bandwidth allocation to change + * @alloc_up: New upstream bandwidth in Mb/s + * @alloc_down: New downstream bandwidth in Mb/s + * + * Tries to change tunnel bandwidth allocation. If succeeds returns %0 + * and updates @alloc_up and @alloc_down to that was actually allocated + * (it may not be the same as passed originally). Returns negative errno + * in case of failure. + */ +int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, + int *alloc_down) +{ + if (!tb_tunnel_is_active(tunnel)) + return -EINVAL; + + if (tunnel->alloc_bandwidth) + return tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down); + + return -EOPNOTSUPP; +} + /** * tb_tunnel_consumed_bandwidth() - Return bandwidth consumed by the tunnel * @tunnel: Tunnel to check diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index bb4d1f1d6d0b..d7bbdf8c8186 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -29,6 +29,9 @@ enum tb_tunnel_type { * @init: Optional tunnel specific initialization * @deinit: Optional tunnel specific de-initialization * @activate: Optional tunnel specific activation/deactivation + * @maximum_bandwidth: + * @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel + * @alloc_bandwidth: Change tunnel bandwidth allocation * @consumed_bandwidth: Return how much bandwidth the tunnel consumes * @release_unused_bandwidth: Release all unused bandwidth * @reclaim_available_bandwidth: Reclaim back available bandwidth @@ -40,6 +43,8 @@ enum tb_tunnel_type { * Only set if the bandwidth needs to be limited. * @allocated_up: Allocated upstream bandwidth (only for USB3) * @allocated_down: Allocated downstream bandwidth (only for USB3) + * @bw_mode: DP bandwidth allocation mode registers can be used to + * determine consumed and allocated bandwidth */ struct tb_tunnel { struct tb *tb; @@ -50,6 +55,12 @@ struct tb_tunnel { int (*init)(struct tb_tunnel *tunnel); void (*deinit)(struct tb_tunnel *tunnel); int (*activate)(struct tb_tunnel *tunnel, bool activate); + int (*maximum_bandwidth)(struct tb_tunnel *tunnel, int *max_up, + int *max_down); + int (*allocated_bandwidth)(struct tb_tunnel *tunnel, int *allocated_up, + int *allocated_down); + int (*alloc_bandwidth)(struct tb_tunnel *tunnel, int *alloc_up, + int *alloc_down); int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down); int (*release_unused_bandwidth)(struct tb_tunnel *tunnel); @@ -62,6 +73,7 @@ struct tb_tunnel { int max_down; int allocated_up; int allocated_down; + bool bw_mode; }; struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, @@ -92,6 +104,12 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel); bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel); bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel, const struct tb_port *port); +int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, + int *max_down); +int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up, + int *allocated_down); +int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, + int *alloc_down); int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down); int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel); From f623cf766ef45b3612d41f0b9595b7f0d73aeeb1 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:41 -0300 Subject: [PATCH 043/142] usb: gadget: xudc: Refactor update data role work Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2582d629c9e01687aee905ac0bce33615a8a004a commit 2582d629c9e01687aee905ac0bce33615a8a004a Author: Wayne Chang Date: Fri, 16 Dec 2022 12:20:26 +0800 The notification call chain should be registered after the device gets ready. Otherwise, we might get errors when setting data roles in an incomplete system. This patch refactors update data role work and register the notifier call chain after the system gets ready. Signed-off-by: Wayne Chang Signed-off-by: Haotien Hsu Link: https://lore.kernel.org/r/20221216042026.98517-1-haotienh@nvidia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/gadget/udc/tegra-xudc.c | 33 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index ff697190469b..2b71b33725f1 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -796,21 +796,16 @@ static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc, return -1; } -static int tegra_xudc_vbus_notify(struct notifier_block *nb, - unsigned long action, void *data) +static void tegra_xudc_update_data_role(struct tegra_xudc *xudc, + struct usb_phy *usbphy) { - struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc, - vbus_nb); - struct usb_phy *usbphy = (struct usb_phy *)data; int phy_index; - dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event); - if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) || (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) { dev_dbg(xudc->dev, "Same role(%d) received. Ignore", xudc->device_mode); - return NOTIFY_OK; + return; } xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true : @@ -826,6 +821,18 @@ static int tegra_xudc_vbus_notify(struct notifier_block *nb, xudc->curr_usbphy = usbphy; schedule_work(&xudc->usb_role_sw_work); } +} + +static int tegra_xudc_vbus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc, + vbus_nb); + struct usb_phy *usbphy = (struct usb_phy *)data; + + dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event); + + tegra_xudc_update_data_role(xudc, usbphy); return NOTIFY_OK; } @@ -3521,7 +3528,7 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc) /* Get usb-phy, if utmi phy is available */ xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev, xudc->utmi_phy[i]->dev.of_node, - &xudc->vbus_nb); + NULL); if (IS_ERR(xudc->usbphy[i])) { err = PTR_ERR(xudc->usbphy[i]); dev_err_probe(xudc->dev, err, @@ -3873,6 +3880,14 @@ static int tegra_xudc_probe(struct platform_device *pdev) goto free_eps; } + for (i = 0; i < xudc->soc->num_phys; i++) { + if (!xudc->usbphy[i]) + continue; + + usb_register_notifier(xudc->usbphy[i], &xudc->vbus_nb); + tegra_xudc_update_data_role(xudc, xudc->usbphy[i]); + } + return 0; free_eps: From 6915f491b6b4f33ad99276999c2a193dbefabc73 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:42 -0300 Subject: [PATCH 044/142] USB: Improve usb_fill_* documentation Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=28e1ff70a08d331703f115534bd4278e11451439 commit 28e1ff70a08d331703f115534bd4278e11451439 Author: Ricardo Ribalda Date: Wed, 21 Dec 2022 20:34:51 +0100 Document the transfer buffer requirement. That is, the buffer must be DMAble - otherwise data corruption might occur. Acked-by: Randy Dunlap Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Acked-by: Alan Stern Link: https://lore.kernel.org/r/20221220-usb-dmadoc-v4-0-74a045bf14f4@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- include/linux/usb.h | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/include/linux/usb.h b/include/linux/usb.h index 866ab7eaa733..d7580b040d7e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1628,14 +1628,25 @@ struct urb { * @urb: pointer to the urb to initialize. * @dev: pointer to the struct usb_device for this urb. * @pipe: the endpoint pipe - * @setup_packet: pointer to the setup_packet buffer - * @transfer_buffer: pointer to the transfer buffer + * @setup_packet: pointer to the setup_packet buffer. The buffer must be + * suitable for DMA. + * @transfer_buffer: pointer to the transfer buffer. The buffer must be + * suitable for DMA. * @buffer_length: length of the transfer buffer * @complete_fn: pointer to the usb_complete_t function * @context: what to set the urb context to. * * Initializes a control urb with the proper information needed to submit * it to a device. + * + * The transfer buffer and the setup_packet buffer will most likely be filled + * or read via DMA. The simplest way to get a buffer that can be DMAed to is + * allocating it via kmalloc() or equivalent, even for very small buffers. + * If the buffers are embedded in a bigger structure, there is a risk that + * the buffer itself, the previous fields and/or the next fields are corrupted + * due to cache incoherencies; or slowed down if they are evicted from the + * cache. For more information, check &struct urb. + * */ static inline void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, @@ -1660,13 +1671,17 @@ static inline void usb_fill_control_urb(struct urb *urb, * @urb: pointer to the urb to initialize. * @dev: pointer to the struct usb_device for this urb. * @pipe: the endpoint pipe - * @transfer_buffer: pointer to the transfer buffer + * @transfer_buffer: pointer to the transfer buffer. The buffer must be + * suitable for DMA. * @buffer_length: length of the transfer buffer * @complete_fn: pointer to the usb_complete_t function * @context: what to set the urb context to. * * Initializes a bulk urb with the proper information needed to submit it * to a device. + * + * Refer to usb_fill_control_urb() for a description of the requirements for + * transfer_buffer. */ static inline void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, @@ -1689,7 +1704,8 @@ static inline void usb_fill_bulk_urb(struct urb *urb, * @urb: pointer to the urb to initialize. * @dev: pointer to the struct usb_device for this urb. * @pipe: the endpoint pipe - * @transfer_buffer: pointer to the transfer buffer + * @transfer_buffer: pointer to the transfer buffer. The buffer must be + * suitable for DMA. * @buffer_length: length of the transfer buffer * @complete_fn: pointer to the usb_complete_t function * @context: what to set the urb context to. @@ -1699,6 +1715,9 @@ static inline void usb_fill_bulk_urb(struct urb *urb, * Initializes a interrupt urb with the proper information needed to submit * it to a device. * + * Refer to usb_fill_control_urb() for a description of the requirements for + * transfer_buffer. + * * Note that High Speed and SuperSpeed(+) interrupt endpoints use a logarithmic * encoding of the endpoint interval, and express polling intervals in * microframes (eight per millisecond) rather than in frames (one per From 8d07224c7f00064da5c731395c6bbbdd544fe6e4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:42 -0300 Subject: [PATCH 045/142] dt-bindings: usb: ci-hdrc-usb2: add i.MX8MM compatible Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1c796d93b58903d2ba09441c23ba6df263b6df28 commit 1c796d93b58903d2ba09441c23ba6df263b6df28 Author: Peng Fan Date: Fri, 23 Dec 2022 11:10:11 +0800 Add fsl,imx8mm-usb compatible for i.MX8MM Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221223031012.92932-1-peng.fan@oss.nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index ba51fb1252b9..72ceea575d58 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -11,6 +11,7 @@ Required properties: "fsl,imx6ul-usb" "fsl,imx7d-usb" "fsl,imx7ulp-usb" + "fsl,imx8mm-usb" "lsi,zevio-usb" "qcom,ci-hdrc" "chipidea,usb2" From 4e30afeb548acd9b325651006d535a9afe862c96 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:43 -0300 Subject: [PATCH 046/142] dt-bindings: usb: usbmisc-imx: add i.MX8MM usbmisc Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2c03f7f1ad81da297e569d68d8b1219bbafcdfd1 commit 2c03f7f1ad81da297e569d68d8b1219bbafcdfd1 Author: Peng Fan Date: Fri, 23 Dec 2022 11:10:12 +0800 Add fsl,imx8mm-usbmisc compatible for i.MX8MM Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20221223031012.92932-2-peng.fan@oss.nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/devicetree/bindings/usb/usbmisc-imx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt index b796836d2ce7..29b8f65ff849 100644 --- a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt +++ b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt @@ -8,6 +8,7 @@ Required properties: "fsl,imx6sx-usbmisc" for imx6sx "fsl,imx7d-usbmisc" for imx7d "fsl,imx7ulp-usbmisc" for imx7ulp + "fsl,imx8mm-usbmisc" for imx8mm - reg: Should contain registers location and length Examples: From 5717707b90c120077d237eb459da18dfe3446ff4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:43 -0300 Subject: [PATCH 047/142] usb: typec: tcpci: Request IRQ with IRQF_SHARED Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccb0beb43a57ee8dbc35a3314c4bf1db922c054f Conflicts: * Avoiding commit ("usb: typec: tcpm/tcpci: Convert to i2c's .probe_new()") commit ccb0beb43a57ee8dbc35a3314c4bf1db922c054f Author: Xu Yang Date: Wed, 14 Dec 2022 10:23:34 +0800 Under resource constraints, this interrupt may use other interrupt line or this interrupt line may be shared with other devices as long as they meet the sharing requirements. Besides, This irq flag will not cause other side effect if tcpci driver is the only user. Signed-off-by: Xu Yang Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20221214022334.2520677-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tcpm/tcpci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index cb5c5a92bb1e..f13512ca0176 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -33,6 +33,7 @@ struct tcpci { struct tcpm_port *port; struct regmap *regmap; + unsigned int alert_mask; bool controls_vbus; @@ -632,6 +633,9 @@ static int tcpci_init(struct tcpc_dev *tcpc) if (ret < 0) return ret; } + + tcpci->alert_mask = reg; + return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); } @@ -715,7 +719,7 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) else if (status & TCPC_ALERT_TX_FAILED) tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); - return IRQ_HANDLED; + return IRQ_RETVAL(status & tcpci->alert_mask); } EXPORT_SYMBOL_GPL(tcpci_irq); @@ -839,7 +843,7 @@ static int tcpci_probe(struct i2c_client *client, err = devm_request_threaded_irq(&client->dev, client->irq, NULL, _tcpci_irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, + IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW, dev_name(&client->dev), chip); if (err < 0) { tcpci_unregister_port(chip->tcpci); From 1ed58b17fc6305284416a048df2c365c94e82e56 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:44 -0300 Subject: [PATCH 048/142] usb: typec: intel_pmc_mux: Don't leak the ACPI device reference count Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c3194949ae8fcbe2b7e38670e7c6a5cfd2605edc commit c3194949ae8fcbe2b7e38670e7c6a5cfd2605edc Author: Andy Shevchenko Date: Mon, 2 Jan 2023 22:29:32 +0200 When acpi_dev_get_memory_resources() fails, the reference count is left bumped. Drop it as it's done in the other error paths. Fixes: 43d596e32276 ("usb: typec: intel_pmc_mux: Check the port status before connect") Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230102202933.15968-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/mux/intel_pmc_mux.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index fdbf3694e21f..87e2c9130607 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -614,8 +614,10 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc) INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_memory_resources(adev, &resource_list); - if (ret < 0) + if (ret < 0) { + acpi_dev_put(adev); return ret; + } rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node); if (rentry) From 08e674b639ad2d54c329c97b9f2d08a9c084a91c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:45 -0300 Subject: [PATCH 049/142] usb: typec: intel_pmc_mux: Deduplicate ACPI matching in probe Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8b9c6ab156b5a80d64e5a1d9c248cfcd61794dfc commit 8b9c6ab156b5a80d64e5a1d9c248cfcd61794dfc Author: Andy Shevchenko Date: Mon, 2 Jan 2023 22:29:33 +0200 There is no need to call acpi_dev_present() followed by acpi_dev_get_first_match_dev() as they both do the same with only difference in the returning value. Instead, call the latter and check its result. Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230102202933.15968-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/mux/intel_pmc_mux.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 87e2c9130607..34e4188a40ff 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -602,16 +602,15 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc) int ret; for (dev_id = &iom_acpi_ids[0]; dev_id->id[0]; dev_id++) { - if (acpi_dev_present(dev_id->id, NULL, -1)) { - pmc->iom_port_status_offset = (u32)dev_id->driver_data; - adev = acpi_dev_get_first_match_dev(dev_id->id, NULL, -1); + adev = acpi_dev_get_first_match_dev(dev_id->id, NULL, -1); + if (adev) break; - } } - if (!adev) return -ENODEV; + pmc->iom_port_status_offset = (u32)dev_id->driver_data; + INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_memory_resources(adev, &resource_list); if (ret < 0) { From aa1dc92cb93f8547c2b932ed78ead3286f3aa345 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:45 -0300 Subject: [PATCH 050/142] USB: fix memory leak with using debugfs_lookup() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=30374434edab20e25776f8ecb4bc9d1e54309487 commit 30374434edab20e25776f8ecb4bc9d1e54309487 Author: Greg Kroah-Hartman Date: Fri, 6 Jan 2023 16:28:28 +0100 When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Alan Stern Cc: Jilin Yuan Link: https://lore.kernel.org/r/20230106152828.3790902-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/core/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 11b15d7b357a..a415206cab04 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -998,7 +998,7 @@ static void usb_debugfs_init(void) static void usb_debugfs_cleanup(void) { - debugfs_remove(debugfs_lookup("devices", usb_debug_root)); + debugfs_lookup_and_remove("devices", usb_debug_root); } /* From 9a0d7959334ee71a9e19eb099e3093f628775903 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:46 -0300 Subject: [PATCH 051/142] dt-bindings: usb: tps6598x: Add wakeup property Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=26fe745063e2d7d091020022b96a145f3a82dff8 commit 26fe745063e2d7d091020022b96a145f3a82dff8 Author: Jun Nie Date: Thu, 5 Jan 2023 15:50:57 +0800 Add wakeup property description. People can enable it with adding the property. Signed-off-by: Jun Nie Reviewed-by: Bryan O'Donoghue Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230105075058.924680-1-jun.nie@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/devicetree/bindings/usb/ti,tps6598x.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml b/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml index a4c53b1f1af3..5b6fc713380c 100644 --- a/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml +++ b/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml @@ -23,6 +23,8 @@ properties: reg: maxItems: 1 + wakeup-source: true + interrupts: maxItems: 1 @@ -48,6 +50,7 @@ examples: tps6598x: tps6598x@38 { compatible = "ti,tps6598x"; reg = <0x38>; + wakeup-source; interrupt-parent = <&msmgpio>; interrupts = <107 IRQ_TYPE_LEVEL_LOW>; From b11b62fdeb4047012b037805d1d885ea29803cba Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:46 -0300 Subject: [PATCH 052/142] usb: typec: tipd: Support wakeup Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=481735d64794181916424a36b332bcd54bfb280b Conflicts: * Avoiding commit ("i2c: Make remove callback return void") commit 481735d64794181916424a36b332bcd54bfb280b Author: Jun Nie Date: Thu, 5 Jan 2023 15:50:58 +0800 Enable wakeup when pluging or unpluging USB cable. It is up to other components to hold system in active mode, such as display, so that user can receive the notification. Signed-off-by: Jun Nie Reviewed-by: Heikki Krogerus Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230105075058.924680-2-jun.nie@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tipd/core.c | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 14bb20edd817..33cc9bc87554 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -95,6 +95,7 @@ struct tps6598x { struct power_supply_desc psy_desc; enum power_supply_usb_type usb_type; + int wakeup; u16 pwr_status; }; @@ -846,6 +847,12 @@ static int tps6598x_probe(struct i2c_client *client) i2c_set_clientdata(client, tps); fwnode_handle_put(fwnode); + tps->wakeup = device_property_read_bool(tps->dev, "wakeup-source"); + if (tps->wakeup) { + device_init_wakeup(&client->dev, true); + enable_irq_wake(client->irq); + } + return 0; err_disconnect: @@ -872,6 +879,36 @@ static int tps6598x_remove(struct i2c_client *client) return 0; } +static int __maybe_unused tps6598x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tps6598x *tps = i2c_get_clientdata(client); + + if (tps->wakeup) { + disable_irq(client->irq); + enable_irq_wake(client->irq); + } + + return 0; +} + +static int __maybe_unused tps6598x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tps6598x *tps = i2c_get_clientdata(client); + + if (tps->wakeup) { + disable_irq_wake(client->irq); + enable_irq(client->irq); + } + + return 0; +} + +static const struct dev_pm_ops tps6598x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tps6598x_suspend, tps6598x_resume) +}; + static const struct of_device_id tps6598x_of_match[] = { { .compatible = "ti,tps6598x", }, { .compatible = "apple,cd321x", }, @@ -888,6 +925,7 @@ MODULE_DEVICE_TABLE(i2c, tps6598x_id); static struct i2c_driver tps6598x_i2c_driver = { .driver = { .name = "tps6598x", + .pm = &tps6598x_pm_ops, .of_match_table = tps6598x_of_match, }, .probe_new = tps6598x_probe, From b28311c0f12d4500f7978e2b656c8e40e988c71f Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:47 -0300 Subject: [PATCH 053/142] usb: typec: Add retimer handle to port altmode Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2476de8288cc552cbe98edf3f4d4cc2846cf4b8c commit 2476de8288cc552cbe98edf3f4d4cc2846cf4b8c Author: Prashant Malani Date: Thu, 12 Jan 2023 22:16:06 +0000 Just like it does with muxes, the Type-C bus code can update the state of connected retimers (especially when altmode-related transitions occur). Add a retimer handle to the port altmode struct to enable this. Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230112221609.540754-2-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/bus.h | 2 ++ drivers/usb/typec/class.c | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 56dec268d4dd..c89168857417 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -7,11 +7,13 @@ struct bus_type; struct typec_mux; +struct typec_retimer; struct altmode { unsigned int id; struct typec_altmode adev; struct typec_mux *mux; + struct typec_retimer *retimer; enum typec_port_data roles; diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 5897905cb4f0..ed3d070b1ca4 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -583,6 +583,7 @@ void typec_unregister_altmode(struct typec_altmode *adev) { if (IS_ERR_OR_NULL(adev)) return; + typec_retimer_put(to_altmode(adev)->retimer); typec_mux_put(to_altmode(adev)->mux); device_unregister(&adev->dev); } @@ -2108,16 +2109,26 @@ typec_port_register_altmode(struct typec_port *port, { struct typec_altmode *adev; struct typec_mux *mux; + struct typec_retimer *retimer; mux = typec_mux_get(&port->dev, desc); if (IS_ERR(mux)) return ERR_CAST(mux); - adev = typec_register_altmode(&port->dev, desc); - if (IS_ERR(adev)) + retimer = typec_retimer_get(&port->dev); + if (IS_ERR(retimer)) { typec_mux_put(mux); - else + return ERR_CAST(retimer); + } + + adev = typec_register_altmode(&port->dev, desc); + if (IS_ERR(adev)) { + typec_retimer_put(retimer); + typec_mux_put(mux); + } else { to_altmode(adev)->mux = mux; + to_altmode(adev)->retimer = retimer; + } return adev; } From 24355896c9d665fde2f145b61bc7c07b873a7207 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:48 -0300 Subject: [PATCH 054/142] usb: typec: Add wrapper for bus switch set code Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2c8cb236ed44d071ef2ed1fbb948eea404551788 commit 2c8cb236ed44d071ef2ed1fbb948eea404551788 Author: Prashant Malani Date: Thu, 12 Jan 2023 22:16:07 +0000 Add a wrapper that calls the set() function for various switches associated with a port altmode. Right now, it just wraps the existing typec_mux_set() command, but it can be expanded to include other switches in future patches. No functional changes introduced by this patch. Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230112221609.540754-3-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/bus.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 31c2a3130cad..9f1bbd26ca47 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -27,6 +27,13 @@ typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) return typec_mux_set(alt->mux, &state); } +/* Wrapper to set various Type-C port switches together. */ +static inline int +typec_altmode_set_switches(struct altmode *alt, unsigned long conf, void *data) +{ + return typec_altmode_set_mux(alt, conf, data); +} + static int typec_altmode_set_state(struct typec_altmode *adev, unsigned long conf, void *data) { @@ -35,7 +42,7 @@ static int typec_altmode_set_state(struct typec_altmode *adev, port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; - return typec_altmode_set_mux(port_altmode, conf, data); + return typec_altmode_set_switches(port_altmode, conf, data); } /* -------------------------------------------------------------------------- */ @@ -73,7 +80,7 @@ int typec_altmode_notify(struct typec_altmode *adev, is_port = is_typec_port(adev->dev.parent); partner = altmode->partner; - ret = typec_altmode_set_mux(is_port ? altmode : partner, conf, data); + ret = typec_altmode_set_switches(is_port ? altmode : partner, conf, data); if (ret) return ret; From 33b43561e1af39842bb0651f98a508a387b13227 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:48 -0300 Subject: [PATCH 055/142] usb: typec: Make bus switch code retimer-aware Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6681e43f5095a64bc3e37822f64d91d284a062dc commit 6681e43f5095a64bc3e37822f64d91d284a062dc Author: Prashant Malani Date: Thu, 12 Jan 2023 22:16:08 +0000 Since ports can have retimers associated with them, update the Type-C alternate mode bus code to also set retimer state when the switch state is updated. While we are here, make the typec_retimer_dev_type declaration in the retimer.h file as extern, so that the header file can be successfully included in the bus code without redeclaration compilation errors. Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230112221609.540754-4-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/bus.c | 22 ++++++++++++++++++++++ drivers/usb/typec/retimer.h | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 9f1bbd26ca47..0c8d554240be 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -11,6 +11,22 @@ #include "bus.h" #include "class.h" #include "mux.h" +#include "retimer.h" + +static inline int +typec_altmode_set_retimer(struct altmode *alt, unsigned long conf, void *data) +{ + struct typec_retimer_state state; + + if (!alt->retimer) + return 0; + + state.alt = &alt->adev; + state.mode = conf; + state.data = data; + + return typec_retimer_set(alt->retimer, &state); +} static inline int typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) @@ -31,6 +47,12 @@ typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) static inline int typec_altmode_set_switches(struct altmode *alt, unsigned long conf, void *data) { + int ret; + + ret = typec_altmode_set_retimer(alt, conf, data); + if (ret) + return ret; + return typec_altmode_set_mux(alt, conf, data); } diff --git a/drivers/usb/typec/retimer.h b/drivers/usb/typec/retimer.h index e34bd23323be..d6a5ef9881e1 100644 --- a/drivers/usb/typec/retimer.h +++ b/drivers/usb/typec/retimer.h @@ -12,7 +12,7 @@ struct typec_retimer { #define to_typec_retimer(_dev_) container_of(_dev_, struct typec_retimer, dev) -const struct device_type typec_retimer_dev_type; +extern const struct device_type typec_retimer_dev_type; #define is_typec_retimer(dev) ((dev)->type == &typec_retimer_dev_type) From 354fb1022acbb6b21817eb9fb894771f29d03e6b Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:49 -0300 Subject: [PATCH 056/142] usb: typec: tcpm: Add callbacks to mitigate wakeups due to contaminant Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=599f008c257d913674b2b2f2fa9e273c1058ec2e commit 599f008c257d913674b2b2f2fa9e273c1058ec2e Author: Badhri Jagan Sridharan Date: Sat, 14 Jan 2023 01:32:44 -0800 On some of the TCPC implementations, when the Type-C port is exposed to contaminants, such as water, TCPC stops toggling while reporting OPEN either by the time TCPM reads CC pin status or during CC debounce window. This causes TCPM to be stuck in TOGGLING state. If TCPM is made to restart toggling, the behavior recurs causing redundant CPU wakeups till the USB-C port is free of contaminant. [206199.287817] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [206199.640337] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [206199.985789] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] (or) [ 7853.867577] Start toggling [ 7853.889921] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [ 7855.698765] CC1: 0 -> 0, CC2: 0 -> 5 [state TOGGLING, polarity 0, connected] [ 7855.698790] state change TOGGLING -> SNK_ATTACH_WAIT [rev3 NONE_AMS] [ 7855.698826] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @ 170 ms [rev3 NONE_AMS] [ 7855.703559] CC1: 0 -> 0, CC2: 5 -> 5 [state SNK_ATTACH_WAIT, polarity 0, connected] [ 7855.856555] CC1: 0 -> 0, CC2: 5 -> 0 [state SNK_ATTACH_WAIT, polarity 0, disconnected] [ 7855.856581] state change SNK_ATTACH_WAIT -> SNK_ATTACH_WAIT [rev3 NONE_AMS] [ 7855.856613] pending state change SNK_ATTACH_WAIT -> SNK_UNATTACHED @ 170 ms [rev3 NONE_AMS] [ 7856.027744] state change SNK_ATTACH_WAIT -> SNK_UNATTACHED [delayed 170 ms] [ 7856.181949] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [ 7856.187896] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [ 7857.645630] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [ 7857.647291] CC1: 0 -> 0, CC2: 0 -> 5 [state TOGGLING, polarity 0, connected] [ 7857.647298] state change TOGGLING -> SNK_ATTACH_WAIT [rev3 NONE_AMS] [ 7857.647310] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @ 170 ms [rev3 NONE_AMS] [ 7857.808106] CC1: 0 -> 0, CC2: 5 -> 0 [state SNK_ATTACH_WAIT, polarity 0, disconnected] [ 7857.808123] state change SNK_ATTACH_WAIT -> SNK_ATTACH_WAIT [rev3 NONE_AMS] [ 7857.808150] pending state change SNK_ATTACH_WAIT -> SNK_UNATTACHED @ 170 ms [rev3 NONE_AMS] [ 7857.978727] state change SNK_ATTACH_WAIT -> SNK_UNATTACHED [delayed 170 ms] To mitigate redundant TCPM wakeups, TCPCs which do have the needed hardware can implement the check_contaminant callback which is invoked by TCPM to evaluate for presence of contaminant. Lower level TCPC driver can restart toggling through TCPM_PORT_CLEAN event when the driver detects that USB-C port is free of contaminant. check_contaminant callback also passes the disconnect_while_debounce flag which when true denotes that the CC pins transitioned to OPEN state during the CC debounce window. Signed-off-by: Badhri Jagan Sridharan Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230114093246.1933321-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tcpm/tcpm.c | 55 ++++++++++++++++++++++++++++++++++- include/linux/usb/tcpm.h | 8 +++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 59b366b5c614..5928461b3fad 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -36,6 +36,7 @@ #define FOREACH_STATE(S) \ S(INVALID_STATE), \ S(TOGGLING), \ + S(CHECK_CONTAMINANT), \ S(SRC_UNATTACHED), \ S(SRC_ATTACH_WAIT), \ S(SRC_ATTACHED), \ @@ -249,6 +250,7 @@ enum frs_typec_current { #define TCPM_RESET_EVENT BIT(2) #define TCPM_FRS_EVENT BIT(3) #define TCPM_SOURCING_VBUS BIT(4) +#define TCPM_PORT_CLEAN BIT(5) #define LOG_BUFFER_ENTRIES 1024 #define LOG_BUFFER_ENTRY_SIZE 128 @@ -483,6 +485,13 @@ struct tcpm_port { * SNK_READY for non-pd link. */ bool slow_charger_loop; + + /* + * When true indicates that the lower level drivers indicate potential presence + * of contaminant in the connector pins based on the tcpm state machine + * transitions. + */ + bool potential_contaminant; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -647,7 +656,7 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) /* Do not log while disconnected and unattached */ if (tcpm_port_is_disconnected(port) && (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || - port->state == TOGGLING)) + port->state == TOGGLING || port->state == CHECK_CONTAMINANT)) return; va_start(args, fmt); @@ -3904,15 +3913,28 @@ static void run_state_machine(struct tcpm_port *port) unsigned int msecs; enum tcpm_state upcoming_state; + if (port->tcpc->check_contaminant && port->state != CHECK_CONTAMINANT) + port->potential_contaminant = ((port->enter_state == SRC_ATTACH_WAIT && + port->state == SRC_UNATTACHED) || + (port->enter_state == SNK_ATTACH_WAIT && + port->state == SNK_UNATTACHED)); + port->enter_state = port->state; switch (port->state) { case TOGGLING: break; + case CHECK_CONTAMINANT: + port->tcpc->check_contaminant(port->tcpc); + break; /* SRC states */ case SRC_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); + if (port->potential_contaminant) { + tcpm_set_state(port, CHECK_CONTAMINANT, 0); + break; + } if (tcpm_start_toggling(port, tcpm_rp_cc(port))) { tcpm_set_state(port, TOGGLING, 0); break; @@ -4150,6 +4172,10 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); + if (port->potential_contaminant) { + tcpm_set_state(port, CHECK_CONTAMINANT, 0); + break; + } if (tcpm_start_toggling(port, TYPEC_CC_RD)) { tcpm_set_state(port, TOGGLING, 0); break; @@ -4925,6 +4951,9 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, else if (tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; + case CHECK_CONTAMINANT: + /* Wait for Toggling to be resumed */ + break; case SRC_UNATTACHED: case ACC_UNATTACHED: if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || @@ -5424,6 +5453,15 @@ static void tcpm_pd_event_handler(struct kthread_work *work) port->vbus_source = true; _tcpm_pd_vbus_on(port); } + if (events & TCPM_PORT_CLEAN) { + tcpm_log(port, "port clean"); + if (port->state == CHECK_CONTAMINANT) { + if (tcpm_start_toggling(port, tcpm_rp_cc(port))) + tcpm_set_state(port, TOGGLING, 0); + else + tcpm_set_state(port, tcpm_default_state(port), 0); + } + } spin_lock(&port->pd_event_lock); } @@ -5476,6 +5514,21 @@ void tcpm_sourcing_vbus(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_sourcing_vbus); +void tcpm_port_clean(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_PORT_CLEAN; + spin_unlock(&port->pd_event_lock); + kthread_queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_port_clean); + +bool tcpm_port_is_toggling(struct tcpm_port *port) +{ + return port->port_type == TYPEC_PORT_DRP && port->state == TOGGLING; +} +EXPORT_SYMBOL_GPL(tcpm_port_is_toggling); + static void tcpm_enable_frs_work(struct kthread_work *work) { struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index bffc8d3e14ad..ab7ca872950b 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -114,6 +114,11 @@ enum tcpm_transmit_type { * Optional; The USB Communications Capable bit indicates if port * partner is capable of communication over the USB data lines * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. + * @check_contaminant: + * Optional; The callback is called when CC pins report open status + * at the end of the deboumce period or when the port is still + * toggling. Chip level drivers are expected to check for contaminant + * and call tcpm_clean_port when the port is clean. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -148,6 +153,7 @@ struct tcpc_dev { bool pps_active, u32 requested_vbus_voltage); bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); + void (*check_contaminant)(struct tcpc_dev *dev); }; struct tcpm_port; @@ -165,5 +171,7 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, enum tcpm_transmit_status status); void tcpm_pd_hard_reset(struct tcpm_port *port); void tcpm_tcpc_reset(struct tcpm_port *port); +void tcpm_port_clean(struct tcpm_port *port); +bool tcpm_port_is_toggling(struct tcpm_port *port); #endif /* __LINUX_USB_TCPM_H */ From 4c28f58ba939d1423ff4b91a29a5dc10f5bfd4e4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:49 -0300 Subject: [PATCH 057/142] usb: typec: tcpci: Add callback for evaluating contaminant presence Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=abc028a270f47f86060ef479395d1bb8c2ab6e7e commit abc028a270f47f86060ef479395d1bb8c2ab6e7e Author: Badhri Jagan Sridharan Date: Sat, 14 Jan 2023 01:32:45 -0800 This change adds callback to evaluate presence of contaminant in the TCPCI layer. Signed-off-by: Badhri Jagan Sridharan Link: https://lore.kernel.org/r/20230114093246.1933321-2-badhri@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tcpm/tcpci.c | 11 +++++++++++ include/linux/usb/tcpci.h | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index f13512ca0176..93a9997182c6 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -404,6 +404,14 @@ static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); } +static void tcpci_check_contaminant(struct tcpc_dev *dev) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + + if (tcpci->data->check_contaminant) + tcpci->data->check_contaminant(tcpci, tcpci->data); +} + static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -782,6 +790,9 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; + if (tcpci->data->check_contaminant) + tcpci->tcpc.check_contaminant = tcpci_check_contaminant; + if (tcpci->data->auto_discharge_disconnect) { tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; tcpci->tcpc.set_auto_vbus_discharge_threshold = diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 17657451c762..85e95a3251d3 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -188,6 +188,12 @@ struct tcpci; * Optional; The USB Communications Capable bit indicates if port * partner is capable of communication over the USB data lines * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. + * @check_contaminant: + * Optional; The callback is invoked when chiplevel drivers indicated + * that the USB port needs to be checked for contaminant presence. + * Chip level drivers are expected to check for contaminant and call + * tcpm_clean_port when the port is clean to put the port back into + * toggling state. */ struct tcpci_data { struct regmap *regmap; @@ -204,6 +210,7 @@ struct tcpci_data { void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data, bool capable); + void (*check_contaminant)(struct tcpci *tcpci, struct tcpci_data *data); }; struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); From e5767f27a47a2d04eb28a019f634b338b11a9c04 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:50 -0300 Subject: [PATCH 058/142] usb: host: ehci-fsl: Use DRV_NAME Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4ba2e7cd986270dbb44eb693199a9d3dd3edf984 commit 4ba2e7cd986270dbb44eb693199a9d3dd3edf984 Author: Alexander Stein Date: Mon, 23 Jan 2023 11:00:07 +0100 "fsl-ehci" is used for both MODULE_ALIAS and driver name. As they have to match use DRV_NAME in both locations. Acked-by: Alan Stern Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20230123100007.1479090-1-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/ehci-fsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 38d06e5abfbb..d74fa5ba845b 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -712,7 +712,7 @@ static struct platform_driver ehci_fsl_driver = { .remove = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { - .name = "fsl-ehci", + .name = DRV_NAME, .pm = EHCI_FSL_PM_OPS, }, }; From 98f69f7790f1bc88ff0f988b0aa2a430061f59e8 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:50 -0300 Subject: [PATCH 059/142] dt-bindings: usb: Introduce GPIO-based SBU mux Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b39483d66af1a6244927e02e4433569a8fb90b17 commit b39483d66af1a6244927e02e4433569a8fb90b17 Author: Bjorn Andersson Date: Thu, 12 Jan 2023 20:11:14 -0800 Introduce a binding for GPIO-based mux hardware used for connecting, disconnecting and switching orientation of the SBU lines in USB Type-C applications. Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113041115.4189210-1-quic_bjorande@quicinc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../devicetree/bindings/usb/gpio-sbu-mux.yaml | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml diff --git a/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml b/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml new file mode 100644 index 000000000000..bf4b1d016e1f --- /dev/null +++ b/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/gpio-sbu-mux.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: GPIO-based SBU mux + +maintainers: + - Bjorn Andersson + +description: + In USB Type-C applications the SBU lines needs to be connected, disconnected + and swapped depending on the altmode and orientation. This binding describes + a family of hardware solutions which switches between these modes using GPIO + signals. + +properties: + compatible: + items: + - enum: + - onnn,fsusb43l10x + - pericom,pi3usb102 + - const: gpio-sbu-mux + + enable-gpios: + description: Switch enable GPIO + + select-gpios: + description: Orientation select + + vcc-supply: + description: power supply + + mode-switch: + description: Flag the port as possible handle of altmode switching + type: boolean + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the SBU mux to a TypeC controller for the purpose of + handling altmode muxing and orientation switching. + +required: + - compatible + - enable-gpios + - select-gpios + - mode-switch + - orientation-switch + - port + +additionalProperties: false + +examples: + - | + #include + + tcpm { + connector { + compatible = "usb-c-connector"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + tcpm_hs_out: endpoint { + remote-endpoint = <&usb_hs_phy_in>; + }; + }; + + port@1 { + reg = <1>; + tcpm_ss_out: endpoint { + remote-endpoint = <&usb_ss_phy_in>; + }; + }; + + port@2 { + reg = <2>; + tcpm_sbu_out: endpoint { + remote-endpoint = <&sbu_mux_in>; + }; + }; + }; + }; + }; + + sbu-mux { + compatible = "pericom,pi3usb102", "gpio-sbu-mux"; + + enable-gpios = <&tlmm 101 GPIO_ACTIVE_LOW>; + select-gpios = <&tlmm 164 GPIO_ACTIVE_HIGH>; + + mode-switch; + orientation-switch; + + port { + sbu_mux_in: endpoint { + remote-endpoint = <&tcpm_sbu_out>; + }; + }; + }; +... From 33cb4fc98d0728b17821cbdc3454318d953805e2 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 6 Jul 2023 12:08:39 -0300 Subject: [PATCH 060/142] redhat: configs: Add unset CONFIG_TYPEC_MUX_GPIO_SBU option Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: RHEL-only Adding unset CONFIG_TYPEC_MUX_GPIO_SBU config option on RHEL following what was done on kernel-ark Signed-off-by: Desnes Nunes --- redhat/configs/rhel/generic/CONFIG_TYPEC_MUX_GPIO_SBU | 1 + 1 file changed, 1 insertion(+) create mode 100644 redhat/configs/rhel/generic/CONFIG_TYPEC_MUX_GPIO_SBU diff --git a/redhat/configs/rhel/generic/CONFIG_TYPEC_MUX_GPIO_SBU b/redhat/configs/rhel/generic/CONFIG_TYPEC_MUX_GPIO_SBU new file mode 100644 index 000000000000..bd5c3d8f0be6 --- /dev/null +++ b/redhat/configs/rhel/generic/CONFIG_TYPEC_MUX_GPIO_SBU @@ -0,0 +1 @@ +# CONFIG_TYPEC_MUX_GPIO_SBU is not set From 49ee1440533f77439ceb90c38f24298d6371b062 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:51 -0300 Subject: [PATCH 061/142] usb: typec: mux: Introduce GPIO-based SBU mux Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=065ded319d39ce91a3785fa15020cd5a17c6c5d2 commit 065ded319d39ce91a3785fa15020cd5a17c6c5d2 Author: Bjorn Andersson Date: Thu, 12 Jan 2023 20:11:15 -0800 A design found in various Qualcomm-based boards is to use a USB switch, controlled through a pair of GPIO lines to connect, disconnect and switch the orientation of the SBU lines in USB Type-C applications. This introduces a generic driver, which implements the typec_switch and typec_mux interfaces to perform these operations. Reviewed-by: Heikki Krogerus Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113041115.4189210-2-quic_bjorande@quicinc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/mux/Kconfig | 6 + drivers/usb/typec/mux/Makefile | 1 + drivers/usb/typec/mux/gpio-sbu-mux.c | 172 +++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 drivers/usb/typec/mux/gpio-sbu-mux.c diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 5eb2c17d72c1..c46fa4f9d3df 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -12,6 +12,12 @@ config TYPEC_MUX_FSA4480 common USB Type-C connector. If compiled as a module, the module will be named fsa4480. +config TYPEC_MUX_GPIO_SBU + tristate "Generic GPIO based SBU mux for USB Type-C applications" + help + Say Y or M if your system uses a GPIO based mux for managing the + connected state and the swapping of the SBU lines in a Type-C port. + config TYPEC_MUX_PI3USB30532 tristate "Pericom PI3USB30532 Type-C cross switch driver" depends on I2C diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index e52a56c16bfb..dda67e19b58b 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC_MUX_FSA4480) += fsa4480.o +obj-$(CONFIG_TYPEC_MUX_GPIO_SBU) += gpio-sbu-mux.o obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c new file mode 100644 index 000000000000..f62516dafe8f --- /dev/null +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_sbu_mux { + struct gpio_desc *enable_gpio; + struct gpio_desc *select_gpio; + + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + struct mutex lock; /* protect enabled and swapped */ + bool enabled; + bool swapped; +}; + +static int gpio_sbu_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct gpio_sbu_mux *sbu_mux = typec_switch_get_drvdata(sw); + bool enabled; + bool swapped; + + mutex_lock(&sbu_mux->lock); + + enabled = sbu_mux->enabled; + swapped = sbu_mux->swapped; + + switch (orientation) { + case TYPEC_ORIENTATION_NONE: + enabled = false; + break; + case TYPEC_ORIENTATION_NORMAL: + swapped = false; + break; + case TYPEC_ORIENTATION_REVERSE: + swapped = true; + break; + } + + if (enabled != sbu_mux->enabled) + gpiod_set_value(sbu_mux->enable_gpio, enabled); + + if (swapped != sbu_mux->swapped) + gpiod_set_value(sbu_mux->select_gpio, swapped); + + sbu_mux->enabled = enabled; + sbu_mux->swapped = swapped; + + mutex_unlock(&sbu_mux->lock); + + return 0; +} + +static int gpio_sbu_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct gpio_sbu_mux *sbu_mux = typec_mux_get_drvdata(mux); + + mutex_lock(&sbu_mux->lock); + + switch (state->mode) { + case TYPEC_STATE_SAFE: + case TYPEC_STATE_USB: + sbu_mux->enabled = false; + break; + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_E: + sbu_mux->enabled = true; + break; + default: + break; + } + + gpiod_set_value(sbu_mux->enable_gpio, sbu_mux->enabled); + + mutex_unlock(&sbu_mux->lock); + + return 0; +} + +static int gpio_sbu_mux_probe(struct platform_device *pdev) +{ + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct device *dev = &pdev->dev; + struct gpio_sbu_mux *sbu_mux; + + sbu_mux = devm_kzalloc(dev, sizeof(*sbu_mux), GFP_KERNEL); + if (!sbu_mux) + return -ENOMEM; + + mutex_init(&sbu_mux->lock); + + sbu_mux->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(sbu_mux->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(sbu_mux->enable_gpio), + "unable to acquire enable gpio\n"); + + sbu_mux->select_gpio = devm_gpiod_get(dev, "select", GPIOD_OUT_LOW); + if (IS_ERR(sbu_mux->select_gpio)) + return dev_err_probe(dev, PTR_ERR(sbu_mux->select_gpio), + "unable to acquire select gpio\n"); + + sw_desc.drvdata = sbu_mux; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = gpio_sbu_switch_set; + + sbu_mux->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(sbu_mux->sw)) + return dev_err_probe(dev, PTR_ERR(sbu_mux->sw), + "failed to register typec switch\n"); + + mux_desc.drvdata = sbu_mux; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = gpio_sbu_mux_set; + + sbu_mux->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(sbu_mux->mux)) { + typec_switch_unregister(sbu_mux->sw); + return dev_err_probe(dev, PTR_ERR(sbu_mux->mux), + "failed to register typec mux\n"); + } + + platform_set_drvdata(pdev, sbu_mux); + + return 0; +} + +static int gpio_sbu_mux_remove(struct platform_device *pdev) +{ + struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev); + + gpiod_set_value(sbu_mux->enable_gpio, 0); + + typec_mux_unregister(sbu_mux->mux); + typec_switch_unregister(sbu_mux->sw); + + return 0; +} + +static const struct of_device_id gpio_sbu_mux_match[] = { + { .compatible = "gpio-sbu-mux", }, + {} +}; +MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); + +static struct platform_driver gpio_sbu_mux_driver = { + .probe = gpio_sbu_mux_probe, + .remove = gpio_sbu_mux_remove, + .driver = { + .name = "gpio_sbu_mux", + .of_match_table = gpio_sbu_mux_match, + }, +}; +module_platform_driver(gpio_sbu_mux_driver); + +MODULE_DESCRIPTION("GPIO based SBU mux driver"); +MODULE_LICENSE("GPL"); From a09e6cd5090b43171f748f15adc1aacc8572f6f9 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:51 -0300 Subject: [PATCH 062/142] usb: typec: altmodes/displayport: Update active state Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=25d6d1bfc213bce03d2e34c9e43477e01ffba7c3 commit 25d6d1bfc213bce03d2e34c9e43477e01ffba7c3 Author: Prashant Malani Date: Fri, 20 Jan 2023 20:58:26 +0000 Update the altmode "active" state when we receive Acks for Enter and Exit Mode commands. Having the right state is necessary to change Pin Assignments using the 'pin_assignment" sysfs file. Cc: Heikki Krogerus Reviewed-by: Benson Leung Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Signed-off-by: Prashant Malani Link: https://lore.kernel.org/r/20230120205827.740900-1-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/altmodes/displayport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index d2e72230613a..662cd043b50e 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -277,9 +277,11 @@ static int dp_altmode_vdm(struct typec_altmode *alt, case CMDT_RSP_ACK: switch (cmd) { case CMD_ENTER_MODE: + typec_altmode_update_active(alt, true); dp->state = DP_STATE_UPDATE; break; case CMD_EXIT_MODE: + typec_altmode_update_active(alt, false); dp->data.status = 0; dp->data.conf = 0; break; From f2b04829918e0c3d8b28988d05644bcacbc4d234 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:52 -0300 Subject: [PATCH 063/142] usb: typec: tcpm: Remove altmode active state updates Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9e6f4c8b880bb34851c21db3869e3096d113ccbf commit 9e6f4c8b880bb34851c21db3869e3096d113ccbf Author: Prashant Malani Date: Fri, 20 Jan 2023 20:58:28 +0000 Since the "active" state for partner altmodes is now being taken care of by the altmode driver itself (specifically, DisplayPort altmode), we no longer need to do so from the port driver. So remove the calls to typec_altmode_update_active() from TCPM. Suggested-by: Heikki Krogerus Reviewed-by: Benson Leung Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Prashant Malani Link: https://lore.kernel.org/r/20230120205827.740900-2-pmalani@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tcpm/tcpm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5928461b3fad..a0d943d78580 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1702,14 +1702,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, } break; case CMD_ENTER_MODE: - if (adev && pdev) { - typec_altmode_update_active(pdev, true); + if (adev && pdev) *adev_action = ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL; - } return 0; case CMD_EXIT_MODE: if (adev && pdev) { - typec_altmode_update_active(pdev, false); /* Back to USB Operation */ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM; return 0; From 8d1a294c8c6309eb7988abea2cd4d2f0e33ad628 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:52 -0300 Subject: [PATCH 064/142] dt-bindings: usb: Remove obsolete brcm,bcm3384-usb.txt Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=60c4da9f3c3c6e8fcd3a12452bcd14181e17cb2c commit 60c4da9f3c3c6e8fcd3a12452bcd14181e17cb2c Author: Rob Herring Date: Mon, 23 Jan 2023 21:05:15 -0600 The "brcm,bcm3384-ohci" and "brcm,bcm3384-ehci" compatibles are already documented in generic-ohci.yaml and generic-ehci.yaml, respectively, so remove the old txt binding. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230110-dt-usb-v3-1-5af0541fcf8c@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../devicetree/bindings/usb/brcm,bcm3384-usb.txt | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt diff --git a/Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt b/Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt deleted file mode 100644 index 452c45c7bf29..000000000000 --- a/Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt +++ /dev/null @@ -1,11 +0,0 @@ -* Broadcom USB controllers - -Required properties: -- compatible: "brcm,bcm3384-ohci", "brcm,bcm3384-ehci" - - These currently use the generic-ohci and generic-ehci drivers. On some - systems, special handling may be needed in the following cases: - - - Restoring state after systemwide power save modes - - Sharing PHYs with the USBD (UDC) hardware - - Figuring out which controllers are disabled on ASIC bondout variants From 5ed531bec3016a4c5da81e5661910e2123bae782 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:53 -0300 Subject: [PATCH 065/142] dt-bindings: usb: Convert multiple "usb-ohci" bindings to DT schema Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4aa466190a2d49a90fa7a6f9dac2dd10e46fdece Conflicts: * Avoiding commit ("dt-bindings: Remove 'Device Tree Bindings' from end of title:") commit 4aa466190a2d49a90fa7a6f9dac2dd10e46fdece Author: Rob Herring Date: Mon, 23 Jan 2023 21:05:16 -0600 "usb-ohci" is another "generic" OHCI controller compatible string used by several platforms. Add it to the generic-ohci.yaml schema and remove all the old binding docs. Marvell pxa-usb.txt has "usb-ohci" in the example, but actual users don't, so drop it. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230110-dt-usb-v3-2-5af0541fcf8c@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../bindings/powerpc/nintendo/wii.txt | 10 ------ .../devicetree/bindings/usb/generic-ohci.yaml | 28 +++++++++++++-- .../devicetree/bindings/usb/ohci-nxp.txt | 24 ------------- .../devicetree/bindings/usb/pxa-usb.txt | 2 +- .../devicetree/bindings/usb/spear-usb.txt | 35 ------------------- 5 files changed, 26 insertions(+), 73 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/ohci-nxp.txt delete mode 100644 Documentation/devicetree/bindings/usb/spear-usb.txt diff --git a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt index c4d78f28d23c..3ff6ebbb4998 100644 --- a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt +++ b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt @@ -97,16 +97,6 @@ Nintendo Wii device tree - reg : should contain the EXI registers location and length - interrupts : should contain the EXI interrupt -1.g) The Open Host Controller Interface (OHCI) nodes - - Represent the USB 1.x Open Host Controller Interfaces. - - Required properties: - - - compatible : should be "nintendo,hollywood-usb-ohci","usb-ohci" - - reg : should contain the OHCI registers location and length - - interrupts : should contain the OHCI interrupt - 1.h) The Enhanced Host Controller Interface (EHCI) node Represents the USB 2.0 Enhanced Host Controller Interface. diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index 180361b79f52..c5d916ff7cb8 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: USB OHCI Controller Device Tree Bindings -allOf: - - $ref: "usb-hcd.yaml" - maintainers: - Greg Kroah-Hartman @@ -49,6 +46,13 @@ properties: - snps,hsdk-v1.0-ohci - const: generic-ohci - const: generic-ohci + - items: + - enum: + - cavium,octeon-6335-ohci + - nintendo,hollywood-usb-ohci + - nxp,ohci-nxp + - st,spear600-ohci + - const: usb-ohci reg: maxItems: 1 @@ -118,11 +122,29 @@ properties: - host - otg + transceiver: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The associated ISP1301 device. Necessary for the UDC controller for + connecting to the USB physical layer. + required: - compatible - reg - interrupts +allOf: + - $ref: usb-hcd.yaml + - if: + not: + properties: + compatible: + contains: + const: nxp,ohci-nxp + then: + properties: + transceiver: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/usb/ohci-nxp.txt b/Documentation/devicetree/bindings/usb/ohci-nxp.txt deleted file mode 100644 index 71e28c1017ed..000000000000 --- a/Documentation/devicetree/bindings/usb/ohci-nxp.txt +++ /dev/null @@ -1,24 +0,0 @@ -* OHCI controller, NXP ohci-nxp variant - -Required properties: -- compatible: must be "nxp,ohci-nxp" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: The OHCI interrupt -- transceiver: phandle of the associated ISP1301 device - this is necessary for - the UDC controller for connecting to the USB physical layer - -Example (LPC32xx): - - isp1301: usb-transceiver@2c { - compatible = "nxp,isp1301"; - reg = <0x2c>; - }; - - ohci@31020000 { - compatible = "nxp,ohci-nxp"; - reg = <0x31020000 0x300>; - interrupt-parent = <&mic>; - interrupts = <0x3b 0>; - transceiver = <&isp1301>; - }; diff --git a/Documentation/devicetree/bindings/usb/pxa-usb.txt b/Documentation/devicetree/bindings/usb/pxa-usb.txt index 9c331799b87c..53fdae4fa6f6 100644 --- a/Documentation/devicetree/bindings/usb/pxa-usb.txt +++ b/Documentation/devicetree/bindings/usb/pxa-usb.txt @@ -22,7 +22,7 @@ Optional properties: Example: usb0: ohci@4c000000 { - compatible = "marvell,pxa-ohci", "usb-ohci"; + compatible = "marvell,pxa-ohci"; reg = <0x4c000000 0x100000>; interrupts = <18>; marvell,enable-port1; diff --git a/Documentation/devicetree/bindings/usb/spear-usb.txt b/Documentation/devicetree/bindings/usb/spear-usb.txt deleted file mode 100644 index 1dc91cc459c0..000000000000 --- a/Documentation/devicetree/bindings/usb/spear-usb.txt +++ /dev/null @@ -1,35 +0,0 @@ -ST SPEAr SoC USB controllers: ------------------------------ - -EHCI: ------ - -Required properties: -- compatible: "st,spear600-ehci" -- interrupts: Should contain the EHCI interrupt - -Example: - - ehci@e1800000 { - compatible = "st,spear600-ehci", "usb-ehci"; - reg = <0xe1800000 0x1000>; - interrupt-parent = <&vic1>; - interrupts = <27>; - }; - - -OHCI: ------ - -Required properties: -- compatible: "st,spear600-ohci" -- interrupts: Should contain the OHCI interrupt - -Example: - - ohci@e1900000 { - compatible = "st,spear600-ohci", "usb-ohci"; - reg = <0xe1800000 0x1000>; - interrupt-parent = <&vic1>; - interrupts = <26>; - }; From 7897c1dedc5561a3a503cf3a550ef6e45d8228c7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:53 -0300 Subject: [PATCH 066/142] dt-bindings: usb: Convert OMAP OHCI/EHCI bindings to schema Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76ea4926dc8d1d68d611ebcd8a2790bc846589e6 commit 76ea4926dc8d1d68d611ebcd8a2790bc846589e6 Author: Rob Herring Date: Mon, 23 Jan 2023 21:05:17 -0600 The OMAP OHCI and EHCI USB host bindings follow the generic binding, so add the compatibles and remove the old txt binding docs. The examples in omap-usb-host.txt don't match actual users, so update them dropping the fallback compatible. Signed-off-by: Rob Herring Acked-by: Lee Jones Link: https://lore.kernel.org/r/20230110-dt-usb-v3-3-5af0541fcf8c@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../devicetree/bindings/mfd/omap-usb-host.txt | 8 ++--- .../devicetree/bindings/usb/ehci-omap.txt | 31 ------------------- .../devicetree/bindings/usb/generic-ehci.yaml | 1 + .../devicetree/bindings/usb/generic-ohci.yaml | 4 ++- .../devicetree/bindings/usb/ohci-omap3.txt | 15 --------- 5 files changed, 8 insertions(+), 51 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/ehci-omap.txt delete mode 100644 Documentation/devicetree/bindings/usb/ohci-omap3.txt diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt index aa1eaa59581b..a0d8c30c2631 100644 --- a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt +++ b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt @@ -64,8 +64,8 @@ Required properties if child node exists: Properties for children: The OMAP HS USB Host subsystem contains EHCI and OHCI controllers. -See Documentation/devicetree/bindings/usb/ehci-omap.txt and -Documentation/devicetree/bindings/usb/ohci-omap3.txt. +See Documentation/devicetree/bindings/usb/generic-ehci.yaml and +Documentation/devicetree/bindings/usb/generic-ohci.yaml. Example for OMAP4: @@ -78,14 +78,14 @@ usbhshost: usbhshost@4a064000 { ranges; usbhsohci: ohci@4a064800 { - compatible = "ti,ohci-omap3", "usb-ohci"; + compatible = "ti,ohci-omap3"; reg = <0x4a064800 0x400>; interrupt-parent = <&gic>; interrupts = <0 76 0x4>; }; usbhsehci: ehci@4a064c00 { - compatible = "ti,ehci-omap", "usb-ehci"; + compatible = "ti,ehci-omap"; reg = <0x4a064c00 0x400>; interrupt-parent = <&gic>; interrupts = <0 77 0x4>; diff --git a/Documentation/devicetree/bindings/usb/ehci-omap.txt b/Documentation/devicetree/bindings/usb/ehci-omap.txt deleted file mode 100644 index d77e11a975a2..000000000000 --- a/Documentation/devicetree/bindings/usb/ehci-omap.txt +++ /dev/null @@ -1,31 +0,0 @@ -OMAP HS USB EHCI controller - -This device is usually the child of the omap-usb-host -Documentation/devicetree/bindings/mfd/omap-usb-host.txt - -Required properties: - -- compatible: should be "ti,ehci-omap" -- reg: should contain one register range i.e. start and length -- interrupts: description of the interrupt line - -Optional properties: - -- phys: list of phandles to PHY nodes. - This property is required if at least one of the ports are in - PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY - -To specify the port mode, see -Documentation/devicetree/bindings/mfd/omap-usb-host.txt - -Example for OMAP4: - -usbhsehci: ehci@4a064c00 { - compatible = "ti,ehci-omap"; - reg = <0x4a064c00 0x400>; - interrupts = <0 77 0x4>; -}; - -&usbhsehci { - phys = <&hsusb1_phy 0 &hsusb3_phy>; -}; diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 079f7cff0c24..742137738645 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -73,6 +73,7 @@ properties: - const: usb-ehci - enum: - generic-ehci + - ti,ehci-omap - usb-ehci reg: diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index c5d916ff7cb8..44a0544bfc8f 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -45,7 +45,9 @@ properties: - ingenic,jz4740-ohci - snps,hsdk-v1.0-ohci - const: generic-ohci - - const: generic-ohci + - enum: + - generic-ohci + - ti,ohci-omap3 - items: - enum: - cavium,octeon-6335-ohci diff --git a/Documentation/devicetree/bindings/usb/ohci-omap3.txt b/Documentation/devicetree/bindings/usb/ohci-omap3.txt deleted file mode 100644 index ce8c47cff6d0..000000000000 --- a/Documentation/devicetree/bindings/usb/ohci-omap3.txt +++ /dev/null @@ -1,15 +0,0 @@ -OMAP HS USB OHCI controller (OMAP3 and later) - -Required properties: - -- compatible: should be "ti,ohci-omap3" -- reg: should contain one register range i.e. start and length -- interrupts: description of the interrupt line - -Example for OMAP4: - -usbhsohci: ohci@4a064800 { - compatible = "ti,ohci-omap3"; - reg = <0x4a064800 0x400>; - interrupts = <0 76 0x4>; -}; From 9f6cc399f9ef47f5289c18108d13cd7acd8fe200 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:54 -0300 Subject: [PATCH 067/142] dt-bindings: usb: Convert Marvell Orion EHCI to DT schema Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c1140ebfd7bf64dbd8dae25a92a6bf8aa97276f8 commit c1140ebfd7bf64dbd8dae25a92a6bf8aa97276f8 Author: Rob Herring Date: Mon, 23 Jan 2023 21:05:18 -0600 The Marvell Orion EHCI binding is just some compatible strings, so add it to the generic-ehci.yaml schema. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20230110-dt-usb-v3-4-5af0541fcf8c@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../devicetree/bindings/usb/ehci-orion.txt | 22 ------------------- .../devicetree/bindings/usb/generic-ehci.yaml | 2 ++ 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/ehci-orion.txt diff --git a/Documentation/devicetree/bindings/usb/ehci-orion.txt b/Documentation/devicetree/bindings/usb/ehci-orion.txt deleted file mode 100644 index 2855bae79fda..000000000000 --- a/Documentation/devicetree/bindings/usb/ehci-orion.txt +++ /dev/null @@ -1,22 +0,0 @@ -* EHCI controller, Orion Marvell variants - -Required properties: -- compatible: must be one of the following - "marvell,orion-ehci" - "marvell,armada-3700-ehci" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: The EHCI interrupt - -Optional properties: -- clocks: reference to the clock -- phys: reference to the USB PHY -- phy-names: name of the USB PHY, should be "usb" - -Example: - - ehci@50000 { - compatible = "marvell,orion-ehci"; - reg = <0x50000 0x1000>; - interrupts = <19>; - }; diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 742137738645..a5d1f3b90b78 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -73,6 +73,8 @@ properties: - const: usb-ehci - enum: - generic-ehci + - marvell,armada-3700-ehci + - marvell,orion-ehci - ti,ehci-omap - usb-ehci From 3d2dd8ca6a67bf88e165173114e88385e93bdef9 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:55 -0300 Subject: [PATCH 068/142] thunderbolt: Handle bandwidth allocation mode enablement notification Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ace75e18e736bffda1eaf9fd7eab596ecccb4877 commit ace75e18e736bffda1eaf9fd7eab596ecccb4877 Author: Mika Westerberg Date: Mon, 23 Jan 2023 08:28:20 +0200 When the graphics side enables bandwidth allocation mode the DP IN adapter sends notification to the connection manager about this. Currently the handler misses this and tries to allocate 0 Mb/s that then makes the graphics side to think the request failed. Fix this by properly handling the enablement notification. Fixes: 6ce3563520be ("thunderbolt: Add support for DisplayPort bandwidth allocation mode") Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tb.c | 10 +++++++--- drivers/thunderbolt/usb4.c | 7 ++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 0b891d749a96..7bfbc9ca9ba4 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1756,11 +1756,15 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work) goto unlock; } - requested_bw = usb4_dp_port_requested_bw(in); - if (requested_bw < 0) { - tb_port_dbg(in, "no bandwidth request active\n"); + ret = usb4_dp_port_requested_bw(in); + if (ret < 0) { + if (ret == -ENODATA) + tb_port_dbg(in, "no bandwidth request active\n"); + else + tb_port_warn(in, "failed to read requested bandwidth\n"); goto unlock; } + requested_bw = ret; tb_port_dbg(in, "requested bandwidth %d Mb/s\n", requested_bw); diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 2a9266fb5c0f..1e5e9c147a31 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -2732,7 +2732,8 @@ int usb4_dp_port_allocate_bw(struct tb_port *port, int bw) * Reads the DPCD (graphics driver) requested bandwidth and returns it * in Mb/s. Takes the programmed granularity into account. In case of * error returns negative errno. Specifically returns %-EOPNOTSUPP if - * the adapter does not support bandwidth allocation mode. + * the adapter does not support bandwidth allocation mode, and %ENODATA + * if there is no active bandwidth request from the graphics driver. */ int usb4_dp_port_requested_bw(struct tb_port *port) { @@ -2750,10 +2751,10 @@ int usb4_dp_port_requested_bw(struct tb_port *port) ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_adap + ADP_DP_CS_8, 1); if (ret) - return 0; + return ret; if (!(val & ADP_DP_CS_8_DR)) - return 0; + return -ENODATA; return (val & ADP_DP_CS_8_REQUESTED_BW_MASK) * granularity; } From d3ef98a5aca13fa1d100c6d50c09c06f6dfef0b7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:56 -0300 Subject: [PATCH 069/142] thunderbolt: Add missing kernel-doc comment to tb_tunnel_maximum_bandwidth() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=06cbcbfaa6510eed406e3b6d5d071386b9830689 commit 06cbcbfaa6510eed406e3b6d5d071386b9830689 Author: Mika Westerberg Date: Wed, 18 Jan 2023 14:44:10 +0200 These were missing from the original commit so add them now. No functional changes. Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/tunnel.c | 10 ++++++++++ drivers/thunderbolt/tunnel.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index e143369b9c0f..831f124268cc 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -2080,6 +2080,16 @@ static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel) return true; } +/** + * tb_tunnel_maximum_bandwidth() - Return maximum possible bandwidth + * @tunnel: Tunnel to check + * @max_up: Maximum upstream bandwidth in Mb/s + * @max_down: Maximum downstream bandwidth in Mb/s + * + * Returns maximum possible bandwidth this tunnel can go if not limited + * by other bandwidth clients. If the tunnel does not support this + * returns %-EOPNOTSUPP. + */ int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, int *max_down) { diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index d7bbdf8c8186..bf690f7beeee 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -29,7 +29,7 @@ enum tb_tunnel_type { * @init: Optional tunnel specific initialization * @deinit: Optional tunnel specific de-initialization * @activate: Optional tunnel specific activation/deactivation - * @maximum_bandwidth: + * @maximum_bandwidth: Returns maximum possible bandwidth for this tunnel * @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel * @alloc_bandwidth: Change tunnel bandwidth allocation * @consumed_bandwidth: Return how much bandwidth the tunnel consumes From 7e9683362f281d170dde8c41c3076d1242c8edbf Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:57 -0300 Subject: [PATCH 070/142] media: uvcvideo: Add GUID for BGRA/X 8:8:8:8 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=015d44c2b700ba9639dd29672ba362796cc0be54 commit 015d44c2b700ba9639dd29672ba362796cc0be54 Author: Marek Vasut Date: Fri, 27 Jan 2023 00:14:52 +0100 The Cypress EZUSB FX3 UVC example can be configured to report pixel format "e436eb7e-524f-11ce-9f53-0020af0ba770". This is its GUID for BGRA/X 8:8:8:8. The UVC 1.5 spec [1] only defines GUIDs for YUY2, NV12, M420 and I420. This seems to be an extension documented in the Microsoft Windows Media Format SDK[2]. This Media Format SDK defines this GUID as corresponding to `MEDIASUBTYPE_RGB32`, which is confirmed by [4] as `MEDIASUBTYPE_ARGB32` has different GUID. Note that in my case, the FX3 UVC can output either channel order, BGR or RGB or any other mix for that matter. Since Linux commit 1b8dc32286a1a ("[media] uvcvideo: Add GUID for BGR 8:8:8") defined a GUID for `MEDIASUBTYPE_RGB24` channel order as BGR, keep this change consistent and define `MEDIASUBTYPE_RGB32` as BGR as well. Document [3] also indicates the channel order is BGR. [1] https://www.usb.org/document-library/video-class-v15-document-set [2] https://learn.microsoft.com/en-us/windows/win32/wmformat/media-type-identifiers [3] https://learn.microsoft.com/en-us/windows/win32/directshow/uncompressed-rgb-video-subtypes [4] https://gix.github.io/media-types/ Signed-off-by: Marek Vasut Reviewed-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20230126231456.3402323-2-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- include/media/v4l2-uvc.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/media/v4l2-uvc.h b/include/media/v4l2-uvc.h index f83e31661333..b010a36fc1d9 100644 --- a/include/media/v4l2-uvc.h +++ b/include/media/v4l2-uvc.h @@ -99,6 +99,9 @@ #define UVC_GUID_FORMAT_BGR3 \ { 0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \ 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} +#define UVC_GUID_FORMAT_BGR4 \ + { 0x7e, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \ + 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} #define UVC_GUID_FORMAT_M420 \ { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} @@ -266,6 +269,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_BGR3, .fcc = V4L2_PIX_FMT_BGR24, }, + { + .name = "BGRA/X 8:8:8:8 (BGR4)", + .guid = UVC_GUID_FORMAT_BGR4, + .fcc = V4L2_PIX_FMT_XBGR32, + }, { .name = "H.264", .guid = UVC_GUID_FORMAT_H264, From ff6191830ca0f18921e968b2e4406e09c4179c99 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:48:58 -0300 Subject: [PATCH 071/142] usb: gadget: uvc: add v4l2 enumeration api calls Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=588b9e85609bcb2f84a2be83591480aa943943b6 commit 588b9e85609bcb2f84a2be83591480aa943943b6 Author: Michael Grzeschik Date: Sat, 10 Sep 2022 00:13:34 +0200 This patch adds support to the v4l2 VIDIOCs for enum_format, enum_framesizes and enum_frameintervals. This way, the userspace application can use these VIDIOCS to query the via configfs exported frame capabilities. With thes callbacks the userspace doesn't have to bring its own configfs parser. Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20220909221335.15033-4-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/gadget/function/f_uvc.c | 30 +++++ drivers/usb/gadget/function/uvc.h | 2 + drivers/usb/gadget/function/uvc_v4l2.c | 176 +++++++++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f4f6cf75930b..f0d14b31c144 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -888,6 +888,7 @@ static void uvc_free(struct usb_function *f) struct uvc_device *uvc = to_uvc(f); struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, func_inst); + config_item_put(&uvc->header->item); --opts->refcnt; kfree(uvc); } @@ -941,6 +942,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) struct uvc_device *uvc; struct f_uvc_opts *opts; struct uvc_descriptor_header **strm_cls; + struct config_item *streaming, *header, *h; uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); if (uvc == NULL) @@ -973,6 +975,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->desc.fs_streaming = opts->fs_streaming; uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; + + streaming = config_group_find_item(&opts->func_inst.group, "streaming"); + if (!streaming) + goto err_config; + + header = config_group_find_item(to_config_group(streaming), "header"); + config_item_put(streaming); + if (!header) + goto err_config; + + h = config_group_find_item(to_config_group(header), "h"); + config_item_put(header); + if (!h) + goto err_config; + + uvc->header = to_uvcg_streaming_header(h); + config_item_put(h); + if (!uvc->header->linked) { + mutex_unlock(&opts->lock); + kfree(uvc); + return ERR_PTR(-EBUSY); + } + ++opts->refcnt; mutex_unlock(&opts->lock); @@ -988,6 +1013,11 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->func.bind_deactivated = true; return &uvc->func; + +err_config: + mutex_unlock(&opts->lock); + kfree(uvc); + return ERR_PTR(-ENOENT); } DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 58e383afdd44..641cf2e7afaf 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -133,6 +133,8 @@ struct uvc_device { bool func_connected; wait_queue_head_t func_connected_queue; + struct uvcg_streaming_header *header; + /* Descriptors */ struct { const struct uvc_descriptor_header * const *fs_control; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 511f106f9843..c8666ed91e39 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -18,12 +18,92 @@ #include #include #include +#include #include "f_uvc.h" #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" #include "uvc_v4l2.h" +#include "uvc_configfs.h" + +static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) +{ + char guid[16] = UVC_GUID_FORMAT_MJPEG; + struct uvc_format_desc *format; + struct uvcg_uncompressed *unc; + + if (uformat->type == UVCG_UNCOMPRESSED) { + unc = to_uvcg_uncompressed(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } + + format = uvc_format_by_guid(guid); + if (!format) + return ERR_PTR(-EINVAL); + + return format; +} + +static struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index) +{ + struct uvcg_format_ptr *format; + struct uvcg_format *uformat = NULL; + int i = 1; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (index == i) { + uformat = format->fmt; + break; + } + i++; + } + + return uformat; +} + +static struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, + struct uvcg_format *uformat, + int index) +{ + struct uvcg_format_ptr *format; + struct uvcg_frame_ptr *frame; + struct uvcg_frame *uframe = NULL; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (format->fmt->type != uformat->type) + continue; + list_for_each_entry(frame, &format->fmt->frames, entry) { + if (index == frame->frm->frame.b_frame_index) { + uframe = frame->frm; + break; + } + } + } + + return uframe; +} + +static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, + u32 pixelformat) +{ + struct uvcg_format_ptr *format; + struct uvcg_format *uformat = NULL; + + list_for_each_entry(format, &uvc->header->formats, entry) { + struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); + + if (fmtdesc->fcc == pixelformat) { + uformat = format->fmt; + break; + } + } + + return uformat; +} /* -------------------------------------------------------------------------- * Requests handling @@ -134,6 +214,99 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return 0; } +static int +uvc_v4l2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvcg_format *uformat = NULL; + struct uvcg_frame *uframe = NULL; + struct uvcg_frame_ptr *frame; + + uformat = find_format_by_pix(uvc, fival->pixel_format); + if (!uformat) + return -EINVAL; + + list_for_each_entry(frame, &uformat->frames, entry) { + if (frame->frm->frame.w_width == fival->width && + frame->frm->frame.w_height == fival->height) { + uframe = frame->frm; + break; + } + } + if (!uframe) + return -EINVAL; + + if (fival->index >= uframe->frame.b_frame_interval_type) + return -EINVAL; + + fival->discrete.numerator = + uframe->dw_frame_interval[fival->index]; + + /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */ + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.denominator = 10000000; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + return 0; +} + +static int +uvc_v4l2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvcg_format *uformat = NULL; + struct uvcg_frame *uframe = NULL; + + uformat = find_format_by_pix(uvc, fsize->pixel_format); + if (!uformat) + return -EINVAL; + + if (fsize->index >= uformat->num_frames) + return -EINVAL; + + uframe = find_frame_by_index(uvc, uformat, fsize->index + 1); + if (!uframe) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = uframe->frame.w_width; + fsize->discrete.height = uframe->frame.w_height; + + return 0; +} + +static int +uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_format_desc *fmtdesc; + struct uvcg_format *uformat; + + if (f->index >= uvc->header->num_fmt) + return -EINVAL; + + uformat = find_format_by_index(uvc, f->index + 1); + if (!uformat) + return -EINVAL; + + if (uformat->type != UVCG_UNCOMPRESSED) + f->flags |= V4L2_FMT_FLAG_COMPRESSED; + + fmtdesc = to_uvc_format(uformat); + f->pixelformat = fmtdesc->fcc; + + strscpy(f->description, fmtdesc->name, sizeof(f->description)); + f->description[strlen(fmtdesc->name) - 1] = 0; + + return 0; +} + static int uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { @@ -300,6 +473,9 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_querycap = uvc_v4l2_querycap, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals, + .vidioc_enum_framesizes = uvc_v4l2_enum_framesizes, + .vidioc_enum_fmt_vid_out = uvc_v4l2_enum_format, .vidioc_reqbufs = uvc_v4l2_reqbufs, .vidioc_querybuf = uvc_v4l2_querybuf, .vidioc_qbuf = uvc_v4l2_qbuf, From ad3bf72b243cb00c5543e8f5f289c58a9ccbf496 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:00 -0300 Subject: [PATCH 072/142] usb: gadget: uvc: add v4l2 try_format api call Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e219a712bc06dc68ecccb3085cb91438bee2466a commit e219a712bc06dc68ecccb3085cb91438bee2466a Author: Michael Grzeschik Date: Sat, 10 Sep 2022 00:13:35 +0200 This patch adds the uvc_v4l2_try_format api call to validate the setting of v4l2_format. It will fallback to the nearest allowed framesize. Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20220909221335.15033-5-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/gadget/function/uvc_v4l2.c | 110 +++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index c8666ed91e39..96721697c689 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -48,6 +48,31 @@ static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) return format; } +static int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat, + struct uvcg_frame *uframe) +{ + struct uvcg_uncompressed *u; + + if (uformat->type == UVCG_UNCOMPRESSED) { + u = to_uvcg_uncompressed(&uformat->group.cg_item); + if (!u) + return 0; + + return u->desc.bBitsPerPixel * uframe->frame.w_width / 8; + } + + return 0; +} + +static int uvc_get_frame_size(struct uvcg_format *uformat, + struct uvcg_frame *uframe) +{ + unsigned int bpl = uvc_v4l2_get_bytesperline(uformat, uframe); + + return bpl ? bpl * uframe->frame.w_height : + uframe->frame.dw_max_video_frame_buffer_size; +} + static struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index) { struct uvcg_format_ptr *format; @@ -105,6 +130,50 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, return uformat; } +static struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc, + struct uvcg_format *uformat, + u16 rw, u16 rh) +{ + struct uvc_video *video = &uvc->video; + struct uvcg_format_ptr *format; + struct uvcg_frame_ptr *frame; + struct uvcg_frame *uframe = NULL; + unsigned int d, maxd; + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + maxd = (unsigned int)-1; + + list_for_each_entry(format, &uvc->header->formats, entry) { + if (format->fmt->type != uformat->type) + continue; + + list_for_each_entry(frame, &format->fmt->frames, entry) { + u16 w, h; + + w = frame->frm->frame.w_width; + h = frame->frm->frame.w_height; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + uframe = frame->frm; + } + + if (maxd == 0) + break; + } + } + + if (!uframe) + uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh); + + return uframe; +} + /* -------------------------------------------------------------------------- * Requests handling */ @@ -214,6 +283,46 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return 0; } +static int +uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct uvcg_format *uformat; + struct uvcg_frame *uframe; + u8 *fcc; + + if (fmt->type != video->queue.queue.type) + return -EINVAL; + + fcc = (u8 *)&fmt->fmt.pix.pixelformat; + uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat); + if (!uformat) + return -EINVAL; + + uframe = find_closest_frame_by_size(uvc, uformat, + fmt->fmt.pix.width, fmt->fmt.pix.height); + if (!uframe) + return -EINVAL; + + fmt->fmt.pix.width = uframe->frame.w_width; + fmt->fmt.pix.height = uframe->frame.w_height; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); + fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe); + fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + fmt->fmt.pix.priv = 0; + + return 0; +} + static int uvc_v4l2_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) @@ -471,6 +580,7 @@ uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_querycap = uvc_v4l2_querycap, + .vidioc_try_fmt_vid_out = uvc_v4l2_try_format, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, .vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals, From 8413b5025a6937b24c97e4928468400d2833041f Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:00 -0300 Subject: [PATCH 073/142] usb: uvc: move media/v4l2-uvc.h to usb/uvc.h Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e1d5d71d189f290343fb1f18eecf77335c5d1ef3 commit e1d5d71d189f290343fb1f18eecf77335c5d1ef3 Author: Michael Grzeschik Date: Fri, 27 Jan 2023 00:14:53 +0100 Since the headerfile is only used in usb devices it is better placed with the other usb files. Reviewed-by: Laurent Pinchart Reviewed-by: Daniel Scally Tested-by: Daniel Scally Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20230126231456.3402323-3-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_ctrl.c | 2 +- drivers/media/usb/uvc/uvc_driver.c | 2 +- drivers/usb/gadget/function/uvc_v4l2.c | 2 +- include/{media/v4l2-uvc.h => linux/usb/uvc.h} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename include/{media/v4l2-uvc.h => linux/usb/uvc.h} (100%) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index c3aeba3fe31b..5e9d3da862dd 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -14,13 +14,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "uvcvideo.h" diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b9f3787fdbab..478dc7d7a56c 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include -#include #include "uvcvideo.h" diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 96721697c689..bb8f39cdf8ae 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include "f_uvc.h" #include "uvc.h" diff --git a/include/media/v4l2-uvc.h b/include/linux/usb/uvc.h similarity index 100% rename from include/media/v4l2-uvc.h rename to include/linux/usb/uvc.h From 67cd9069de87b2a1936d2409717a703d90899d44 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:01 -0300 Subject: [PATCH 074/142] usb: uvc: move uvc_fmts and uvc_format_by_guid to own compile unit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=466be4c9a6f0b7810991b4ac6c3e55345ea63954 Conflicts: * Avoiding commits ("media: move ttpci-eeprom to common"), 9958d30f38b9 ("media: Kconfig: cleanup VIDEO_DEV dependencies") and <6cdc31b2d709> ("media: media/*/Kconfig: sort entries") - pctv452e is not supported commit 466be4c9a6f0b7810991b4ac6c3e55345ea63954 Author: Michael Grzeschik Date: Fri, 27 Jan 2023 00:14:54 +0100 The media driver USB_VIDEO_CLASS and USB_F_UVC are using the same function uvc_format_by_guid. Since the function is inline, every user will get a copy of the used uvc_fmts array and the function. This patch moves the code to an own compile unit and add this dependency as UVC_COMMON to both users. Reviewed-by: Laurent Pinchart Reviewed-by: Daniel Scally Tested-by: Daniel Scally Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20230126231456.3402323-4-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/media/common/Kconfig | 3 + drivers/media/common/Makefile | 1 + drivers/media/common/uvc.c | 221 ++++++++++++++++++++++++++++++++++ drivers/media/usb/uvc/Kconfig | 1 + drivers/usb/gadget/Kconfig | 1 + include/linux/usb/uvc.h | 210 +------------------------------- 6 files changed, 228 insertions(+), 209 deletions(-) create mode 100644 drivers/media/common/uvc.c diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 0f6bde0f793e..15cec2c2d39e 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -6,6 +6,9 @@ config MEDIA_COMMON_OPTIONS comment "common driver options" depends on MEDIA_COMMON_OPTIONS +config UVC_COMMON + tristate + config VIDEO_CX2341X tristate diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 55b5a1900124..a42bddf7c615 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/ +obj-$(CONFIG_UVC_COMMON) += uvc.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c new file mode 100644 index 000000000000..bb96d9e26d80 --- /dev/null +++ b/drivers/media/common/uvc.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------------ + * Video formats + */ + +static struct uvc_format_desc uvc_fmts[] = { + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:0 (NV12)", + .guid = UVC_GUID_FORMAT_NV12, + .fcc = V4L2_PIX_FMT_NV12, + }, + { + .name = "MJPEG", + .guid = UVC_GUID_FORMAT_MJPEG, + .fcc = V4L2_PIX_FMT_MJPEG, + }, + { + .name = "YVU 4:2:0 (YV12)", + .guid = UVC_GUID_FORMAT_YV12, + .fcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "YUV 4:2:0 (I420)", + .guid = UVC_GUID_FORMAT_I420, + .fcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:0 (M420)", + .guid = UVC_GUID_FORMAT_M420, + .fcc = V4L2_PIX_FMT_M420, + }, + { + .name = "YUV 4:2:2 (UYVY)", + .guid = UVC_GUID_FORMAT_UYVY, + .fcc = V4L2_PIX_FMT_UYVY, + }, + { + .name = "Greyscale 8-bit (Y800)", + .guid = UVC_GUID_FORMAT_Y800, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "Greyscale 8-bit (Y8 )", + .guid = UVC_GUID_FORMAT_Y8, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "Greyscale 8-bit (D3DFMT_L8)", + .guid = UVC_GUID_FORMAT_D3DFMT_L8, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "IR 8-bit (L8_IR)", + .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "Greyscale 10-bit (Y10 )", + .guid = UVC_GUID_FORMAT_Y10, + .fcc = V4L2_PIX_FMT_Y10, + }, + { + .name = "Greyscale 12-bit (Y12 )", + .guid = UVC_GUID_FORMAT_Y12, + .fcc = V4L2_PIX_FMT_Y12, + }, + { + .name = "Greyscale 16-bit (Y16 )", + .guid = UVC_GUID_FORMAT_Y16, + .fcc = V4L2_PIX_FMT_Y16, + }, + { + .name = "BGGR Bayer (BY8 )", + .guid = UVC_GUID_FORMAT_BY8, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, + { + .name = "BGGR Bayer (BA81)", + .guid = UVC_GUID_FORMAT_BA81, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, + { + .name = "GBRG Bayer (GBRG)", + .guid = UVC_GUID_FORMAT_GBRG, + .fcc = V4L2_PIX_FMT_SGBRG8, + }, + { + .name = "GRBG Bayer (GRBG)", + .guid = UVC_GUID_FORMAT_GRBG, + .fcc = V4L2_PIX_FMT_SGRBG8, + }, + { + .name = "RGGB Bayer (RGGB)", + .guid = UVC_GUID_FORMAT_RGGB, + .fcc = V4L2_PIX_FMT_SRGGB8, + }, + { + .name = "RGB565", + .guid = UVC_GUID_FORMAT_RGBP, + .fcc = V4L2_PIX_FMT_RGB565, + }, + { + .name = "BGR 8:8:8 (BGR3)", + .guid = UVC_GUID_FORMAT_BGR3, + .fcc = V4L2_PIX_FMT_BGR24, + }, + { + .name = "BGRA/X 8:8:8:8 (BGR4)", + .guid = UVC_GUID_FORMAT_BGR4, + .fcc = V4L2_PIX_FMT_XBGR32, + }, + { + .name = "H.264", + .guid = UVC_GUID_FORMAT_H264, + .fcc = V4L2_PIX_FMT_H264, + }, + { + .name = "H.265", + .guid = UVC_GUID_FORMAT_H265, + .fcc = V4L2_PIX_FMT_HEVC, + }, + { + .name = "Greyscale 8 L/R (Y8I)", + .guid = UVC_GUID_FORMAT_Y8I, + .fcc = V4L2_PIX_FMT_Y8I, + }, + { + .name = "Greyscale 12 L/R (Y12I)", + .guid = UVC_GUID_FORMAT_Y12I, + .fcc = V4L2_PIX_FMT_Y12I, + }, + { + .name = "Depth data 16-bit (Z16)", + .guid = UVC_GUID_FORMAT_Z16, + .fcc = V4L2_PIX_FMT_Z16, + }, + { + .name = "Bayer 10-bit (SRGGB10P)", + .guid = UVC_GUID_FORMAT_RW10, + .fcc = V4L2_PIX_FMT_SRGGB10P, + }, + { + .name = "Bayer 16-bit (SBGGR16)", + .guid = UVC_GUID_FORMAT_BG16, + .fcc = V4L2_PIX_FMT_SBGGR16, + }, + { + .name = "Bayer 16-bit (SGBRG16)", + .guid = UVC_GUID_FORMAT_GB16, + .fcc = V4L2_PIX_FMT_SGBRG16, + }, + { + .name = "Bayer 16-bit (SRGGB16)", + .guid = UVC_GUID_FORMAT_RG16, + .fcc = V4L2_PIX_FMT_SRGGB16, + }, + { + .name = "Bayer 16-bit (SGRBG16)", + .guid = UVC_GUID_FORMAT_GR16, + .fcc = V4L2_PIX_FMT_SGRBG16, + }, + { + .name = "Depth data 16-bit (Z16)", + .guid = UVC_GUID_FORMAT_INVZ, + .fcc = V4L2_PIX_FMT_Z16, + }, + { + .name = "Greyscale 10-bit (Y10 )", + .guid = UVC_GUID_FORMAT_INVI, + .fcc = V4L2_PIX_FMT_Y10, + }, + { + .name = "IR:Depth 26-bit (INZI)", + .guid = UVC_GUID_FORMAT_INZI, + .fcc = V4L2_PIX_FMT_INZI, + }, + { + .name = "4-bit Depth Confidence (Packed)", + .guid = UVC_GUID_FORMAT_CNF4, + .fcc = V4L2_PIX_FMT_CNF4, + }, + { + .name = "HEVC", + .guid = UVC_GUID_FORMAT_HEVC, + .fcc = V4L2_PIX_FMT_HEVC, + }, +}; + +struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) +{ + unsigned int len = ARRAY_SIZE(uvc_fmts); + unsigned int i; + + for (i = 0; i < len; ++i) { + if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) + return &uvc_fmts[i]; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(uvc_format_by_guid); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig index 4c2f4a3216f2..3d3bd2641f44 100644 --- a/drivers/media/usb/uvc/Kconfig +++ b/drivers/media/usb/uvc/Kconfig @@ -3,6 +3,7 @@ config USB_VIDEO_CLASS tristate "USB Video Class (UVC)" depends on VIDEO_V4L2 select VIDEOBUF2_VMALLOC + select UVC_COMMON help Support for the USB Video Class (UVC). Currently only video input devices, such as webcams, are supported. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dd58094f0b85..30c38019039d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -203,6 +203,7 @@ config USB_F_UAC2 config USB_F_UVC tristate + select UVC_COMMON config USB_F_MIDI tristate diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index b010a36fc1d9..8cebb46602a1 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -148,220 +148,12 @@ { 'H', 'E', 'V', 'C', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} -/* ------------------------------------------------------------------------ - * Video formats - */ - struct uvc_format_desc { char *name; u8 guid[16]; u32 fcc; }; -static struct uvc_format_desc uvc_fmts[] = { - { - .name = "YUV 4:2:2 (YUYV)", - .guid = UVC_GUID_FORMAT_YUY2, - .fcc = V4L2_PIX_FMT_YUYV, - }, - { - .name = "YUV 4:2:2 (YUYV)", - .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, - .fcc = V4L2_PIX_FMT_YUYV, - }, - { - .name = "YUV 4:2:0 (NV12)", - .guid = UVC_GUID_FORMAT_NV12, - .fcc = V4L2_PIX_FMT_NV12, - }, - { - .name = "MJPEG", - .guid = UVC_GUID_FORMAT_MJPEG, - .fcc = V4L2_PIX_FMT_MJPEG, - }, - { - .name = "YVU 4:2:0 (YV12)", - .guid = UVC_GUID_FORMAT_YV12, - .fcc = V4L2_PIX_FMT_YVU420, - }, - { - .name = "YUV 4:2:0 (I420)", - .guid = UVC_GUID_FORMAT_I420, - .fcc = V4L2_PIX_FMT_YUV420, - }, - { - .name = "YUV 4:2:0 (M420)", - .guid = UVC_GUID_FORMAT_M420, - .fcc = V4L2_PIX_FMT_M420, - }, - { - .name = "YUV 4:2:2 (UYVY)", - .guid = UVC_GUID_FORMAT_UYVY, - .fcc = V4L2_PIX_FMT_UYVY, - }, - { - .name = "Greyscale 8-bit (Y800)", - .guid = UVC_GUID_FORMAT_Y800, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "Greyscale 8-bit (Y8 )", - .guid = UVC_GUID_FORMAT_Y8, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "Greyscale 8-bit (D3DFMT_L8)", - .guid = UVC_GUID_FORMAT_D3DFMT_L8, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "IR 8-bit (L8_IR)", - .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, - .fcc = V4L2_PIX_FMT_GREY, - }, - { - .name = "Greyscale 10-bit (Y10 )", - .guid = UVC_GUID_FORMAT_Y10, - .fcc = V4L2_PIX_FMT_Y10, - }, - { - .name = "Greyscale 12-bit (Y12 )", - .guid = UVC_GUID_FORMAT_Y12, - .fcc = V4L2_PIX_FMT_Y12, - }, - { - .name = "Greyscale 16-bit (Y16 )", - .guid = UVC_GUID_FORMAT_Y16, - .fcc = V4L2_PIX_FMT_Y16, - }, - { - .name = "BGGR Bayer (BY8 )", - .guid = UVC_GUID_FORMAT_BY8, - .fcc = V4L2_PIX_FMT_SBGGR8, - }, - { - .name = "BGGR Bayer (BA81)", - .guid = UVC_GUID_FORMAT_BA81, - .fcc = V4L2_PIX_FMT_SBGGR8, - }, - { - .name = "GBRG Bayer (GBRG)", - .guid = UVC_GUID_FORMAT_GBRG, - .fcc = V4L2_PIX_FMT_SGBRG8, - }, - { - .name = "GRBG Bayer (GRBG)", - .guid = UVC_GUID_FORMAT_GRBG, - .fcc = V4L2_PIX_FMT_SGRBG8, - }, - { - .name = "RGGB Bayer (RGGB)", - .guid = UVC_GUID_FORMAT_RGGB, - .fcc = V4L2_PIX_FMT_SRGGB8, - }, - { - .name = "RGB565", - .guid = UVC_GUID_FORMAT_RGBP, - .fcc = V4L2_PIX_FMT_RGB565, - }, - { - .name = "BGR 8:8:8 (BGR3)", - .guid = UVC_GUID_FORMAT_BGR3, - .fcc = V4L2_PIX_FMT_BGR24, - }, - { - .name = "BGRA/X 8:8:8:8 (BGR4)", - .guid = UVC_GUID_FORMAT_BGR4, - .fcc = V4L2_PIX_FMT_XBGR32, - }, - { - .name = "H.264", - .guid = UVC_GUID_FORMAT_H264, - .fcc = V4L2_PIX_FMT_H264, - }, - { - .name = "H.265", - .guid = UVC_GUID_FORMAT_H265, - .fcc = V4L2_PIX_FMT_HEVC, - }, - { - .name = "Greyscale 8 L/R (Y8I)", - .guid = UVC_GUID_FORMAT_Y8I, - .fcc = V4L2_PIX_FMT_Y8I, - }, - { - .name = "Greyscale 12 L/R (Y12I)", - .guid = UVC_GUID_FORMAT_Y12I, - .fcc = V4L2_PIX_FMT_Y12I, - }, - { - .name = "Depth data 16-bit (Z16)", - .guid = UVC_GUID_FORMAT_Z16, - .fcc = V4L2_PIX_FMT_Z16, - }, - { - .name = "Bayer 10-bit (SRGGB10P)", - .guid = UVC_GUID_FORMAT_RW10, - .fcc = V4L2_PIX_FMT_SRGGB10P, - }, - { - .name = "Bayer 16-bit (SBGGR16)", - .guid = UVC_GUID_FORMAT_BG16, - .fcc = V4L2_PIX_FMT_SBGGR16, - }, - { - .name = "Bayer 16-bit (SGBRG16)", - .guid = UVC_GUID_FORMAT_GB16, - .fcc = V4L2_PIX_FMT_SGBRG16, - }, - { - .name = "Bayer 16-bit (SRGGB16)", - .guid = UVC_GUID_FORMAT_RG16, - .fcc = V4L2_PIX_FMT_SRGGB16, - }, - { - .name = "Bayer 16-bit (SGRBG16)", - .guid = UVC_GUID_FORMAT_GR16, - .fcc = V4L2_PIX_FMT_SGRBG16, - }, - { - .name = "Depth data 16-bit (Z16)", - .guid = UVC_GUID_FORMAT_INVZ, - .fcc = V4L2_PIX_FMT_Z16, - }, - { - .name = "Greyscale 10-bit (Y10 )", - .guid = UVC_GUID_FORMAT_INVI, - .fcc = V4L2_PIX_FMT_Y10, - }, - { - .name = "IR:Depth 26-bit (INZI)", - .guid = UVC_GUID_FORMAT_INZI, - .fcc = V4L2_PIX_FMT_INZI, - }, - { - .name = "4-bit Depth Confidence (Packed)", - .guid = UVC_GUID_FORMAT_CNF4, - .fcc = V4L2_PIX_FMT_CNF4, - }, - { - .name = "HEVC", - .guid = UVC_GUID_FORMAT_HEVC, - .fcc = V4L2_PIX_FMT_HEVC, - }, -}; - -static inline struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) -{ - unsigned int len = ARRAY_SIZE(uvc_fmts); - unsigned int i; - - for (i = 0; i < len; ++i) { - if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) - return &uvc_fmts[i]; - } - - return NULL; -} +struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]); #endif /* __LINUX_V4L2_UVC_H */ From 69f8781559a71922c9c4fd144e61d49bc4b8c0d4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:01 -0300 Subject: [PATCH 075/142] usb: uvc: make uvc_format_desc table const Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8ecb17a86c0fbb86ea9fb4fa26e742600e945794 commit 8ecb17a86c0fbb86ea9fb4fa26e742600e945794 Author: Michael Grzeschik Date: Fri, 27 Jan 2023 00:14:55 +0100 Since the uvc_fmts array can not be modified we declare it const and change every user of the uvc_format_by_guid function aswell. Reviewed-by: Laurent Pinchart Reviewed-by: Daniel Scally Tested-by: Daniel Scally Signed-off-by: Michael Grzeschik Link: https://lore.kernel.org/r/20230126231456.3402323-5-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/media/common/uvc.c | 4 ++-- drivers/media/usb/uvc/uvc_driver.c | 2 +- drivers/usb/gadget/function/uvc_v4l2.c | 8 ++++---- include/linux/usb/uvc.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c index bb96d9e26d80..2b4df3e8f48a 100644 --- a/drivers/media/common/uvc.c +++ b/drivers/media/common/uvc.c @@ -11,7 +11,7 @@ * Video formats */ -static struct uvc_format_desc uvc_fmts[] = { +static const struct uvc_format_desc uvc_fmts[] = { { .name = "YUV 4:2:2 (YUYV)", .guid = UVC_GUID_FORMAT_YUY2, @@ -204,7 +204,7 @@ static struct uvc_format_desc uvc_fmts[] = { }, }; -struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) +const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) { unsigned int len = ARRAY_SIZE(uvc_fmts); unsigned int i; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 478dc7d7a56c..42ff9795f700 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -225,7 +225,7 @@ static int uvc_parse_format(struct uvc_device *dev, { struct usb_interface *intf = streaming->intf; struct usb_host_interface *alts = intf->cur_altsetting; - struct uvc_format_desc *fmtdesc; + const struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; unsigned int width_multiplier = 1; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index bb8f39cdf8ae..1d417eb6499a 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -27,10 +27,10 @@ #include "uvc_v4l2.h" #include "uvc_configfs.h" -static struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) +static const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) { char guid[16] = UVC_GUID_FORMAT_MJPEG; - struct uvc_format_desc *format; + const struct uvc_format_desc *format; struct uvcg_uncompressed *unc; if (uformat->type == UVCG_UNCOMPRESSED) { @@ -119,7 +119,7 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, struct uvcg_format *uformat = NULL; list_for_each_entry(format, &uvc->header->formats, entry) { - struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); + const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); if (fmtdesc->fcc == pixelformat) { uformat = format->fmt; @@ -394,7 +394,7 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_format_desc *fmtdesc; + const struct uvc_format_desc *fmtdesc; struct uvcg_format *uformat; if (f->index >= uvc->header->num_fmt) diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 8cebb46602a1..b0210c5c5406 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -154,6 +154,6 @@ struct uvc_format_desc { u32 fcc; }; -struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]); +const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]); #endif /* __LINUX_V4L2_UVC_H */ From c5ab7c075804aca8357f7e08c95e9a04c79ec28d Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:02 -0300 Subject: [PATCH 076/142] usb: uvc: use v4l2_fill_fmtdesc instead of open coded format name Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2d83eb5d24e1c8dba386928fcbf76d3b581a632d commit 2d83eb5d24e1c8dba386928fcbf76d3b581a632d Author: Michael Grzeschik Date: Fri, 27 Jan 2023 00:14:56 +0100 Since v4l2_fill_fmtdesc will be called in the ioctl v4l_enum_fmt anyway. We can set the format description and compressed flag from v4l_fill_fmtdesc and can remove the extra name field in uvc_format_desc. Reviewed-by: Daniel Scally Tested-by: Daniel Scally Signed-off-by: Michael Grzeschik Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20230126231456.3402323-6-m.grzeschik@pengutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/media/common/uvc.c | 38 -------------------------- drivers/usb/gadget/function/uvc_v4l2.c | 6 ---- include/linux/usb/uvc.h | 1 - 3 files changed, 45 deletions(-) diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c index 2b4df3e8f48a..9c0ba7a6c185 100644 --- a/drivers/media/common/uvc.c +++ b/drivers/media/common/uvc.c @@ -13,192 +13,154 @@ static const struct uvc_format_desc uvc_fmts[] = { { - .name = "YUV 4:2:2 (YUYV)", .guid = UVC_GUID_FORMAT_YUY2, .fcc = V4L2_PIX_FMT_YUYV, }, { - .name = "YUV 4:2:2 (YUYV)", .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, .fcc = V4L2_PIX_FMT_YUYV, }, { - .name = "YUV 4:2:0 (NV12)", .guid = UVC_GUID_FORMAT_NV12, .fcc = V4L2_PIX_FMT_NV12, }, { - .name = "MJPEG", .guid = UVC_GUID_FORMAT_MJPEG, .fcc = V4L2_PIX_FMT_MJPEG, }, { - .name = "YVU 4:2:0 (YV12)", .guid = UVC_GUID_FORMAT_YV12, .fcc = V4L2_PIX_FMT_YVU420, }, { - .name = "YUV 4:2:0 (I420)", .guid = UVC_GUID_FORMAT_I420, .fcc = V4L2_PIX_FMT_YUV420, }, { - .name = "YUV 4:2:0 (M420)", .guid = UVC_GUID_FORMAT_M420, .fcc = V4L2_PIX_FMT_M420, }, { - .name = "YUV 4:2:2 (UYVY)", .guid = UVC_GUID_FORMAT_UYVY, .fcc = V4L2_PIX_FMT_UYVY, }, { - .name = "Greyscale 8-bit (Y800)", .guid = UVC_GUID_FORMAT_Y800, .fcc = V4L2_PIX_FMT_GREY, }, { - .name = "Greyscale 8-bit (Y8 )", .guid = UVC_GUID_FORMAT_Y8, .fcc = V4L2_PIX_FMT_GREY, }, { - .name = "Greyscale 8-bit (D3DFMT_L8)", .guid = UVC_GUID_FORMAT_D3DFMT_L8, .fcc = V4L2_PIX_FMT_GREY, }, { - .name = "IR 8-bit (L8_IR)", .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, .fcc = V4L2_PIX_FMT_GREY, }, { - .name = "Greyscale 10-bit (Y10 )", .guid = UVC_GUID_FORMAT_Y10, .fcc = V4L2_PIX_FMT_Y10, }, { - .name = "Greyscale 12-bit (Y12 )", .guid = UVC_GUID_FORMAT_Y12, .fcc = V4L2_PIX_FMT_Y12, }, { - .name = "Greyscale 16-bit (Y16 )", .guid = UVC_GUID_FORMAT_Y16, .fcc = V4L2_PIX_FMT_Y16, }, { - .name = "BGGR Bayer (BY8 )", .guid = UVC_GUID_FORMAT_BY8, .fcc = V4L2_PIX_FMT_SBGGR8, }, { - .name = "BGGR Bayer (BA81)", .guid = UVC_GUID_FORMAT_BA81, .fcc = V4L2_PIX_FMT_SBGGR8, }, { - .name = "GBRG Bayer (GBRG)", .guid = UVC_GUID_FORMAT_GBRG, .fcc = V4L2_PIX_FMT_SGBRG8, }, { - .name = "GRBG Bayer (GRBG)", .guid = UVC_GUID_FORMAT_GRBG, .fcc = V4L2_PIX_FMT_SGRBG8, }, { - .name = "RGGB Bayer (RGGB)", .guid = UVC_GUID_FORMAT_RGGB, .fcc = V4L2_PIX_FMT_SRGGB8, }, { - .name = "RGB565", .guid = UVC_GUID_FORMAT_RGBP, .fcc = V4L2_PIX_FMT_RGB565, }, { - .name = "BGR 8:8:8 (BGR3)", .guid = UVC_GUID_FORMAT_BGR3, .fcc = V4L2_PIX_FMT_BGR24, }, { - .name = "BGRA/X 8:8:8:8 (BGR4)", .guid = UVC_GUID_FORMAT_BGR4, .fcc = V4L2_PIX_FMT_XBGR32, }, { - .name = "H.264", .guid = UVC_GUID_FORMAT_H264, .fcc = V4L2_PIX_FMT_H264, }, { - .name = "H.265", .guid = UVC_GUID_FORMAT_H265, .fcc = V4L2_PIX_FMT_HEVC, }, { - .name = "Greyscale 8 L/R (Y8I)", .guid = UVC_GUID_FORMAT_Y8I, .fcc = V4L2_PIX_FMT_Y8I, }, { - .name = "Greyscale 12 L/R (Y12I)", .guid = UVC_GUID_FORMAT_Y12I, .fcc = V4L2_PIX_FMT_Y12I, }, { - .name = "Depth data 16-bit (Z16)", .guid = UVC_GUID_FORMAT_Z16, .fcc = V4L2_PIX_FMT_Z16, }, { - .name = "Bayer 10-bit (SRGGB10P)", .guid = UVC_GUID_FORMAT_RW10, .fcc = V4L2_PIX_FMT_SRGGB10P, }, { - .name = "Bayer 16-bit (SBGGR16)", .guid = UVC_GUID_FORMAT_BG16, .fcc = V4L2_PIX_FMT_SBGGR16, }, { - .name = "Bayer 16-bit (SGBRG16)", .guid = UVC_GUID_FORMAT_GB16, .fcc = V4L2_PIX_FMT_SGBRG16, }, { - .name = "Bayer 16-bit (SRGGB16)", .guid = UVC_GUID_FORMAT_RG16, .fcc = V4L2_PIX_FMT_SRGGB16, }, { - .name = "Bayer 16-bit (SGRBG16)", .guid = UVC_GUID_FORMAT_GR16, .fcc = V4L2_PIX_FMT_SGRBG16, }, { - .name = "Depth data 16-bit (Z16)", .guid = UVC_GUID_FORMAT_INVZ, .fcc = V4L2_PIX_FMT_Z16, }, { - .name = "Greyscale 10-bit (Y10 )", .guid = UVC_GUID_FORMAT_INVI, .fcc = V4L2_PIX_FMT_Y10, }, { - .name = "IR:Depth 26-bit (INZI)", .guid = UVC_GUID_FORMAT_INZI, .fcc = V4L2_PIX_FMT_INZI, }, { - .name = "4-bit Depth Confidence (Packed)", .guid = UVC_GUID_FORMAT_CNF4, .fcc = V4L2_PIX_FMT_CNF4, }, { - .name = "HEVC", .guid = UVC_GUID_FORMAT_HEVC, .fcc = V4L2_PIX_FMT_HEVC, }, diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 1d417eb6499a..537440530a6c 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -404,15 +404,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) if (!uformat) return -EINVAL; - if (uformat->type != UVCG_UNCOMPRESSED) - f->flags |= V4L2_FMT_FLAG_COMPRESSED; - fmtdesc = to_uvc_format(uformat); f->pixelformat = fmtdesc->fcc; - strscpy(f->description, fmtdesc->name, sizeof(f->description)); - f->description[strlen(fmtdesc->name) - 1] = 0; - return 0; } diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index b0210c5c5406..88d96095bcb1 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -149,7 +149,6 @@ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} struct uvc_format_desc { - char *name; u8 guid[16]; u32 fcc; }; From 9934c847d1d6fe2ec95663a29a91306803d1ac3d Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:02 -0300 Subject: [PATCH 077/142] Documentation: usb: correct spelling Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e4157519ad46c7bd81b1c1e76d634aa0033d00e5 commit e4157519ad46c7bd81b1c1e76d634aa0033d00e5 Author: Randy Dunlap Date: Thu, 26 Jan 2023 22:40:02 -0800 Correct spelling problems for Documentation/usb/ as reported by codespell. Signed-off-by: Randy Dunlap Cc: linux-usb@vger.kernel.org Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Link: https://lore.kernel.org/r/20230127064005.1558-33-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/usb/chipidea.rst | 19 ++++++++++--------- Documentation/usb/gadget-testing.rst | 2 +- Documentation/usb/mass-storage.rst | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Documentation/usb/chipidea.rst b/Documentation/usb/chipidea.rst index 68473abe2823..d9920c24eca0 100644 --- a/Documentation/usb/chipidea.rst +++ b/Documentation/usb/chipidea.rst @@ -35,10 +35,10 @@ which can show otg fsm variables and some controller registers value:: 1) Power up 2 Freescale i.MX6Q sabre SD boards with gadget class driver loaded (e.g. g_mass_storage). -2) Connect 2 boards with usb cable with one end is micro A plug, the other end +2) Connect 2 boards with usb cable: one end is micro A plug, the other end is micro B plug. - The A-device(with micro A plug inserted) should enumerate B-device. + The A-device (with micro A plug inserted) should enumerate B-device. 3) Role switch @@ -54,18 +54,19 @@ which can show otg fsm variables and some controller registers value:: echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req - or, by introducing HNP polling, B-Host can know when A-peripheral wish - to be host role, so this role switch also can be trigged in A-peripheral - side by answering the polling from B-Host, this can be done on A-device:: + or, by introducing HNP polling, B-Host can know when A-peripheral wishes to + be in the host role, so this role switch also can be triggered in + A-peripheral side by answering the polling from B-Host. This can be done on + A-device:: echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req A-device should switch back to host and enumerate B-device. -5) Remove B-device(unplug micro B plug) and insert again in 10 seconds, +5) Remove B-device (unplug micro B plug) and insert again in 10 seconds; A-device should enumerate B-device again. -6) Remove B-device(unplug micro B plug) and insert again after 10 seconds, +6) Remove B-device (unplug micro B plug) and insert again after 10 seconds; A-device should NOT enumerate B-device. if A-device wants to use bus: @@ -105,7 +106,7 @@ July 27, 2012 Revision 2.0 version 1.1a" 2. How to enable USB as system wakeup source -------------------------------------------- Below is the example for how to enable USB as system wakeup source -at imx6 platform. +on an imx6 platform. 2.1 Enable core's wakeup:: @@ -128,6 +129,6 @@ at imx6 platform. echo enabled > /sys/bus/usb/devices/1-1/power/wakeup If the system has only one usb port, and you want usb wakeup at this port, you -can use below script to enable usb wakeup:: +can use the below script to enable usb wakeup:: for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done; diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 2278c9ffb74a..2fca40443dc9 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -813,7 +813,7 @@ the user must provide the following: ================== ==================================================== Each frame description contains frame interval specification, and each -such specification consists of a number of lines with an inverval value +such specification consists of a number of lines with an interval value in each line. The rules stated above are best illustrated with an example:: # mkdir functions/uvc.usb0/control/header/h diff --git a/Documentation/usb/mass-storage.rst b/Documentation/usb/mass-storage.rst index f399ec631599..80a601a60931 100644 --- a/Documentation/usb/mass-storage.rst +++ b/Documentation/usb/mass-storage.rst @@ -150,7 +150,7 @@ Module parameters - bcdDevice -- USB Device version (BCD) (16 bit integer) - iManufacturer -- USB Manufacturer string (string) - iProduct -- USB Product string (string) - - iSerialNumber -- SerialNumber string (sting) + - iSerialNumber -- SerialNumber string (string) sysfs entries ============= From 77cf4a620bce90507d9b8c9543d361190a2b13b7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:03 -0300 Subject: [PATCH 078/142] dt-bindings: usb: phy: nop: Fix a typo ("specifiy") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c2c304dfc983060085c92153071d0e0f4cb12a37 commit c2c304dfc983060085c92153071d0e0f4cb12a37 Author: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 29 Jan 2023 13:42:58 +0100 Spell it correctly as "specify". Signed-off-by: Jonathan Neuschäfer Acked-by: Rob Herring Link: https://lore.kernel.org/r/20230129124258.1295503-1-j.neuschaefer@gmx.net Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml index 326131dcf14d..921b986adc47 100644 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml @@ -35,7 +35,7 @@ properties: maxItems: 1 vbus-regulator: - description: Should specifiy the regulator supplying current drawn from + description: Should specify the regulator supplying current drawn from the VBus line. $ref: /schemas/types.yaml#/definitions/phandle From 4132ee7e0948539089db2d300763832d31fe86c9 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:03 -0300 Subject: [PATCH 079/142] dt-bindings: usb: fsa4480: Use generic node name Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e225947035bcd15f95e6f340e708fa37f309c2c3 commit e225947035bcd15f95e6f340e708fa37f309c2c3 Author: Konrad Dybcio Date: Mon, 30 Jan 2023 12:31:50 +0100 Node names should be generic. Change fsa4480@ to typec-mux@. Fixes: 01afa882f12d ("dt-bindings: usb: Add binding for fcs,fsa4480") Signed-off-by: Konrad Dybcio Acked-by: Rob Herring Link: https://lore.kernel.org/r/20230130113151.2130063-1-konrad.dybcio@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml index 9473f26b0621..51120fe90322 100644 --- a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml +++ b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml @@ -51,7 +51,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - fsa4480@42 { + typec-mux@42 { compatible = "fcs,fsa4480"; reg = <0x42>; From 3cd5040ed4165d91b5498c5324038febd6296cf2 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:04 -0300 Subject: [PATCH 080/142] usb: chipidea: ci_hdrc_imx: use dev_err_probe Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3a1bd0494352bd89ec50ee595ababfe180f2d63e commit 3a1bd0494352bd89ec50ee595ababfe180f2d63e Author: Alexander Stein Date: Mon, 30 Jan 2023 10:41:51 +0100 Add error message if finding USB PHY fails or is deferred. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20230130094151.95174-1-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/chipidea/ci_hdrc_imx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 0dc482542d85..2eeccf4ec9d6 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -413,15 +413,19 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); - if (ret != -ENODEV) + if (ret != -ENODEV) { + dev_err_probe(dev, ret, "Failed to parse fsl,usbphy\n"); goto err_clk; + } data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); - if (ret == -ENODEV) + if (ret == -ENODEV) { data->phy = NULL; - else + } else { + dev_err_probe(dev, ret, "Failed to parse phys\n"); goto err_clk; + } } } From 9bab4922f299b3966f3df25314cf312d6ad527fe Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:04 -0300 Subject: [PATCH 081/142] usb: early: xhci-dbc: Fix a potential out-of-bound memory access Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a4a97ab3db5c081eb6e7dba91306adefb461e0bd commit a4a97ab3db5c081eb6e7dba91306adefb461e0bd Author: Christophe JAILLET Date: Sun, 29 Jan 2023 19:23:08 +0100 If xdbc_bulk_write() fails, the values in 'buf' can be anything. So the string is not guaranteed to be NULL terminated when xdbc_trace() is called. Reserve an extra byte, which will be zeroed automatically because 'buf' is a static variable, in order to avoid troubles, should it happen. Fixes: aeb9dd1de98c ("usb/early: Add driver for xhci debug capability") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/d6a7562c5e839a195cee85db6dc81817f9372cb1.1675016180.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/early/xhci-dbc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index bfb7e2b85299..7ef0a4b39762 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -874,7 +874,8 @@ retry: static void early_xdbc_write(struct console *con, const char *str, u32 n) { - static char buf[XDBC_MAX_PACKET]; + /* static variables are zeroed, so buf is always NULL terminated */ + static char buf[XDBC_MAX_PACKET + 1]; int chunk, ret; int use_cr = 0; From 4c3f915f3fca0dacfdd351a8b830103fed457725 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:04 -0300 Subject: [PATCH 082/142] usb: early: xhci-dbc: Optimize early_xdbc_write() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e662c16f822fe93ae11769cffb8bf0d867417633 commit e662c16f822fe93ae11769cffb8bf0d867417633 Author: Christophe JAILLET Date: Sun, 29 Jan 2023 19:23:09 +0100 There is no point in zeroing 'buf'. It would be cleared only once, and if the 'while' loop is executed several times, all but the first run would have a 'dirty' buffer. Moreover, the size of the chunk is computed in the loop and this size is passed to xdbc_bulk_write(). So remove this useless memset(). Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/687bbcd940c59fbddd0e3a8b578fd3422962e50f.1675016180.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/early/xhci-dbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 7ef0a4b39762..02e793d3aa67 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -881,7 +881,7 @@ static void early_xdbc_write(struct console *con, const char *str, u32 n) if (!xdbc.xdbc_reg) return; - memset(buf, 0, XDBC_MAX_PACKET); + while (n > 0) { for (chunk = 0; chunk < XDBC_MAX_PACKET && n > 0; str++, chunk++, n--) { From 252bc7ba65d1bc96e01abab24a63a870dc342e38 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:05 -0300 Subject: [PATCH 083/142] usb: early: xhci-dbc: Use memcpy_and_pad() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=49814e2c9c5776c7dc7cfd151aba15bd91804c3c commit 49814e2c9c5776c7dc7cfd151aba15bd91804c3c Author: Christophe JAILLET Date: Sun, 29 Jan 2023 19:23:10 +0100 Instead of zeroing some memory and then copying data in part or all of it, use memcpy_and_pad(). This avoids writing some memory twice and should save a few cycles. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/b447a7e9778d3f9e6997eb9494f1687dc2d5d3bf.1675016180.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/early/xhci-dbc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 02e793d3aa67..a6834b449f17 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -499,8 +499,7 @@ static int xdbc_bulk_transfer(void *data, int size, bool read) addr = xdbc.in_dma; xdbc.flags |= XDBC_FLAGS_IN_PROCESS; } else { - memset(xdbc.out_buf, 0, XDBC_MAX_PACKET); - memcpy(xdbc.out_buf, data, size); + memcpy_and_pad(xdbc.out_buf, XDBC_MAX_PACKET, data, size, 0); addr = xdbc.out_dma; xdbc.flags |= XDBC_FLAGS_OUT_PROCESS; } From 22be31384d12f76d778a44316d0c08ce4bd588ac Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:05 -0300 Subject: [PATCH 084/142] USB: core: Don't hold device lock while reading the "descriptors" sysfs file Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=45bf39f8df7f05efb83b302c65ae3b9bc92b7065 commit 45bf39f8df7f05efb83b302c65ae3b9bc92b7065 Author: Alan Stern Date: Tue, 31 Jan 2023 15:49:04 -0500 Ever since commit 83e83ecb79a8 ("usb: core: get config and string descriptors for unauthorized devices") was merged in 2013, there has been no mechanism for reallocating the rawdescriptors buffers in struct usb_device after the initial enumeration. Before that commit, the buffers would be deallocated when a device was deauthorized and reallocated when it was authorized and enumerated. This means that the locking in the read_descriptors() routine is not needed, since the buffers it reads will never be reallocated while the routine is running. This locking can interfere with user programs trying to read a hub's descriptors via sysfs while new child devices of the hub are being initialized, since the hub is locked during this procedure. Since the locking in read_descriptors() hasn't been needed for over nine years, we can remove it. Reported-and-tested-by: Troels Liebe Bentsen Signed-off-by: Alan Stern CC: stable@vger.kernel.org Link: https://lore.kernel.org/r/Y9l+wDTRbuZABzsE@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/core/hub.c | 5 ++--- drivers/usb/core/sysfs.c | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ebe803d4ad4d..d837548d2024 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2389,9 +2389,8 @@ static int usb_enumerate_device_otg(struct usb_device *udev) * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) * - * This is only called by usb_new_device() and usb_authorize_device() - * and FIXME -- all comments that apply to them apply here wrt to - * environment. + * This is only called by usb_new_device() -- all comments that apply there + * apply here wrt to environment. * * If the device is WUSB and not authorized, we don't attempt to read * the string descriptors, as they will be errored out by the device diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 8217032dfb85..b63f78e48c74 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -869,11 +869,7 @@ read_descriptors(struct file *filp, struct kobject *kobj, size_t srclen, n; int cfgno; void *src; - int retval; - retval = usb_lock_device_interruptible(udev); - if (retval < 0) - return -EINTR; /* The binary attribute begins with the device descriptor. * Following that are the raw descriptor entries for all the * configurations (config plus subsidiary descriptors). @@ -898,7 +894,6 @@ read_descriptors(struct file *filp, struct kobject *kobj, off -= srclen; } } - usb_unlock_device(udev); return count - nleft; } From 664930684065afe7a21e2b7f4c247ca007ff5127 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:06 -0300 Subject: [PATCH 085/142] usb: remove ohci-tmio driver Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=89480065bc4ccacb470c9b8b4e4ac38fc2c2b005 commit 89480065bc4ccacb470c9b8b4e4ac38fc2c2b005 Author: Arnd Bergmann Date: Fri, 7 Oct 2022 12:10:01 +0200 The TMIO MFD driver is getting removed, so its OHCI portion is not used any more either. Cc: Alan Stern Cc: linux-usb@vger.kernel.org Acked-by: Greg Kroah-Hartman Acked-by: Robert Jarzmik Signed-off-by: Arnd Bergmann Signed-off-by: Desnes Nunes --- drivers/usb/host/ohci-hcd.c | 18 -- drivers/usb/host/ohci-tmio.c | 364 ----------------------------------- 2 files changed, 382 deletions(-) delete mode 100644 drivers/usb/host/ohci-tmio.c diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 0457dd9f6c19..4f9982ecfb58 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1264,11 +1264,6 @@ MODULE_LICENSE ("GPL"); #define SM501_OHCI_DRIVER ohci_hcd_sm501_driver #endif -#ifdef CONFIG_MFD_TC6393XB -#include "ohci-tmio.c" -#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver -#endif - static int __init ohci_hcd_mod_init(void) { int retval = 0; @@ -1306,19 +1301,9 @@ static int __init ohci_hcd_mod_init(void) goto error_sm501; #endif -#ifdef TMIO_OHCI_DRIVER - retval = platform_driver_register(&TMIO_OHCI_DRIVER); - if (retval < 0) - goto error_tmio; -#endif - return retval; /* Error path */ -#ifdef TMIO_OHCI_DRIVER - platform_driver_unregister(&TMIO_OHCI_DRIVER); - error_tmio: -#endif #ifdef SM501_OHCI_DRIVER platform_driver_unregister(&SM501_OHCI_DRIVER); error_sm501: @@ -1345,9 +1330,6 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { -#ifdef TMIO_OHCI_DRIVER - platform_driver_unregister(&TMIO_OHCI_DRIVER); -#endif #ifdef SM501_OHCI_DRIVER platform_driver_unregister(&SM501_OHCI_DRIVER); #endif diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c deleted file mode 100644 index 49539b9f0e94..000000000000 --- a/drivers/usb/host/ohci-tmio.c +++ /dev/null @@ -1,364 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * OHCI HCD(Host Controller Driver) for USB. - * - *(C) Copyright 1999 Roman Weissgaerber - *(C) Copyright 2000-2002 David Brownell - *(C) Copyright 2002 Hewlett-Packard Company - * - * Bus glue for Toshiba Mobile IO(TMIO) Controller's OHCI core - * (C) Copyright 2005 Chris Humbert - * (C) Copyright 2007, 2008 Dmitry Baryshkov - * - * This is known to work with the following variants: - * TC6393XB revision 3 (32kB SRAM) - * - * The TMIO's OHCI core DMAs through a small internal buffer that - * is directly addressable by the CPU. - * - * Written from sparse documentation from Toshiba and Sharp's driver - * for the 2.4 kernel, - * usb-ohci-tc6393.c(C) Copyright 2004 Lineo Solutions, Inc. - */ - -#include -#include -#include -#include - -/*-------------------------------------------------------------------------*/ - -/* - * USB Host Controller Configuration Register - */ -#define CCR_REVID 0x08 /* b Revision ID */ -#define CCR_BASE 0x10 /* l USB Control Register Base Address Low */ -#define CCR_ILME 0x40 /* b Internal Local Memory Enable */ -#define CCR_PM 0x4c /* w Power Management */ -#define CCR_INTC 0x50 /* b INT Control */ -#define CCR_LMW1L 0x54 /* w Local Memory Window 1 LMADRS Low */ -#define CCR_LMW1H 0x56 /* w Local Memory Window 1 LMADRS High */ -#define CCR_LMW1BL 0x58 /* w Local Memory Window 1 Base Address Low */ -#define CCR_LMW1BH 0x5A /* w Local Memory Window 1 Base Address High */ -#define CCR_LMW2L 0x5C /* w Local Memory Window 2 LMADRS Low */ -#define CCR_LMW2H 0x5E /* w Local Memory Window 2 LMADRS High */ -#define CCR_LMW2BL 0x60 /* w Local Memory Window 2 Base Address Low */ -#define CCR_LMW2BH 0x62 /* w Local Memory Window 2 Base Address High */ -#define CCR_MISC 0xFC /* b MISC */ - -#define CCR_PM_GKEN 0x0001 -#define CCR_PM_CKRNEN 0x0002 -#define CCR_PM_USBPW1 0x0004 -#define CCR_PM_USBPW2 0x0008 -#define CCR_PM_USBPW3 0x0010 -#define CCR_PM_PMEE 0x0100 -#define CCR_PM_PMES 0x8000 - -/*-------------------------------------------------------------------------*/ - -struct tmio_hcd { - void __iomem *ccr; - spinlock_t lock; /* protects RMW cycles */ -}; - -#define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1)) - -/*-------------------------------------------------------------------------*/ - -static void tmio_write_pm(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - u16 pm; - unsigned long flags; - - spin_lock_irqsave(&tmio->lock, flags); - - pm = CCR_PM_GKEN | CCR_PM_CKRNEN | - CCR_PM_PMEE | CCR_PM_PMES; - - tmio_iowrite16(pm, tmio->ccr + CCR_PM); - spin_unlock_irqrestore(&tmio->lock, flags); -} - -static void tmio_stop_hc(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - u16 pm; - - pm = CCR_PM_GKEN | CCR_PM_CKRNEN; - switch (ohci->num_ports) { - default: - dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports); - fallthrough; - case 3: - pm |= CCR_PM_USBPW3; - fallthrough; - case 2: - pm |= CCR_PM_USBPW2; - fallthrough; - case 1: - pm |= CCR_PM_USBPW1; - } - tmio_iowrite8(0, tmio->ccr + CCR_INTC); - tmio_iowrite8(0, tmio->ccr + CCR_ILME); - tmio_iowrite16(0, tmio->ccr + CCR_BASE); - tmio_iowrite16(0, tmio->ccr + CCR_BASE + 2); - tmio_iowrite16(pm, tmio->ccr + CCR_PM); -} - -static void tmio_start_hc(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - unsigned long base = hcd->rsrc_start; - - tmio_write_pm(dev); - tmio_iowrite16(base, tmio->ccr + CCR_BASE); - tmio_iowrite16(base >> 16, tmio->ccr + CCR_BASE + 2); - tmio_iowrite8(1, tmio->ccr + CCR_ILME); - tmio_iowrite8(2, tmio->ccr + CCR_INTC); - - dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n", - tmio_ioread8(tmio->ccr + CCR_REVID), - (u64) hcd->rsrc_start, hcd->irq); -} - -static int ohci_tmio_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int ret; - - if ((ret = ohci_init(ohci)) < 0) - return ret; - - if ((ret = ohci_run(ohci)) < 0) { - dev_err(hcd->self.controller, "can't start %s\n", - hcd->self.bus_name); - ohci_stop(hcd); - return ret; - } - - return 0; -} - -static const struct hc_driver ohci_tmio_hc_driver = { - .description = hcd_name, - .product_desc = "TMIO OHCI USB Host Controller", - .hcd_priv_size = sizeof(struct ohci_hcd) + sizeof (struct tmio_hcd), - - /* generic hardware linkage */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* basic lifecycle operations */ - .start = ohci_tmio_start, - .stop = ohci_stop, - .shutdown = ohci_shutdown, - - /* managing i/o requests and associated device resources */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* scheduling support */ - .get_frame_number = ohci_get_frame, - - /* root hub support */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ -static struct platform_driver ohci_hcd_tmio_driver; - -static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - struct resource *regs = platform_get_resource(dev, IORESOURCE_MEM, 0); - struct resource *config = platform_get_resource(dev, IORESOURCE_MEM, 1); - struct resource *sram = platform_get_resource(dev, IORESOURCE_MEM, 2); - int irq = platform_get_irq(dev, 0); - struct tmio_hcd *tmio; - struct ohci_hcd *ohci; - struct usb_hcd *hcd; - int ret; - - if (usb_disabled()) - return -ENODEV; - - if (!cell || !regs || !config || !sram) - return -EINVAL; - - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev)); - if (!hcd) { - ret = -ENOMEM; - goto err_usb_create_hcd; - } - - hcd->rsrc_start = regs->start; - hcd->rsrc_len = resource_size(regs); - - tmio = hcd_to_tmio(hcd); - - spin_lock_init(&tmio->lock); - - tmio->ccr = ioremap(config->start, resource_size(config)); - if (!tmio->ccr) { - ret = -ENOMEM; - goto err_ioremap_ccr; - } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - ret = -ENOMEM; - goto err_ioremap_regs; - } - - if (cell->enable) { - ret = cell->enable(dev); - if (ret) - goto err_enable; - } - - tmio_start_hc(dev); - ohci = hcd_to_ohci(hcd); - ohci_hcd_init(ohci); - - ret = usb_hcd_setup_local_mem(hcd, sram->start, sram->start, - resource_size(sram)); - if (ret < 0) - goto err_enable; - - ret = usb_add_hcd(hcd, irq, 0); - if (ret) - goto err_add_hcd; - - device_wakeup_enable(hcd->self.controller); - if (ret == 0) - return ret; - - usb_remove_hcd(hcd); - -err_add_hcd: - tmio_stop_hc(dev); - if (cell->disable) - cell->disable(dev); -err_enable: - iounmap(hcd->regs); -err_ioremap_regs: - iounmap(tmio->ccr); -err_ioremap_ccr: - usb_put_hcd(hcd); -err_usb_create_hcd: - - return ret; -} - -static int ohci_hcd_tmio_drv_remove(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - const struct mfd_cell *cell = mfd_get_cell(dev); - - usb_remove_hcd(hcd); - tmio_stop_hc(dev); - if (cell->disable) - cell->disable(dev); - iounmap(hcd->regs); - iounmap(tmio->ccr); - usb_put_hcd(hcd); - - return 0; -} - -#ifdef CONFIG_PM -static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - unsigned long flags; - u8 misc; - int ret; - - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - - spin_lock_irqsave(&tmio->lock, flags); - - misc = tmio_ioread8(tmio->ccr + CCR_MISC); - misc |= 1 << 3; /* USSUSP */ - tmio_iowrite8(misc, tmio->ccr + CCR_MISC); - - spin_unlock_irqrestore(&tmio->lock, flags); - - if (cell->suspend) { - ret = cell->suspend(dev); - if (ret) - return ret; - } - return 0; -} - -static int ohci_hcd_tmio_drv_resume(struct platform_device *dev) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - struct usb_hcd *hcd = platform_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct tmio_hcd *tmio = hcd_to_tmio(hcd); - unsigned long flags; - u8 misc; - int ret; - - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - - if (cell->resume) { - ret = cell->resume(dev); - if (ret) - return ret; - } - - tmio_start_hc(dev); - - spin_lock_irqsave(&tmio->lock, flags); - - misc = tmio_ioread8(tmio->ccr + CCR_MISC); - misc &= ~(1 << 3); /* USSUSP */ - tmio_iowrite8(misc, tmio->ccr + CCR_MISC); - - spin_unlock_irqrestore(&tmio->lock, flags); - - ohci_resume(hcd, false); - - return 0; -} -#else -#define ohci_hcd_tmio_drv_suspend NULL -#define ohci_hcd_tmio_drv_resume NULL -#endif - -static struct platform_driver ohci_hcd_tmio_driver = { - .probe = ohci_hcd_tmio_drv_probe, - .remove = ohci_hcd_tmio_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .suspend = ohci_hcd_tmio_drv_suspend, - .resume = ohci_hcd_tmio_drv_resume, - .driver = { - .name = "tmio-ohci", - }, -}; From 529bb13bf5b7a21d19cec3ca62e2364173ff60ae Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:07 -0300 Subject: [PATCH 086/142] usb: ohci-omap: avoid unused-variable warning Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=903b39e119245b52b9e929984189d7c96aa44d55 commit 903b39e119245b52b9e929984189d7c96aa44d55 Author: Arnd Bergmann Date: Wed, 18 Jan 2023 09:27:34 +0100 The dead code removal has led to 'need_transceiver' not being used at all when OTG support is disabled: drivers/usb/host/ohci-omap.c: In function 'ohci_omap_reset': drivers/usb/host/ohci-omap.c:99:33: error: unused variable 'need_transceiver' [-Werror=unused-variable] 99 | int need_transceiver = (config->otg != 0); Change the #ifdef check into an IS_ENABLED() check to make the code more readable and let the compiler see where it is used. Fixes: 8825acd7cc8a ("ARM: omap1: remove dead code") Reviewed-by: Greg Kroah-Hartman Acked-by: Alan Stern Signed-off-by: Arnd Bergmann Signed-off-by: Desnes Nunes --- drivers/usb/host/ohci-omap.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index cb29701df911..dd8b50dcd5a4 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -67,8 +67,6 @@ static void omap_ohci_clock_power(struct ohci_omap_priv *priv, int on) } } -#ifdef CONFIG_USB_OTG - static void start_hnp(struct ohci_hcd *ohci) { struct usb_hcd *hcd = ohci_to_hcd(ohci); @@ -87,8 +85,6 @@ static void start_hnp(struct ohci_hcd *ohci) local_irq_restore(flags); } -#endif - /*-------------------------------------------------------------------------*/ static int ohci_omap_reset(struct usb_hcd *hcd) @@ -115,8 +111,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) if (config->ocpi_enable) config->ocpi_enable(); -#ifdef CONFIG_USB_OTG - if (need_transceiver) { + if (IS_ENABLED(CONFIG_USB_OTG) && need_transceiver) { hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!IS_ERR_OR_NULL(hcd->usb_phy)) { int status = otg_set_host(hcd->usb_phy->otg, @@ -133,7 +128,6 @@ static int ohci_omap_reset(struct usb_hcd *hcd) hcd->skip_phy_initialization = 1; ohci->start_hnp = start_hnp; } -#endif omap_ohci_clock_power(priv, 1); From 11dd9cfbaa7d0adf051f554c503783c00d65ca75 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:08 -0300 Subject: [PATCH 087/142] xhci: split out rcar/rz support from xhci-plat.c Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ec5499d338ece9db9b7590649d3cfcc4d7f9603d Conflicts: * Even though rcar/rz are not supported here, xhci-plat is. Hence, let's untangle the xhci-plat from the rcar code without having to merge all rz code, which is completely avoided here. Among other patches, this avoids commit ("usb: gadget: udc: renesas_usb3: Add support for RZ/V2M") and commit <9cad72dfc556> ("usb: gadget: Add support for RZ/V2M USB3DRD driver") commit ec5499d338ece9db9b7590649d3cfcc4d7f9603d Author: Arnd Bergmann Date: Tue, 31 Jan 2023 16:04:31 +0100 The USB_XHCI_RZV2M and USB_RENESAS_USB3 select other drivers based on the enabled SoC types, which leads to build failures when the dependencies are not met: WARNING: unmet direct dependencies detected for USB_RZV2M_USB3DRD Depends on [n]: USB_SUPPORT [=y] && USB_GADGET [=n] && (ARCH_R9A09G011 [=n] || COMPILE_TEST [=y]) Selected by [m]: - USB_XHCI_RZV2M [=m] && USB_SUPPORT [=y] && USB [=y] && USB_XHCI_HCD [=m] && USB_XHCI_PLATFORM [=m] && (ARCH_R9A09G011 [=n] || COMPILE_TEST [=y]) ERROR: modpost: "rzv2m_usb3drd_reset" [drivers/usb/host/xhci-plat-hcd.ko] undefined! The xhci-rcar driver has a reverse dependency with the xhci core, and it depends on the UDC driver in turn. To untangle this, make the xhci-rcar.ko driver a standalone module that just calls into the xhci-plat.ko module like other drivers do. This allows handling the dependency on the USB_RZV2M_USB3DRD driver to only affect the xhci-rcar module and simplify the xhci-plat module. It also allows leaving out the hacks for broken dma mask and nested devices from the rcar side and keep that only in the generic xhci driver. As a future cleanup, the marvell and dwc3 specific bits of xhci-plat.c could be moved out as well, but that is not required for this bugfix. Fixes: c52c9acc415e ("xhci: host: Add Renesas RZ/V2M SoC support") Signed-off-by: Arnd Bergmann Tested-by: Biju Das Link: https://lore.kernel.org/r/20230131150531.12347-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/Makefile | 6 +- drivers/usb/host/xhci-plat.c | 116 +++++++++++++++-------------------- drivers/usb/host/xhci-plat.h | 7 +++ drivers/usb/host/xhci-rcar.c | 91 +++++++++++++++++++++++++-- drivers/usb/host/xhci-rcar.h | 55 ----------------- 6 files changed, 148 insertions(+), 129 deletions(-) delete mode 100644 drivers/usb/host/xhci-rcar.h diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5f77d6df1baa..b96cf23fe654 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -53,7 +53,6 @@ config USB_XHCI_PCI_RENESAS config USB_XHCI_PLATFORM tristate "Generic xHCI driver for a platform device" - select USB_XHCI_RCAR if ARCH_RENESAS help Adds an xHCI host driver for a generic platform device, which provides a memory space and an irq. @@ -91,6 +90,7 @@ config USB_XHCI_RCAR tristate "xHCI support for Renesas R-Car SoCs" depends on USB_XHCI_PLATFORM depends on ARCH_RENESAS || COMPILE_TEST + default ARCH_RENESAS help Say 'Y' to enable the support for the xHCI host controller found in Renesas R-Car ARM SoCs. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 2948983618fb..1996360e7e98 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -25,14 +25,13 @@ xhci-plat-hcd-y := xhci-plat.o ifneq ($(CONFIG_USB_XHCI_MVEBU), ) xhci-plat-hcd-y += xhci-mvebu.o endif -ifneq ($(CONFIG_USB_XHCI_RCAR), ) - xhci-plat-hcd-y += xhci-rcar.o -endif ifneq ($(CONFIG_DEBUG_FS),) xhci-hcd-y += xhci-debugfs.o endif +xhci-rcar-hcd-y += xhci-rcar.o + obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o @@ -71,6 +70,7 @@ obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o +obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar-hcd.o obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk-hcd.o obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 5fb55bf19493..1fccc567aa77 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -23,7 +23,6 @@ #include "xhci.h" #include "xhci-plat.h" #include "xhci-mvebu.h" -#include "xhci-rcar.h" static struct hc_driver __read_mostly xhci_plat_hc_driver; @@ -114,14 +113,6 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { .init_quirk = xhci_mvebu_a3700_init_quirk, }; -static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) -}; - -static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) -}; - static const struct xhci_plat_priv xhci_plat_brcm = { .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, }; @@ -140,27 +131,6 @@ static const struct of_device_id usb_xhci_of_match[] = { }, { .compatible = "marvell,armada3700-xhci", .data = &xhci_plat_marvell_armada3700, - }, { - .compatible = "renesas,xhci-r8a7790", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7791", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7793", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7795", - .data = &xhci_plat_renesas_rcar_gen3, - }, { - .compatible = "renesas,xhci-r8a7796", - .data = &xhci_plat_renesas_rcar_gen3, - }, { - .compatible = "renesas,rcar-gen2-xhci", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,rcar-gen3-xhci", - .data = &xhci_plat_renesas_rcar_gen3, }, { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, @@ -173,11 +143,10 @@ static const struct of_device_id usb_xhci_of_match[] = { MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif -static int xhci_plat_probe(struct platform_device *pdev) +int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match) { - const struct xhci_plat_priv *priv_match; const struct hc_driver *driver; - struct device *sysdev, *tmpdev; + struct device *tmpdev; struct xhci_hcd *xhci; struct resource *res; struct usb_hcd *hcd, *usb3_hcd; @@ -195,31 +164,10 @@ static int xhci_plat_probe(struct platform_device *pdev) if (irq < 0) return irq; - /* - * sysdev must point to a device that is known to the system firmware - * or PCI hardware. We handle these three cases here: - * 1. xhci_plat comes from firmware - * 2. xhci_plat is child of a device from firmware (dwc3-plat) - * 3. xhci_plat is grandchild of a pci device (dwc3-pci) - */ - for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) { - if (is_of_node(sysdev->fwnode) || - is_acpi_device_node(sysdev->fwnode)) - break; -#ifdef CONFIG_PCI - else if (sysdev->bus == &pci_bus_type) - break; -#endif - } - if (!sysdev) sysdev = &pdev->dev; - if (WARN_ON(!sysdev->dma_mask)) - /* Platform did not initialize dma_mask */ - ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); - else - ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); + ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); if (ret) return ret; @@ -271,11 +219,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if (ret) goto disable_reg_clk; - if (pdev->dev.of_node) - priv_match = of_device_get_match_data(&pdev->dev); - else - priv_match = dev_get_platdata(&pdev->dev); - if (priv_match) { priv = hcd_to_xhci_priv(hcd); /* Just copy data for now */ @@ -386,8 +329,47 @@ disable_runtime: return ret; } +EXPORT_SYMBOL_GPL(xhci_plat_probe); -static int xhci_plat_remove(struct platform_device *dev) +static int xhci_generic_plat_probe(struct platform_device *pdev) +{ + const struct xhci_plat_priv *priv_match; + struct device *sysdev; + int ret; + + /* + * sysdev must point to a device that is known to the system firmware + * or PCI hardware. We handle these three cases here: + * 1. xhci_plat comes from firmware + * 2. xhci_plat is child of a device from firmware (dwc3-plat) + * 3. xhci_plat is grandchild of a pci device (dwc3-pci) + */ + for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) { + if (is_of_node(sysdev->fwnode) || + is_acpi_device_node(sysdev->fwnode)) + break; +#ifdef CONFIG_PCI + else if (sysdev->bus == &pci_bus_type) + break; +#endif + } + + if (WARN_ON(!sysdev->dma_mask)) { + /* Platform did not initialize dma_mask */ + ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); + if (ret) + return ret; + } + + if (pdev->dev.of_node) + priv_match = of_device_get_match_data(&pdev->dev); + else + priv_match = dev_get_platdata(&pdev->dev); + + return xhci_plat_probe(pdev, sysdev, priv_match); +} + +int xhci_plat_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -420,6 +402,7 @@ static int xhci_plat_remove(struct platform_device *dev) return 0; } +EXPORT_SYMBOL_GPL(xhci_plat_remove); static int __maybe_unused xhci_plat_suspend(struct device *dev) { @@ -496,13 +479,14 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) return xhci_resume(xhci, 0); } -static const struct dev_pm_ops xhci_plat_pm_ops = { +const struct dev_pm_ops xhci_plat_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, NULL) }; +EXPORT_SYMBOL_GPL(xhci_plat_pm_ops); #ifdef CONFIG_ACPI static const struct acpi_device_id usb_xhci_acpi_match[] = { @@ -513,8 +497,8 @@ static const struct acpi_device_id usb_xhci_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); #endif -static struct platform_driver usb_xhci_driver = { - .probe = xhci_plat_probe, +static struct platform_driver usb_generic_xhci_driver = { + .probe = xhci_generic_plat_probe, .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { @@ -529,13 +513,13 @@ MODULE_ALIAS("platform:xhci-hcd"); static int __init xhci_plat_init(void) { xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides); - return platform_driver_register(&usb_xhci_driver); + return platform_driver_register(&usb_generic_xhci_driver); } module_init(xhci_plat_init); static void __exit xhci_plat_exit(void) { - platform_driver_unregister(&usb_xhci_driver); + platform_driver_unregister(&usb_generic_xhci_driver); } module_exit(xhci_plat_exit); diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 1fb149d1fbce..83b5b5aa9f8e 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -21,4 +21,11 @@ struct xhci_plat_priv { #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) #define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv) + +int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, + const struct xhci_plat_priv *priv_match); + +int xhci_plat_remove(struct platform_device *dev); +extern const struct dev_pm_ops xhci_plat_pm_ops; + #endif /* _XHCI_PLAT_H */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index aef0258a7160..91764aa6a9f5 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -10,12 +10,16 @@ #include #include #include +#include #include #include #include "xhci.h" #include "xhci-plat.h" -#include "xhci-rcar.h" + +#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" +#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem" +#define XHCI_RCAR_FIRMWARE_NAME_V3 "r8a779x_usb3_v3.dlmem" /* * - The V3 firmware is for almost all R-Car Gen3 (except r8a7795 ES1.x) @@ -108,7 +112,7 @@ static int xhci_rcar_is_gen2(struct device *dev) of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); } -void xhci_rcar_start(struct usb_hcd *hcd) +static void xhci_rcar_start(struct usb_hcd *hcd) { u32 temp; @@ -203,7 +207,7 @@ static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd) } /* This function needs to initialize a "phy" of usb before */ -int xhci_rcar_init_quirk(struct usb_hcd *hcd) +static int xhci_rcar_init_quirk(struct usb_hcd *hcd) { /* If hcd->regs is NULL, we don't just call the following function */ if (!hcd->regs) @@ -215,7 +219,7 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd) return xhci_rcar_download_firmware(hcd); } -int xhci_rcar_resume_quirk(struct usb_hcd *hcd) +static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) { int ret; @@ -225,3 +229,82 @@ int xhci_rcar_resume_quirk(struct usb_hcd *hcd) return ret; } + +/* + * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set + * to 1. However, these SoCs don't support 64-bit address memory + * pointers. So, this driver clears the AC64 bit of xhci->hcc_params + * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in + * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk. + * + * And, since the firmware/internal CPU control the USBSTS.STS_HALT + * and the process speed is down when the roothub port enters U3, + * long delay for the handshake of STS_HALT is neeed in xhci_suspend() + * by using the XHCI_SLOW_SUSPEND quirk. + */ +#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ + .firmware_name = firmware, \ + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \ + XHCI_SLOW_SUSPEND, \ + .init_quirk = xhci_rcar_init_quirk, \ + .plat_start = xhci_rcar_start, \ + .resume_quirk = xhci_rcar_resume_quirk, + +static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) +}; + +static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) +}; + +static const struct of_device_id usb_xhci_of_match[] = { + { + .compatible = "renesas,xhci-r8a7790", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7791", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7793", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7795", + .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,xhci-r8a7796", + .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,rcar-gen2-xhci", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,rcar-gen3-xhci", + .data = &xhci_plat_renesas_rcar_gen3, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, usb_xhci_of_match); + +static int xhci_renesas_probe(struct platform_device *pdev) +{ + const struct xhci_plat_priv *priv_match; + + priv_match = of_device_get_match_data(&pdev->dev); + + return xhci_plat_probe(pdev, NULL, priv_match); +} + +static struct platform_driver usb_xhci_renesas_driver = { + .probe = xhci_renesas_probe, + .remove = xhci_plat_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "xhci-renesas-hcd", + .pm = &xhci_plat_pm_ops, + .of_match_table = of_match_ptr(usb_xhci_of_match), + }, +}; +module_platform_driver(usb_xhci_renesas_driver); + +MODULE_DESCRIPTION("xHCI Platform Host Controller Driver for Renesas R-Car"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h deleted file mode 100644 index 048ad3b8a6c7..000000000000 --- a/drivers/usb/host/xhci-rcar.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * drivers/usb/host/xhci-rcar.h - * - * Copyright (C) 2014 Renesas Electronics Corporation - */ - -#ifndef _XHCI_RCAR_H -#define _XHCI_RCAR_H - -#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" -#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem" -#define XHCI_RCAR_FIRMWARE_NAME_V3 "r8a779x_usb3_v3.dlmem" - -#if IS_ENABLED(CONFIG_USB_XHCI_RCAR) -void xhci_rcar_start(struct usb_hcd *hcd); -int xhci_rcar_init_quirk(struct usb_hcd *hcd); -int xhci_rcar_resume_quirk(struct usb_hcd *hcd); -#else -static inline void xhci_rcar_start(struct usb_hcd *hcd) -{ -} - -static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd) -{ - return 0; -} - -static inline int xhci_rcar_resume_quirk(struct usb_hcd *hcd) -{ - return 0; -} -#endif - -/* - * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set - * to 1. However, these SoCs don't support 64-bit address memory - * pointers. So, this driver clears the AC64 bit of xhci->hcc_params - * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in - * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk. - * - * And, since the firmware/internal CPU control the USBSTS.STS_HALT - * and the process speed is down when the roothub port enters U3, - * long delay for the handshake of STS_HALT is neeed in xhci_suspend() - * by using the XHCI_SLOW_SUSPEND quirk. - */ -#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ - .firmware_name = firmware, \ - .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \ - XHCI_SLOW_SUSPEND, \ - .init_quirk = xhci_rcar_init_quirk, \ - .plat_start = xhci_rcar_start, \ - .resume_quirk = xhci_rcar_resume_quirk, - -#endif /* _XHCI_RCAR_H */ From 692470a60e2da1086585544702888631c2896d42 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:08 -0300 Subject: [PATCH 088/142] dt-bindings: usb: Add Cypress cypd4226 Type-C controller Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fb9a1b80e68b2a16ff41b644e2a2e559461c6440 commit fb9a1b80e68b2a16ff41b644e2a2e559461c6440 Author: Wayne Chang Date: Tue, 31 Jan 2023 17:57:43 +0000 Add the device-tree binding documentation for Cypress cypd4226 dual Type-C controller. Signed-off-by: Wayne Chang Signed-off-by: Jon Hunter Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230131175748.256423-2-jonathanh@nvidia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../bindings/usb/cypress,cypd4226.yaml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml diff --git a/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml new file mode 100644 index 000000000000..75eec4a9a020 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/cypress,cypd4226.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cypress cypd4226 Type-C Controller + +maintainers: + - Wayne Chang + +description: + The Cypress cypd4226 is a dual Type-C controller that is controlled + via an I2C interface. + +properties: + compatible: + const: cypress,cypd4226 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + reg: + const: 0x08 + + interrupts: + items: + - description: cypd4226 host interrupt + + firmware-name: + enum: + - nvidia,gpu + - nvidia,jetson-agx-xavier + description: | + The name of the CCGx firmware built for product series. + should be set one of following: + - "nvidia,gpu" for the NVIDIA RTX product series + - "nvidia,jetson-agx-xavier" for the NVIDIA Jetson product series + +patternProperties: + '^connector@[01]$': + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + properties: + reg: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +anyOf: + - required: + - connector@0 + - required: + - connector@1 + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <2>; + + typec@8 { + compatible = "cypress,cypd4226"; + reg = <0x08>; + interrupt-parent = <&gpio_aon>; + interrupts = ; + firmware-name = "nvidia,jetson-agx-xavier"; + #address-cells = <1>; + #size-cells = <0>; + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + label = "USB-C"; + data-role = "dual"; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + endpoint { + remote-endpoint = <&usb_role_switch0>; + }; + }; + }; + }; + }; + }; From c7df93c3135d478634979946df3c29e7178f54b6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:09 -0300 Subject: [PATCH 089/142] xhci: fix event ring segment table related masks and variables in header Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8c1cbec9db1ab044167a7594c88bb5906c9d3ee4 commit 8c1cbec9db1ab044167a7594c88bb5906c9d3ee4 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:55 +0200 xHC controller can supports up to 1024 interrupters. To fit these change the max_interrupters varable from u8 to u16. Add a separate mask for the reserve and preserve bits [5:0] in the erst base register and use it instead of the ERST_PRT_MASK. ERSR_PTR_MASK [3:0] is intended for masking bits in the event ring dequeue pointer register. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-mem.c | 4 ++-- drivers/usb/host/xhci.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 81ca2bc1f0be..679befa97c7a 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2529,8 +2529,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Set ERST base address for ir_set 0 = 0x%llx", (unsigned long long)xhci->erst.erst_dma_addr); val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + val_64 &= ERST_BASE_RSVDP; + val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); /* Set the event ring dequeue address */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7547037b57bf..0dd92f9089fe 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -513,6 +513,9 @@ struct xhci_intr_reg { /* Preserve bits 16:31 of erst_size */ #define ERST_SIZE_MASK (0xffff << 16) +/* erst_base bitmasks */ +#define ERST_BASE_RSVDP (0x3f) + /* erst_dequeue bitmasks */ /* Dequeue ERST Segment Index (DESI) - Segment number (or alias) * where the current dequeue pointer lies. This is an optional HW hint. @@ -1774,7 +1777,7 @@ struct xhci_hcd { u8 sbrn; u16 hci_version; u8 max_slots; - u8 max_interrupters; + u16 max_interrupters; u8 max_ports; u8 isoc_threshold; /* imod_interval in ns (I * 250ns) */ From 43be6121ddb6373a3f801a7739985ba9f73b3345 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:09 -0300 Subject: [PATCH 090/142] xhci: remove xhci_test_trb_in_td_math early development check Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54f9927dfe2266402a226d5f51d38236bdca0590 commit 54f9927dfe2266402a226d5f51d38236bdca0590 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:56 +0200 Time to remove this test trb in td math check that was added in early stage of xhci driver development. It verified that the size, alignment and boundaries of the event and command rings allocated by the driver itself are correct. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-mem.c | 160 ------------------------------------ 1 file changed, 160 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 679befa97c7a..bf9bb29f924b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1929,164 +1929,6 @@ no_bw: xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static int xhci_test_trb_in_td(struct xhci_hcd *xhci, - struct xhci_segment *input_seg, - union xhci_trb *start_trb, - union xhci_trb *end_trb, - dma_addr_t input_dma, - struct xhci_segment *result_seg, - char *test_name, int test_number) -{ - unsigned long long start_dma; - unsigned long long end_dma; - struct xhci_segment *seg; - - start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); - end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); - - seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false); - if (seg != result_seg) { - xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", - test_name, test_number); - xhci_warn(xhci, "Tested TRB math w/ seg %p and " - "input DMA 0x%llx\n", - input_seg, - (unsigned long long) input_dma); - xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " - "ending TRB %p (0x%llx DMA)\n", - start_trb, start_dma, - end_trb, end_dma); - xhci_warn(xhci, "Expected seg %p, got seg %p\n", - result_seg, seg); - trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, - true); - return -1; - } - return 0; -} - -/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ -static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) -{ - struct { - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } simple_test_vector [] = { - /* A zeroed DMA field should fail */ - { 0, NULL }, - /* One TRB before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 16, NULL }, - /* One byte before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 1, NULL }, - /* Starting TRB should succeed */ - { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, - /* Ending TRB should succeed */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, - xhci->event_ring->first_seg }, - /* One byte after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, - /* One TRB after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, - /* An address of all ones should fail */ - { (dma_addr_t) (~0), NULL }, - }; - struct { - struct xhci_segment *input_seg; - union xhci_trb *start_trb; - union xhci_trb *end_trb; - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } complex_test_vector [] = { - /* Test feeding a valid DMA address from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid start and end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->cmd_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* TRB in this ring, but after this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[0], - .end_trb = &xhci->event_ring->first_seg->trbs[3], - .input_dma = xhci->event_ring->first_seg->dma + 4*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[3], - .end_trb = &xhci->event_ring->first_seg->trbs[6], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but after this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, - .result_seg = NULL, - }, - /* TRB not in this ring, and we have a wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - }; - - unsigned int num_tests; - int i, ret; - - num_tests = ARRAY_SIZE(simple_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - xhci->event_ring->first_seg, - xhci->event_ring->first_seg->trbs, - &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - simple_test_vector[i].input_dma, - simple_test_vector[i].result_seg, - "Simple", i); - if (ret < 0) - return ret; - } - - num_tests = ARRAY_SIZE(complex_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - complex_test_vector[i].input_seg, - complex_test_vector[i].start_trb, - complex_test_vector[i].end_trb, - complex_test_vector[i].input_dma, - complex_test_vector[i].result_seg, - "Complex", i); - if (ret < 0) - return ret; - } - xhci_dbg(xhci, "TRB math tests passed.\n"); - return 0; -} - static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) { u64 temp; @@ -2506,8 +2348,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 0, flags); if (!xhci->event_ring) goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) - goto fail; ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); if (ret) From eaa407f5a874fe84bd1ee675f1b2ccd24b798b07 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:09 -0300 Subject: [PATCH 091/142] xhci: Refactor interrupter code for initial multi interrupter support. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b17a57f89f69069458d0a9d9b04281ce48da7ebb commit b17a57f89f69069458d0a9d9b04281ce48da7ebb Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:57 +0200 xHC supports several interrupters, each with its own mmio register set, event ring and MSI/MSI-X vector. Transfers can be assigned different interrupters when queued. See xhci 4.17 for details. Current driver only supports one interrupter. Create a xhci_interrupter structure containing an event ring, pointer to mmio registers for this interrupter, variables to store registers over s3 suspend, erst, etc. Add functions to create and free an interrupter, and pass an interrupter pointer to functions that deal with events. Secondary interrupters are also useful without having an interrupt vector. One use case is the xHCI audio sideband offloading where a DSP can take care of specific audio endpoints. When all transfer events of an offloaded endpoint can be mapped to a separate interrupter event ring the DSP can poll this ring, and we can mask these events preventing waking up the CPU. Only minor functional changes such as clearing some of the interrupter registers when freeing the interrupter. Still create only one primary interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 168 +++++++++++++++++++++----------- drivers/usb/host/xhci-ring.c | 68 +++++++------ drivers/usb/host/xhci.c | 54 ++++++---- drivers/usb/host/xhci.h | 24 +++-- 5 files changed, 196 insertions(+), 120 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index dc832ddf7033..0bc7fe11f749 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -692,7 +692,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bf9bb29f924b..6b83c5c35cf8 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1819,17 +1819,43 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, return 0; } -void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +static void +xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - size_t size; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + size_t erst_size; + u64 tmp64; + u32 tmp; - size = sizeof(struct xhci_erst_entry) * (erst->num_entries); - if (erst->entries) - dma_free_coherent(dev, size, - erst->entries, - erst->erst_dma_addr); - erst->entries = NULL; + if (!ir) + return; + + erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries); + if (ir->erst.entries) + dma_free_coherent(dev, erst_size, + ir->erst.entries, + ir->erst.erst_dma_addr); + ir->erst.entries = NULL; + + /* + * Clean out interrupter registers except ERSTBA. Clearing either the + * low or high 32 bits of ERSTBA immediately causes the controller to + * dereference the partially cleared 64 bit address, causing IOMMU error. + */ + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + + /* free interrrupter event ring */ + if (ir->event_ring) + xhci_ring_free(xhci, ir->event_ring); + ir->event_ring = NULL; + + kfree(ir); } void xhci_mem_cleanup(struct xhci_hcd *xhci) @@ -1839,12 +1865,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_erst(xhci, &xhci->erst); - - if (xhci->event_ring) - xhci_ring_free(xhci, xhci->event_ring); - xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_free_interrupter(xhci, xhci->interrupter); + xhci->interrupter = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); @@ -1929,18 +1952,18 @@ no_bw: xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) +static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { u64 temp; dma_addr_t deq; - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; /* Don't clear the EHB bit (which is RW1C) because * there might be more events to service. @@ -1950,7 +1973,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) "// Write event ring dequeue pointer, " "preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, @@ -2217,6 +2240,68 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +static struct xhci_interrupter * +xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_interrupter *ir; + u64 erst_base; + u32 erst_size; + int ret; + + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return NULL; + } + + if (xhci->interrupter) { + xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); + return NULL; + } + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); + if (!ir) + return NULL; + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, + 0, flags); + if (!ir->event_ring) { + xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); + goto fail_ir; + } + + ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); + if (ret) { + xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); + goto fail_ev; + + } + /* set ERST count with the number of entries in the segment table */ + erst_size = readl(&ir->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &ir->ir_set->erst_size); + + erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (ir->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base); + + /* Set the event ring dequeue address of this interrupter */ + xhci_set_hc_event_deq(xhci, ir); + + return ir; + +fail_ev: + xhci_ring_free(xhci, ir->event_ring); +fail_ir: + kfree(ir); + + return NULL; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2224,7 +2309,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; u32 page_size, temp; - int i, ret; + int i; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2337,47 +2422,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; - /* - * Event ring setup: Allocate a normal ring, but also setup - * the event ring segment table (ERST). Section 4.9.3. - */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - 0, flags); - if (!xhci->event_ring) + /* allocate and set up primary interrupter with an event ring. */ + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Allocating primary event ring"); + xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupter) goto fail; - ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); - if (ret) - goto fail; - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); - - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_BASE_RSVDP; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); - xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; /* diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5f1ecdee2c1c..451d48b87cf7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1833,7 +1833,8 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) } static void handle_port_status(struct xhci_hcd *xhci, - union xhci_trb *event) + struct xhci_interrupter *ir, + union xhci_trb *event) { struct usb_hcd *hcd; u32 port_id; @@ -1856,7 +1857,7 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((port_id <= 0) || (port_id > max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); return; } @@ -1986,7 +1987,7 @@ static void handle_port_status(struct xhci_hcd *xhci, cleanup: /* Update event ring dequeue pointer before dropping the lock */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Don't make the USB core poll the roothub if we got a bad port status * change event. Besides, at that point we can't tell which roothub @@ -2519,7 +2520,8 @@ finish_td: * At this point, the host controller is probably hosed and should be reset. */ static int handle_tx_event(struct xhci_hcd *xhci, - struct xhci_transfer_event *event) + struct xhci_interrupter *ir, + struct xhci_transfer_event *event) { struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; @@ -2868,7 +2870,7 @@ cleanup: * processing missed tds. */ if (!handling_skipped_tds) - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* * If ep->skip is set, it means there are missed tds on the @@ -2883,8 +2885,8 @@ cleanup: err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( - xhci->event_ring->deq_seg, - xhci->event_ring->dequeue), + ir->event_ring->deq_seg, + ir->event_ring->dequeue), lower_32_bits(le64_to_cpu(event->buffer)), upper_32_bits(le64_to_cpu(event->buffer)), le32_to_cpu(event->transfer_len), @@ -2898,7 +2900,7 @@ err_out: * Returns >0 for "possibly more events to process" (caller should call again), * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static int xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { union xhci_trb *event; int update_ptrs = 1; @@ -2906,18 +2908,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci) int ret; /* Event ring hasn't been allocated yet. */ - if (!xhci->event_ring || !xhci->event_ring->dequeue) { - xhci_err(xhci, "ERROR event ring not ready\n"); + if (!ir || !ir->event_ring || !ir->event_ring->dequeue) { + xhci_err(xhci, "ERROR interrupter not ready\n"); return -ENOMEM; } - event = xhci->event_ring->dequeue; + event = ir->event_ring->dequeue; /* Does the HC or OS own the TRB? */ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != - xhci->event_ring->cycle_state) + ir->event_ring->cycle_state) return 0; - trace_xhci_handle_event(xhci->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic); /* * Barrier between reading the TRB_CYCLE (valid) flag above and any @@ -2932,11 +2934,11 @@ static int xhci_handle_event(struct xhci_hcd *xhci) handle_cmd_completion(xhci, &event->event_cmd); break; case TRB_PORT_STATUS: - handle_port_status(xhci, event); + handle_port_status(xhci, ir, event); update_ptrs = 0; break; case TRB_TRANSFER: - ret = handle_tx_event(xhci, &event->trans_event); + ret = handle_tx_event(xhci, ir, &event->trans_event); if (ret >= 0) update_ptrs = 0; break; @@ -2960,7 +2962,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci) if (update_ptrs) /* Update SW event ring dequeue pointer */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Are there more items on the event ring? Caller will call us again to * check. @@ -2974,16 +2976,17 @@ static int xhci_handle_event(struct xhci_hcd *xhci) * - To avoid "Event Ring Full Error" condition */ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, - union xhci_trb *event_ring_deq) + struct xhci_interrupter *ir, + union xhci_trb *event_ring_deq) { u64 temp_64; dma_addr_t deq; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ - if (event_ring_deq != xhci->event_ring->dequeue) { - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + if (event_ring_deq != ir->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (deq == 0) xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); /* @@ -3001,7 +3004,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, /* Clear the event handler busy flag (RW1C) */ temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue); } /* @@ -3013,6 +3016,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); union xhci_trb *event_ring_deq; + struct xhci_interrupter *ir; irqreturn_t ret = IRQ_NONE; u64 temp_64; u32 status; @@ -3050,11 +3054,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) status |= STS_EINT; writel(status, &xhci->op_regs->status); + /* This is the handler of the primary interrupter */ + ir = xhci->interrupter; if (!hcd->msi_enabled) { u32 irq_pending; - irq_pending = readl(&xhci->ir_set->irq_pending); + irq_pending = readl(&ir->ir_set->irq_pending); irq_pending |= IMAN_IP; - writel(irq_pending, &xhci->ir_set->irq_pending); + writel(irq_pending, &ir->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING || @@ -3064,22 +3070,22 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* Clear the event handler busy flag (RW1C); * the event ring should be empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); ret = IRQ_HANDLED; goto out; } - event_ring_deq = xhci->event_ring->dequeue; + event_ring_deq = ir->event_ring->dequeue; /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) { + while (xhci_handle_event(xhci, ir) > 0) { if (event_loop++ < TRBS_PER_SEGMENT / 2) continue; - xhci_update_erst_dequeue(xhci, event_ring_deq); - event_ring_deq = xhci->event_ring->dequeue; + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + event_ring_deq = ir->event_ring->dequeue; /* ring is half-full, force isoc trbs to interrupt more often */ if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN) @@ -3088,7 +3094,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) event_loop = 0; } - xhci_update_erst_dequeue(xhci, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); ret = IRQ_HANDLED; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f61fda4715cc..8dc3f2c00577 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -613,6 +613,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; unsigned long flags; u32 temp; @@ -628,8 +629,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -665,7 +666,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - + struct xhci_interrupter *ir = xhci->interrupter; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -680,17 +681,17 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "ERST deq = 64'h%0lx", (long unsigned int) temp_64); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set the interrupt modulation register"); - temp = readl(&xhci->ir_set->irq_control); + temp = readl(&ir->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; - writel(temp, &xhci->ir_set->irq_control); + writel(temp, &ir->ir_set->irq_control); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; @@ -769,8 +770,8 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -832,28 +833,36 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - xhci->s3.erst_size = readl(&xhci->ir_set->erst_size); - xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); - xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending); - xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); + + if (!ir) + return; + + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); - xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); - xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); - writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1218,8 +1227,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -5334,6 +5343,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if ((!xhci->max_interrupters) || + xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + xhci->quirks |= quirks; get_quirks(dev, xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0dd92f9089fe..95eb235d1f70 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1687,11 +1687,6 @@ struct s3_save { u32 dev_nt; u64 dcbaa_ptr; u32 config_reg; - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u64 erst_base; - u64 erst_dequeue; }; /* Use for lpm */ @@ -1718,7 +1713,18 @@ struct xhci_bus_state { struct completion u3exit_done[USB_MAXCHILDREN]; }; - +struct xhci_interrupter { + struct xhci_ring *event_ring; + struct xhci_erst erst; + struct xhci_intr_reg __iomem *ir_set; + unsigned int intr_num; + /* For interrupter registers save and restore over suspend/resume */ + u32 s3_irq_pending; + u32 s3_irq_control; + u32 s3_erst_size; + u64 s3_erst_base; + u64 s3_erst_dequeue; +}; /* * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host. @@ -1761,8 +1767,6 @@ struct xhci_hcd { struct xhci_op_regs __iomem *op_regs; struct xhci_run_regs __iomem *run_regs; struct xhci_doorbell_array __iomem *dba; - /* Our HCD's current interrupter register set */ - struct xhci_intr_reg __iomem *ir_set; /* Cached register copies of read-only HC data */ __u32 hcs_params1; @@ -1797,6 +1801,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; + struct xhci_interrupter *interrupter; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -1807,8 +1812,7 @@ struct xhci_hcd { struct delayed_work cmd_timer; struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; - struct xhci_ring *event_ring; - struct xhci_erst erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; From e556c7769a94e7502b633680be3d601b680b7f0c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:10 -0300 Subject: [PATCH 092/142] xhci: add helpers for enabling and disabling interrupters Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=52dd0483e822d097fd6f522af2438a9b6f6eb0a9 commit 52dd0483e822d097fd6f522af2438a9b6f6eb0a9 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:58 +0200 Simple helpers to set and clear the IE (interrupter enable) bit for an interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8dc3f2c00577..6183ce8574b1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -292,6 +292,32 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } +static int xhci_enable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + +static int xhci_disable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + #ifdef CONFIG_USB_PCI /* * Set up MSI @@ -610,7 +636,6 @@ static int xhci_init(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ - static int xhci_run_finished(struct xhci_hcd *xhci) { struct xhci_interrupter *ir = xhci->interrupter; @@ -629,8 +654,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); + xhci_enable_interrupter(ir); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -734,6 +758,7 @@ static void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir = xhci->interrupter; mutex_lock(&xhci->mutex); @@ -770,8 +795,7 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(ir); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -1227,8 +1251,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(xhci->interrupter); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); From 12b3c6502f838492b3aa64b726bae0a4d3358a2c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:10 -0300 Subject: [PATCH 093/142] xhci: cleanup xhci_hub_control port references Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=faaae0190dcd1e230616c85bbc3b339f27ba5b81 commit faaae0190dcd1e230616c85bbc3b339f27ba5b81 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:59 +0200 Both port number and port structure of a port are referred to several times when handing hub requests in xhci. Use more suitable data types and readable names for these. Cleanup only, no functional changes Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 123 ++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7750a5eed435..181c070d6a99 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1209,11 +1209,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 test_mode = 0; struct xhci_hub *rhub; struct xhci_port **ports; + struct xhci_port *port; + int portnum1; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; + portnum1 = wIndex & 0xff; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1247,10 +1250,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + wIndex--; - temp = readl(ports[wIndex]->addr); + port = ports[portnum1 - 1]; + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1263,7 +1268,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", - hcd->self.busnum, wIndex + 1, temp, status); + hcd->self.busnum, portnum1, temp, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ @@ -1275,7 +1280,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(ports[wIndex]->addr + PORTLI); + port_li = readl(port->addr + PORTLI); status = xhci_get_ext_port_status(temp, port_li); put_unaligned_le32(status, &buf[4]); } @@ -1289,11 +1294,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, test_mode = (wIndex & 0xff00) >> 8; /* The MSB of wIndex is the U1/U2 timeout */ timeout = (wIndex & 0xff00) >> 8; + wIndex &= 0xff; - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; wIndex--; - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1303,11 +1311,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); spin_lock_irqsave(&xhci->lock, flags); @@ -1316,16 +1323,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); goto error; } slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (!slot_id) { xhci_warn(xhci, "slot_id is zero\n"); goto error; @@ -1335,21 +1342,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], XDEV_U3); + xhci_set_link_state(xhci, port, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); temp = xhci_port_state_to_neutral(temp); /* * Clear all change bits, so that we get a new @@ -1358,18 +1365,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - writel(temp | PORT_PE, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + writel(temp | PORT_PE, port->addr); + temp = readl(port->addr); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { xhci_dbg(xhci, "Enable port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); - temp = readl(ports[wIndex]->addr); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); + temp = readl(port->addr); break; } @@ -1399,11 +1405,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; } /* Port must be enabled */ @@ -1414,8 +1419,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Can't set port link state above '3' (U3) */ if (link_state > USB_SS_PORT_LS_U3) { xhci_warn(xhci, "Cannot set port %d-%d link state %d\n", - hcd->self.busnum, wIndex + 1, - link_state); + hcd->self.busnum, portnum1, link_state); goto error; } @@ -1440,8 +1444,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, reinit_completion(&bus_state->u3exit_done[wIndex]); } if (pls <= XDEV_U3) /* U1, U2, U3 */ - xhci_set_link_state(xhci, ports[wIndex], - USB_SS_PORT_LS_U0); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); if (!wait_u0) { if (pls > XDEV_U3) goto error; @@ -1451,16 +1454,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex], msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; } if (link_state == USB_SS_PORT_LS_U3) { int retries = 16; slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (slot_id) { /* unlock to execute stop endpoint * commands */ @@ -1469,16 +1472,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } - xhci_set_link_state(xhci, ports[wIndex], USB_SS_PORT_LS_U3); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3); spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); bus_state->suspended_ports |= 1 << wIndex; } break; @@ -1493,39 +1496,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - writel(temp, ports[wIndex]->addr); + writel(temp, port->addr); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: - xhci_set_remote_wake_mask(xhci, ports[wIndex], - wake_mask); - temp = readl(ports[wIndex]->addr); + xhci_set_remote_wake_mask(xhci, port, wake_mask); + temp = readl(port->addr); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - writel(temp, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + writel(temp, port->addr); + temp = readl(port->addr); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(port->addr + PORTPMSC); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, port->addr + PORTPMSC); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(port->addr + PORTPMSC); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, port->addr + PORTPMSC); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ @@ -1541,13 +1543,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* unblock any posted writes */ - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; case ClearPortFeature: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; + wIndex--; - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1557,7 +1562,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1568,20 +1573,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, set_bit(wIndex, &bus_state->resuming_ports); usb_hcd_start_port_resume(&hcd->self, wIndex); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_RESUME); + xhci_set_link_state(xhci, port, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } bus_state->port_c_suspend |= 1 << wIndex; slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto error; @@ -1599,11 +1602,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: xhci_clear_port_change_bit(xhci, wValue, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(hcd, xhci, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp); break; case USB_PORT_FEAT_POWER: xhci_set_port_power(xhci, hcd, wIndex, false, &flags); From 15f4846d56a35af45c4150f13fff496bd19ee9b4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:11 -0300 Subject: [PATCH 094/142] xhci: pass port pointer as parameter to xhci_set_port_power() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a66095a957ce3ce2a5154f7981845942f26e477d commit a66095a957ce3ce2a5154f7981845942f26e477d Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:00 +0200 Pass the port structure pointer directly to xhci_set_port_power() instead of hcd and port index. cleanup Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 181c070d6a99..238d05206d2c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -666,20 +666,18 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) * It will release and re-aquire the lock while calling ACPI * method. */ -static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, - u16 index, bool on, unsigned long *flags) +static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, + bool on, unsigned long *flags) __must_hold(&xhci->lock) { - struct xhci_hub *rhub; - struct xhci_port *port; + struct usb_hcd *hcd; u32 temp; - rhub = xhci_get_rhub(hcd); - port = rhub->ports[index]; + hcd = port->rhub->hcd; temp = readl(port->addr); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", - hcd->self.busnum, index + 1, on ? "ON" : "OFF", temp); + hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); temp = xhci_port_state_to_neutral(temp); @@ -694,10 +692,10 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, spin_unlock_irqrestore(&xhci->lock, *flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, - index); + port->hcd_portnum); if (temp) usb_acpi_set_power_state(hcd->self.root_hub, - index, on); + port->hcd_portnum, on); spin_lock_irqsave(&xhci->lock, *flags); } @@ -721,7 +719,6 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, u16 wIndex, unsigned long *flags) __must_hold(&xhci->lock) { - struct usb_hcd *usb3_hcd = xhci_get_usb3_hcd(xhci); int i, retval; /* Disable all Device Slots */ @@ -742,10 +739,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, xhci_dbg(xhci, "Disable all port (PP = 0)\n"); /* Power off USB3 ports*/ for (i = 0; i < xhci->usb3_rhub.num_ports; i++) - xhci_set_port_power(xhci, usb3_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb3_rhub.ports[i], false, flags); /* Power off USB2 ports*/ for (i = 0; i < xhci->usb2_rhub.num_ports; i++) - xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb2_rhub.ports[i], false, flags); /* Stop the controller */ xhci_dbg(xhci, "Stop controller\n"); retval = xhci_halt(xhci); @@ -1492,7 +1489,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * However, hub_wq will ignore the roothub events until * the roothub is registered. */ - xhci_set_port_power(xhci, hcd, wIndex, true, &flags); + xhci_set_port_power(xhci, port, true, &flags); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); @@ -1609,7 +1606,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp); break; case USB_PORT_FEAT_POWER: - xhci_set_port_power(xhci, hcd, wIndex, false, &flags); + xhci_set_port_power(xhci, port, false, &flags); break; case USB_PORT_FEAT_TEST: retval = xhci_exit_test_mode(xhci); From d67e985fc6d58edbba589f2ac13c5c6cde8cf783 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:11 -0300 Subject: [PATCH 095/142] xhci: move port specific items such as state completions to port structure Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2996e9fc00c378987c18ecbafe5624581b18c0d6 commit 2996e9fc00c378987c18ecbafe5624581b18c0d6 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:01 +0200 Now that we have a port structure for each port it makes sense to move per port variables, timestamps and completions there. Get rid of storing bitfileds and arrays of port specific items per bus. Move unsigned long resume_done; insigned long rexit_ports struct completion rexit_done; struct completion u3exit_done; Rename rexit_ports to rexit_active, and remove a redundant hcd speed check while checking if rexit_active is set. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 31 +++++++++++++++---------------- drivers/usb/host/xhci-mem.c | 10 +++------- drivers/usb/host/xhci-ring.c | 13 ++++++------- drivers/usb/host/xhci.h | 9 ++++----- 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 238d05206d2c..75c9609f32f0 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -936,7 +936,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, return -EINVAL; } /* did port event handler already start resume timing? */ - if (!bus_state->resume_done[wIndex]) { + if (!port->resume_done) { /* If not, maybe we are in a host initated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { /* Host initated resume doesn't time the resume @@ -953,28 +953,27 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(wIndex, &bus_state->resuming_ports); - bus_state->resume_done[wIndex] = timeout; + port->resume_done = timeout; mod_timer(&hcd->rh_timer, timeout); usb_hcd_start_port_resume(&hcd->self, wIndex); } /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) { + } else if (time_after_eq(jiffies, port->resume_done)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", hcd->self.busnum, wIndex + 1); - bus_state->resume_done[wIndex] = 0; + port->resume_done = 0; clear_bit(wIndex, &bus_state->resuming_ports); - - set_bit(wIndex, &bus_state->rexit_ports); + port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, *flags); time_left = wait_for_completion_timeout( - &bus_state->rexit_done[wIndex], + &port->rexit_done, msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS)); spin_lock_irqsave(&xhci->lock, *flags); @@ -993,7 +992,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); *status |= USB_PORT_STAT_SUSPEND; - clear_bit(wIndex, &bus_state->rexit_ports); + port->rexit_active = false; } usb_hcd_end_port_resume(&hcd->self, wIndex); @@ -1100,10 +1099,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (bus_state->resume_done[portnum]) + if (port->resume_done) usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); - bus_state->resume_done[portnum] = 0; + port->resume_done = 0; clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); @@ -1175,11 +1174,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, * Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ - if ((bus_state->resume_done[wIndex] || + if ((port->resume_done || test_bit(wIndex, &bus_state->resuming_ports)) && (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - bus_state->resume_done[wIndex] = 0; + port->resume_done = 0; clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } @@ -1438,7 +1437,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, pls == XDEV_RESUME || pls == XDEV_RECOVERY) { wait_u0 = true; - reinit_completion(&bus_state->u3exit_done[wIndex]); + reinit_completion(&port->u3exit_done); } if (pls <= XDEV_U3) /* U1, U2, U3 */ xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); @@ -1448,7 +1447,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; } spin_unlock_irqrestore(&xhci->lock, flags); - if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex], + if (!wait_for_completion_timeout(&port->u3exit_done, msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", hcd->self.busnum, portnum1); @@ -1688,8 +1687,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || - (bus_state->resume_done[i] && time_after_eq( - jiffies, bus_state->resume_done[i]))) { + (ports[i]->resume_done && time_after_eq( + jiffies, ports[i]->resume_done))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6b83c5c35cf8..d0a9467aa5fc 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2154,6 +2154,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * i; xhci->hw_ports[i].hw_portnum = i; + + init_completion(&xhci->hw_ports[i].rexit_done); + init_completion(&xhci->hw_ports[i].u3exit_done); } xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, @@ -2439,13 +2442,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) */ for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; - for (i = 0; i < USB_MAXCHILDREN; i++) { - xhci->usb2_rhub.bus_state.resume_done[i] = 0; - xhci->usb3_rhub.bus_state.resume_done[i] = 0; - /* Only the USB 2.0 completions will ever be used. */ - init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]); - init_completion(&xhci->usb3_rhub.bus_state.u3exit_done[i]); - } if (scratchpad_alloc(xhci, flags)) goto fail; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 451d48b87cf7..611580d4adad 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } else if (!test_bit(hcd_portnum, &bus_state->resuming_ports)) { xhci_dbg(xhci, "resume HS port %d\n", port_id); - bus_state->resume_done[hcd_portnum] = jiffies + + port->resume_done = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(hcd_portnum, &bus_state->resuming_ports); /* Do the rest in GetPortStatus after resume time delay. @@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci, */ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); mod_timer(&hcd->rh_timer, - bus_state->resume_done[hcd_portnum]); + port->resume_done); usb_hcd_start_port_resume(&hcd->self, hcd_portnum); bogus_port_status = true; } @@ -1945,7 +1945,7 @@ static void handle_port_status(struct xhci_hcd *xhci, (portsc & PORT_PLS_MASK) == XDEV_U1 || (portsc & PORT_PLS_MASK) == XDEV_U2)) { xhci_dbg(xhci, "resume SS port %d finished\n", port_id); - complete(&bus_state->u3exit_done[hcd_portnum]); + complete(&port->u3exit_done); /* We've just brought the device into U0/1/2 through either the * Resume state after a device remote wakeup, or through the * U3Exit state after a host-initiated resume. If it's a device @@ -1970,10 +1970,9 @@ static void handle_port_status(struct xhci_hcd *xhci, * RExit to a disconnect state). If so, let the driver know it's * out of the RExit state. */ - if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 && - test_and_clear_bit(hcd_portnum, - &bus_state->rexit_ports)) { - complete(&bus_state->rexit_done[hcd_portnum]); + if (hcd->speed < HCD_USB3 && port->rexit_active) { + complete(&port->rexit_done); + port->rexit_active = false; bogus_port_status = true; goto cleanup; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 95eb235d1f70..578e219292fd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1704,13 +1704,8 @@ struct xhci_bus_state { u32 port_c_suspend; u32 suspended_ports; u32 port_remote_wakeup; - unsigned long resume_done[USB_MAXCHILDREN]; /* which ports have started to resume */ unsigned long resuming_ports; - /* Which ports are waiting on RExit to U0 transition. */ - unsigned long rexit_ports; - struct completion rexit_done[USB_MAXCHILDREN]; - struct completion u3exit_done[USB_MAXCHILDREN]; }; struct xhci_interrupter { @@ -1745,6 +1740,10 @@ struct xhci_port { struct xhci_hub *rhub; struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; + unsigned long resume_done; + bool rexit_active; + struct completion rexit_done; + struct completion u3exit_done; }; struct xhci_hub { From c12422549f7990cf2417d6bfaad87fb44b3ba5e2 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:12 -0300 Subject: [PATCH 096/142] xhci: Pass port structure as parameter to xhci_disable_port(). Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6baf7e749ab3aa4fcfeef3a26e8ec2306572cd05 commit 6baf7e749ab3aa4fcfeef3a26e8ec2306572cd05 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:02 +0200 Pass the port structure to xhci_disable_port() instead of address, index, and value. re-read the port portsc value before disabling the port. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 75c9609f32f0..b27969e3cdcf 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -578,13 +578,16 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) return; } -static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 wIndex, __le32 __iomem *addr, u32 port_status) +static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) { + struct usb_hcd *hcd; + u32 portsc; + + hcd = port->rhub->hcd; + /* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed >= HCD_USB3) { - xhci_dbg(xhci, "Ignoring request to disable " - "SuperSpeed port.\n"); + xhci_dbg(xhci, "Ignoring request to disable SuperSpeed port.\n"); return; } @@ -594,11 +597,15 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, return; } + portsc = readl(port->addr); + portsc = xhci_port_state_to_neutral(portsc); + /* Write 1 to disable the port */ - writel(port_status | PORT_PE, addr); - port_status = readl(addr); + writel(portsc | PORT_PE, port->addr); + + portsc = readl(port->addr); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", - hcd->self.busnum, wIndex + 1, port_status); + hcd->self.busnum, port->hcd_portnum + 1, portsc); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, @@ -1601,8 +1608,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp); break; case USB_PORT_FEAT_ENABLE: - xhci_disable_port(hcd, xhci, wIndex, - port->addr, temp); + xhci_disable_port(xhci, port); break; case USB_PORT_FEAT_POWER: xhci_set_port_power(xhci, port, false, &flags); From 8361617e4885451e754e33b451aa569a1b7ed4ef Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:12 -0300 Subject: [PATCH 097/142] xhci: rename resume_done to resume_timestamp Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a909d629ae77b97b6288bc3cfe68560454bf79c6 commit a909d629ae77b97b6288bc3cfe68560454bf79c6 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:03 +0200 resume_done is just a timestamp, avoid confusing it with completions related to port state transitions that are named *_done Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 20 ++++++++++---------- drivers/usb/host/xhci-ring.c | 4 ++-- drivers/usb/host/xhci.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b27969e3cdcf..a2053aa9b4a9 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -943,7 +943,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, return -EINVAL; } /* did port event handler already start resume timing? */ - if (!port->resume_done) { + if (!port->resume_timestamp) { /* If not, maybe we are in a host initated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { /* Host initated resume doesn't time the resume @@ -960,18 +960,18 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(wIndex, &bus_state->resuming_ports); - port->resume_done = timeout; + port->resume_timestamp = timeout; mod_timer(&hcd->rh_timer, timeout); usb_hcd_start_port_resume(&hcd->self, wIndex); } /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, port->resume_done)) { + } else if (time_after_eq(jiffies, port->resume_timestamp)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", hcd->self.busnum, wIndex + 1); - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); port->rexit_active = true; @@ -1106,10 +1106,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (port->resume_done) + if (port->resume_timestamp) usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); @@ -1181,11 +1181,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, * Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ - if ((port->resume_done || + if ((port->resume_timestamp || test_bit(wIndex, &bus_state->resuming_ports)) && (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } @@ -1693,8 +1693,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || - (ports[i]->resume_done && time_after_eq( - jiffies, ports[i]->resume_done))) { + (ports[i]->resume_timestamp && time_after_eq( + jiffies, ports[i]->resume_timestamp))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 611580d4adad..eb788c60c1c0 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } else if (!test_bit(hcd_portnum, &bus_state->resuming_ports)) { xhci_dbg(xhci, "resume HS port %d\n", port_id); - port->resume_done = jiffies + + port->resume_timestamp = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(hcd_portnum, &bus_state->resuming_ports); /* Do the rest in GetPortStatus after resume time delay. @@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci, */ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); mod_timer(&hcd->rh_timer, - port->resume_done); + port->resume_timestamp); usb_hcd_start_port_resume(&hcd->self, hcd_portnum); bogus_port_status = true; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 578e219292fd..786002bb35db 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1740,7 +1740,7 @@ struct xhci_port { struct xhci_hub *rhub; struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; - unsigned long resume_done; + unsigned long resume_timestamp; bool rexit_active; struct completion rexit_done; struct completion u3exit_done; From 61ab59d13732707ff1d2a6770f710da1e960d828 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:13 -0300 Subject: [PATCH 098/142] xhci: clear usb2 resume related variables in one place. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0e6275452ce26d7ff274a5c1b15ed581a26f7986 commit 0e6275452ce26d7ff274a5c1b15ed581a26f7986 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:04 +0200 Initially resume related USB2 variables were cleared once port successfully resumed to U0. Later code was added to clean up stale resume variables in case of port failed to resume to U0. Clear the variables in one place after port is no longer resuming or in suspended U3 state. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-11-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 38 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a2053aa9b4a9..541ccc45ea51 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1090,7 +1090,6 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; - int ret; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1106,23 +1105,30 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (port->resume_timestamp) - usb_hcd_end_port_resume(&port->rhub->hcd->self, - portnum); - port->resume_timestamp = 0; - clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); bus_state->port_c_suspend |= 1 << portnum; } } if (link_state == XDEV_RESUME) { - ret = xhci_handle_usb2_port_link_resume(port, status, - portsc, flags); - if (ret) - return; + xhci_handle_usb2_port_link_resume(port, status, portsc, + flags); } } + + /* + * Clear usb2 resume signalling variables if port is no longer suspended + * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a + * error state. Resume related variables should be cleared in all those cases. + */ + if ((link_state != XDEV_U3 && + link_state != XDEV_RESUME) && + (port->resume_timestamp || + test_bit(portnum, &bus_state->resuming_ports))) { + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + } } /* @@ -1177,18 +1183,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, else xhci_get_usb2_port_status(port, &status, raw_port_status, flags); - /* - * Clear stale usb2 resume signalling variables in case port changed - * state during resume signalling. For example on error - */ - if ((port->resume_timestamp || - test_bit(wIndex, &bus_state->resuming_ports)) && - (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && - (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - port->resume_timestamp = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&hcd->self, wIndex); - } if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; From 71902e606856213f9d413dc44f74414826eacc11 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:13 -0300 Subject: [PATCH 099/142] xhci: decouple usb2 port resume and get_port_status request handling Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b0425784b942fffbbdb804896197f1dbccda37c5 commit b0425784b942fffbbdb804896197f1dbccda37c5 Author: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:05 +0200 The get port status hub request code in xhci-hub.c will complete usb2 port resume signalling if signalling has been going on for long enough. The code that completes the resume signalling, and the code that returns the port status have gotten too intertwined, so separate them a bit. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-hub.c | 47 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 541ccc45ea51..0054d02239e2 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -924,7 +924,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, - u32 *status, u32 portsc, + u32 portsc, unsigned long *flags) { struct xhci_bus_state *bus_state; @@ -939,7 +939,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { - *status = 0xffffffff; return -EINVAL; } /* did port event handler already start resume timing? */ @@ -973,6 +972,8 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); + + reinit_completion(&port->rexit_done); port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); @@ -989,7 +990,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); - *status = 0xffffffff; return -ENODEV; } xhci_ring_device(xhci, slot_id); @@ -998,22 +998,19 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); - *status |= USB_PORT_STAT_SUSPEND; - port->rexit_active = false; + /* + * keep rexit_active set if U0 transition failed so we + * know to report PORT_STAT_SUSPEND status back to + * usbcore. It will be cleared later once the port is + * out of RESUME/U3 state + */ } usb_hcd_end_port_resume(&hcd->self, wIndex); bus_state->port_c_suspend |= 1 << wIndex; bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * USB_RESUME_TIME. Report the port status as SUSPEND, - * let the usbcore check port status again and clear - * resume signaling later. - */ - *status |= USB_PORT_STAT_SUSPEND; } + return 0; } @@ -1090,6 +1087,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; + int err; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1111,8 +1109,12 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, } } if (link_state == XDEV_RESUME) { - xhci_handle_usb2_port_link_resume(port, status, portsc, - flags); + err = xhci_handle_usb2_port_link_resume(port, portsc, + flags); + if (err < 0) + *status = 0xffffffff; + else if (port->resume_timestamp || port->rexit_active) + *status |= USB_PORT_STAT_SUSPEND; } } @@ -1121,13 +1123,14 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a * error state. Resume related variables should be cleared in all those cases. */ - if ((link_state != XDEV_U3 && - link_state != XDEV_RESUME) && - (port->resume_timestamp || - test_bit(portnum, &bus_state->resuming_ports))) { - port->resume_timestamp = 0; - clear_bit(portnum, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + if (link_state != XDEV_U3 && link_state != XDEV_RESUME) { + if (port->resume_timestamp || + test_bit(portnum, &bus_state->resuming_ports)) { + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + } + port->rexit_active = 0; } } From c8259ab699816fa20bddf9813d894ac1694b15df Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:14 -0300 Subject: [PATCH 100/142] usb: remove the dead USB_OHCI_SH option Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f6dfc2136fb2e8dc3f571a5caff6b6e88281fc0 commit 4f6dfc2136fb2e8dc3f571a5caff6b6e88281fc0 Author: Christoph Hellwig Date: Fri, 13 Jan 2023 07:23:19 +0100 USB_OHCI_SH is a dummy option that never builds any code, remove it. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230113062339.1909087-3-hch@lst.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/Kconfig | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b96cf23fe654..77c6277a01a9 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -549,17 +549,6 @@ config USB_OHCI_HCD_SSB If unsure, say N. -config USB_OHCI_SH - bool "OHCI support for SuperH USB controller (DEPRECATED)" - depends on SUPERH || COMPILE_TEST - select USB_OHCI_HCD_PLATFORM - help - This option is deprecated now and the driver was removed, use - USB_OHCI_HCD_PLATFORM instead. - - Enables support for the on-chip OHCI controller on the SuperH. - If you use the PCI OHCI controller, this option is not necessary. - config USB_OHCI_EXYNOS tristate "OHCI support for Samsung S5P/Exynos SoC Series" depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST From 5fb822bea0424d4ce58d9bf8f5d1f8c3c0cdf36d Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:14 -0300 Subject: [PATCH 101/142] USB: serial: option: add support for VW/Skoda "Carstick LTE" Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=617c331d91077f896111044628c096802551dc66 commit 617c331d91077f896111044628c096802551dc66 Author: Florian Zumbiehl Date: Mon, 6 Feb 2023 02:04:28 +0100 Add support for VW/Skoda "Carstick LTE" D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1c9e ProdID=7605 Rev=02.00 S: Manufacturer=USB Modem S: Product=USB Modem C: #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#=0x0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#=0x1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#=0x2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) The stick has AT command interfaces on interfaces 1, 2, and 3, and does PPP on interface 3. Signed-off-by: Florian Zumbiehl Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Desnes Nunes --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ee5ac4ef7e16..e6d8d9b35ad0 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -402,6 +402,8 @@ static void option_instat_callback(struct urb *urb); #define LONGCHEER_VENDOR_ID 0x1c9e /* 4G Systems products */ +/* This one was sold as the VW and Skoda "Carstick LTE" */ +#define FOUR_G_SYSTEMS_PRODUCT_CARSTICK_LTE 0x7605 /* This is the 4G XS Stick W14 a.k.a. Mobilcom Debitel Surf-Stick * * It seems to contain a Qualcomm QSC6240/6290 chipset */ #define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603 @@ -1976,6 +1978,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(2) }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, + { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_CARSTICK_LTE), + .driver_info = RSVD(0) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), .driver_info = NCTRL(0) | NCTRL(1) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100), From e96834608ce42da06226e721e841054dfbaea3ec Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:15 -0300 Subject: [PATCH 102/142] USB: chipidea: fix memory leak with using debugfs_lookup() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ff35f3ea3baba5b81416ac02d005cfbf6dd182fa commit ff35f3ea3baba5b81416ac02d005cfbf6dd182fa Author: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:23 +0100 When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Peter Chen Link: https://lore.kernel.org/r/20230202153235.2412790-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/chipidea/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index faf6b078b6c4..bbc610e5bd69 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -364,5 +364,5 @@ void dbg_create_files(struct ci_hdrc *ci) */ void dbg_remove_files(struct ci_hdrc *ci) { - debugfs_remove(debugfs_lookup(dev_name(ci->dev), usb_debug_root)); + debugfs_lookup_and_remove(dev_name(ci->dev), usb_debug_root); } From b497f1ee49c240af2703c47c0c9815d205a68ae5 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:15 -0300 Subject: [PATCH 103/142] USB: ULPI: fix memory leak with using debugfs_lookup() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8f4d25eba599c4bd4b5ea8ae8752cda480a9d563 commit 8f4d25eba599c4bd4b5ea8ae8752cda480a9d563 Author: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:24 +0100 When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230202153235.2412790-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/common/ulpi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index d7c8461976ce..38703781ee2d 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -271,7 +271,7 @@ static int ulpi_regs_show(struct seq_file *seq, void *data) } DEFINE_SHOW_ATTRIBUTE(ulpi_regs); -#define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL) +static struct dentry *ulpi_root; static int ulpi_register(struct device *dev, struct ulpi *ulpi) { @@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) return ret; } - root = debugfs_create_dir(dev_name(dev), ULPI_ROOT); + root = debugfs_create_dir(dev_name(dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", @@ -349,8 +349,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface); */ void ulpi_unregister_interface(struct ulpi *ulpi) { - debugfs_remove_recursive(debugfs_lookup(dev_name(&ulpi->dev), - ULPI_ROOT)); + debugfs_lookup_and_remove(dev_name(&ulpi->dev), ulpi_root); device_unregister(&ulpi->dev); } EXPORT_SYMBOL_GPL(ulpi_unregister_interface); @@ -360,12 +359,11 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_interface); static int __init ulpi_init(void) { int ret; - struct dentry *root; - root = debugfs_create_dir(KBUILD_MODNAME, NULL); + ulpi_root = debugfs_create_dir(KBUILD_MODNAME, NULL); ret = bus_register(&ulpi_bus); if (ret) - debugfs_remove(root); + debugfs_remove(ulpi_root); return ret; } subsys_initcall(ulpi_init); @@ -373,7 +371,7 @@ subsys_initcall(ulpi_init); static void __exit ulpi_exit(void) { bus_unregister(&ulpi_bus); - debugfs_remove_recursive(ULPI_ROOT); + debugfs_remove(ulpi_root); } module_exit(ulpi_exit); From be68e21fc3f3fec6428ff195560c770927901ecc Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:16 -0300 Subject: [PATCH 104/142] USB: uhci: fix memory leak with using debugfs_lookup() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0a3f82c79c86278e7f144564b1cb6cc5c3657144 commit 0a3f82c79c86278e7f144564b1cb6cc5c3657144 Author: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:25 +0100 When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Alan Stern Link: https://lore.kernel.org/r/20230202153235.2412790-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/uhci-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c22b51af83fc..7cdc2fa7c28f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -536,8 +536,8 @@ static void release_uhci(struct uhci_hcd *uhci) uhci->is_initialized = 0; spin_unlock_irq(&uhci->lock); - debugfs_remove(debugfs_lookup(uhci_to_hcd(uhci)->self.bus_name, - uhci_debugfs_root)); + debugfs_lookup_and_remove(uhci_to_hcd(uhci)->self.bus_name, + uhci_debugfs_root); for (i = 0; i < UHCI_NUM_SKELQH; i++) uhci_free_qh(uhci, uhci->skelqh[i]); @@ -700,7 +700,7 @@ err_alloc_frame_cpu: uhci->frame, uhci->frame_dma_handle); err_alloc_frame: - debugfs_remove(debugfs_lookup(hcd->self.bus_name, uhci_debugfs_root)); + debugfs_lookup_and_remove(hcd->self.bus_name, uhci_debugfs_root); return retval; } From dd1285833d90c97347d339ac5af0ae28d03ff399 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:16 -0300 Subject: [PATCH 105/142] usb: host: xhci: mvebu: Iterate over array indexes instead of using pointer math Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0fbd2cda92cdb00f72080665554a586f88bca821 commit 0fbd2cda92cdb00f72080665554a586f88bca821 Author: Kees Cook Date: Sat, 4 Feb 2023 10:36:52 -0800 Walking the dram->cs array was seen as accesses beyond the first array item by the compiler. Instead, use the array index directly. This allows for run-time bounds checking under CONFIG_UBSAN_BOUNDS as well. Seen with GCC 13 with -fstrict-flex-arrays: In function 'xhci_mvebu_mbus_config', inlined from 'xhci_mvebu_mbus_init_quirk' at ../drivers/usb/host/xhci-mvebu.c:66:2: ../drivers/usb/host/xhci-mvebu.c:37:28: warning: array subscript 0 is outside array bounds of 'const struct mbus_dram_window[0]' [-Warray-bounds=] 37 | writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | | ~~^~~~~~ Cc: Mathias Nyman Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230204183651.never.663-kees@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-mvebu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 60651a50770f..87f1597a0e5a 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -32,7 +32,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base, /* Program each DRAM CS in a seperate window */ for (win = 0; win < dram->num_cs; win++) { - const struct mbus_dram_window *cs = dram->cs + win; + const struct mbus_dram_window *cs = &dram->cs[win]; writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | (dram->mbus_dram_target_id << 4) | 1, From 010674703e0bc80fef403149151d5c8c59f8deec Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:17 -0300 Subject: [PATCH 106/142] USB: ene_usb6250: Allocate enough memory for full object Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce33e64c1788912976b61314b56935abd4bc97ef commit ce33e64c1788912976b61314b56935abd4bc97ef Author: Kees Cook Date: Sat, 4 Feb 2023 10:35:46 -0800 The allocation of PageBuffer is 512 bytes in size, but the dereferencing of struct ms_bootblock_idi (also size 512) happens at a calculated offset within the allocation, which means the object could potentially extend beyond the end of the allocation. Avoid this case by just allocating enough space to catch any accesses beyond the end. Seen with GCC 13: ../drivers/usb/storage/ene_ub6250.c: In function 'ms_lib_process_bootblock': ../drivers/usb/storage/ene_ub6250.c:1050:44: warning: array subscript 'struct ms_bootblock_idi[0]' is partly outside array bounds of 'unsigned char[512]' [-Warray-bounds=] 1050 | if (le16_to_cpu(idi->wIDIgeneralConfiguration) != MS_IDI_GENERAL_CONF) | ^~ ../include/uapi/linux/byteorder/little_endian.h:37:51: note: in definition of macro '__le16_to_cpu' 37 | #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) | ^ ../drivers/usb/storage/ene_ub6250.c:1050:29: note: in expansion of macro 'le16_to_cpu' 1050 | if (le16_to_cpu(idi->wIDIgeneralConfiguration) != MS_IDI_GENERAL_CONF) | ^~~~~~~~~~~ In file included from ../drivers/usb/storage/ene_ub6250.c:5: In function 'kmalloc', inlined from 'ms_lib_process_bootblock' at ../drivers/usb/storage/ene_ub6250.c:942:15: ../include/linux/slab.h:580:24: note: at offset [256, 512] into object of size 512 allocated by 'kmalloc_trace' 580 | return kmalloc_trace( | ^~~~~~~~~~~~~~ 581 | kmalloc_caches[kmalloc_type(flags)][index], | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 582 | flags, size); | ~~~~~~~~~~~~ Cc: Alan Stern Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230204183546.never.849-kees@kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/storage/ene_ub6250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 6012603f3630..97c66c0d91f4 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -939,7 +939,7 @@ static int ms_lib_process_bootblock(struct us_data *us, u16 PhyBlock, u8 *PageDa struct ms_lib_type_extdat ExtraData; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; - PageBuffer = kmalloc(MS_BYTES_PER_PAGE, GFP_KERNEL); + PageBuffer = kzalloc(MS_BYTES_PER_PAGE * 2, GFP_KERNEL); if (PageBuffer == NULL) return (u32)-1; From 19896ecd03afe6442161689aa19b55803bfb0158 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:17 -0300 Subject: [PATCH 107/142] usb: uvc: Enumerate valid values for color matching Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e16cab9c1596e251761d2bfb5e1467950d616963 commit e16cab9c1596e251761d2bfb5e1467950d616963 Author: Daniel Scally Date: Thu, 2 Feb 2023 11:41:37 +0000 The color matching descriptors defined in the UVC Specification contain 3 fields with discrete numeric values representing particular settings. Enumerate those values so that later code setting them can be more readable. Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Scally Link: https://lore.kernel.org/r/20230202114142.300858-2-dan.scally@ideasonboard.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- include/uapi/linux/usb/video.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index bfdae12cdacf..c58854fb7d94 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -179,6 +179,36 @@ #define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3) #define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4) +/* 3.9.2.6 Color Matching Descriptor Values */ +enum uvc_color_primaries_values { + UVC_COLOR_PRIMARIES_UNSPECIFIED, + UVC_COLOR_PRIMARIES_BT_709_SRGB, + UVC_COLOR_PRIMARIES_BT_470_2_M, + UVC_COLOR_PRIMARIES_BT_470_2_B_G, + UVC_COLOR_PRIMARIES_SMPTE_170M, + UVC_COLOR_PRIMARIES_SMPTE_240M, +}; + +enum uvc_transfer_characteristics_values { + UVC_TRANSFER_CHARACTERISTICS_UNSPECIFIED, + UVC_TRANSFER_CHARACTERISTICS_BT_709, + UVC_TRANSFER_CHARACTERISTICS_BT_470_2_M, + UVC_TRANSFER_CHARACTERISTICS_BT_470_2_B_G, + UVC_TRANSFER_CHARACTERISTICS_SMPTE_170M, + UVC_TRANSFER_CHARACTERISTICS_SMPTE_240M, + UVC_TRANSFER_CHARACTERISTICS_LINEAR, + UVC_TRANSFER_CHARACTERISTICS_SRGB, +}; + +enum uvc_matrix_coefficients { + UVC_MATRIX_COEFFICIENTS_UNSPECIFIED, + UVC_MATRIX_COEFFICIENTS_BT_709, + UVC_MATRIX_COEFFICIENTS_FCC, + UVC_MATRIX_COEFFICIENTS_BT_470_2_B_G, + UVC_MATRIX_COEFFICIENTS_SMPTE_170M, + UVC_MATRIX_COEFFICIENTS_SMPTE_240M, +}; + /* ------------------------------------------------------------------------ * UVC structures */ From ea83cb5c74aefe9155ee04ddb117f8d0d4a642af Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:18 -0300 Subject: [PATCH 108/142] dt-bindings: usb: convert fcs,fusb302.txt to yaml Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f9b8556d5799b612404e19b21dd7624d551f71df commit f9b8556d5799b612404e19b21dd7624d551f71df Author: Johan Jonker Date: Thu, 22 Dec 2022 15:28:53 +0100 Convert fcs,fusb302.txt to yaml. Changed: Add vbus-supply property Signed-off-by: Johan Jonker Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/0336a3c4-4a43-c983-11d7-e2ae16187fc8@gmail.com Signed-off-by: Rob Herring Signed-off-by: Desnes Nunes --- .../devicetree/bindings/usb/fcs,fusb302.txt | 34 ---------- .../devicetree/bindings/usb/fcs,fusb302.yaml | 67 +++++++++++++++++++ 2 files changed, 67 insertions(+), 34 deletions(-) delete mode 100644 Documentation/devicetree/bindings/usb/fcs,fusb302.txt create mode 100644 Documentation/devicetree/bindings/usb/fcs,fusb302.yaml diff --git a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt b/Documentation/devicetree/bindings/usb/fcs,fusb302.txt deleted file mode 100644 index 60e4654297af..000000000000 --- a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt +++ /dev/null @@ -1,34 +0,0 @@ -Fairchild FUSB302 Type-C Port controllers - -Required properties : -- compatible : "fcs,fusb302" -- reg : I2C slave address -- interrupts : Interrupt specifier - -Required sub-node: -- connector : The "usb-c-connector" attached to the FUSB302 IC. The bindings - of the connector node are specified in: - - Documentation/devicetree/bindings/connector/usb-connector.yaml - - -Example: - -fusb302: typec-portc@54 { - compatible = "fcs,fusb302"; - reg = <0x54>; - interrupt-parent = <&nmi_intc>; - interrupts = <0 IRQ_TYPE_LEVEL_LOW>; - - usb_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - power-role = "dual"; - try-power-role = "sink"; - source-pdos = ; - sink-pdos = ; - op-sink-microwatt = <10000000>; - }; -}; diff --git a/Documentation/devicetree/bindings/usb/fcs,fusb302.yaml b/Documentation/devicetree/bindings/usb/fcs,fusb302.yaml new file mode 100644 index 000000000000..b396ea0ab10c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/fcs,fusb302.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/fcs,fusb302.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Fairchild FUSB302 Type-C Port controller + +maintainers: + - Rob Herring + +properties: + compatible: + const: fcs,fusb302 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vbus-supply: + description: VBUS power supply + + connector: + type: object + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - vbus-supply + - connector + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec-portc@54 { + compatible = "fcs,fusb302"; + reg = <0x54>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + vbus-supply = <&vbus_typec>; + + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; + }; + }; + }; From 01e0e7a6f42c589e497b8d8f1c37b0c872fac4d6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:18 -0300 Subject: [PATCH 109/142] media: usb: dvb-usb-v2: af9015.c: return 0 instead of 'ret'. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a0ccbc65bc751f9337ed985bac3741a596872854 commit a0ccbc65bc751f9337ed985bac3741a596872854 Author: Hans Verkuil Date: Thu, 26 Jan 2023 13:26:19 +0100 Since 'ret' is known to be 0, just return '0'. This fixes two smatch warnings: af9015.c:1168 af9015_rc_query() warn: missing error code? 'ret' af9015.c:1177 af9015_rc_query() warn: missing error code? 'ret' Signed-off-by: Hans Verkuil Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Desnes Nunes --- drivers/media/usb/dvb-usb-v2/af9015.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index d33514acc2b5..4014f7d07330 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1165,7 +1165,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) /* If any of these are non-zero, assume invalid data */ if (buf[1] || buf[2] || buf[3]) { dev_dbg(&intf->dev, "invalid data\n"); - return ret; + return 0; } /* Check for repeat of previous code */ @@ -1174,7 +1174,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) dev_dbg(&intf->dev, "key repeated\n"); rc_repeat(d->rc_dev); state->rc_repeat = buf[6]; - return ret; + return 0; } /* Only process key if canary killed */ From 17fd8a939959b835b2daea959c90b80cf6a57aa7 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:19 -0300 Subject: [PATCH 110/142] media: usb: siano: Fix use after free bugs caused by do_submit_urb Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebad8e731c1c06adf04621d6fd327b860c0861b5 commit ebad8e731c1c06adf04621d6fd327b860c0861b5 Author: Duoming Zhou Date: Mon, 23 Jan 2023 03:04:38 +0100 There are UAF bugs caused by do_submit_urb(). One of the KASan reports is shown below: [ 36.403605] BUG: KASAN: use-after-free in worker_thread+0x4a2/0x890 [ 36.406105] Read of size 8 at addr ffff8880059600e8 by task kworker/0:2/49 [ 36.408316] [ 36.408867] CPU: 0 PID: 49 Comm: kworker/0:2 Not tainted 6.2.0-rc3-15798-g5a41237ad1d4-dir8 [ 36.411696] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g15584 [ 36.416157] Workqueue: 0x0 (events) [ 36.417654] Call Trace: [ 36.418546] [ 36.419320] dump_stack_lvl+0x96/0xd0 [ 36.420522] print_address_description+0x75/0x350 [ 36.421992] print_report+0x11b/0x250 [ 36.423174] ? _raw_spin_lock_irqsave+0x87/0xd0 [ 36.424806] ? __virt_addr_valid+0xcf/0x170 [ 36.426069] ? worker_thread+0x4a2/0x890 [ 36.427355] kasan_report+0x131/0x160 [ 36.428556] ? worker_thread+0x4a2/0x890 [ 36.430053] worker_thread+0x4a2/0x890 [ 36.431297] ? worker_clr_flags+0x90/0x90 [ 36.432479] kthread+0x166/0x190 [ 36.433493] ? kthread_blkcg+0x50/0x50 [ 36.434669] ret_from_fork+0x22/0x30 [ 36.435923] [ 36.436684] [ 36.437215] Allocated by task 24: [ 36.438289] kasan_set_track+0x50/0x80 [ 36.439436] __kasan_kmalloc+0x89/0xa0 [ 36.440566] smsusb_probe+0x374/0xc90 [ 36.441920] usb_probe_interface+0x2d1/0x4c0 [ 36.443253] really_probe+0x1d5/0x580 [ 36.444539] __driver_probe_device+0xe3/0x130 [ 36.446085] driver_probe_device+0x49/0x220 [ 36.447423] __device_attach_driver+0x19e/0x1b0 [ 36.448931] bus_for_each_drv+0xcb/0x110 [ 36.450217] __device_attach+0x132/0x1f0 [ 36.451470] bus_probe_device+0x59/0xf0 [ 36.452563] device_add+0x4ec/0x7b0 [ 36.453830] usb_set_configuration+0xc63/0xe10 [ 36.455230] usb_generic_driver_probe+0x3b/0x80 [ 36.456166] printk: console [ttyGS0] disabled [ 36.456569] usb_probe_device+0x90/0x110 [ 36.459523] really_probe+0x1d5/0x580 [ 36.461027] __driver_probe_device+0xe3/0x130 [ 36.462465] driver_probe_device+0x49/0x220 [ 36.463847] __device_attach_driver+0x19e/0x1b0 [ 36.465229] bus_for_each_drv+0xcb/0x110 [ 36.466466] __device_attach+0x132/0x1f0 [ 36.467799] bus_probe_device+0x59/0xf0 [ 36.469010] device_add+0x4ec/0x7b0 [ 36.470125] usb_new_device+0x863/0xa00 [ 36.471374] hub_event+0x18c7/0x2220 [ 36.472746] process_one_work+0x34c/0x5b0 [ 36.474041] worker_thread+0x4b7/0x890 [ 36.475216] kthread+0x166/0x190 [ 36.476267] ret_from_fork+0x22/0x30 [ 36.477447] [ 36.478160] Freed by task 24: [ 36.479239] kasan_set_track+0x50/0x80 [ 36.480512] kasan_save_free_info+0x2b/0x40 [ 36.481808] ____kasan_slab_free+0x122/0x1a0 [ 36.483173] __kmem_cache_free+0xc4/0x200 [ 36.484563] smsusb_term_device+0xcd/0xf0 [ 36.485896] smsusb_probe+0xc85/0xc90 [ 36.486976] usb_probe_interface+0x2d1/0x4c0 [ 36.488303] really_probe+0x1d5/0x580 [ 36.489498] __driver_probe_device+0xe3/0x130 [ 36.491140] driver_probe_device+0x49/0x220 [ 36.492475] __device_attach_driver+0x19e/0x1b0 [ 36.493988] bus_for_each_drv+0xcb/0x110 [ 36.495171] __device_attach+0x132/0x1f0 [ 36.496617] bus_probe_device+0x59/0xf0 [ 36.497875] device_add+0x4ec/0x7b0 [ 36.498972] usb_set_configuration+0xc63/0xe10 [ 36.500264] usb_generic_driver_probe+0x3b/0x80 [ 36.501740] usb_probe_device+0x90/0x110 [ 36.503084] really_probe+0x1d5/0x580 [ 36.504241] __driver_probe_device+0xe3/0x130 [ 36.505548] driver_probe_device+0x49/0x220 [ 36.506766] __device_attach_driver+0x19e/0x1b0 [ 36.508368] bus_for_each_drv+0xcb/0x110 [ 36.509646] __device_attach+0x132/0x1f0 [ 36.510911] bus_probe_device+0x59/0xf0 [ 36.512103] device_add+0x4ec/0x7b0 [ 36.513215] usb_new_device+0x863/0xa00 [ 36.514736] hub_event+0x18c7/0x2220 [ 36.516130] process_one_work+0x34c/0x5b0 [ 36.517396] worker_thread+0x4b7/0x890 [ 36.518591] kthread+0x166/0x190 [ 36.519599] ret_from_fork+0x22/0x30 [ 36.520851] [ 36.521405] Last potentially related work creation: [ 36.523143] kasan_save_stack+0x3f/0x60 [ 36.524275] kasan_record_aux_stack_noalloc+0x9d/0xb0 [ 36.525831] insert_work+0x25/0x130 [ 36.527039] __queue_work+0x4d4/0x620 [ 36.528236] queue_work_on+0x72/0xb0 [ 36.529344] __usb_hcd_giveback_urb+0x13f/0x1b0 [ 36.530819] dummy_timer+0x350/0x1a40 [ 36.532149] call_timer_fn+0x2c/0x190 [ 36.533567] expire_timers+0x69/0x1f0 [ 36.534736] __run_timers+0x289/0x2d0 [ 36.535841] run_timer_softirq+0x2d/0x60 [ 36.537110] __do_softirq+0x116/0x380 [ 36.538377] [ 36.538950] Second to last potentially related work creation: [ 36.540855] kasan_save_stack+0x3f/0x60 [ 36.542084] kasan_record_aux_stack_noalloc+0x9d/0xb0 [ 36.543592] insert_work+0x25/0x130 [ 36.544891] __queue_work+0x4d4/0x620 [ 36.546168] queue_work_on+0x72/0xb0 [ 36.547328] __usb_hcd_giveback_urb+0x13f/0x1b0 [ 36.548805] dummy_timer+0x350/0x1a40 [ 36.550116] call_timer_fn+0x2c/0x190 [ 36.551570] expire_timers+0x69/0x1f0 [ 36.552762] __run_timers+0x289/0x2d0 [ 36.553916] run_timer_softirq+0x2d/0x60 [ 36.555118] __do_softirq+0x116/0x380 [ 36.556239] [ 36.556807] The buggy address belongs to the object at ffff888005960000 [ 36.556807] which belongs to the cache kmalloc-4k of size 4096 [ 36.560652] The buggy address is located 232 bytes inside of [ 36.560652] 4096-byte region [ffff888005960000, ffff888005961000) [ 36.564791] [ 36.565355] The buggy address belongs to the physical page: [ 36.567212] page:000000004f0a0731 refcount:1 mapcount:0 mapping:0000000000000000 index:0x00 [ 36.570534] head:000000004f0a0731 order:3 compound_mapcount:0 subpages_mapcount:0 compound0 [ 36.573717] flags: 0x100000000010200(slab|head|node=0|zone=1) [ 36.575481] raw: 0100000000010200 ffff888001042140 dead000000000122 0000000000000000 [ 36.577842] raw: 0000000000000000 0000000000040004 00000001ffffffff 0000000000000000 [ 36.580175] page dumped because: kasan: bad access detected [ 36.581994] [ 36.582548] Memory state around the buggy address: [ 36.583983] ffff88800595ff80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 36.586240] ffff888005960000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 36.588884] >ffff888005960080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 36.591071] ^ [ 36.593295] ffff888005960100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 36.595705] ffff888005960180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 36.598026] ================================================================== [ 36.600224] Disabling lock debugging due to kernel taint [ 36.602681] general protection fault, probably for non-canonical address 0x43600a000000060I [ 36.607129] CPU: 0 PID: 49 Comm: kworker/0:2 Tainted: G B 6.2.0-rc3-15798-8 [ 36.611115] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g15584 [ 36.615026] Workqueue: events do_submit_urb [ 36.616290] RIP: 0010:_raw_spin_lock_irqsave+0x8a/0xd0 [ 36.618107] Code: 24 00 00 00 00 48 89 df be 04 00 00 00 e8 9e b5 c6 fe 48 89 ef be 04 00 5 [ 36.623522] RSP: 0018:ffff888004b6fcf0 EFLAGS: 00010046 [ 36.625072] RAX: 0000000000000000 RBX: 043600a000000060 RCX: ffffffff9fc0e0d7 [ 36.627206] RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffff888004b6fcf0 [ 36.629813] RBP: ffff888004b6fcf0 R08: dffffc0000000000 R09: ffffed100096df9f [ 36.631974] R10: dfffe9100096dfa0 R11: 1ffff1100096df9e R12: ffff888005960020 [ 36.634285] R13: ffff8880059600f0 R14: 0000000000000246 R15: 0000000000000001 [ 36.636438] FS: 0000000000000000(0000) GS:ffff88806d600000(0000) knlGS:0000000000000000 [ 36.639092] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 36.640951] CR2: 00007f07476819a3 CR3: 0000000004a34000 CR4: 00000000000006f0 [ 36.643411] Call Trace: [ 36.644215] [ 36.644902] smscore_getbuffer+0x3e/0x1e0 [ 36.646147] do_submit_urb+0x4f/0x190 [ 36.647449] process_one_work+0x34c/0x5b0 [ 36.648777] worker_thread+0x4b7/0x890 [ 36.649984] ? worker_clr_flags+0x90/0x90 [ 36.651166] kthread+0x166/0x190 [ 36.652151] ? kthread_blkcg+0x50/0x50 [ 36.653547] ret_from_fork+0x22/0x30 [ 36.655051] [ 36.655733] Modules linked in: [ 36.656787] ---[ end trace 0000000000000000 ]--- [ 36.658328] RIP: 0010:_raw_spin_lock_irqsave+0x8a/0xd0 [ 36.660045] Code: 24 00 00 00 00 48 89 df be 04 00 00 00 e8 9e b5 c6 fe 48 89 ef be 04 00 5 [ 36.665730] RSP: 0018:ffff888004b6fcf0 EFLAGS: 00010046 [ 36.667448] RAX: 0000000000000000 RBX: 043600a000000060 RCX: ffffffff9fc0e0d7 [ 36.669675] RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffff888004b6fcf0 [ 36.672645] RBP: ffff888004b6fcf0 R08: dffffc0000000000 R09: ffffed100096df9f [ 36.674921] R10: dfffe9100096dfa0 R11: 1ffff1100096df9e R12: ffff888005960020 [ 36.677034] R13: ffff8880059600f0 R14: 0000000000000246 R15: 0000000000000001 [ 36.679184] FS: 0000000000000000(0000) GS:ffff88806d600000(0000) knlGS:0000000000000000 [ 36.681655] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 36.683383] CR2: 00007f07476819a3 CR3: 0000000004a34000 CR4: 00000000000006f0 [ 36.685733] Kernel panic - not syncing: Fatal exception [ 36.688585] Kernel Offset: 0x1d400000 from 0xffffffff81000000 (relocation range: 0xfffffff) [ 36.692199] ---[ end Kernel panic - not syncing: Fatal exception ]--- When the siano device is plugged in, it may call the following functions to initialize the device. smsusb_probe()-->smsusb_init_device()-->smscore_start_device(). When smscore_start_device() gets failed, the function smsusb_term_device() will be called and smsusb_device_t will be deallocated. Although we use usb_kill_urb() in smsusb_stop_streaming() to cancel transfer requests and wait for them to finish, the worker threads that are scheduled by smsusb_onresponse() may be still running. As a result, the UAF bugs could happen. We add cancel_work_sync() in smsusb_stop_streaming() in order that the worker threads could finish before the smsusb_device_t is deallocated. Fixes: dd47fbd40e6e ("[media] smsusb: don't sleep while atomic") Signed-off-by: Duoming Zhou Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Desnes Nunes --- drivers/media/usb/siano/smsusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index fe9c7b3a950e..6f443c542c6d 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -179,6 +179,7 @@ static void smsusb_stop_streaming(struct smsusb_device_t *dev) for (i = 0; i < MAX_URBS; i++) { usb_kill_urb(&dev->surbs[i].urb); + cancel_work_sync(&dev->surbs[i].wq); if (dev->surbs[i].cb) { smscore_putbuffer(dev->coredev, dev->surbs[i].cb); From 8088556ae7825d6a8cccb8c6cd1397bc1850365c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:19 -0300 Subject: [PATCH 111/142] xhci: host: potential NULL dereference in xhci_generic_plat_probe() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77191db5ba7bd321fbbf4315675ee774a2b5a362 commit 77191db5ba7bd321fbbf4315675ee774a2b5a362 Author: Dan Carpenter Date: Thu, 9 Feb 2023 16:43:45 +0300 It's possible to exit the loop with "sysdev" set to NULL. In that case we should use "&pdev->dev". Fixes: ec5499d338ec ("xhci: split out rcar/rz support from xhci-plat.c") Signed-off-by: Dan Carpenter Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/Y+T4kTcJwRwxNHJq@kili Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-plat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1fccc567aa77..cf58fffba21f 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -354,6 +354,9 @@ static int xhci_generic_plat_probe(struct platform_device *pdev) #endif } + if (!sysdev) + sysdev = &pdev->dev; + if (WARN_ON(!sysdev->dma_mask)) { /* Platform did not initialize dma_mask */ ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); From 61477ba5e6c0f598163352307d59b473e99203fd Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:20 -0300 Subject: [PATCH 112/142] usb: typec: pd: Remove usb_suspend_supported sysfs from sink PDO Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e4e7b2dc27c4bb877d850eaff69d41410b2f4237 commit e4e7b2dc27c4bb877d850eaff69d41410b2f4237 Author: Saranya Gopal Date: Tue, 14 Feb 2023 17:15:42 +0530 As per USB PD specification, 28th bit of fixed supply sink PDO represents "higher capability" attribute and not "usb suspend supported" attribute. So, this patch removes the usb_suspend_supported attribute from sink PDO. Fixes: 662a60102c12 ("usb: typec: Separate USB Power Delivery from USB Type-C") Cc: stable Reported-by: Rajaram Regupathy Signed-off-by: Saranya Gopal Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230214114543.205103-1-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/pd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index dc72005d68db..b5ab26422c34 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -161,7 +161,6 @@ static struct device_type source_fixed_supply_type = { static struct attribute *sink_fixed_supply_attrs[] = { &dev_attr_dual_role_power.attr, - &dev_attr_usb_suspend_supported.attr, &dev_attr_unconstrained_power.attr, &dev_attr_usb_communication_capable.attr, &dev_attr_dual_role_data.attr, From 8096d151be628c390e415ff1280a3081facc6094 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:20 -0300 Subject: [PATCH 113/142] usb: typec: pd: Add higher capability sysfs for sink PDO Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c620f4d5b25bcbb851daa1f88edc764cf5f29cb6 commit c620f4d5b25bcbb851daa1f88edc764cf5f29cb6 Author: Saranya Gopal Date: Tue, 14 Feb 2023 17:15:43 +0530 28th bit of fixed supply sink PDO represents higher capability. When this bit is set, the sink device needs more than vsafe5V (eg: 12 V) to provide full functionality. This patch adds this higher capability sysfs interface for sink PDO. 28th bit of fixed supply source PDO represents usb_suspend_supported attribute. This usb_suspend_supported sysfs is already exposed for source PDOs. This patch adds 'source-capabilities' in usb_suspend_supported sysfs documentation for additional clarity. Signed-off-by: Saranya Gopal Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230214114543.205103-2-saranya.gopal@intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- .../ABI/testing/sysfs-class-usb_power_delivery | 11 ++++++++++- drivers/usb/typec/pd.c | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-usb_power_delivery b/Documentation/ABI/testing/sysfs-class-usb_power_delivery index ce2b1b563cb3..1bf9d1d7902c 100644 --- a/Documentation/ABI/testing/sysfs-class-usb_power_delivery +++ b/Documentation/ABI/testing/sysfs-class-usb_power_delivery @@ -69,7 +69,7 @@ Description: This file contains boolean value that tells does the device support both source and sink power roles. -What: /sys/class/usb_power_delivery/...//1:fixed_supply/usb_suspend_supported +What: /sys/class/usb_power_delivery/.../source-capabilities/1:fixed_supply/usb_suspend_supported Date: May 2022 Contact: Heikki Krogerus Description: @@ -78,6 +78,15 @@ Description: will follow the USB 2.0 and USB 3.2 rules for suspend and resume. +What: /sys/class/usb_power_delivery/.../sink-capabilities/1:fixed_supply/higher_capability +Date: February 2023 +Contact: Saranya Gopal +Description: + This file shows the value of the Higher capability bit in + vsafe5V Fixed Supply Object. If the bit is set, then the sink + needs more than vsafe5V(eg. 12 V) to provide full functionality. + Valid values: 0, 1 + What: /sys/class/usb_power_delivery/...//1:fixed_supply/unconstrained_power Date: May 2022 Contact: Heikki Krogerus diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index b5ab26422c34..59c537a5e600 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -48,6 +48,13 @@ usb_suspend_supported_show(struct device *dev, struct device_attribute *attr, ch } static DEVICE_ATTR_RO(usb_suspend_supported); +static ssize_t +higher_capability_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_HIGHER_CAP)); +} +static DEVICE_ATTR_RO(higher_capability); + static ssize_t unconstrained_power_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -161,6 +168,7 @@ static struct device_type source_fixed_supply_type = { static struct attribute *sink_fixed_supply_attrs[] = { &dev_attr_dual_role_power.attr, + &dev_attr_higher_capability.attr, &dev_attr_unconstrained_power.attr, &dev_attr_usb_communication_capable.attr, &dev_attr_dual_role_data.attr, From bb9a58f8e51669db4971d29c57fab35f7d215fd6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:20 -0300 Subject: [PATCH 114/142] thunderbolt: Add quirk to disable CLx Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7af9da8ce8f9a16221ecd8ba4280582f5bd452fc commit 7af9da8ce8f9a16221ecd8ba4280582f5bd452fc Author: Sanjay R Mehta Date: Tue, 14 Feb 2023 13:13:50 -0600 Add QUIRK_NO_CLX to disable the CLx state for hardware which doesn't supports it. AMD Yellow Carp and Pink Sardine don't support CLx state, hence disabling it using QUIRK_NO_CLX. Cc: stable@vger.kernel.org Signed-off-by: Sanjay R Mehta Signed-off-by: Basavaraj Natikar [mw: added debug log when the quirk is run] Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/quirks.c | 13 +++++++++++++ drivers/thunderbolt/tb.h | 11 ++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index b5f2ec79c4d6..ae28a03fa890 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -20,6 +20,12 @@ static void quirk_dp_credit_allocation(struct tb_switch *sw) } } +static void quirk_clx_disable(struct tb_switch *sw) +{ + sw->quirks |= QUIRK_NO_CLX; + tb_sw_dbg(sw, "disabling CL states\n"); +} + struct tb_quirk { u16 hw_vendor_id; u16 hw_device_id; @@ -37,6 +43,13 @@ static const struct tb_quirk tb_quirks[] = { * DP buffers. */ { 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation }, + /* + * CLx is not supported on AMD USB4 Yellow Carp and Pink Sardine platforms. + */ + { 0x0438, 0x0208, 0x0000, 0x0000, quirk_clx_disable }, + { 0x0438, 0x0209, 0x0000, 0x0000, quirk_clx_disable }, + { 0x0438, 0x020a, 0x0000, 0x0000, quirk_clx_disable }, + { 0x0438, 0x020b, 0x0000, 0x0000, quirk_clx_disable }, }; /** diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 2953cf38c29e..becc4bb40805 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -23,6 +23,11 @@ #define NVM_MAX_SIZE SZ_512K #define NVM_DATA_DWORDS 16 +/* Keep link controller awake during update */ +#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0) +/* Disable CLx if not supported */ +#define QUIRK_NO_CLX BIT(1) + /** * struct tb_nvm - Structure holding NVM information * @dev: Owner of the NVM @@ -1019,6 +1024,9 @@ static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw, */ static inline bool tb_switch_is_clx_supported(const struct tb_switch *sw) { + if (sw->quirks & QUIRK_NO_CLX) + return false; + return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); } @@ -1291,9 +1299,6 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port); void usb4_port_device_remove(struct usb4_port *usb4); int usb4_port_device_resume(struct usb4_port *usb4); -/* Keep link controller awake during update */ -#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0) - void tb_check_quirks(struct tb_switch *sw); #ifdef CONFIG_ACPI From b55ce0ac04127ab52dd208492c96b16ada5127f2 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:21 -0300 Subject: [PATCH 115/142] usb: ucsi: Fix NULL pointer deref in ucsi_connector_change() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f87fb985452ab2083967103ac00bfd68fb182764 commit f87fb985452ab2083967103ac00bfd68fb182764 Author: Hans de Goede Date: Wed, 8 Mar 2023 16:42:42 +0100 When ucsi_init() fails, ucsi->connector is NULL, yet in case of ucsi_acpi we may still get events which cause the ucs_acpi code to call ucsi_connector_change(), which then derefs the NULL ucsi->connector pointer. Fix this by not setting ucsi->ntfy inside ucsi_init() until ucsi_init() has succeeded, so that ucsi_connector_change() ignores the events because UCSI_ENABLE_NTFY_CONNECTOR_CHANGE is not set in the ntfy mask. Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Link: https://bugzilla.kernel.org/show_bug.cgi?id=217106 Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230308154244.722337-2-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/ucsi/ucsi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f632350f6dcb..0623861c597b 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1329,7 +1329,7 @@ out_unlock: static int ucsi_init(struct ucsi *ucsi) { struct ucsi_connector *con; - u64 command; + u64 command, ntfy; int ret; int i; @@ -1341,8 +1341,8 @@ static int ucsi_init(struct ucsi *ucsi) } /* Enable basic notifications */ - ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; - command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; + ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; + command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) goto err_reset; @@ -1374,12 +1374,13 @@ static int ucsi_init(struct ucsi *ucsi) } /* Enable all notifications */ - ucsi->ntfy = UCSI_ENABLE_NTFY_ALL; - command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; + ntfy = UCSI_ENABLE_NTFY_ALL; + command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) goto err_unregister; + ucsi->ntfy = ntfy; return 0; err_unregister: From 57c4427893e024623e5b50db3d5b9bcd1c867026 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:21 -0300 Subject: [PATCH 116/142] usb: ucsi: Fix ucsi->connector race Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c commit 0482c34ec6f8557e06cd0f8e2d0e20e8ede6a22c Author: Hans de Goede Date: Wed, 8 Mar 2023 16:42:43 +0100 ucsi_init() which runs from a workqueue sets ucsi->connector and on an error will clear it again. ucsi->connector gets dereferenced by ucsi_resume(), this checks for ucsi->connector being NULL in case ucsi_init() has not finished yet; or in case ucsi_init() has failed. ucsi_init() setting ucsi->connector and then clearing it again on an error creates a race where the check in ucsi_resume() may pass, only to have ucsi->connector free-ed underneath it when ucsi_init() hits an error. Fix this race by making ucsi_init() store the connector array in a local variable and only assign it to ucsi->connector on success. Fixes: bdc62f2bae8f ("usb: typec: ucsi: Simplified registration and I/O API") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230308154244.722337-3-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/ucsi/ucsi.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 0623861c597b..8d1baf28df55 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1125,12 +1125,11 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; } -static int ucsi_register_port(struct ucsi *ucsi, int index) +static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { struct usb_power_delivery_desc desc = { ucsi->cap.pd_version}; struct usb_power_delivery_capabilities_desc pd_caps; struct usb_power_delivery_capabilities *pd_cap; - struct ucsi_connector *con = &ucsi->connector[index]; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; enum usb_role u_role = USB_ROLE_NONE; @@ -1151,7 +1150,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) init_completion(&con->complete); mutex_init(&con->lock); INIT_LIST_HEAD(&con->partner_tasks); - con->num = index + 1; con->ucsi = ucsi; cap->fwnode = ucsi_find_fwnode(con); @@ -1328,7 +1326,7 @@ out_unlock: */ static int ucsi_init(struct ucsi *ucsi) { - struct ucsi_connector *con; + struct ucsi_connector *con, *connector; u64 command, ntfy; int ret; int i; @@ -1359,16 +1357,16 @@ static int ucsi_init(struct ucsi *ucsi) } /* Allocate the connectors. Released in ucsi_unregister() */ - ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, - sizeof(*ucsi->connector), GFP_KERNEL); - if (!ucsi->connector) { + connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); + if (!connector) { ret = -ENOMEM; goto err_reset; } /* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { - ret = ucsi_register_port(ucsi, i); + connector[i].num = i + 1; + ret = ucsi_register_port(ucsi, &connector[i]); if (ret) goto err_unregister; } @@ -1380,11 +1378,12 @@ static int ucsi_init(struct ucsi *ucsi) if (ret < 0) goto err_unregister; + ucsi->connector = connector; ucsi->ntfy = ntfy; return 0; err_unregister: - for (con = ucsi->connector; con->port; con++) { + for (con = connector; con->port; con++) { ucsi_unregister_partner(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_port_psy(con); @@ -1400,10 +1399,7 @@ err_unregister: typec_unregister_port(con->port); con->port = NULL; } - - kfree(ucsi->connector); - ucsi->connector = NULL; - + kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); ucsi_reset_ppm(ucsi); From aaf2294c3db51f46925b36363d4924b64d0b621c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:22 -0300 Subject: [PATCH 117/142] usb: ucsi_acpi: Increase the command completion timeout Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=02d210f434249a7edbc160969b75df030dc6934d commit 02d210f434249a7edbc160969b75df030dc6934d Author: Hans de Goede Date: Wed, 8 Mar 2023 16:42:44 +0100 Commit 130a96d698d7 ("usb: typec: ucsi: acpi: Increase command completion timeout value") increased the timeout from 5 seconds to 60 seconds due to issues related to alternate mode discovery. After the alternate mode discovery switch to polled mode the timeout was reduced, but instead of being set back to 5 seconds it was reduced to 1 second. This is causing problems when using a Lenovo ThinkPad X1 yoga gen7 connected over Type-C to a LG 27UL850-W (charging DP over Type-C). When the monitor is already connected at boot the following error is logged: "PPM init failed (-110)", /sys/class/typec is empty and on unplugging the NULL pointer deref fixed earlier in this series happens. When the monitor is connected after boot the following error is logged instead: "GET_CONNECTOR_STATUS failed (-110)". Setting the timeout back to 5 seconds fixes both cases. Fixes: e08065069fc7 ("usb: typec: ucsi: acpi: Reduce the command completion timeout") Cc: stable@vger.kernel.org Reviewed-by: Heikki Krogerus Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230308154244.722337-4-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/ucsi/ucsi_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index ce0c8ef80c04..62206a6b8ea7 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -78,7 +78,7 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset, if (ret) goto out_clear_bit; - if (!wait_for_completion_timeout(&ua->complete, HZ)) + if (!wait_for_completion_timeout(&ua->complete, 5 * HZ)) ret = -ETIMEDOUT; out_clear_bit: From 30593c52d3b15ba6be029b8dfec34a492952f29c Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:22 -0300 Subject: [PATCH 118/142] usb: typec: tcpm: fix create duplicate source-capabilities file Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a826492fc9dfe32afd70fff93955ae8174bbf14b commit a826492fc9dfe32afd70fff93955ae8174bbf14b Author: Xu Yang Date: Wed, 15 Feb 2023 13:49:51 +0800 The kernel will dump in the below cases: sysfs: cannot create duplicate filename '/devices/virtual/usb_power_delivery/pd1/source-capabilities' 1. After soft reset has completed, an Explicit Contract negotiation occurs. The sink device will receive source capabilitys again. This will cause a duplicate source-capabilities file be created. 2. Power swap twice on a device that is initailly sink role. This will unregister existing capabilities when above cases occurs. Fixes: 8203d26905ee ("usb: typec: tcpm: Register USB Power Delivery Capabilities") cc: Signed-off-by: Xu Yang Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230215054951.238394-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/tcpm/tcpm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a0d943d78580..7f39cb9b3429 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4570,6 +4570,9 @@ static void run_state_machine(struct tcpm_port *port) case SOFT_RESET: port->message_id = 0; port->rx_msgid = -1; + /* remove existing capabilities */ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; tcpm_pd_send_control(port, PD_CTRL_ACCEPT); tcpm_ams_finish(port); if (port->pwr_role == TYPEC_SOURCE) { @@ -4589,6 +4592,9 @@ static void run_state_machine(struct tcpm_port *port) case SOFT_RESET_SEND: port->message_id = 0; port->rx_msgid = -1; + /* remove existing capabilities */ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else @@ -4718,6 +4724,9 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SNK_STARTUP, 0); break; case PR_SWAP_SNK_SRC_SINK_OFF: + /* will be source, remove existing capabilities */ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; /* * Prevent vbus discharge circuit from turning on during PR_SWAP * as this is not a disconnect. From 68a0051ecde8d452c05aac710c2c66dcef8b5fc4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:22 -0300 Subject: [PATCH 119/142] usb: typec: tcpm: fix warning when handle discover_identity message Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=abfc4fa28f0160df61c7149567da4f6494dfb488 commit abfc4fa28f0160df61c7149567da4f6494dfb488 Author: Xu Yang Date: Thu, 16 Feb 2023 11:15:15 +0800 Since both source and sink device can send discover_identity message in PD3, kernel may dump below warning: Signed-off-by: Desnes Nunes ------------[ cut here ]------------ WARNING: CPU: 0 PID: 169 at drivers/usb/typec/tcpm/tcpm.c:1446 tcpm_queue_vdm+0xe0/0xf0 Modules linked in: CPU: 0 PID: 169 Comm: 1-0050 Not tainted 6.1.1-00038-g6a3c36cf1da2-dirty #567 Hardware name: NXP i.MX8MPlus EVK board (DT) pstate: 20000005 (nzCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : tcpm_queue_vdm+0xe0/0xf0 lr : tcpm_queue_vdm+0x2c/0xf0 sp : ffff80000c19bcd0 x29: ffff80000c19bcd0 x28: 0000000000000001 x27: ffff0000d11c8ab8 x26: ffff0000d11cc000 x25: 0000000000000000 x24: 00000000ff008081 x23: 0000000000000001 x22: 00000000ff00a081 x21: ffff80000c19bdbc x20: 0000000000000000 x19: ffff0000d11c8080 x18: ffffffffffffffff x17: 0000000000000000 x16: 0000000000000000 x15: ffff0000d716f580 x14: 0000000000000001 x13: ffff0000d716f507 x12: 0000000000000001 x11: 0000000000000000 x10: 0000000000000020 x9 : 00000000000ee098 x8 : 00000000ffffffff x7 : 000000000000001c x6 : ffff0000d716f580 x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 x2 : ffff80000c19bdbc x1 : 00000000ff00a081 x0 : 0000000000000004 Call trace: tcpm_queue_vdm+0xe0/0xf0 tcpm_pd_rx_handler+0x340/0x1ab0 kthread_worker_fn+0xcc/0x18c kthread+0x10c/0x110 ret_from_fork+0x10/0x20 ---[ end trace 0000000000000000 ]--- Below sequences may trigger this warning: tcpm_send_discover_work(work) tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); tcpm_queue_vdm(port, header, data, count); port->vdm_state = VDM_STATE_READY; vdm_state_machine_work(work); <-- received discover_identity from partner vdm_run_state_machine(port); port->vdm_state = VDM_STATE_SEND_MESSAGE; mod_vdm_delayed_work(port, x); tcpm_pd_rx_handler(work); tcpm_pd_data_request(port, msg); tcpm_handle_vdm_request(port, msg->payload, cnt); tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); --> WARN_ON(port->vdm_state > VDM_STATE_DONE); For this case, the state machine could still send out discover identity message later if we skip current discover_identity message. So we should handle the received message firstly and override the pending discover_identity message without warning in this case. Then, a delayed send_discover work will send discover_identity message again. Fixes: e00943e91678 ("usb: typec: tcpm: PD3.0 sinks can send Discover Identity even in device mode") cc: Signed-off-by: Xu Yang Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230216031515.4151117-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7f39cb9b3429..1ee774c263f0 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1445,10 +1445,18 @@ static int tcpm_ams_start(struct tcpm_port *port, enum tcpm_ams ams) static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, const u32 *data, int cnt) { + u32 vdo_hdr = port->vdo_data[0]; + WARN_ON(!mutex_is_locked(&port->lock)); - /* Make sure we are not still processing a previous VDM packet */ - WARN_ON(port->vdm_state > VDM_STATE_DONE); + /* If is sending discover_identity, handle received message first */ + if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) { + port->send_discover = true; + mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); + } else { + /* Make sure we are not still processing a previous VDM packet */ + WARN_ON(port->vdm_state > VDM_STATE_DONE); + } port->vdo_count = cnt + 1; port->vdo_data[0] = header; @@ -1948,11 +1956,13 @@ static void vdm_run_state_machine(struct tcpm_port *port) switch (PD_VDO_CMD(vdo_hdr)) { case CMD_DISCOVER_IDENT: res = tcpm_ams_start(port, DISCOVER_IDENTITY); - if (res == 0) + if (res == 0) { port->send_discover = false; - else if (res == -EAGAIN) + } else if (res == -EAGAIN) { + port->vdo_data[0] = 0; mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); + } break; case CMD_DISCOVER_SVID: res = tcpm_ams_start(port, DISCOVER_SVIDS); @@ -2035,6 +2045,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) unsigned long timeout; port->vdm_retries = 0; + port->vdo_data[0] = 0; port->vdm_state = VDM_STATE_BUSY; timeout = vdm_ready_timeout(vdo_hdr); mod_vdm_delayed_work(port, timeout); From 11bae3f25f424ba9569988913fafeaa505ee28be Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:23 -0300 Subject: [PATCH 120/142] thunderbolt: Fix memory leak in margining Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=acec726473822bc6b585961f4ca2a11fa7f28341 commit acec726473822bc6b585961f4ca2a11fa7f28341 Author: Mika Westerberg Date: Fri, 3 Mar 2023 11:25:08 +0200 Memory for the usb4->margining needs to be relased for the upstream port of the router as well, even though the debugfs directory gets released with the router device removal. Fix this. Fixes: d0f1e0c2a699 ("thunderbolt: Add support for receiver lane margining") Cc: stable@vger.kernel.org Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/debugfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 4339e706cc3a..f92ad71ef983 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -942,7 +942,8 @@ static void margining_port_remove(struct tb_port *port) snprintf(dir_name, sizeof(dir_name), "port%d", port->port); parent = debugfs_lookup(dir_name, port->sw->debugfs_dir); - debugfs_remove_recursive(debugfs_lookup("margining", parent)); + if (parent) + debugfs_remove_recursive(debugfs_lookup("margining", parent)); kfree(port->usb4->margining); port->usb4->margining = NULL; @@ -967,19 +968,18 @@ static void margining_switch_init(struct tb_switch *sw) static void margining_switch_remove(struct tb_switch *sw) { + struct tb_port *upstream, *downstream; struct tb_switch *parent_sw; - struct tb_port *downstream; u64 route = tb_route(sw); if (!route) return; - /* - * Upstream is removed with the router itself but we need to - * remove the downstream port margining directory. - */ + upstream = tb_upstream_port(sw); parent_sw = tb_switch_parent(sw); downstream = tb_port_at(route, parent_sw); + + margining_port_remove(upstream); margining_port_remove(downstream); } From c278c4c4bd330c9e1314e4270823a69179a0eab6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:23 -0300 Subject: [PATCH 121/142] thunderbolt: Add missing UNSET_INBOUND_SBTX for retimer access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cd0c1e582b055dea615001b8bd8eccaf6f69f7ce commit cd0c1e582b055dea615001b8bd8eccaf6f69f7ce Author: Gil Fine Date: Fri, 3 Mar 2023 00:17:24 +0200 According to USB4 retimer specification, the process of firmware update sequence requires issuing a SET_INBOUND_SBTX port operation that later shall be followed by UNSET_INBOUND_SBTX port operation. This last step is not currently issued by the driver but it is necessary to make sure the retimers are put back to passthrough mode even during enumeration. If this step is missing the link may not come up properly after soft-reboot for example. For this reason issue UNSET_INBOUND_SBTX after SET_INBOUND_SBTX for enumeration and also when the NVM upgrade is run. Reported-by: Christian Schaubschläger Link: https://lore.kernel.org/linux-usb/b556f5ed-5ee8-9990-9910-afd60db93310@gmx.at/ Cc: stable@vger.kernel.org Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/retimer.c | 23 +++++++++++++++++++++-- drivers/thunderbolt/sb_regs.h | 1 + drivers/thunderbolt/tb.h | 1 + drivers/thunderbolt/usb4.c | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 56008eb91e2e..9cc28197dbc4 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -187,6 +187,22 @@ static ssize_t nvm_authenticate_show(struct device *dev, return ret; } +static void tb_retimer_set_inbound_sbtx(struct tb_port *port) +{ + int i; + + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) + usb4_port_retimer_set_inbound_sbtx(port, i); +} + +static void tb_retimer_unset_inbound_sbtx(struct tb_port *port) +{ + int i; + + for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) + usb4_port_retimer_unset_inbound_sbtx(port, i); +} + static ssize_t nvm_authenticate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -213,6 +229,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, rt->auth_status = 0; if (val) { + tb_retimer_set_inbound_sbtx(rt->port); if (val == AUTHENTICATE_ONLY) { ret = tb_retimer_nvm_authenticate(rt, true); } else { @@ -232,6 +249,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, } exit_unlock: + tb_retimer_unset_inbound_sbtx(rt->port); mutex_unlock(&rt->tb->lock); exit_rpm: pm_runtime_mark_last_busy(&rt->dev); @@ -440,8 +458,7 @@ int tb_retimer_scan(struct tb_port *port, bool add) * Enable sideband channel for each retimer. We can do this * regardless whether there is device connected or not. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_set_inbound_sbtx(port, i); + tb_retimer_set_inbound_sbtx(port); /* * Before doing anything else, read the authentication status. @@ -464,6 +481,8 @@ int tb_retimer_scan(struct tb_port *port, bool add) break; } + tb_retimer_unset_inbound_sbtx(port); + if (!last_idx) return 0; diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 5185cf3e4d97..f37a4320f10a 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -20,6 +20,7 @@ enum usb4_sb_opcode { USB4_SB_OPCODE_ROUTER_OFFLINE = 0x4e45534c, /* "LSEN" */ USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */ USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */ + USB4_SB_OPCODE_UNSET_INBOUND_SBTX = 0x50555355, /* "USUP" */ USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */ USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */ USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */ diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index becc4bb40805..273dc6faefa6 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1242,6 +1242,7 @@ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing, int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors); int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index); +int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index); int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf, u8 size); int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg, diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 1e5e9c147a31..95ff02395822 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1578,6 +1578,20 @@ int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index) 500); } +/** + * usb4_port_retimer_unset_inbound_sbtx() - Disable sideband channel transactions + * @port: USB4 port + * @index: Retimer index + * + * Disables sideband channel transations on SBTX. The reverse of + * usb4_port_retimer_set_inbound_sbtx(). + */ +int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index) +{ + return usb4_port_retimer_op(port, index, + USB4_SB_OPCODE_UNSET_INBOUND_SBTX, 500); +} + /** * usb4_port_retimer_read() - Read from retimer sideband registers * @port: USB4 port From 1005c14ae2e164ff87a0e1f06c29ff5c8b980d1a Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:24 -0300 Subject: [PATCH 122/142] thunderbolt: Call tb_check_quirks() after initializing adapters Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d2d6ddf188f609861489d5d188d545856a3ed399 commit d2d6ddf188f609861489d5d188d545856a3ed399 Author: Mika Westerberg Date: Fri, 3 Feb 2023 15:55:41 +0200 In order to apply quirks based on certain adapter types move call to tb_check_quirks() happen after the adapters are initialized. This should not affect the existing quirks. Cc: stable@vger.kernel.org Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/switch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index e1ad1b437291..9579486abd64 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2968,8 +2968,6 @@ int tb_switch_add(struct tb_switch *sw) dev_warn(&sw->dev, "reading DROM failed: %d\n", ret); tb_sw_dbg(sw, "uid: %#llx\n", sw->uid); - tb_check_quirks(sw); - ret = tb_switch_set_uuid(sw); if (ret) { dev_err(&sw->dev, "failed to set UUID\n"); @@ -2988,6 +2986,8 @@ int tb_switch_add(struct tb_switch *sw) } } + tb_check_quirks(sw); + tb_switch_default_link_ports(sw); ret = tb_switch_update_link_attributes(sw); From 5d0633fed2546a1ae20e3c16a967c03adf331b9b Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:24 -0300 Subject: [PATCH 123/142] thunderbolt: Limit USB3 bandwidth of certain Intel USB4 host routers Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f0a57dd33b3eadf540912cd130db727ea824d174 commit f0a57dd33b3eadf540912cd130db727ea824d174 Author: Gil Fine Date: Tue, 31 Jan 2023 13:04:52 +0200 Current Intel USB4 host routers have hardware limitation that the USB3 bandwidth cannot go higher than 16376 Mb/s. Work this around by adding a new quirk that limits the bandwidth for the affected host routers. Cc: stable@vger.kernel.org Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/quirks.c | 31 +++++++++++++++++++++++++++++++ drivers/thunderbolt/tb.h | 3 +++ drivers/thunderbolt/usb4.c | 17 +++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index ae28a03fa890..1157b8869bcc 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -26,6 +26,19 @@ static void quirk_clx_disable(struct tb_switch *sw) tb_sw_dbg(sw, "disabling CL states\n"); } +static void quirk_usb3_maximum_bandwidth(struct tb_switch *sw) +{ + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (!tb_port_is_usb3_down(port)) + continue; + port->max_bw = 16376; + tb_port_dbg(port, "USB3 maximum bandwidth limited to %u Mb/s\n", + port->max_bw); + } +} + struct tb_quirk { u16 hw_vendor_id; u16 hw_device_id; @@ -43,6 +56,24 @@ static const struct tb_quirk tb_quirks[] = { * DP buffers. */ { 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation }, + /* + * Limit the maximum USB3 bandwidth for the following Intel USB4 + * host routers due to a hardware issue. + */ + { 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI0, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI1, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI0, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI1, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_MTL_M_NHI0, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI0, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI1, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, /* * CLx is not supported on AMD USB4 Yellow Carp and Pink Sardine platforms. */ diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 273dc6faefa6..a54d4039f010 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -272,6 +272,8 @@ struct tb_bandwidth_group { * @group: Bandwidth allocation group the adapter is assigned to. Only * used for DP IN adapters for now. * @group_list: The adapter is linked to the group's list of ports through this + * @max_bw: Maximum possible bandwidth through this adapter if set to + * non-zero. * * In USB4 terminology this structure represents an adapter (protocol or * lane adapter). @@ -299,6 +301,7 @@ struct tb_port { unsigned int dma_credits; struct tb_bandwidth_group *group; struct list_head group_list; + unsigned int max_bw; }; /** diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 95ff02395822..6e87cf993c68 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1882,6 +1882,15 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index, usb4_port_retimer_nvm_read_block, &info); } +static inline unsigned int +usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw) +{ + /* Take the possible bandwidth limitation into account */ + if (port->max_bw) + return min(bw, port->max_bw); + return bw; +} + /** * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate * @port: USB3 adapter port @@ -1903,7 +1912,9 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port) return ret; lr = (val & ADP_USB3_CS_4_MSLR_MASK) >> ADP_USB3_CS_4_MSLR_SHIFT; - return lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000; + ret = lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000; + + return usb4_usb3_port_max_bandwidth(port, ret); } /** @@ -1930,7 +1941,9 @@ int usb4_usb3_port_actual_link_rate(struct tb_port *port) return 0; lr = val & ADP_USB3_CS_4_ALR_MASK; - return lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; + ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; + + return usb4_usb3_port_max_bandwidth(port, ret); } static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) From fe748472b00240c6c6a4154ee00703128120048d Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:25 -0300 Subject: [PATCH 124/142] thunderbolt: Use scale field when allocating USB3 bandwidth Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c82510b1d87bdebfe916048857d2ef46f1778aa5 commit c82510b1d87bdebfe916048857d2ef46f1778aa5 Author: Mika Westerberg Date: Tue, 27 Dec 2022 11:55:26 +0200 When tunneling aggregated USB3 (20 Gb/s) the bandwidth values that are programmed to the ADP_USB3_CS_2 go higher than 4096 and that does not fit anymore to the 12-bit field. Fix this by scaling the value using the scale field accordingly. Fixes: 3b1d8d577ca8 ("thunderbolt: Implement USB3 bandwidth negotiation routines") Cc: stable@vger.kernel.org Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/usb4.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 6e87cf993c68..a0996cb2893c 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -2094,18 +2094,30 @@ static int usb4_usb3_port_write_allocated_bandwidth(struct tb_port *port, int downstream_bw) { u32 val, ubw, dbw, scale; - int ret; + int ret, max_bw; - /* Read the used scale, hardware default is 0 */ - ret = tb_port_read(port, &scale, TB_CFG_PORT, - port->cap_adap + ADP_USB3_CS_3, 1); + /* Figure out suitable scale */ + scale = 0; + max_bw = max(upstream_bw, downstream_bw); + while (scale < 64) { + if (mbps_to_usb3_bw(max_bw, scale) < 4096) + break; + scale++; + } + + if (WARN_ON(scale >= 64)) + return -EINVAL; + + ret = tb_port_write(port, &scale, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_3, 1); if (ret) return ret; - scale &= ADP_USB3_CS_3_SCALE_MASK; ubw = mbps_to_usb3_bw(upstream_bw, scale); dbw = mbps_to_usb3_bw(downstream_bw, scale); + tb_port_dbg(port, "scaled bandwidth %u/%u, scale %u\n", ubw, dbw, scale); + ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_adap + ADP_USB3_CS_2, 1); if (ret) From 56820606020a6a37096909e83ee81a3022b6f8ce Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:25 -0300 Subject: [PATCH 125/142] USB: serial: cp210x: add Silicon Labs IFS-USB-DATACABLE IDs Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=71f8afa2b66e356f435b6141b4a9ccf953e18356 commit 71f8afa2b66e356f435b6141b4a9ccf953e18356 Author: Kees Jan Koster Date: Sat, 18 Feb 2023 15:18:30 +0100 The Silicon Labs IFS-USB-DATACABLE is used in conjunction with for example the Quint UPSes. It is used to enable Modbus communication with the UPS to query configuration, power and battery status. Signed-off-by: Kees Jan Koster Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Desnes Nunes --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 080245e237e9..805825fa5de0 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -120,6 +120,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ { USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */ { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ + { USB_DEVICE(0x10C4, 0x82AA) }, /* Silicon Labs IFS-USB-DATACABLE used with Quint UPS */ { USB_DEVICE(0x10C4, 0x82EF) }, /* CESINEL FALCO 6105 AC Power Supply */ { USB_DEVICE(0x10C4, 0x82F1) }, /* CESINEL MEDCAL EFD Earth Fault Detector */ { USB_DEVICE(0x10C4, 0x82F2) }, /* CESINEL MEDCAL ST Network Analyzer */ From f5f63a99a089f7ba40e3b3862f49aeff9d4cf369 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:25 -0300 Subject: [PATCH 126/142] USB: serial: option: add Telit FE990 compositions Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=773e8e7d07b753474b2ccd605ff092faaa9e65b9 commit 773e8e7d07b753474b2ccd605ff092faaa9e65b9 Author: Enrico Sau Date: Tue, 14 Mar 2023 10:00:59 +0100 Add the following Telit FE990 compositions: 0x1080: tty, adb, rmnet, tty, tty, tty, tty 0x1081: tty, adb, mbim, tty, tty, tty, tty 0x1082: rndis, tty, adb, tty, tty, tty, tty 0x1083: tty, adb, ecm, tty, tty, tty, tty Signed-off-by: Enrico Sau Link: https://lore.kernel.org/r/20230314090059.77876-1-enrico.sau@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Desnes Nunes --- drivers/usb/serial/option.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e6d8d9b35ad0..1621f66db25f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1300,6 +1300,14 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990 (PCIe) */ .driver_info = RSVD(0) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990 (rmnet) */ + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990 (MBIM) */ + .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1082, 0xff), /* Telit FE990 (RNDIS) */ + .driver_info = NCTRL(2) | RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990 (ECM) */ + .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), From 37ae813aa304abb3136909b241c0656777a17e30 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:26 -0300 Subject: [PATCH 127/142] uas: Add US_FL_NO_REPORT_OPCODES for JMicron JMS583Gen 2 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a37eb61b6ec064ac794b8a1e89fd33eb582fe51d commit a37eb61b6ec064ac794b8a1e89fd33eb582fe51d Author: Yaroslav Furman Date: Sun, 12 Mar 2023 11:07:45 +0200 Just like other JMicron JMS5xx enclosures, it chokes on report-opcodes, let's avoid them. Signed-off-by: Yaroslav Furman Cc: stable Link: https://lore.kernel.org/r/20230312090745.47962-1-yaro330@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index c7b763d6d102..1f8c9b16a0fb 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -111,6 +111,13 @@ UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BROKEN_FUA), +/* Reported by: Yaroslav Furman */ +UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, + "JMicron", + "JMS583Gen 2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* Reported-by: Thinh Nguyen */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY", From cc8444516d6b1fe9893338536c578fab5adc7eab Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:26 -0300 Subject: [PATCH 128/142] usb: chipdea: core: fix return -EINVAL if request role is the same with current role Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3670de80678961eda7fa2220883fc77c16868951 commit 3670de80678961eda7fa2220883fc77c16868951 Author: Xu Yang Date: Fri, 17 Mar 2023 14:15:15 +0800 It should not return -EINVAL if the request role is the same with current role, return non-error and without do anything instead. Fixes: a932a8041ff9 ("usb: chipidea: core: add sysfs group") cc: Acked-by: Peter Chen Signed-off-by: Xu Yang Link: https://lore.kernel.org/r/20230317061516.2451728-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/chipidea/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 27c601296130..b6f2a41de20e 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -984,9 +984,12 @@ static ssize_t role_store(struct device *dev, strlen(ci->roles[role]->name))) break; - if (role == CI_ROLE_END || role == ci->role) + if (role == CI_ROLE_END) return -EINVAL; + if (role == ci->role) + return n; + pm_runtime_get_sync(dev); disable_irq(ci->irq); ci_role_stop(ci); From b78cb2ce59518e08089f8effa68b384b69a796f6 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:27 -0300 Subject: [PATCH 129/142] usb: chipidea: core: fix possible concurrent when switch role Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=451b15ed138ec15bffbebb58a00ebdd884c3e659 commit 451b15ed138ec15bffbebb58a00ebdd884c3e659 Author: Xu Yang Date: Fri, 17 Mar 2023 14:15:16 +0800 The user may call role_store() when driver is handling ci_handle_id_switch() which is triggerred by otg event or power lost event. Unfortunately, the controller may go into chaos in this case. Fix this by protecting it with mutex lock. Fixes: a932a8041ff9 ("usb: chipidea: core: add sysfs group") cc: Acked-by: Peter Chen Signed-off-by: Xu Yang Link: https://lore.kernel.org/r/20230317061516.2451728-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/chipidea/ci.h | 2 ++ drivers/usb/chipidea/core.c | 8 +++++++- drivers/usb/chipidea/otg.c | 5 ++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 005c67cb3afb..f210b7489fd5 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -208,6 +208,7 @@ struct hw_bank { * @in_lpm: if the core in low power mode * @wakeup_int: if wakeup interrupt occur * @rev: The revision number for controller + * @mutex: protect code from concorrent running when doing role switch */ struct ci_hdrc { struct device *dev; @@ -260,6 +261,7 @@ struct ci_hdrc { bool in_lpm; bool wakeup_int; enum ci_revision rev; + struct mutex mutex; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index b6f2a41de20e..281fc51720ce 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -987,8 +987,12 @@ static ssize_t role_store(struct device *dev, if (role == CI_ROLE_END) return -EINVAL; - if (role == ci->role) + mutex_lock(&ci->mutex); + + if (role == ci->role) { + mutex_unlock(&ci->mutex); return n; + } pm_runtime_get_sync(dev); disable_irq(ci->irq); @@ -998,6 +1002,7 @@ static ssize_t role_store(struct device *dev, ci_handle_vbus_change(ci); enable_irq(ci->irq); pm_runtime_put_sync(dev); + mutex_unlock(&ci->mutex); return (ret == 0) ? n : ret; } @@ -1033,6 +1038,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&ci->lock); + mutex_init(&ci->mutex); ci->dev = dev; ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 622c3b68aa1e..f5490f2a5b6b 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -167,8 +167,10 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci) void ci_handle_id_switch(struct ci_hdrc *ci) { - enum ci_role role = ci_otg_role(ci); + enum ci_role role; + mutex_lock(&ci->mutex); + role = ci_otg_role(ci); if (role != ci->role) { dev_dbg(ci->dev, "switching from %s to %s\n", ci_role(ci)->name, ci->roles[role]->name); @@ -198,6 +200,7 @@ void ci_handle_id_switch(struct ci_hdrc *ci) if (role == CI_ROLE_GADGET) ci_handle_vbus_change(ci); } + mutex_unlock(&ci->mutex); } /** * ci_otg_work - perform otg (vbus/id) event handle From 28352d0694a2c42664b6f18fbc24d77f84bbf0be Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:27 -0300 Subject: [PATCH 130/142] usb: xhci: tegra: fix sleep in atomic call Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c7f9d2e413dc06a157c4e5dccde84aaf4655eb3 commit 4c7f9d2e413dc06a157c4e5dccde84aaf4655eb3 Author: Wayne Chang Date: Mon, 27 Mar 2023 17:55:48 +0800 When we set the dual-role port to Host mode, we observed the following splat: [ 167.057718] BUG: sleeping function called from invalid context at include/linux/sched/mm.h:229 [ 167.057872] Workqueue: events tegra_xusb_usb_phy_work [ 167.057954] Call trace: [ 167.057962] dump_backtrace+0x0/0x210 [ 167.057996] show_stack+0x30/0x50 [ 167.058020] dump_stack_lvl+0x64/0x84 [ 167.058065] dump_stack+0x14/0x34 [ 167.058100] __might_resched+0x144/0x180 [ 167.058140] __might_sleep+0x64/0xd0 [ 167.058171] slab_pre_alloc_hook.constprop.0+0xa8/0x110 [ 167.058202] __kmalloc_track_caller+0x74/0x2b0 [ 167.058233] kvasprintf+0xa4/0x190 [ 167.058261] kasprintf+0x58/0x90 [ 167.058285] tegra_xusb_find_port_node.isra.0+0x58/0xd0 [ 167.058334] tegra_xusb_find_port+0x38/0xa0 [ 167.058380] tegra_xusb_padctl_get_usb3_companion+0x38/0xd0 [ 167.058430] tegra_xhci_id_notify+0x8c/0x1e0 [ 167.058473] notifier_call_chain+0x88/0x100 [ 167.058506] atomic_notifier_call_chain+0x44/0x70 [ 167.058537] tegra_xusb_usb_phy_work+0x60/0xd0 [ 167.058581] process_one_work+0x1dc/0x4c0 [ 167.058618] worker_thread+0x54/0x410 [ 167.058650] kthread+0x188/0x1b0 [ 167.058672] ret_from_fork+0x10/0x20 The function tegra_xusb_padctl_get_usb3_companion eventually calls tegra_xusb_find_port and this in turn calls kasprintf which might sleep and so cannot be called from an atomic context. Fix this by moving the call to tegra_xusb_padctl_get_usb3_companion to the tegra_xhci_id_work function where it is really needed. Fixes: f836e7843036 ("usb: xhci-tegra: Add OTG support") Cc: stable@vger.kernel.org Signed-off-by: Wayne Chang Signed-off-by: Haotien Hsu Link: https://lore.kernel.org/r/20230327095548.1599470-1-haotienh@nvidia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-tegra.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 1ff22f675930..a88c39e525c2 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1360,6 +1360,9 @@ static void tegra_xhci_id_work(struct work_struct *work) mutex_unlock(&tegra->lock); + tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl, + tegra->otg_usb2_port); + if (tegra->host_mode) { /* switch to host mode */ if (tegra->otg_usb3_port >= 0) { @@ -1474,9 +1477,6 @@ static int tegra_xhci_id_notify(struct notifier_block *nb, } tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); - tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion( - tegra->padctl, - tegra->otg_usb2_port); tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false; From 0adb822e3eeb877f405fd042267af2ce4c515422 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:27 -0300 Subject: [PATCH 131/142] USB: serial: option: add Quectel RM500U-CN modem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7708a3858e69db91a8b69487994f33b96d20192a commit 7708a3858e69db91a8b69487994f33b96d20192a Author: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 28 Mar 2023 20:41:31 +0200 This modem supports several modes with a class network function and a number of serial functions, all using ff/00/00 The device ID is the same in all modes. RNDIS mode Signed-off-by: Desnes Nunes ---------- T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2c7c ProdID=0900 Rev= 4.04 S: Manufacturer=Quectel S: Product=RM500U-CN S: SerialNumber=0123456789ABCDEF C:* #Ifs= 7 Cfg#= 1 Atr=c0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03 I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms ECM mode -------- T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2c7c ProdID=0900 Rev= 4.04 S: Manufacturer=Quectel S: Product=RM500U-CN S: SerialNumber=0123456789ABCDEF C:* #Ifs= 7 Cfg#= 1 Atr=c0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=06 Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms NCM mode -------- T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 5 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=2c7c ProdID=0900 Rev= 4.04 S: Manufacturer=Quectel S: Product=RM500U-CN S: SerialNumber=0123456789ABCDEF C:* #Ifs= 7 Cfg#= 1 Atr=c0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Reported-by: Andrew Green Cc: stable@vger.kernel.org Signed-off-by: Bjørn Mork Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 1621f66db25f..f31cc3c76329 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1198,6 +1198,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 0x0900, 0xff, 0, 0), /* RM500U-CN */ + .driver_info = ZLP }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, From 129a794667b6e90dc1ba3e537316af50650bcee4 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:28 -0300 Subject: [PATCH 132/142] xhci: also avoid the XHCI_ZERO_64B_REGS quirk with a passthrough iommu Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ecaa4902439298f6b0e29f47424a86b310a9ff4f commit ecaa4902439298f6b0e29f47424a86b310a9ff4f Author: D Scott Phillips Date: Thu, 30 Mar 2023 17:30:54 +0300 Previously the quirk was skipped when no iommu was present. The same rationale for skipping the quirk also applies in the iommu.passthrough=1 case. Skip applying the XHCI_ZERO_64B_REGS quirk if the device's iommu domain is passthrough. Fixes: 12de0a35c996 ("xhci: Add quirk to zero 64bit registers on Renesas PCIe controllers") Cc: stable Signed-off-by: D Scott Phillips Acked-by: Marc Zyngier Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230330143056.1390020-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6183ce8574b1..bdb6dd819a3b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -228,6 +229,7 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us) static void xhci_zero_64b_regs(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct iommu_domain *domain; int err, i; u64 val; u32 intrs; @@ -246,7 +248,9 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) * an iommu. Doing anything when there is no iommu is definitely * unsafe... */ - if (!(xhci->quirks & XHCI_ZERO_64B_REGS) || !device_iommu_mapped(dev)) + domain = iommu_get_domain_for_dev(dev); + if (!(xhci->quirks & XHCI_ZERO_64B_REGS) || !domain || + domain->type == IOMMU_DOMAIN_IDENTITY) return; xhci_info(xhci, "Zeroing 64bit base registers, expecting fault\n"); From 4632aedd838e8826cfaae660ab41f8f6291ed5eb Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:28 -0300 Subject: [PATCH 133/142] Revert "usb: xhci-pci: Set PROBE_PREFER_ASYNCHRONOUS" Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8e77d3d59d7b5da13deda1d832c51b8bbdbe2037 commit 8e77d3d59d7b5da13deda1d832c51b8bbdbe2037 Author: Mathias Nyman Date: Thu, 30 Mar 2023 17:30:55 +0300 This reverts commit 4c2604a9a6899bab195edbee35fc8d64ce1444aa. Asynch probe caused regression in a setup with both Renesas and Intel xHC controllers. Devices connected to the Renesas disconnected shortly after boot. With Asynch probe the busnumbers got interleaved. xhci_hcd 0000:00:14.0: new USB bus registered, assigned bus number 1 xhci_hcd 0000:04:00.0: new USB bus registered, assigned bus number 2 xhci_hcd 0000:00:14.0: new USB bus registered, assigned bus number 3 xhci_hcd 0000:04:00.0: new USB bus registered, assigned bus number 4 Reason why this commit causes regression is still unknown, but revert it while debugging the issue. Fixes: 4c2604a9a689 ("usb: xhci-pci: Set PROBE_PREFER_ASYNCHRONOUS") Cc: stable Link: https://lore.kernel.org/linux-usb/20230307132120.5897c5af@deangelis.fenrir.org.uk Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230330143056.1390020-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-pci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index fb988e4ea924..6db07ca419c3 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -771,12 +771,11 @@ static struct pci_driver xhci_pci_driver = { /* suspend and resume implemented later */ .shutdown = usb_hcd_pci_shutdown, - .driver = { #ifdef CONFIG_PM - .pm = &usb_hcd_pci_pm_ops, -#endif - .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .driver = { + .pm = &usb_hcd_pci_pm_ops }, +#endif }; static int __init xhci_pci_init(void) From 5d569d9a9e1ed9faf40e3076d090efd6dce4c6f8 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:28 -0300 Subject: [PATCH 134/142] xhci: Free the command allocated for setting LPM if we return early Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f6caea4855553a8b99ba3ec23ecdb5ed8262f26c commit f6caea4855553a8b99ba3ec23ecdb5ed8262f26c Author: Mathias Nyman Date: Thu, 30 Mar 2023 17:30:56 +0300 The command allocated to set exit latency LPM values need to be freed in case the command is never queued. This would be the case if there is no change in exit latency values, or device is missing. Reported-by: Mirsad Goran Todorovac Link: https://lore.kernel.org/linux-usb/24263902-c9b3-ce29-237b-1c3d6918f4fe@alu.unizg.hr Tested-by: Mirsad Goran Todorovac Fixes: 5c2a380a5aa8 ("xhci: Allocate separate command structures for each LPM command") Cc: Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230330143056.1390020-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index bdb6dd819a3b..6307bae9cddf 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4442,6 +4442,7 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci, if (!virt_dev || max_exit_latency == virt_dev->current_mel) { spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, command); return 0; } From 3b67f5eb59c1a8d8e7381f8a5f9afcf2ff11c388 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 15 Jun 2023 17:49:29 -0300 Subject: [PATCH 135/142] usb: typec: altmodes/displayport: Fix configure initial pin assignment Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=eddebe39602efe631b83ff8d03f26eba12cfd760 commit eddebe39602efe631b83ff8d03f26eba12cfd760 Author: RD Babiera Date: Wed, 29 Mar 2023 21:51:59 +0000 While determining the initial pin assignment to be sent in the configure message, using the DP_PIN_ASSIGN_DP_ONLY_MASK mask causes the DFP_U to send both Pin Assignment C and E when both are supported by the DFP_U and UFP_U. The spec (Table 5-7 DFP_U Pin Assignment Selection Mandates, VESA DisplayPort Alt Mode Standard v2.0) indicates that the DFP_U never selects Pin Assignment E when Pin Assignment C is offered. Update the DP_PIN_ASSIGN_DP_ONLY_MASK conditional to intially select only Pin Assignment C if it is available. Fixes: 0e3bb7d6894d ("usb: typec: Add driver for DisplayPort alternate mode") Cc: stable@vger.kernel.org Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230329215159.2046932-1-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/typec/altmodes/displayport.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 662cd043b50e..8f3e884222ad 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -112,8 +112,12 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC && pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK) pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK; - else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) + else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) { pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK; + /* Default to pin assign C if available */ + if (pin_assign & BIT(DP_PIN_ASSIGN_C)) + pin_assign = BIT(DP_PIN_ASSIGN_C); + } if (!pin_assign) return -EINVAL; From a2fa9e782a862bd2765589172448f963e82f44de Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Wed, 14 Jun 2023 19:47:30 -0300 Subject: [PATCH 136/142] Revert "dt-bindings: usb: renesas, usb3-peri: Document RZ/V2M r9a09g011 support" Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: RHEL-only Conflicts: * RZ/V2M r9a09g011 is not supported hence this should had not been merged on the previous v6.2 rebase. This reverts commit 3c3ce77c9dab7f9628a9a96e881d0afb50ed74a6 Signed-off-by: Desnes Nunes --- .../bindings/usb/renesas,usb3-peri.yaml | 99 +++---------------- 1 file changed, 15 insertions(+), 84 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/renesas,usb3-peri.yaml b/Documentation/devicetree/bindings/usb/renesas,usb3-peri.yaml index 55dfd121b555..9fcf54b10b07 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usb3-peri.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usb3-peri.yaml @@ -11,55 +11,27 @@ maintainers: properties: compatible: - oneOf: - - items: - - enum: - - renesas,r8a774a1-usb3-peri # RZ/G2M - - renesas,r8a774b1-usb3-peri # RZ/G2N - - renesas,r8a774c0-usb3-peri # RZ/G2E - - renesas,r8a774e1-usb3-peri # RZ/G2H - - renesas,r8a7795-usb3-peri # R-Car H3 - - renesas,r8a7796-usb3-peri # R-Car M3-W - - renesas,r8a77961-usb3-peri # R-Car M3-W+ - - renesas,r8a77965-usb3-peri # R-Car M3-N - - renesas,r8a77990-usb3-peri # R-Car E3 - - const: renesas,rcar-gen3-usb3-peri - - - items: - - enum: - - renesas,r9a09g011-usb3-peri # RZ/V2M - - const: renesas,rzv2m-usb3-peri + items: + - enum: + - renesas,r8a774a1-usb3-peri # RZ/G2M + - renesas,r8a774b1-usb3-peri # RZ/G2N + - renesas,r8a774c0-usb3-peri # RZ/G2E + - renesas,r8a774e1-usb3-peri # RZ/G2H + - renesas,r8a7795-usb3-peri # R-Car H3 + - renesas,r8a7796-usb3-peri # R-Car M3-W + - renesas,r8a77961-usb3-peri # R-Car M3-W+ + - renesas,r8a77965-usb3-peri # R-Car M3-N + - renesas,r8a77990-usb3-peri # R-Car E3 + - const: renesas,rcar-gen3-usb3-peri reg: maxItems: 1 interrupts: - minItems: 1 - items: - - description: Combined interrupt for DMA, SYS and ERR - - description: Dual Role Device (DRD) - - description: Battery Charging - - description: Global Purpose Input - - interrupt-names: - minItems: 1 - items: - - const: all_p - - const: drd - - const: bc - - const: gpi + maxItems: 1 clocks: - minItems: 1 - items: - - description: Main clock - - description: Register access clock - - clock-names: - minItems: 1 - items: - - const: aclk - - const: reg + maxItems: 1 phys: maxItems: 1 @@ -71,15 +43,7 @@ properties: maxItems: 1 resets: - minItems: 1 - items: - - description: Peripheral reset - - description: DRD reset - - reset-names: - items: - - const: aresetn_p - - const: drd_reset + maxItems: 1 usb-role-switch: $ref: /schemas/types.yaml#/definitions/flag @@ -114,39 +78,6 @@ required: - interrupts - clocks -allOf: - - if: - properties: - compatible: - contains: - enum: - - renesas,rzv2m-usb3-peri - then: - properties: - clocks: - minItems: 2 - clock-names: - minItems: 2 - interrupts: - minItems: 4 - interrupt-names: - minItems: 4 - resets: - minItems: 2 - required: - - clock-names - - interrupt-names - - resets - - reset-names - else: - properties: - clocks: - maxItems: 1 - interrupts: - maxItems: 1 - resets: - maxItems: 1 - additionalProperties: false examples: From c2db49fa2f95abec3691bb810491784e3efaa489 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Tue, 20 Jun 2023 11:32:37 -0300 Subject: [PATCH 137/142] media: uvcvideo: Don't expose unsupported formats to userspace Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81f3affa19d6ab0c32aef46b053838219eef7e71 commit 81f3affa19d6ab0c32aef46b053838219eef7e71 Author: Laurent Pinchart Date: Thu Apr 20 10:45:59 2023 +0100 When the uvcvideo driver encounters a format descriptor with an unknown format GUID, it creates a corresponding struct uvc_format instance with the fcc field set to 0. Since commit 50459f103edf ("media: uvcvideo: Remove format descriptions"), the driver relies on the V4L2 core to provide the format description string, which the V4L2 core can't do without a valid 4CC. This triggers a WARN_ON. As a format with a zero 4CC can't be selected, it is unusable for applications. Ignore the format completely without creating a uvc_format instance, which fixes the warning. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217252 Link: https://bugzilla.redhat.com/show_bug.cgi?id=2180107 Fixes: 50459f103edf ("media: uvcvideo: Remove format descriptions") Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Desnes Nunes --- drivers/media/usb/uvc/uvc_driver.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 42ff9795f700..235fc4f52a98 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -251,14 +251,17 @@ static int uvc_parse_format(struct uvc_device *dev, /* Find the format descriptor from its GUID. */ fmtdesc = uvc_format_by_guid(&buffer[5]); - if (fmtdesc != NULL) { - format->fcc = fmtdesc->fcc; - } else { + if (!fmtdesc) { + /* + * Unknown video formats are not fatal errors, the + * caller will skip this descriptor. + */ dev_info(&streaming->intf->dev, "Unknown video format %pUl\n", &buffer[5]); - format->fcc = 0; + return 0; } + format->fcc = fmtdesc->fcc; format->bpp = buffer[21]; /* @@ -675,7 +678,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, interval = (u32 *)&frame[nframes]; streaming->format = format; - streaming->nformats = nformats; + streaming->nformats = 0; /* Parse the format descriptors. */ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { @@ -689,7 +692,10 @@ static int uvc_parse_streaming(struct uvc_device *dev, &interval, buffer, buflen); if (ret < 0) goto error; + if (!ret) + break; + streaming->nformats++; frame += format->nframes; format++; From f04abf75d3e00414327c4de32e4d17e35daea962 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 6 Jul 2023 12:38:25 -0300 Subject: [PATCH 138/142] usb: host: xhci-plat: Improve clock handling in probe() Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8c6e8b09617915e8af3ab7dbfecd8f0a9c7cf94f commit 8c6e8b09617915e8af3ab7dbfecd8f0a9c7cf94f Author: Biju Das Date: Sat Jan 21 14:58:48 2023 +0000 It is always better to acquire all the clock resources first and then do the clock operations. This patch acquires all the optional clocks first and then calls corresponding prepare_enable(). There is no functional change. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230121145853.4792-8-biju.das.jz@bp.renesas.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-plat.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index cf58fffba21f..6aa9c04903dc 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -205,16 +205,16 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s goto put_hcd; } - ret = clk_prepare_enable(xhci->reg_clk); - if (ret) - goto put_hcd; - xhci->clk = devm_clk_get_optional(&pdev->dev, NULL); if (IS_ERR(xhci->clk)) { ret = PTR_ERR(xhci->clk); - goto disable_reg_clk; + goto put_hcd; } + ret = clk_prepare_enable(xhci->reg_clk); + if (ret) + goto put_hcd; + ret = clk_prepare_enable(xhci->clk); if (ret) goto disable_reg_clk; From c1de9fbb19fea63e5176f32c15926ef4b5331207 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Thu, 6 Jul 2023 12:40:13 -0300 Subject: [PATCH 139/142] usb: host: xhci-plat: Add reset support Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=224eb5311d6a8c180932465873d809b48a2470bf commit 224eb5311d6a8c180932465873d809b48a2470bf Author: Biju Das Date: Sat Jan 21 14:58:49 2023 +0000 Add optional reset support. This is in preparation to adding USB xHCI support for RZ/V2M SoC. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230121145853.4792-9-biju.das.jz@bp.renesas.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Desnes Nunes --- drivers/usb/host/xhci-plat.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 6aa9c04903dc..b9f9625467d6 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-plat.h" @@ -211,10 +212,20 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s goto put_hcd; } - ret = clk_prepare_enable(xhci->reg_clk); + xhci->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(xhci->reset)) { + ret = PTR_ERR(xhci->reset); + goto put_hcd; + } + + ret = reset_control_deassert(xhci->reset); if (ret) goto put_hcd; + ret = clk_prepare_enable(xhci->reg_clk); + if (ret) + goto err_reset; + ret = clk_prepare_enable(xhci->clk); if (ret) goto disable_reg_clk; @@ -320,6 +331,9 @@ disable_clk: disable_reg_clk: clk_disable_unprepare(xhci->reg_clk); +err_reset: + reset_control_assert(xhci->reset); + put_hcd: usb_put_hcd(hcd); @@ -397,6 +411,7 @@ int xhci_plat_remove(struct platform_device *dev) clk_disable_unprepare(clk); clk_disable_unprepare(reg_clk); + reset_control_assert(xhci->reset); usb_put_hcd(hcd); pm_runtime_disable(&dev->dev); From f1c752810671ad74d9e66483603362062c44d18f Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Fri, 7 Jul 2023 10:23:50 -0300 Subject: [PATCH 140/142] media: usb: siano: Fix warning due to null work_func_t function pointer Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6f489a966fbeb0da63d45c2c66a8957eab604bf6 commit 6f489a966fbeb0da63d45c2c66a8957eab604bf6 Author: Duoming Zhou Date: Tue, 23 May 2023 07:59:32 +0800 The previous commit ebad8e731c1c ("media: usb: siano: Fix use after free bugs caused by do_submit_urb") adds cancel_work_sync() in smsusb_stop_streaming(). But smsusb_stop_streaming() may be called, even if the work_struct surb->wq has not been initialized. As a result, the warning will occur. One of the processes that could lead to warning is shown below: smsusb_probe() smsusb_init_device() if (!dev->in_ep || !dev->out_ep || align < 0) { smsusb_term_device(intf); smsusb_stop_streaming() cancel_work_sync(&dev->surbs[i].wq); __cancel_work_timer() __flush_work() if (WARN_ON(!work->func)) // work->func is null The log reported by syzbot is shown below: WARNING: CPU: 0 PID: 897 at kernel/workqueue.c:3066 __flush_work+0x798/0xa80 kernel/workqueue.c:3063 Modules linked in: CPU: 0 PID: 897 Comm: kworker/0:2 Not tainted 6.2.0-rc1-syzkaller #0 RIP: 0010:__flush_work+0x798/0xa80 kernel/workqueue.c:3066 ... RSP: 0018:ffffc9000464ebf8 EFLAGS: 00010246 RAX: 1ffff11002dbb420 RBX: 0000000000000021 RCX: 1ffffffff204fa4e RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffff888016dda0e8 RBP: ffffc9000464ed98 R08: 0000000000000001 R09: ffffffff90253b2f R10: 0000000000000001 R11: 0000000000000000 R12: ffff888016dda0e8 R13: ffff888016dda0e8 R14: ffff888016dda100 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8880b9a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ffd4331efe8 CR3: 000000000b48e000 CR4: 00000000003506f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: __cancel_work_timer+0x315/0x460 kernel/workqueue.c:3160 smsusb_stop_streaming drivers/media/usb/siano/smsusb.c:182 [inline] smsusb_term_device+0xda/0x2d0 drivers/media/usb/siano/smsusb.c:344 smsusb_init_device+0x400/0x9ce drivers/media/usb/siano/smsusb.c:419 smsusb_probe+0xbbd/0xc55 drivers/media/usb/siano/smsusb.c:567 ... This patch adds check before cancel_work_sync(). If surb->wq has not been initialized, the cancel_work_sync() will not be executed. Reported-by: syzbot+27b0b464864741b18b99@syzkaller.appspotmail.com Fixes: ebad8e731c1c ("media: usb: siano: Fix use after free bugs caused by do_submit_urb") Signed-off-by: Duoming Zhou Signed-off-by: Hans Verkuil Signed-off-by: Desnes Nunes --- drivers/media/usb/siano/smsusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 6f443c542c6d..640737d3b8ae 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -179,7 +179,8 @@ static void smsusb_stop_streaming(struct smsusb_device_t *dev) for (i = 0; i < MAX_URBS; i++) { usb_kill_urb(&dev->surbs[i].urb); - cancel_work_sync(&dev->surbs[i].wq); + if (dev->surbs[i].wq.func) + cancel_work_sync(&dev->surbs[i].wq); if (dev->surbs[i].cb) { smscore_putbuffer(dev->coredev, dev->surbs[i].cb); From 77c4a024e41ecce7ffcb7eb90a92b0e4165472bc Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Mon, 10 Jul 2023 20:26:01 -0300 Subject: [PATCH 141/142] thunderbolt: Clear registers properly when auto clear isn't in use Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c4af8e3fecd03b0aedcd38145955605cfebe7e3a commit c4af8e3fecd03b0aedcd38145955605cfebe7e3a Author: Mario Limonciello Date: Mon, 24 Apr 2023 14:55:54 -0500 When `QUIRK_AUTO_CLEAR_INT` isn't set, interrupt masking should be cleared by writing to Interrupt Mask Clear (IMR) and interrupt status should be cleared properly at shutdown/init. This fixes an error where interrupts are left enabled during resume from hibernation with `CONFIG_USB4=y`. Fixes: 468c49f44759 ("thunderbolt: Disable interrupt auto clear for rings") Cc: stable@vger.kernel.org # v6.3 Reported-by: Takashi Iwai Link: https://bugzilla.kernel.org/show_bug.cgi?id=217343 Signed-off-by: Mario Limonciello Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/nhi.c | 29 ++++++++++++++++++++++++----- drivers/thunderbolt/nhi_regs.h | 2 ++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index cfebec107f3f..0a525f44ea31 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -54,6 +54,21 @@ static int ring_interrupt_index(const struct tb_ring *ring) return bit; } +static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring) +{ + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) + return; + iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); +} + +static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring) +{ + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) + ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + ring); + else + iowrite32(~0, nhi->iobase + REG_RING_INT_CLEAR + ring); +} + /* * ring_interrupt_active() - activate/deactivate interrupts for a single ring * @@ -61,8 +76,8 @@ static int ring_interrupt_index(const struct tb_ring *ring) */ static void ring_interrupt_active(struct tb_ring *ring, bool active) { - int reg = REG_RING_INTERRUPT_BASE + - ring_interrupt_index(ring) / 32 * 4; + int index = ring_interrupt_index(ring) / 32 * 4; + int reg = REG_RING_INTERRUPT_BASE + index; int interrupt_bit = ring_interrupt_index(ring) & 31; int mask = 1 << interrupt_bit; u32 old, new; @@ -123,7 +138,11 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) "interrupt for %s %d is already %s\n", RING_TYPE(ring), ring->hop, active ? "enabled" : "disabled"); - iowrite32(new, ring->nhi->iobase + reg); + + if (active) + iowrite32(new, ring->nhi->iobase + reg); + else + nhi_mask_interrupt(ring->nhi, mask, index); } /* @@ -136,11 +155,11 @@ static void nhi_disable_interrupts(struct tb_nhi *nhi) int i = 0; /* disable interrupts */ for (i = 0; i < RING_INTERRUPT_REG_COUNT(nhi); i++) - iowrite32(0, nhi->iobase + REG_RING_INTERRUPT_BASE + 4 * i); + nhi_mask_interrupt(nhi, ~0, 4 * i); /* clear interrupt status bits */ for (i = 0; i < RING_NOTIFY_REG_COUNT(nhi); i++) - ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + 4 * i); + nhi_clear_interrupt(nhi, 4 * i); } /* ring helper methods */ diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index faef165a919c..6ba295815477 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -93,6 +93,8 @@ struct ring_desc { #define REG_RING_INTERRUPT_BASE 0x38200 #define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32) +#define REG_RING_INTERRUPT_MASK_CLEAR_BASE 0x38208 + #define REG_INT_THROTTLING_RATE 0x38c00 /* Interrupt Vector Allocation */ From 1b3aa8a36abc6974e952fef6a86b2a0f0d5b9bb1 Mon Sep 17 00:00:00 2001 From: Desnes Nunes Date: Mon, 10 Jul 2023 22:50:51 -0300 Subject: [PATCH 142/142] thunderbolt: Mask ring interrupt on Intel hardware as well Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2212495 Upstream Status: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9f9666e65359d5047089aef97ac87c50f624ecb0 commit 9f9666e65359d5047089aef97ac87c50f624ecb0 Author: Mika Westerberg Date: Tue, 30 May 2023 08:48:29 +0300 When resuming from system sleep states the driver issues following warning on Intel hardware: thunderbolt 0000:07:00.0: interrupt for TX ring 0 is already enabled The reason for this is that the commit in question did not mask the ring interrupt on Intel hardware leaving the interrupt active. Fix this by masking it also in Intel hardware. Reported-by: beld zhang Tested-by: beld zhang Closes: https://lore.kernel.org/linux-usb/ZHKW5NeabmfhgLbY@debian.me/ Fixes: c4af8e3fecd0 ("thunderbolt: Clear registers properly when auto clear isn't in use") Cc: stable@vger.kernel.org Reviewed-by: Mario Limonciello Signed-off-by: Mika Westerberg Signed-off-by: Desnes Nunes --- drivers/thunderbolt/nhi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 0a525f44ea31..4a6a3802d7e5 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -56,9 +56,14 @@ static int ring_interrupt_index(const struct tb_ring *ring) static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring) { - if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) - return; - iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); + if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) { + u32 val; + + val = ioread32(nhi->iobase + REG_RING_INTERRUPT_BASE + ring); + iowrite32(val & ~mask, nhi->iobase + REG_RING_INTERRUPT_BASE + ring); + } else { + iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring); + } } static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring)