From 74098cc06e753d3ffd8398b040a3a1dfb65260c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bartosik?= Date: Thu, 27 Nov 2025 11:16:44 +0000 Subject: [PATCH 01/22] xhci: dbgtty: fix device unregister: fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixup replaces tty_vhangup() call with call to tty_port_tty_vhangup(). Both calls hangup tty device synchronously however tty_port_tty_vhangup() increases reference count during the hangup operation using scoped_guard(tty_port_tty). Cc: stable Fixes: 1f73b8b56cf3 ("xhci: dbgtty: fix device unregister") Signed-off-by: Ɓukasz Bartosik Link: https://patch.msgid.link/20251127111644.3161386-1-ukaszb@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgtty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index 57cdda4e09c8..90282e51e23e 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -554,7 +554,7 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) * Hang up the TTY. This wakes up any blocked * writers and causes subsequent writes to fail. */ - tty_vhangup(port->port.tty); + tty_port_tty_vhangup(&port->port); tty_unregister_device(dbc_tty_driver, port->minor); xhci_dbc_tty_exit_port(port); From e1003aa7ec9eccdde4c926bd64ef42816ad55f25 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Wed, 26 Nov 2025 11:12:21 +0530 Subject: [PATCH 02/22] usb: dwc3: keep susphy enabled during exit to avoid controller faults On some platforms, switching USB roles from host to device can trigger controller faults due to premature PHY power-down. This occurs when the PHY is disabled too early during teardown, causing synchronization issues between the PHY and controller. Keep susphy enabled during dwc3_host_exit() and dwc3_gadget_exit() ensures the PHY remains in a low-power state capable of handling required commands during role switch. Cc: stable Fixes: 6d735722063a ("usb: dwc3: core: Prevent phy suspend during init") Suggested-by: Thinh Nguyen Signed-off-by: Udipto Goswami Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20251126054221.120638-1-udipto.goswami@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 2 +- drivers/usb/dwc3/host.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index bc3fe31638b9..8a35a6901db7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -4826,7 +4826,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) if (!dwc->gadget) return; - dwc3_enable_susphy(dwc, false); + dwc3_enable_susphy(dwc, true); usb_del_gadget(dwc->gadget); dwc3_gadget_free_endpoints(dwc); usb_put_gadget(dwc->gadget); diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index cf6512ed17a6..96b588bd08cd 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -227,7 +227,7 @@ void dwc3_host_exit(struct dwc3 *dwc) if (dwc->sys_wakeup) device_init_wakeup(&dwc->xhci->dev, false); - dwc3_enable_susphy(dwc, false); + dwc3_enable_susphy(dwc, true); platform_device_unregister(dwc->xhci); dwc->xhci = NULL; } From d14cd998e67ba8f1cca52a260a1ce1a60954fd8b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 11:11:07 +0100 Subject: [PATCH 03/22] usb: typec: ucsi: huawei-gaokin: add DRM dependency Selecting DRM_AUX_HPD_BRIDGE is not possible from a built-in driver when CONFIG_DRM=m: WARNING: unmet direct dependencies detected for DRM_AUX_HPD_BRIDGE Depends on [m]: HAS_IOMEM [=y] && DRM [=m] && DRM_BRIDGE [=y] && OF [=y] Selected by [y]: - UCSI_HUAWEI_GAOKUN [=y] && USB_SUPPORT [=y] && TYPEC [=y] && TYPEC_UCSI [=y] && EC_HUAWEI_GAOKUN [=y] && DRM_BRIDGE [=y] && OF [=y] Add the same dependency we have in similar drivers to work around this. Fixes: 00327d7f2c8c ("usb: typec: ucsi: add Huawei Matebook E Go ucsi driver") Cc: stable Signed-off-by: Arnd Bergmann Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251204101111.1035975-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index 7fcb1e1de5d6..b812be4d0e67 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -96,6 +96,7 @@ config UCSI_LENOVO_YOGA_C630 config UCSI_HUAWEI_GAOKUN tristate "UCSI Interface Driver for Huawei Matebook E Go" depends on EC_HUAWEI_GAOKUN + depends on DRM || !DRM select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help This driver enables UCSI support on the Huawei Matebook E Go tablet, From 36cc7e09df9e43db21b46519b740145410dd9f4a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Thu, 4 Dec 2025 21:21:29 +0800 Subject: [PATCH 04/22] usb: renesas_usbhs: Fix a resource leak in usbhs_pipe_malloc() usbhsp_get_pipe() set pipe's flags to IS_USED. In error paths, usbhsp_put_pipe() is required to clear pipe's flags to prevent pipe exhaustion. Fixes: f1407d5c6624 ("usb: renesas_usbhs: Add Renesas USBHS common code") Cc: stable Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20251204132129.109234-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/pipe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 75fff2e4cbc6..56fc3ff5016f 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -713,11 +713,13 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, /* make sure pipe is not busy */ ret = usbhsp_pipe_barrier(pipe); if (ret < 0) { + usbhsp_put_pipe(pipe); dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe)); return NULL; } if (usbhsp_setup_pipecfg(pipe, is_host, dir_in, &pipecfg)) { + usbhsp_put_pipe(pipe); dev_err(dev, "can't setup pipe\n"); return NULL; } From 41ca62e3e21e48c2903b3b45e232cf4f2ff7434f Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Fri, 5 Dec 2025 11:48:31 +0800 Subject: [PATCH 05/22] usb: phy: fsl-usb: Fix use-after-free in delayed work during device removal The delayed work item otg_event is initialized in fsl_otg_conf() and scheduled under two conditions: 1. When a host controller binds to the OTG controller. 2. When the USB ID pin state changes (cable insertion/removal). A race condition occurs when the device is removed via fsl_otg_remove(): the fsl_otg instance may be freed while the delayed work is still pending or executing. This leads to use-after-free when the work function fsl_otg_event() accesses the already freed memory. The problematic scenario: (detach thread) | (delayed work) fsl_otg_remove() | kfree(fsl_otg_dev) //FREE| fsl_otg_event() | og = container_of(...) //USE | og-> //USE Fix this by calling disable_delayed_work_sync() in fsl_otg_remove() before deallocating the fsl_otg structure. This ensures the delayed work is properly canceled and completes execution prior to memory deallocation. This bug was identified through static analysis. Fixes: 0807c500a1a6 ("USB: add Freescale USB OTG Transceiver driver") Cc: stable Signed-off-by: Duoming Zhou Link: https://patch.msgid.link/20251205034831.12846-1-duoming@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-fsl-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 40ac68e52cee..e266a47c4d48 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -988,6 +988,7 @@ static void fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); + disable_delayed_work_sync(&fsl_otg_dev->otg_event); usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); From 128bb7fab342546352603bde8b49ff54e3af0529 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Sat, 6 Dec 2025 15:04:45 +0800 Subject: [PATCH 06/22] usb: typec: altmodes/displayport: Drop the device reference in dp_altmode_probe() In error paths, call typec_altmode_put_plug() to drop the device reference obtained by typec_altmode_get_plug(). Fixes: 71ba4fe56656 ("usb: typec: altmodes/displayport: add SOP' support") Cc: stable Signed-off-by: Haoxiang Li Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251206070445.190770-1-lihaoxiang@isrc.iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 8d111ad3b71b..d96ab106a980 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -766,12 +766,16 @@ int dp_altmode_probe(struct typec_altmode *alt) if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & - DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) + DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) { + typec_altmode_put_plug(plug); return -ENODEV; + } dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); - if (!dp) + if (!dp) { + typec_altmode_put_plug(plug); return -ENOMEM; + } INIT_WORK(&dp->work, dp_altmode_work); mutex_init(&dp->lock); From c84117912bddd9e5d87e68daf182410c98181407 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Mon, 15 Dec 2025 10:09:31 +0800 Subject: [PATCH 07/22] USB: lpc32xx_udc: Fix error handling in probe lpc32xx_udc_probe() acquires an i2c_client reference through isp1301_get_client() but fails to release it in both error handling paths and the normal removal path. This could result in a reference count leak for the I2C device, preventing proper cleanup and potentially leading to resource exhaustion. Add put_device() to release the reference in the probe failure path and in the remove function. Calling path: isp1301_get_client() -> of_find_i2c_device_by_node() -> i2c_find_device_by_fwnode(). As comments of i2c_find_device_by_fwnode() says, 'The user must call put_device(&client->dev) once done with the i2c client.' Found by code review. Cc: stable Fixes: 24a28e428351 ("USB: gadget driver for LPC32xx") Signed-off-by: Ma Ke Link: https://patch.msgid.link/20251215020931.15324-1-make24@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/lpc32xx_udc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 1a7d3c4f652f..73c0f28a8585 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3020,7 +3020,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (retval) - return retval; + goto i2c_fail; udc->board = &lpc32xx_usbddata; @@ -3038,28 +3038,32 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) /* Get IRQs */ for (i = 0; i < 4; i++) { udc->udp_irq[i] = platform_get_irq(pdev, i); - if (udc->udp_irq[i] < 0) - return udc->udp_irq[i]; + if (udc->udp_irq[i] < 0) { + retval = udc->udp_irq[i]; + goto i2c_fail; + } } udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); - return PTR_ERR(udc->udp_baseaddr); + retval = PTR_ERR(udc->udp_baseaddr); + goto i2c_fail; } /* Get USB device clock */ udc->usb_slv_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(udc->usb_slv_clk)) { dev_err(udc->dev, "failed to acquire USB device clock\n"); - return PTR_ERR(udc->usb_slv_clk); + retval = PTR_ERR(udc->usb_slv_clk); + goto i2c_fail; } /* Enable USB device clock */ retval = clk_prepare_enable(udc->usb_slv_clk); if (retval < 0) { dev_err(udc->dev, "failed to start USB device clock\n"); - return retval; + goto i2c_fail; } /* Setup deferred workqueue data */ @@ -3161,6 +3165,8 @@ dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); i2c_fail: + if (udc->isp1301_i2c_client) + put_device(&udc->isp1301_i2c_client->dev); clk_disable_unprepare(udc->usb_slv_clk); dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); @@ -3189,6 +3195,9 @@ static void lpc32xx_udc_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); + if (udc->isp1301_i2c_client) + put_device(&udc->isp1301_i2c_client->dev); + clk_disable_unprepare(udc->usb_slv_clk); } From 14ad4c10d5bdd413ff9a914260e89b5f54b7a2c7 Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Tue, 16 Dec 2025 06:22:02 -0600 Subject: [PATCH 08/22] usb: typec: ucsi: Fix null pointer dereference in ucsi_sync_control_common Add missing null check for cci parameter before dereferencing it in ucsi_sync_control_common(). The function can be called with cci=NULL from ucsi_acknowledge(), which leads to a null pointer dereference when accessing *cci in the condition check. The crash occurs because the code checks if cci is not null before calling ucsi->ops->read_cci(ucsi, cci), but then immediately dereferences cci without a null check in the following condition: (*cci & UCSI_CCI_COMMAND_COMPLETE). KASAN trace: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:ucsi_sync_control_common+0x2ae/0x4e0 [typec_ucsi] Cc: stable Fixes: 667ecac55861 ("usb: typec: ucsi: return CCI and message from sync_control callback") Reviewed-by: Heikki Krogerus Signed-off-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20251216122210.5457-1-superm1@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 9b3df776137a..7129973f19e7 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -97,7 +97,7 @@ out_clear_bit: if (!ret && cci) ret = ucsi->ops->read_cci(ucsi, cci); - if (!ret && ucsi->message_in_size > 0 && + if (!ret && cci && ucsi->message_in_size > 0 && (*cci & UCSI_CCI_COMMAND_COMPLETE)) ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in, ucsi->message_in_size); From 3b4961313d31e200c9e974bb1536cdea217f78b5 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Thu, 11 Dec 2025 10:49:36 +0400 Subject: [PATCH 09/22] usb: dwc3: of-simple: fix clock resource leak in dwc3_of_simple_probe When clk_bulk_prepare_enable() fails, the error path jumps to err_resetc_assert, skipping clk_bulk_put_all() and leaking the clock references acquired by clk_bulk_get_all(). Add err_clk_put_all label to properly release clock resources in all error paths. Found via static analysis and code review. Fixes: c0c61471ef86 ("usb: dwc3: of-simple: Convert to bulk clk API") Cc: stable Signed-off-by: Miaoqian Lin Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20251211064937.2360510-1-linmq006@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-of-simple.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index a4954a21be93..c116143335d9 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -70,11 +70,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) simple->num_clocks = ret; ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks); if (ret) - goto err_resetc_assert; + goto err_clk_put_all; ret = of_platform_populate(np, NULL, NULL, dev); if (ret) - goto err_clk_put; + goto err_clk_disable; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -82,8 +82,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) return 0; -err_clk_put: +err_clk_disable: clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); +err_clk_put_all: clk_bulk_put_all(simple->num_clocks, simple->clks); err_resetc_assert: From 0831269b5f71594882accfceb02638124f88955d Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Thu, 18 Dec 2025 09:23:18 +0800 Subject: [PATCH 10/22] usb: usb-storage: Maintain minimal modifications to the bcdDevice range. We cannot determine which models require the NO_ATA_1X and IGNORE_RESIDUE quirks aside from the EL-R12 optical drive device. Fixes: 955a48a5353f ("usb: usb-storage: No additional quirks need to be added to the EL-R12 optical drive.") Signed-off-by: Chen Changcheng Link: https://patch.msgid.link/20251218012318.15978-1-chenchangcheng@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index b695f5ba9a40..939a98c2d3f7 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -98,7 +98,7 @@ UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160, US_FL_NO_ATA_1X), /* Reported-by: Benjamin Tissoires */ -UNUSUAL_DEV(0x13fd, 0x3940, 0x0309, 0x0309, +UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x0309, "Initio Corporation", "INIC-3069", USB_SC_DEVICE, USB_PR_DEVICE, NULL, From 5106dbab44fba8ec6dede3f4e75d17f5aa777ec8 Mon Sep 17 00:00:00 2001 From: Hsin-Te Yuan Date: Thu, 18 Dec 2025 15:37:57 +0800 Subject: [PATCH 11/22] usb: typec: ucsi: Get connector status after enable notifications Originally, the notification for connector change will be enabled after the first read of the connector status. Therefore, if the event happens during this window, it will be missing and make the status unsynced. Get the connector status only after enabling the notification for connector change to ensure the status is synced. Fixes: c1b0bc2dabfa ("usb: typec: Add support for UCSI interface") Cc: stable Tested-by: Kenneth R. Crudup Reviewed-by: Heikki Krogerus Signed-off-by: Hsin-Te Yuan Link: https://patch.msgid.link/20251218-ucsi-v7-1-aea83e83fb12@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 133 +++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 59 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7129973f19e7..b153ed5bffb1 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1624,11 +1624,71 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; } +static void ucsi_init_port(struct ucsi *ucsi, struct ucsi_connector *con) +{ + enum usb_role u_role = USB_ROLE_NONE; + int ret; + + /* Get the status */ + ret = ucsi_get_connector_status(con, false); + if (ret) { + dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); + return; + } + + if (ucsi->ops->connector_status) + ucsi->ops->connector_status(con); + + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { + case UCSI_CONSTAT_PARTNER_TYPE_UFP: + case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: + u_role = USB_ROLE_HOST; + fallthrough; + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: + typec_set_data_role(con->port, TYPEC_HOST); + break; + case UCSI_CONSTAT_PARTNER_TYPE_DFP: + u_role = USB_ROLE_DEVICE; + typec_set_data_role(con->port, TYPEC_DEVICE); + break; + default: + break; + } + + /* Check if there is already something connected */ + if (UCSI_CONSTAT(con, CONNECTED)) { + typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); + ucsi_register_partner(con); + ucsi_pwr_opmode_change(con); + ucsi_orientation(con); + ucsi_port_psy_changed(con); + if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) + ucsi_get_partner_identity(con); + if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) + ucsi_check_cable(con); + } + + /* Only notify USB controller if partner supports USB data */ + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) + u_role = USB_ROLE_NONE; + + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); + if (ret) + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", + con->num, u_role); + + if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { + ucsi_register_device_pdos(con); + ucsi_get_src_pdos(con); + ucsi_check_altmodes(con); + ucsi_check_connector_capability(con); + } +} + static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; - enum usb_role u_role = USB_ROLE_NONE; u64 command; char *name; int ret; @@ -1729,63 +1789,6 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) goto out; } - /* Get the status */ - ret = ucsi_get_connector_status(con, false); - if (ret) { - dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); - goto out; - } - - if (ucsi->ops->connector_status) - ucsi->ops->connector_status(con); - - switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { - case UCSI_CONSTAT_PARTNER_TYPE_UFP: - case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: - u_role = USB_ROLE_HOST; - fallthrough; - case UCSI_CONSTAT_PARTNER_TYPE_CABLE: - typec_set_data_role(con->port, TYPEC_HOST); - break; - case UCSI_CONSTAT_PARTNER_TYPE_DFP: - u_role = USB_ROLE_DEVICE; - typec_set_data_role(con->port, TYPEC_DEVICE); - break; - default: - break; - } - - /* Check if there is already something connected */ - if (UCSI_CONSTAT(con, CONNECTED)) { - typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); - ucsi_register_partner(con); - ucsi_pwr_opmode_change(con); - ucsi_orientation(con); - ucsi_port_psy_changed(con); - if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) - ucsi_get_partner_identity(con); - if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) - ucsi_check_cable(con); - } - - /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) - u_role = USB_ROLE_NONE; - - ret = usb_role_switch_set_role(con->usb_role_sw, u_role); - if (ret) { - dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", - con->num, u_role); - ret = 0; - } - - if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { - ucsi_register_device_pdos(con); - ucsi_get_src_pdos(con); - ucsi_check_altmodes(con); - ucsi_check_connector_capability(con); - } - trace_ucsi_register_port(con->num, con); out: @@ -1903,17 +1906,29 @@ static int ucsi_init(struct ucsi *ucsi) goto err_unregister; } + /* Delay other interactions with each connector until ucsi_init_port is done */ + for (i = 0; i < ucsi->cap.num_connectors; i++) + mutex_lock(&connector[i].lock); + /* Enable all supported notifications */ ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; ucsi->message_in_size = 0; ret = ucsi_send_command(ucsi, command); - if (ret < 0) + if (ret < 0) { + for (i = 0; i < ucsi->cap.num_connectors; i++) + mutex_unlock(&connector[i].lock); goto err_unregister; + } ucsi->connector = connector; ucsi->ntfy = ntfy; + for (i = 0; i < ucsi->cap.num_connectors; i++) { + ucsi_init_port(ucsi, &connector[i]); + mutex_unlock(&connector[i].lock); + } + mutex_lock(&ucsi->ppm_lock); ret = ucsi->ops->read_cci(ucsi, &cci); mutex_unlock(&ucsi->ppm_lock); From 782be79e4551550d7a82b1957fc0f7347e6d461f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Dec 2025 16:35:15 +0100 Subject: [PATCH 12/22] usb: gadget: lpc32xx_udc: fix clock imbalance in error path A recent change fixing a device reference leak introduced a clock imbalance by reusing an error path so that the clock may be disabled before having been enabled. Note that the clock framework allows for passing in NULL clocks so there is no risk for a NULL pointer dereference. Also drop the bogus I2C client NULL check added by the offending commit as the pointer has already been verified to be non-NULL. Fixes: c84117912bdd ("USB: lpc32xx_udc: Fix error handling in probe") Cc: stable@vger.kernel.org Cc: Ma Ke Signed-off-by: Johan Hovold Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251218153519.19453-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/lpc32xx_udc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 73c0f28a8585..a962d4294fbe 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3020,7 +3020,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (retval) - goto i2c_fail; + goto err_put_client; udc->board = &lpc32xx_usbddata; @@ -3040,7 +3040,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) udc->udp_irq[i] = platform_get_irq(pdev, i); if (udc->udp_irq[i] < 0) { retval = udc->udp_irq[i]; - goto i2c_fail; + goto err_put_client; } } @@ -3048,7 +3048,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); retval = PTR_ERR(udc->udp_baseaddr); - goto i2c_fail; + goto err_put_client; } /* Get USB device clock */ @@ -3056,14 +3056,14 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (IS_ERR(udc->usb_slv_clk)) { dev_err(udc->dev, "failed to acquire USB device clock\n"); retval = PTR_ERR(udc->usb_slv_clk); - goto i2c_fail; + goto err_put_client; } /* Enable USB device clock */ retval = clk_prepare_enable(udc->usb_slv_clk); if (retval < 0) { dev_err(udc->dev, "failed to start USB device clock\n"); - goto i2c_fail; + goto err_put_client; } /* Setup deferred workqueue data */ @@ -3165,9 +3165,10 @@ dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); i2c_fail: - if (udc->isp1301_i2c_client) - put_device(&udc->isp1301_i2c_client->dev); clk_disable_unprepare(udc->usb_slv_clk); +err_put_client: + put_device(&udc->isp1301_i2c_client->dev); + dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); return retval; @@ -3195,10 +3196,9 @@ static void lpc32xx_udc_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); - if (udc->isp1301_i2c_client) - put_device(&udc->isp1301_i2c_client->dev); - clk_disable_unprepare(udc->usb_slv_clk); + + put_device(&udc->isp1301_i2c_client->dev); } #ifdef CONFIG_PM From b4b64fda4d30a83a7f00e92a0c8a1d47699609f3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Dec 2025 16:35:16 +0100 Subject: [PATCH 13/22] usb: phy: isp1301: fix non-OF device reference imbalance A recent change fixing a device reference leak in a UDC driver introduced a potential use-after-free in the non-OF case as the isp1301_get_client() helper only increases the reference count for the returned I2C device in the OF case. Increment the reference count also for non-OF so that the caller can decrement it unconditionally. Note that this is inherently racy just as using the returned I2C device is since nothing is preventing the PHY driver from being unbound while in use. Fixes: c84117912bdd ("USB: lpc32xx_udc: Fix error handling in probe") Cc: stable@vger.kernel.org Cc: Ma Ke Signed-off-by: Johan Hovold Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251218153519.19453-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-isp1301.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index f9b5c411aee4..2940f0c84e1b 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -149,7 +149,12 @@ struct i2c_client *isp1301_get_client(struct device_node *node) return client; /* non-DT: only one ISP1301 chip supported */ - return isp1301_i2c_client; + if (isp1301_i2c_client) { + get_device(&isp1301_i2c_client->dev); + return isp1301_i2c_client; + } + + return NULL; } EXPORT_SYMBOL_GPL(isp1301_get_client); From b4c61e542faf8c9131d69ecfc3ad6de96d1b2ab8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Dec 2025 16:35:17 +0100 Subject: [PATCH 14/22] usb: ohci-nxp: fix device leak on probe failure Make sure to drop the reference taken when looking up the PHY I2C device during probe on probe failure (e.g. probe deferral) and on driver unbind. Fixes: 73108aa90cbf ("USB: ohci-nxp: Use isp1301 driver") Cc: stable@vger.kernel.org # 3.5 Reported-by: Ma Ke Link: https://lore.kernel.org/lkml/20251117013428.21840-1-make24@iscas.ac.cn/ Signed-off-by: Johan Hovold Acked-by: Alan Stern Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251218153519.19453-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 24d5a1dc5056..509ca7d8d513 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -223,6 +223,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) fail_resource: usb_put_hcd(hcd); fail_disable: + put_device(&isp1301_i2c_client->dev); isp1301_i2c_client = NULL; return ret; } @@ -234,6 +235,7 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_nxp_stop_hc(); usb_put_hcd(hcd); + put_device(&isp1301_i2c_client->dev); isp1301_i2c_client = NULL; } From ad6fb7367eaa87caa15e9be2129d8e8018a33cba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Dec 2025 16:35:18 +0100 Subject: [PATCH 15/22] usb: gadget: lpc32xx_udc: clean up probe error labels Error labels should be named after what they do rather than after from where they are jumped to. Rename the probe error labels for consistency and to improve readability. Signed-off-by: Johan Hovold Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251218153519.19453-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/lpc32xx_udc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index a962d4294fbe..83c7e243dcf9 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3084,7 +3084,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (!udc->udca_v_base) { dev_err(udc->dev, "error getting UDCA region\n"); retval = -ENOMEM; - goto i2c_fail; + goto err_disable_clk; } udc->udca_p_base = dma_handle; dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n", @@ -3097,7 +3097,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (!udc->dd_cache) { dev_err(udc->dev, "error getting DD DMA region\n"); retval = -ENOMEM; - goto dma_alloc_fail; + goto err_free_dma; } /* Clear USB peripheral and initialize gadget endpoints */ @@ -3111,14 +3111,14 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "LP request irq %d failed\n", udc->udp_irq[IRQ_USB_LP]); - goto irq_req_fail; + goto err_destroy_pool; } retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq, 0, "udc_hp", udc); if (retval < 0) { dev_err(udc->dev, "HP request irq %d failed\n", udc->udp_irq[IRQ_USB_HP]); - goto irq_req_fail; + goto err_destroy_pool; } retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA], @@ -3126,7 +3126,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "DEV request irq %d failed\n", udc->udp_irq[IRQ_USB_DEVDMA]); - goto irq_req_fail; + goto err_destroy_pool; } /* The transceiver interrupt is used for VBUS detection and will @@ -3137,7 +3137,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "VBUS request irq %d failed\n", udc->udp_irq[IRQ_USB_ATX]); - goto irq_req_fail; + goto err_destroy_pool; } /* Initialize wait queue */ @@ -3146,7 +3146,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval < 0) - goto add_gadget_fail; + goto err_destroy_pool; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); @@ -3158,13 +3158,12 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION); return 0; -add_gadget_fail: -irq_req_fail: +err_destroy_pool: dma_pool_destroy(udc->dd_cache); -dma_alloc_fail: +err_free_dma: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); -i2c_fail: +err_disable_clk: clk_disable_unprepare(udc->usb_slv_clk); err_put_client: put_device(&udc->isp1301_i2c_client->dev); From 8ff242c04157244020888fa234a6b145415d5a65 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Dec 2025 16:35:19 +0100 Subject: [PATCH 16/22] usb: ohci-nxp: clean up probe error labels Error labels should be named after what they do rather than after from where they are jumped to. Rename the probe error labels for consistency and to improve readability. Signed-off-by: Johan Hovold Reviewed-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251218153519.19453-6-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 509ca7d8d513..7663f2aa35e9 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -169,13 +169,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) - goto fail_disable; + goto err_put_client; dev_dbg(&pdev->dev, "%s: " DRIVER_DESC " (nxp)\n", hcd_name); if (usb_disabled()) { dev_err(&pdev->dev, "USB is disabled\n"); ret = -ENODEV; - goto fail_disable; + goto err_put_client; } /* Enable USB host clock */ @@ -183,7 +183,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) if (IS_ERR(usb_host_clk)) { dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n"); ret = PTR_ERR(usb_host_clk); - goto fail_disable; + goto err_put_client; } isp1301_configure(); @@ -192,13 +192,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) if (!hcd) { dev_err(&pdev->dev, "Failed to allocate HC buffer\n"); ret = -ENOMEM; - goto fail_disable; + goto err_put_client; } hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); - goto fail_resource; + goto err_put_hcd; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); @@ -206,7 +206,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; - goto fail_resource; + goto err_put_hcd; } ohci_nxp_start_hc(); @@ -220,9 +220,9 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) } ohci_nxp_stop_hc(); -fail_resource: +err_put_hcd: usb_put_hcd(hcd); -fail_disable: +err_put_client: put_device(&isp1301_i2c_client->dev); isp1301_i2c_client = NULL; return ret; From 0dc580d4a6518b539cbb2877dd9b283df14aac6f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2025 15:54:06 +0100 Subject: [PATCH 17/22] Revert "usb: typec: ucsi: Get connector status after enable notifications" This reverts commit 5106dbab44fba8ec6dede3f4e75d17f5aa777ec8. There are reported issues in this file, so revert the commit for now so that the original offending changes can be reverted and working systems can be restored. This can come back at a later time if it is rebased yet-again (sorry.) Cc: stable Cc: Johan Hovold Link: https://lore.kernel.org/r/20251222152204.2846-1-johan@kernel.org Fixes: 5106dbab44fb ("usb: typec: ucsi: Get connector status after enable notifications") Cc: Kenneth R. Crudup Cc: Heikki Krogerus Cc: Hsin-Te Yuan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 133 +++++++++++++++------------------- 1 file changed, 59 insertions(+), 74 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index b153ed5bffb1..7129973f19e7 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1624,71 +1624,11 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) return NULL; } -static void ucsi_init_port(struct ucsi *ucsi, struct ucsi_connector *con) -{ - enum usb_role u_role = USB_ROLE_NONE; - int ret; - - /* Get the status */ - ret = ucsi_get_connector_status(con, false); - if (ret) { - dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); - return; - } - - if (ucsi->ops->connector_status) - ucsi->ops->connector_status(con); - - switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { - case UCSI_CONSTAT_PARTNER_TYPE_UFP: - case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: - u_role = USB_ROLE_HOST; - fallthrough; - case UCSI_CONSTAT_PARTNER_TYPE_CABLE: - typec_set_data_role(con->port, TYPEC_HOST); - break; - case UCSI_CONSTAT_PARTNER_TYPE_DFP: - u_role = USB_ROLE_DEVICE; - typec_set_data_role(con->port, TYPEC_DEVICE); - break; - default: - break; - } - - /* Check if there is already something connected */ - if (UCSI_CONSTAT(con, CONNECTED)) { - typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); - ucsi_register_partner(con); - ucsi_pwr_opmode_change(con); - ucsi_orientation(con); - ucsi_port_psy_changed(con); - if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) - ucsi_get_partner_identity(con); - if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) - ucsi_check_cable(con); - } - - /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) - u_role = USB_ROLE_NONE; - - ret = usb_role_switch_set_role(con->usb_role_sw, u_role); - if (ret) - dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", - con->num, u_role); - - if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { - ucsi_register_device_pdos(con); - ucsi_get_src_pdos(con); - ucsi_check_altmodes(con); - ucsi_check_connector_capability(con); - } -} - static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; + enum usb_role u_role = USB_ROLE_NONE; u64 command; char *name; int ret; @@ -1789,6 +1729,63 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) goto out; } + /* Get the status */ + ret = ucsi_get_connector_status(con, false); + if (ret) { + dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); + goto out; + } + + if (ucsi->ops->connector_status) + ucsi->ops->connector_status(con); + + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { + case UCSI_CONSTAT_PARTNER_TYPE_UFP: + case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: + u_role = USB_ROLE_HOST; + fallthrough; + case UCSI_CONSTAT_PARTNER_TYPE_CABLE: + typec_set_data_role(con->port, TYPEC_HOST); + break; + case UCSI_CONSTAT_PARTNER_TYPE_DFP: + u_role = USB_ROLE_DEVICE; + typec_set_data_role(con->port, TYPEC_DEVICE); + break; + default: + break; + } + + /* Check if there is already something connected */ + if (UCSI_CONSTAT(con, CONNECTED)) { + typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); + ucsi_register_partner(con); + ucsi_pwr_opmode_change(con); + ucsi_orientation(con); + ucsi_port_psy_changed(con); + if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) + ucsi_get_partner_identity(con); + if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) + ucsi_check_cable(con); + } + + /* Only notify USB controller if partner supports USB data */ + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) + u_role = USB_ROLE_NONE; + + ret = usb_role_switch_set_role(con->usb_role_sw, u_role); + if (ret) { + dev_err(ucsi->dev, "con:%d: failed to set usb role:%d\n", + con->num, u_role); + ret = 0; + } + + if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { + ucsi_register_device_pdos(con); + ucsi_get_src_pdos(con); + ucsi_check_altmodes(con); + ucsi_check_connector_capability(con); + } + trace_ucsi_register_port(con->num, con); out: @@ -1906,29 +1903,17 @@ static int ucsi_init(struct ucsi *ucsi) goto err_unregister; } - /* Delay other interactions with each connector until ucsi_init_port is done */ - for (i = 0; i < ucsi->cap.num_connectors; i++) - mutex_lock(&connector[i].lock); - /* Enable all supported notifications */ ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; ucsi->message_in_size = 0; ret = ucsi_send_command(ucsi, command); - if (ret < 0) { - for (i = 0; i < ucsi->cap.num_connectors; i++) - mutex_unlock(&connector[i].lock); + if (ret < 0) goto err_unregister; - } ucsi->connector = connector; ucsi->ntfy = ntfy; - for (i = 0; i < ucsi->cap.num_connectors; i++) { - ucsi_init_port(ucsi, &connector[i]); - mutex_unlock(&connector[i].lock); - } - mutex_lock(&ucsi->ppm_lock); ret = ucsi->ops->read_cci(ucsi, &cci); mutex_unlock(&ucsi->ppm_lock); From df3e3169303f7084db3990c999701934af0c04c0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2025 15:57:16 +0100 Subject: [PATCH 18/22] Revert "usb: typec: ucsi: Fix null pointer dereference in ucsi_sync_control_common" This reverts commit 14ad4c10d5bdd413ff9a914260e89b5f54b7a2c7. The originally offending commit will be reverted instead of this fix up at this point in time, so revert this fix. Cc: Heikki Krogerus Cc: Mario Limonciello (AMD) Cc: stable Cc: Johan Hovold Fixes: 14ad4c10d5bd ("usb: typec: ucsi: Fix null pointer dereference in ucsi_sync_control_common") Link: https://lore.kernel.org/r/20251222152204.2846-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7129973f19e7..9b3df776137a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -97,7 +97,7 @@ out_clear_bit: if (!ret && cci) ret = ucsi->ops->read_cci(ucsi, cci); - if (!ret && cci && ucsi->message_in_size > 0 && + if (!ret && ucsi->message_in_size > 0 && (*cci & UCSI_CCI_COMMAND_COMPLETE)) ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in, ucsi->message_in_size); From 784b756dde9151b2f44da313f390f25139f1a64e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Dec 2025 16:22:01 +0100 Subject: [PATCH 19/22] Revert "usb: typec: ucsi: Add support for SET_PDOS command" This reverts commit 1b474ee01fbb73b1365adbf9b3067f7375e471ee. The new buffer management code that this feature relies on is broken so revert for now. The interface for writing data and support for UCSI_SET_PDOS looks like it could use some more thought as well. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251222152204.2846-2-johan@kernel.org --- drivers/usb/typec/ucsi/debugfs.c | 1 - drivers/usb/typec/ucsi/ucsi.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 174f4d53b777..90d11b79d2c0 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -37,7 +37,6 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_USB: case UCSI_SET_POWER_LEVEL: case UCSI_READ_POWER_LEVEL: - case UCSI_SET_PDOS: ucsi->message_in_size = 0; ret = ucsi_send_command(ucsi, val); break; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index f946b728c373..d01b796a8d23 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -137,7 +137,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_PD_MESSAGE 0x15 #define UCSI_GET_CAM_CS 0x18 #define UCSI_SET_SINK_PATH 0x1c -#define UCSI_SET_PDOS 0x1d #define UCSI_READ_POWER_LEVEL 0x1e #define UCSI_SET_USB 0x21 #define UCSI_GET_LPM_PPM_INFO 0x22 From 174e7d0f47e5231437bc305277fec7bf4896a8f9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Dec 2025 16:22:02 +0100 Subject: [PATCH 20/22] Revert "usb: typec: ucsi: Enable debugfs for message_out data structure" This reverts commit 775fae520e6ae62c393a8daf42dc534f09692f3f. The new buffer management code that this relies on is broken so revert for now. It also looks like the error handling needs some more thought as the message out size is not reset on errors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251222152204.2846-3-johan@kernel.org --- drivers/usb/typec/ucsi/debugfs.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 90d11b79d2c0..924f93027553 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -110,30 +110,6 @@ static int ucsi_vbus_volt_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt); -static ssize_t ucsi_message_out_write(struct file *file, - const char __user *data, size_t count, loff_t *ppos) -{ - struct ucsi *ucsi = file->private_data; - int ret; - - char *buf __free(kfree) = memdup_user_nul(data, count); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - ucsi->message_out_size = min(count / 2, UCSI_MAX_MESSAGE_OUT_LENGTH); - ret = hex2bin(ucsi->message_out, buf, ucsi->message_out_size); - if (ret) - return ret; - - return count; -} - -static const struct file_operations ucsi_message_out_fops = { - .open = simple_open, - .write = ucsi_message_out_write, - .llseek = generic_file_llseek, -}; - void ucsi_debugfs_register(struct ucsi *ucsi) { ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL); @@ -146,8 +122,6 @@ void ucsi_debugfs_register(struct ucsi *ucsi) debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops); debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops); debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops); - debugfs_create_file("message_out", 0200, ucsi->debugfs->dentry, ucsi, - &ucsi_message_out_fops); } void ucsi_debugfs_unregister(struct ucsi *ucsi) From 2e46b4e024ff61d4533b4f07bb3702ec7466479e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Dec 2025 16:22:03 +0100 Subject: [PATCH 21/22] Revert "usb: typec: ucsi: Add support for message out data structure" This reverts commit db0028637cc832add6d87564fcc2ebb12781b046. The new buffer management code that this feature relies on is broken so revert for now. As for the in buffer, nothing prevents the out message size and buffer from being modified while the message is being processed due to lack of serialisation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251222152204.2846-4-johan@kernel.org --- drivers/usb/typec/ucsi/ucsi.c | 14 -------------- drivers/usb/typec/ucsi/ucsi.h | 2 -- drivers/usb/typec/ucsi/ucsi_acpi.c | 16 ---------------- 3 files changed, 32 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 9b3df776137a..819540713150 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -67,20 +67,6 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci) reinit_completion(&ucsi->complete); - if (ucsi->message_out_size > 0) { - if (!ucsi->ops->write_message_out) { - ucsi->message_out_size = 0; - ret = -EOPNOTSUPP; - goto out_clear_bit; - } - - ret = ucsi->ops->write_message_out(ucsi, ucsi->message_out, - ucsi->message_out_size); - ucsi->message_out_size = 0; - if (ret) - goto out_clear_bit; - } - ret = ucsi->ops->async_control(ucsi, command); if (ret) goto out_clear_bit; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index d01b796a8d23..479bf1f69c72 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -69,7 +69,6 @@ struct dentry; * @read_cci: Read CCI register * @poll_cci: Read CCI register while polling with notifications disabled * @read_message_in: Read message data from UCSI - * @write_message_out: Write message data to UCSI * @sync_control: Blocking control operation * @async_control: Non-blocking control operation * @update_altmodes: Squashes duplicate DP altmodes @@ -85,7 +84,6 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); - int (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len); int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index f9beeb835238..f1d1f6917b09 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -86,21 +86,6 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le return 0; } -static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - - if (!data || !data_len) - return -EINVAL; - - if (ucsi->version <= UCSI_VERSION_1_2) - memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len); - else - memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len); - - return 0; -} - static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); @@ -116,7 +101,6 @@ static const struct ucsi_operations ucsi_acpi_ops = { .read_cci = ucsi_acpi_read_cci, .poll_cci = ucsi_acpi_poll_cci, .read_message_in = ucsi_acpi_read_message_in, - .write_message_out = ucsi_acpi_write_message_out, .sync_control = ucsi_sync_control_common, .async_control = ucsi_acpi_async_control }; From e7a9f6600166efb889453c524eb644cb798acd22 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Dec 2025 16:22:04 +0100 Subject: [PATCH 22/22] Revert "usb: typec: ucsi: Update UCSI structure to have message in and message out fields" This reverts commit 3e082978c33151d576694deac8abde021ea669a8. The new buffer management code has not been tested or reviewed properly and breaks boot of machines like the Lenovo ThinkPad X13s: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 CPU: 0 UID: 0 PID: 813 Comm: kworker/0:3 Not tainted 6.19.0-rc2 #26 PREEMPT Hardware name: LENOVO 21BYZ9SRUS/21BYZ9SRUS, BIOS N3HET87W (1.59 ) 12/05/2023 Workqueue: events ucsi_handle_connector_change [typec_ucsi] Call trace: ucsi_sync_control_common+0xe4/0x1ec [typec_ucsi] (P) ucsi_run_command+0xcc/0x194 [typec_ucsi] ucsi_send_command_common+0x84/0x2a0 [typec_ucsi] ucsi_get_connector_status+0x48/0x78 [typec_ucsi] ucsi_handle_connector_change+0x5c/0x4f4 [typec_ucsi] process_one_work+0x208/0x60c worker_thread+0x244/0x388 The new code completely ignores concurrency so that the message length can be updated while a transaction is ongoing. In the above case, the length ends up being modified by another thread while processing an ack so that the NULL cci pointer is dereferenced. Fixing this will require designing a proper interface for managing these transactions, something which most likely involves reverting most of the offending commit anyway. Revert the broken code to fix the regression and let Intel come up with a properly tested implementation for a later kernel. Fixes: 3e082978c331 ("usb: typec: ucsi: Update UCSI structure to have message in and message out fields") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251222152204.2846-5-johan@kernel.org --- drivers/usb/typec/ucsi/cros_ec_ucsi.c | 5 +- drivers/usb/typec/ucsi/debugfs.c | 9 +- drivers/usb/typec/ucsi/displayport.c | 11 +-- drivers/usb/typec/ucsi/ucsi.c | 104 ++++++++---------------- drivers/usb/typec/ucsi/ucsi.h | 19 ++--- drivers/usb/typec/ucsi/ucsi_acpi.c | 9 +- drivers/usb/typec/ucsi/ucsi_ccg.c | 11 +-- drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 15 ++-- 8 files changed, 71 insertions(+), 112 deletions(-) diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index d753f2188e25..eed2a7d0ebc6 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -105,12 +105,13 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) return 0; } -static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci) +static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci, + void *data, size_t size) { struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, cmd, cci); + ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); switch (ret) { case -EBUSY: /* EC may return -EBUSY if CCI.busy is set. diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 924f93027553..f3684ab787fe 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -37,8 +37,7 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_USB: case UCSI_SET_POWER_LEVEL: case UCSI_READ_POWER_LEVEL: - ucsi->message_in_size = 0; - ret = ucsi_send_command(ucsi, val); + ret = ucsi_send_command(ucsi, val, NULL, 0); break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: @@ -53,9 +52,9 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_GET_ATTENTION_VDO: case UCSI_GET_CAM_CS: case UCSI_GET_LPM_PPM_INFO: - ucsi->message_in_size = sizeof(ucsi->debugfs->response); - ret = ucsi_send_command(ucsi, val); - memcpy(&ucsi->debugfs->response, ucsi->message_in, sizeof(ucsi->debugfs->response)); + ret = ucsi_send_command(ucsi, val, + &ucsi->debugfs->response, + sizeof(ucsi->debugfs->response)); break; default: ret = -EOPNOTSUPP; diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index a09b4900ec76..8aae80b457d7 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -67,14 +67,11 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) } command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); - ucsi->message_in_size = sizeof(cur); - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); if (ret < 0) { if (ucsi->version > 0x0100) goto err_unlock; cur = 0xff; - } else { - memcpy(&cur, ucsi->message_in, ucsi->message_in_size); } if (cur != 0xff) { @@ -129,8 +126,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) } command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); - dp->con->ucsi->message_in_size = 0; - ret = ucsi_send_command(dp->con->ucsi, command); + ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); if (ret < 0) goto out_unlock; @@ -197,8 +193,7 @@ static int ucsi_displayport_configure(struct ucsi_dp *dp) command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); - dp->con->ucsi->message_in_size = 0; - return ucsi_send_command(dp->con->ucsi, command); + return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } static int ucsi_displayport_vdm(struct typec_altmode *alt, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 819540713150..a7b388dc7fa0 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -55,7 +55,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) } EXPORT_SYMBOL_GPL(ucsi_notify_common); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci) +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -83,10 +84,9 @@ out_clear_bit: if (!ret && cci) ret = ucsi->ops->read_cci(ucsi, cci); - if (!ret && ucsi->message_in_size > 0 && + if (!ret && data && (*cci & UCSI_CCI_COMMAND_COMPLETE)) - ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in, - ucsi->message_in_size); + ret = ucsi->ops->read_message_in(ucsi, data, size); return ret; } @@ -103,25 +103,23 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - ucsi->message_in_size = 0; - return ucsi->ops->sync_control(ucsi, ctrl, NULL); + return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); } -static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, bool conn_ack) +static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size, bool conn_ack) { int ret, err; *cci = 0; - if (ucsi->message_in_size > UCSI_MAX_DATA_LENGTH(ucsi)) + if (size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command, cci); + ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); - if (*cci & UCSI_CCI_BUSY) { - ucsi->message_in_size = 0; - return ucsi_run_command(ucsi, UCSI_CANCEL, cci, false) ?: -EBUSY; - } + if (*cci & UCSI_CCI_BUSY) + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; if (ret) return ret; @@ -153,13 +151,10 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) int ret; command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num); - ucsi->message_in_size = sizeof(error); - ret = ucsi_run_command(ucsi, command, &cci, false); + ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); if (ret < 0) return ret; - memcpy(&error, ucsi->message_in, sizeof(error)); - switch (error) { case UCSI_ERROR_INCOMPATIBLE_PARTNER: return -EOPNOTSUPP; @@ -205,7 +200,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) return -EIO; } -static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack) +static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, + void *data, size_t size, bool conn_ack) { u8 connector_num; u32 cci; @@ -233,7 +229,7 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack) mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, cmd, &cci, conn_ack); + ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack); if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); @@ -242,9 +238,10 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack) return ret; } -int ucsi_send_command(struct ucsi *ucsi, u64 command) +int ucsi_send_command(struct ucsi *ucsi, u64 command, + void *data, size_t size) { - return ucsi_send_command_common(ucsi, command, false); + return ucsi_send_command_common(ucsi, command, data, size, false); } EXPORT_SYMBOL_GPL(ucsi_send_command); @@ -322,8 +319,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) int i; command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); - con->ucsi->message_in_size = sizeof(cur); - ret = ucsi_send_command(con->ucsi, command); + ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); if (ret < 0) { if (con->ucsi->version > 0x0100) { dev_err(con->ucsi->dev, @@ -331,8 +327,6 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) return; } cur = 0xff; - } else { - memcpy(&cur, con->ucsi->message_in, sizeof(cur)); } if (cur < UCSI_MAX_ALTMODES) @@ -516,8 +510,7 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_ALTMODE_OFFSET(i); - ucsi->message_in_size = sizeof(alt); - len = ucsi_send_command(con->ucsi, command); + len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt)); /* * We are collecting all altmodes first and then registering. * Some type-C device will return zero length data beyond last @@ -526,8 +519,6 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) if (len < 0) return len; - memcpy(&alt, ucsi->message_in, sizeof(alt)); - /* We got all altmodes, now break out and register them */ if (!len || !alt.svid) break; @@ -595,15 +586,12 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_ALTMODE_OFFSET(i); - con->ucsi->message_in_size = sizeof(alt); - len = ucsi_send_command(con->ucsi, command); + len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); if (len == -EBUSY) continue; if (len <= 0) return len; - memcpy(&alt, con->ucsi->message_in, sizeof(alt)); - /* * This code is requesting one alt mode at a time, but some PPMs * may still return two. If that happens both alt modes need be @@ -671,9 +659,7 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) UCSI_MAX_DATA_LENGTH(con->ucsi)); int ret; - con->ucsi->message_in_size = size; - ret = ucsi_send_command_common(con->ucsi, command, conn_ack); - memcpy(&con->status, con->ucsi->message_in, size); + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); return ret < 0 ? ret : 0; } @@ -696,9 +682,8 @@ static int ucsi_read_pdos(struct ucsi_connector *con, command |= UCSI_GET_PDOS_PDO_OFFSET(offset); command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1); command |= is_source(role) ? UCSI_GET_PDOS_SRC_PDOS : 0; - ucsi->message_in_size = num_pdos * sizeof(u32); - ret = ucsi_send_command(ucsi, command); - memcpy(pdos + offset, ucsi->message_in, num_pdos * sizeof(u32)); + ret = ucsi_send_command(ucsi, command, pdos + offset, + num_pdos * sizeof(u32)); if (ret < 0 && ret != -ETIMEDOUT) dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); @@ -785,9 +770,7 @@ static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient, command |= UCSI_GET_PD_MESSAGE_BYTES(len); command |= UCSI_GET_PD_MESSAGE_TYPE(type); - con->ucsi->message_in_size = len; - ret = ucsi_send_command(con->ucsi, command); - memcpy(data + offset, con->ucsi->message_in, len); + ret = ucsi_send_command(con->ucsi, command, data + offset, len); if (ret < 0) return ret; } @@ -952,9 +935,7 @@ static int ucsi_register_cable(struct ucsi_connector *con) int ret; command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num); - con->ucsi->message_in_size = sizeof(cable_prop); - ret = ucsi_send_command(con->ucsi, command); - memcpy(&cable_prop, con->ucsi->message_in, sizeof(cable_prop)); + ret = ucsi_send_command(con->ucsi, command, &cable_prop, sizeof(cable_prop)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n", ret); return ret; @@ -1015,9 +996,7 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return 0; command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num); - con->ucsi->message_in_size = sizeof(con->cap); - ret = ucsi_send_command(con->ucsi, command); - memcpy(&con->cap, con->ucsi->message_in, sizeof(con->cap)); + ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret); return ret; @@ -1401,8 +1380,7 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) else if (con->ucsi->version >= UCSI_VERSION_2_0) command |= hard ? 0 : UCSI_CONNECTOR_RESET_DATA_VER_2_0; - con->ucsi->message_in_size = 0; - return ucsi_send_command(con->ucsi, command); + return ucsi_send_command(con->ucsi, command, NULL, 0); } static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -1483,8 +1461,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) { int ret; - con->ucsi->message_in_size = 0; - ret = ucsi_send_command(con->ucsi, command); + ret = ucsi_send_command(con->ucsi, command, NULL, 0); if (ret == -ETIMEDOUT) { u64 c; @@ -1492,8 +1469,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) ucsi_reset_ppm(con->ucsi); c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; - con->ucsi->message_in_size = 0; - ucsi_send_command(con->ucsi, c); + ucsi_send_command(con->ucsi, c, NULL, 0); ucsi_reset_connector(con, true); } @@ -1646,13 +1622,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) /* Get connector capability */ command = UCSI_GET_CONNECTOR_CAPABILITY; command |= UCSI_CONNECTOR_NUMBER(con->num); - ucsi->message_in_size = sizeof(con->cap); - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap)); if (ret < 0) goto out_unlock; - memcpy(&con->cap, ucsi->message_in, sizeof(con->cap)); - if (UCSI_CONCAP(con, OPMODE_DRP)) cap->data = TYPEC_PORT_DRD; else if (UCSI_CONCAP(con, OPMODE_DFP)) @@ -1849,20 +1822,17 @@ static int ucsi_init(struct ucsi *ucsi) /* Enable basic notifications */ ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; - ucsi->message_in_size = 0; - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) goto err_reset; /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ucsi->message_in_size = BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE); - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, &ucsi->cap, + BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); if (ret < 0) goto err_reset; - memcpy(&ucsi->cap, ucsi->message_in, BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); - if (!ucsi->cap.num_connectors) { ret = -ENODEV; goto err_reset; @@ -1892,8 +1862,7 @@ static int ucsi_init(struct ucsi *ucsi) /* Enable all supported notifications */ ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; - ucsi->message_in_size = 0; - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) goto err_unregister; @@ -1944,8 +1913,7 @@ static void ucsi_resume_work(struct work_struct *work) /* Restore UCSI notification enable mask after system resume */ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; - ucsi->message_in_size = 0; - ret = ucsi_send_command(ucsi, command); + ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) { dev_err(ucsi->dev, "failed to re-enable notifications (%d)\n", ret); return; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 479bf1f69c72..410389ef173a 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -29,10 +29,6 @@ struct dentry; #define UCSI_MESSAGE_OUT 32 #define UCSIv2_MESSAGE_OUT 272 -/* Define maximum lengths for message buffers */ -#define UCSI_MAX_MESSAGE_IN_LENGTH 256 -#define UCSI_MAX_MESSAGE_OUT_LENGTH 256 - /* UCSI versions */ #define UCSI_VERSION_1_0 0x0100 #define UCSI_VERSION_1_1 0x0110 @@ -84,7 +80,8 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); - int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci); + int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, struct ucsi_altmode *orig, @@ -496,12 +493,6 @@ struct ucsi { unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ - - /* Fixed-size buffers for incoming and outgoing messages */ - u8 message_in[UCSI_MAX_MESSAGE_IN_LENGTH]; - size_t message_in_size; - u8 message_out[UCSI_MAX_MESSAGE_OUT_LENGTH]; - size_t message_out_size; }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) @@ -564,13 +555,15 @@ struct ucsi_connector { struct usb_pd_identity cable_identity; }; -int ucsi_send_command(struct ucsi *ucsi, u64 command); +int ucsi_send_command(struct ucsi *ucsi, u64 command, + void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci); +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size); #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index f1d1f6917b09..6b92f296e985 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -105,14 +105,15 @@ static const struct ucsi_operations ucsi_acpi_ops = { .async_control = ucsi_acpi_async_control }; -static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) +static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *val, size_t len) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, command, cci); + ret = ucsi_sync_control_common(ucsi, command, cci, val, len); if (ret < 0) return ret; @@ -124,8 +125,8 @@ static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && ua->check_bogus_event) { /* Clear the bogus change */ - if (*(u16 *)ucsi->message_in == bogus_change) - *(u16 *)ucsi->message_in = 0; + if (*(u16 *)val == bogus_change) + *(u16 *)val = 0; ua->check_bogus_event = false; } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index ead1b2a25c79..d83a0051c737 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -606,7 +606,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command) return ccg_write(uc, reg, (u8 *)&command, sizeof(command)); } -static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) +static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -628,16 +629,16 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } - ret = ucsi_sync_control_common(ucsi, command, cci); + ret = ucsi_sync_control_common(ucsi, command, cci, data, size); switch (UCSI_COMMAND(command)) { case UCSI_GET_CURRENT_CAM: if (uc->has_multiple_dp) - ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)ucsi->message_in); + ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data); break; case UCSI_GET_ALTERNATE_MODES: if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) { - struct ucsi_altmode *alt = (struct ucsi_altmode *)ucsi->message_in; + struct ucsi_altmode *alt = data; if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) ucsi_ccg_nvidia_altmode(uc, alt, command); @@ -645,7 +646,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) break; case UCSI_GET_CAPABILITY: if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) { - struct ucsi_capability *cap = (struct ucsi_capability *)ucsi->message_in; + struct ucsi_capability *cap = data; cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS; } diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index 299081444caa..0187c1c4b21a 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -88,7 +88,8 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, u64 command, - u32 *cci) + u32 *cci, + void *data, size_t size) { int ret; @@ -106,8 +107,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, }; dev_dbg(ucsi->dev, "faking DP altmode for con1\n"); - memset(ucsi->message_in, 0, ucsi->message_in_size); - memcpy(ucsi->message_in, &alt, min(sizeof(alt), ucsi->message_in_size)); + memset(data, 0, size); + memcpy(data, &alt, min(sizeof(alt), size)); *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt)); return 0; } @@ -120,18 +121,18 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES && UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) { dev_dbg(ucsi->dev, "ignoring altmodes for con2\n"); - memset(ucsi->message_in, 0, ucsi->message_in_size); + memset(data, 0, size); *cci = UCSI_CCI_COMMAND_COMPLETE; return 0; } - ret = ucsi_sync_control_common(ucsi, command, cci); + ret = ucsi_sync_control_common(ucsi, command, cci, data, size); if (ret < 0) return ret; /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */ - if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && ucsi->message_in_size > 0) - ucsi->message_in[0]--; + if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data) + ((u8 *)data)[0]--; return ret; }