From 6f64e1872bf6a735a1171eb6c9193d7f91717441 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 22 Sep 2025 15:06:28 +0300 Subject: [PATCH 001/161] usb: misc: ljca: Remove Wentong's e-mail address Wentong's e-mail address no longer works, remove it. Signed-off-by: Sakari Ailus Acked-by: Wentong Wu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb-ljca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index c562630d862c..1846156c0800 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -891,7 +891,7 @@ static struct usb_driver ljca_driver = { }; module_usb_driver(ljca_driver); -MODULE_AUTHOR("Wentong Wu "); +MODULE_AUTHOR("Wentong Wu"); MODULE_AUTHOR("Zhifeng Wang "); MODULE_AUTHOR("Lixu Zhang "); MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver"); From a0da19eda98c227c0cd732c65da8091b1941709c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 24 Sep 2025 12:10:36 +0300 Subject: [PATCH 002/161] usb: core: Drop spaces after function names Drop spaces after function name to comply with the coding style. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20250924091036.1319161-1-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9dd79769cad1..24feb0de1c00 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2696,18 +2696,18 @@ static void hcd_release(struct kref *kref) kfree(hcd); } -struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) +struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd) { if (hcd) - kref_get (&hcd->kref); + kref_get(&hcd->kref); return hcd; } EXPORT_SYMBOL_GPL(usb_get_hcd); -void usb_put_hcd (struct usb_hcd *hcd) +void usb_put_hcd(struct usb_hcd *hcd) { if (hcd) - kref_put (&hcd->kref, hcd_release); + kref_put(&hcd->kref, hcd_release); } EXPORT_SYMBOL_GPL(usb_put_hcd); From 19040e562640fa3a59fbf9ce98f201bf4d2d82d8 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 22 Sep 2025 13:20:42 +0800 Subject: [PATCH 003/161] dt-bindings: usb: uhci: Add reset property The UHCI controller on Aspeed SoCs (including AST2700) requires its reset line to be deasserted before the controller can be used. Add an optional "resets" property to the UHCI device tree bindings to describe the phandle to the reset controller. This property is optional for platforms which do not require explicit reset handling. Signed-off-by: Ryan Chen Acked-by: Conor Dooley Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20250922052045.2421480-2-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-uhci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.yaml b/Documentation/devicetree/bindings/usb/usb-uhci.yaml index d8336f72dc1f..b1f2b9bd7921 100644 --- a/Documentation/devicetree/bindings/usb/usb-uhci.yaml +++ b/Documentation/devicetree/bindings/usb/usb-uhci.yaml @@ -28,6 +28,9 @@ properties: interrupts: maxItems: 1 + resets: + maxItems: 1 + '#ports': $ref: /schemas/types.yaml#/definitions/uint32 From 113ba4270afffa4c1127c31d305e5bac48f27681 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 22 Sep 2025 13:20:43 +0800 Subject: [PATCH 004/161] usb: uhci: Add reset control support Some SoCs, such as the Aspeed AST2700, require the UHCI controller to be taken out of reset before it can operate. Add optional reset control support to the UHCI platform driver. The driver now acquires an optional reset line from device tree, deasserts it during probe, and asserts it again in the error path and shutdown. Signed-off-by: Ryan Chen Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20250922052045.2421480-3-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.h | 1 + drivers/usb/host/uhci-platform.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 13ee2a6144b2..4326d1f3ca76 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -445,6 +445,7 @@ struct uhci_hcd { short load[MAX_PHASE]; /* Periodic allocations */ struct clk *clk; /* (optional) clock source */ + struct reset_control *rsts; /* (optional) clock reset */ /* Reset host controller */ void (*reset_hc) (struct uhci_hcd *uhci); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 62318291f566..aa75b546672b 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -11,6 +11,7 @@ #include #include #include +#include static int uhci_platform_init(struct usb_hcd *hcd) { @@ -132,17 +133,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) goto err_rmr; } + uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(uhci->rsts)) { + ret = PTR_ERR(uhci->rsts); + goto err_clk; + } + ret = reset_control_deassert(uhci->rsts); + if (ret) + goto err_clk; + ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_clk; + goto err_reset; ret = usb_add_hcd(hcd, ret, IRQF_SHARED); if (ret) - goto err_clk; + goto err_reset; device_wakeup_enable(hcd->self.controller); return 0; +err_reset: + reset_control_assert(uhci->rsts); err_clk: clk_disable_unprepare(uhci->clk); err_rmr: @@ -156,6 +168,7 @@ static void uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); struct uhci_hcd *uhci = hcd_to_uhci(hcd); + reset_control_assert(uhci->rsts); clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); usb_put_hcd(hcd); From 8b25c96ab6ed1e9134e130e635ca922ba9a0a264 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 22 Sep 2025 13:20:44 +0800 Subject: [PATCH 005/161] dt-bindings: usb: uhci: Add Aspeed AST2700 compatible Add the compatible string for Aspeed AST2700 SoC. Signed-off-by: Ryan Chen Acked-by: Conor Dooley Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20250922052045.2421480-4-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-uhci.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.yaml b/Documentation/devicetree/bindings/usb/usb-uhci.yaml index b1f2b9bd7921..e050ca203945 100644 --- a/Documentation/devicetree/bindings/usb/usb-uhci.yaml +++ b/Documentation/devicetree/bindings/usb/usb-uhci.yaml @@ -20,6 +20,7 @@ properties: - aspeed,ast2400-uhci - aspeed,ast2500-uhci - aspeed,ast2600-uhci + - aspeed,ast2700-uhci - const: generic-uhci reg: @@ -53,6 +54,15 @@ allOf: required: - clocks + - if: + properties: + compatible: + contains: + const: aspeed,ast2700-uhci + then: + required: + - resets + unevaluatedProperties: false examples: From 18a9ec886d328dc823aa2b2f3ef02009f634539f Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Mon, 22 Sep 2025 13:20:45 +0800 Subject: [PATCH 006/161] usb: uhci: Add Aspeed AST2700 support Unlike earlier Aspeed SoCs (AST2400/2500/2600) which are limited to 32-bit DMA addressing, the UHCI controller in AST2700 supports 64-bit DMA. Update the platform UHCI driver to select the appropriate DMA mask based on the device tree compatible string. Signed-off-by: Ryan Chen Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20250922052045.2421480-5-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index aa75b546672b..37607f985cc0 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -65,9 +65,13 @@ static const struct hc_driver uhci_platform_hc_driver = { .hub_control = uhci_hub_control, }; +static const u64 dma_mask_32 = DMA_BIT_MASK(32); +static const u64 dma_mask_64 = DMA_BIT_MASK(64); + static int uhci_hcd_platform_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const u64 *dma_mask_ptr; struct usb_hcd *hcd; struct uhci_hcd *uhci; struct resource *res; @@ -81,7 +85,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev); + if (!dma_mask_ptr) + dma_mask_ptr = &dma_mask_32; + + ret = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr); if (ret) return ret; @@ -114,7 +122,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) } if (of_device_is_compatible(np, "aspeed,ast2400-uhci") || of_device_is_compatible(np, "aspeed,ast2500-uhci") || - of_device_is_compatible(np, "aspeed,ast2600-uhci")) { + of_device_is_compatible(np, "aspeed,ast2600-uhci") || + of_device_is_compatible(np, "aspeed,ast2700-uhci")) { uhci->is_aspeed = 1; dev_info(&pdev->dev, "Enabled Aspeed implementation workarounds\n"); @@ -191,6 +200,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) static const struct of_device_id platform_uhci_ids[] = { { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, + { .compatible = "aspeed,ast2700-uhci", .data = &dma_mask_64}, {} }; MODULE_DEVICE_TABLE(of, platform_uhci_ids); From f39e7cdd8e4cce94db2ed33aa46192cf9ce35d96 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Sun, 28 Sep 2025 11:24:06 +0800 Subject: [PATCH 007/161] dt-bindings: usb: ehci: Add Aspeed AST2700 compatible Add the compatible string for Aspeed AST2700 SoC. Signed-off-by: Ryan Chen Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250928032407.27764-2-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-ehci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 508d958e698c..4e84bead0232 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -46,6 +46,7 @@ properties: - aspeed,ast2400-ehci - aspeed,ast2500-ehci - aspeed,ast2600-ehci + - aspeed,ast2700-ehci - brcm,bcm3384-ehci - brcm,bcm63268-ehci - brcm,bcm6328-ehci From 274f2232a94f6ca626d60288044e13d9a58c7612 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Sun, 28 Sep 2025 11:24:07 +0800 Subject: [PATCH 008/161] usb: ehci: Add Aspeed AST2700 support Unlike earlier Aspeed SoCs (AST2400/2500/2600) which are limited to 32-bit DMA addressing, the EHCI controller in AST2700 supports 64-bit DMA. Update the EHCI platform driver to make use of this capability by selecting a 64-bit DMA mask when the "aspeed,ast2700-ehci" compatible is present in device tree. Signed-off-by: Ryan Chen Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20250928032407.27764-3-ryan_chen@aspeedtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 6aab45c8525c..bcd1c9073515 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -239,9 +240,11 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + const struct of_device_id *match; struct ehci_platform_priv *priv; struct ehci_hcd *ehci; int err, irq, clk = 0; + bool dma_mask_64; if (usb_disabled()) return -ENODEV; @@ -253,8 +256,13 @@ static int ehci_platform_probe(struct platform_device *dev) if (!pdata) pdata = &ehci_platform_defaults; + dma_mask_64 = pdata->dma_mask_64; + match = of_match_device(dev->dev.driver->of_match_table, &dev->dev); + if (match && match->data) + dma_mask_64 = true; + err = dma_coerce_mask_and_coherent(&dev->dev, - pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (err) { dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); return err; @@ -298,7 +306,9 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_device_is_compatible(dev->dev.of_node, "aspeed,ast2500-ehci") || of_device_is_compatible(dev->dev.of_node, - "aspeed,ast2600-ehci")) + "aspeed,ast2600-ehci") || + of_device_is_compatible(dev->dev.of_node, + "aspeed,ast2700-ehci")) ehci->is_aspeed = 1; if (soc_device_match(quirk_poll_match)) @@ -485,6 +495,7 @@ static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "wm,prizm-ehci", }, { .compatible = "generic-ehci", }, { .compatible = "cavium,octeon-6335-ehci", }, + { .compatible = "aspeed,ast2700-ehci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); From b61cb4419e432e459d5c7f900b0eb24139a859ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Draszik?= Date: Tue, 7 Oct 2025 16:55:04 +0100 Subject: [PATCH 009/161] dt-bindings: usb: samsung,exynos-dwc3: add power-domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DWC3 can be part of a power domain, so we need to allow the relevant property 'power-domains'. Signed-off-by: André Draszik Reviewed-by: Peter Griffin Reviewed-by: Alim Akhtar Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20251007-power-domains-dt-bindings-usb-samsung-exynos-dwc3-v1-1-b63bacad2b42@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index 6d39e5066944..3098845a90f3 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -36,6 +36,9 @@ properties: minItems: 1 maxItems: 4 + power-domains: + maxItems: 1 + ranges: true '#size-cells': From 6e26324df8880beddc5280137693116b6ac059ac Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 18 Sep 2025 17:13:22 +0200 Subject: [PATCH 010/161] usb: usbtmc: Remove unnecessary local variable from usbtmc_ioctl_request The local variable 'res' is only used to temporary store the results of calling copy_from_user() and copy_to_user(). Use the results directly and remove the local variable. Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20250918151328.331015-1-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 75de29725a45..206f1b738ed3 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1936,10 +1936,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, u8 *buffer = NULL; int rv; unsigned int is_in, pipe; - unsigned long res; - res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)); - if (res) + if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest))) return -EFAULT; if (request.req.wLength > USBTMC_BUFSIZE) @@ -1956,9 +1954,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (!is_in) { /* Send control data to device */ - res = copy_from_user(buffer, request.data, - request.req.wLength); - if (res) { + if (copy_from_user(buffer, request.data, + request.req.wLength)) { rv = -EFAULT; goto exit; } @@ -1984,8 +1981,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (rv && is_in) { /* Read control data from device */ - res = copy_to_user(request.data, buffer, rv); - if (res) + if (copy_to_user(request.data, buffer, rv)) rv = -EFAULT; } From 09bf21bf5249880f62fe759b53b14b4b52900c6c Mon Sep 17 00:00:00 2001 From: Lizhi Xu Date: Tue, 16 Sep 2025 09:41:43 +0800 Subject: [PATCH 011/161] usbip: Fix locking bug in RT-enabled kernels Interrupts are disabled before entering usb_hcd_giveback_urb(). A spinlock_t becomes a sleeping lock on PREEMPT_RT, so it cannot be acquired with disabled interrupts. Save the interrupt status and restore it after usb_hcd_giveback_urb(). syz reported: BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 Call Trace: dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120 rt_spin_lock+0xc7/0x2c0 kernel/locking/spinlock_rt.c:57 spin_lock include/linux/spinlock_rt.h:44 [inline] mon_bus_complete drivers/usb/mon/mon_main.c:134 [inline] mon_complete+0x5c/0x200 drivers/usb/mon/mon_main.c:147 usbmon_urb_complete include/linux/usb/hcd.h:738 [inline] __usb_hcd_giveback_urb+0x254/0x5e0 drivers/usb/core/hcd.c:1647 vhci_urb_enqueue+0xb4f/0xe70 drivers/usb/usbip/vhci_hcd.c:818 Reported-by: syzbot+205ef33a3b636b4181fb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=205ef33a3b636b4181fb Signed-off-by: Lizhi Xu Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20250916014143.1439759-1-lizhi.xu@windriver.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 0d6c10a8490c..f7e405abe608 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -831,15 +831,15 @@ out: no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock_irqrestore(&vhci->lock, flags); if (!ret) { /* usb_hcd_giveback_urb() should be called with * irqs disabled */ - local_irq_disable(); + spin_unlock(&vhci->lock); usb_hcd_giveback_urb(hcd, urb, urb->status); - local_irq_enable(); + spin_lock(&vhci->lock); } + spin_unlock_irqrestore(&vhci->lock, flags); return ret; } From 87653d54edd69f9f623d0a650323ec1adbbd2952 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 17 Sep 2025 12:12:31 +0200 Subject: [PATCH 012/161] usb: hub: Use max() to improve usb_set_lpm_pel() Use max() to simplify and improve the readability of usb_set_lpm_pel(). Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20250917101235.58381-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 256fe8c86828..c8a543af3ad9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -277,10 +278,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * device and the parent hub into U0. The exit latency is the bigger of * the device exit latency or the hub exit latency. */ - if (udev_exit_latency > hub_exit_latency) - first_link_pel = udev_exit_latency * 1000; - else - first_link_pel = hub_exit_latency * 1000; + first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000; /* * When the hub starts to receive the LFPS, there is a slight delay for @@ -294,10 +292,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * According to figure C-7 in the USB 3.0 spec, the PEL for this device * is the greater of the two exit latencies. */ - if (first_link_pel > hub_pel) - udev_lpm_params->pel = first_link_pel; - else - udev_lpm_params->pel = hub_pel; + udev_lpm_params->pel = max(first_link_pel, hub_pel); } /* From ed9dd907ee38975d164b563470aa1e1a1abbc79a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 16 Sep 2025 10:05:44 +0800 Subject: [PATCH 013/161] usb: gadget: zero: add function wakeup support When the device working at enhanced superspeed, it needs to send function remote wakeup signal to the host instead of device remote wakeup. Add function wakeup support for the purpose. Signed-off-by: Xu Yang Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250916020544.1301866-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/zero.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index a05785bdeb30..08a21bd0c2ba 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function *func_lb; +static struct usb_function_instance *func_inst_lb; + +static struct usb_function *func_ss; +static struct usb_function_instance *func_inst_ss; + /*-------------------------------------------------------------------------*/ static struct timer_list autoresume_timer; @@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused) { struct usb_composite_dev *cdev = autoresume_cdev; struct usb_gadget *g = cdev->gadget; + int status; /* unconfigured devices can't issue wakeups */ if (!cdev->config) @@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused) * more significant than just a timer firing; likely * because of some direct user request. */ - if (g->speed != USB_SPEED_UNKNOWN) { - int status = usb_gadget_wakeup(g); - INFO(cdev, "%s --> %d\n", __func__, status); + if (g->speed == USB_SPEED_UNKNOWN) + return; + + if (g->speed >= USB_SPEED_SUPER) { + if (loopdefault) + status = usb_func_wakeup(func_lb); + else + status = usb_func_wakeup(func_ss); + } else { + status = usb_gadget_wakeup(g); } + INFO(cdev, "%s --> %d\n", __func__, status); } static void zero_suspend(struct usb_composite_dev *cdev) @@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = { /* .iConfiguration = DYNAMIC */ }; -static struct usb_function *func_ss; -static struct usb_function_instance *func_inst_ss; - static int ss_config_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) { @@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); -static struct usb_function *func_lb; -static struct usb_function_instance *func_inst_lb; - module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(qlen, "depth of loopback queue"); From 978719f90256ebee945562e82613ab6eb03fdaca Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 19 Sep 2025 17:34:31 -0500 Subject: [PATCH 014/161] dt-bindings: usb: xhci: Allow "iommus" and "dr_mode" properties Allow "iommus" property as it's reasonable for any XHCI controller to be behind an IOMMU. Allow "dr_mode" as an XHCI controller can be part of a dual-role controller. In particular, the Marvell Armada 8K XHCI controller uses both of these properties. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250919223433.2399927-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-xhci.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/generic-xhci.yaml b/Documentation/devicetree/bindings/usb/generic-xhci.yaml index a2b94a138999..8875911b43cc 100644 --- a/Documentation/devicetree/bindings/usb/generic-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-xhci.yaml @@ -53,6 +53,14 @@ properties: dma-coherent: true + dr_mode: + enum: + - host + - otg + + iommus: + maxItems: 1 + power-domains: maxItems: 1 From 0e61e71538d2d52971a90b02c3abd43816ad8c70 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 19 Sep 2025 17:34:32 -0500 Subject: [PATCH 015/161] dt-bindings: usb: xhci: Add "generic-xhci" compatible for Marvell Armada 37xx/8k The Marvell Armada 37xx and 8k platforms compatible property don't match the binding schema. They are compatible with the "generic-xhci" compatible. The 37xx does have a quirk for "reset on resume", but that's probably not required to function in all situations. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250919223433.2399927-2-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-xhci.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/generic-xhci.yaml b/Documentation/devicetree/bindings/usb/generic-xhci.yaml index 8875911b43cc..62678abd74b5 100644 --- a/Documentation/devicetree/bindings/usb/generic-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-xhci.yaml @@ -14,12 +14,15 @@ properties: oneOf: - description: Generic xHCI device const: generic-xhci - - description: Armada 37xx/375/38x/8k SoCs + - description: Armada 375/38x SoCs + items: + - enum: + - marvell,armada-375-xhci + - marvell,armada-380-xhci + - description: Armada 37xx/8k SoCs items: - enum: - marvell,armada3700-xhci - - marvell,armada-375-xhci - - marvell,armada-380-xhci - marvell,armada-8k-xhci - const: generic-xhci - description: Broadcom SoCs with power domains From 1c1b2a247c384179fcc97b63bb0c17e112217d7a Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 20 Sep 2025 12:28:03 +0000 Subject: [PATCH 016/161] usb: typec: tipd: Fix error handling in cd321x_read_data_status Right now cd321x_read_data_status always returns true even if it encounters any errors: tps6598x_read_data_status returns a boolean but we treated it as an errno and then we have a bunch of dev_errs in case tps6598x_block_read fails but just continue along and return true. Fix that to correctly report errors to the callee. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-usb/aMvWJo3IkClmFoAA@stanley.mountain/ Signed-off-by: Sven Peter Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20250920-tipd-fix-v1-1-49886d4f081d@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 2b1049c9a6f3..d0c86347251c 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -577,30 +577,36 @@ static bool cd321x_read_data_status(struct tps6598x *tps) int ret; ret = tps6598x_read_data_status(tps); - if (ret < 0) + if (!ret) return false; if (tps->data_status & TPS_DATA_STATUS_DP_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_DP_SID_STATUS, &cd321x->dp_sid_status, sizeof(cd321x->dp_sid_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read DP SID Status: %d\n", ret); + return false; + } } if (tps->data_status & TPS_DATA_STATUS_TBT_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_INTEL_VID_STATUS, &cd321x->intel_vid_status, sizeof(cd321x->intel_vid_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read Intel VID Status: %d\n", ret); + return false; + } } if (tps->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_USB4_STATUS, &cd321x->usb4_status, sizeof(cd321x->usb4_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read USB4 Status: %d\n", ret); + return false; + } } return true; From 51659606d4e00445c202195093f0c200db4d67e4 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 19 Sep 2025 15:11:10 +0800 Subject: [PATCH 017/161] dt-bindings: usb: usbmisc-imx: add fsl,imx94-usbmisc compatible Add "fsl,imx94-usbmisc" compatible. Signed-off-by: Xu Yang Acked-by: Conor Dooley Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250919071111.2558628-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml index ca677d1a8274..d06efe4dbb3b 100644 --- a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml @@ -36,6 +36,7 @@ properties: - fsl,imx8mm-usbmisc - fsl,imx8mn-usbmisc - fsl,imx8ulp-usbmisc + - fsl,imx94-usbmisc - fsl,imx95-usbmisc - const: fsl,imx7d-usbmisc - const: fsl,imx6q-usbmisc From 924aa1d9e0ae081cf3cb4a378e394f6074da911a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 19 Sep 2025 15:11:11 +0800 Subject: [PATCH 018/161] usb: chipidea: imx: add USB support for i.MX94 Add new imx94_usbmisc_ops for i.MX94 due to it has same wakeup logic as i.MX95, but it doesn't need workaround for ERR051725, so pullup is not needed. Signed-off-by: Xu Yang Reviewed-by: Frank Li Acked-by: Peter Chen Link: https://lore.kernel.org/r/20250919071111.2558628-2-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/usbmisc_imx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index b1418885707c..bb027d2bd700 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1224,6 +1224,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = { .power_lost_check = usbmisc_imx7d_power_lost_check, }; +static const struct usbmisc_ops imx94_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx95_set_wakeup, + .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, +}; + static const struct usbmisc_ops imx95_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx95_set_wakeup, @@ -1481,6 +1489,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx8ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx94-usbmisc", + .data = &imx94_usbmisc_ops, + }, { .compatible = "fsl,imx95-usbmisc", .data = &imx95_usbmisc_ops, From ed4a5c5de56ad4e23c9e5da8981639352b63b8ac Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Tue, 23 Sep 2025 18:16:07 +0000 Subject: [PATCH 019/161] usb: typec: class: add typec_get_data_role symbol Alt Mode drivers are responsible for sending Enter Mode through the TCPM, but only a DFP is allowed to send Enter Mode. typec_get_data_role gets the port's data role, which can then be used in altmode drivers via typec_altmode_get_data_role to know if Enter Mode should be sent. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20250923181606.1583584-5-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 13 +++++++++++++ include/linux/usb/typec.h | 1 + include/linux/usb/typec_altmode.h | 13 +++++++++++++ 3 files changed, 27 insertions(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 67a533e35150..9b2647cb199b 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2120,6 +2120,19 @@ void typec_set_data_role(struct typec_port *port, enum typec_data_role role) } EXPORT_SYMBOL_GPL(typec_set_data_role); +/** + * typec_get_data_role - Get port data role + * @port: The USB Type-C Port to query + * + * This routine is used by the altmode drivers to determine if the port is the + * DFP before issuing Enter Mode + */ +enum typec_data_role typec_get_data_role(struct typec_port *port) +{ + return port->data_role; +} +EXPORT_SYMBOL_GPL(typec_get_data_role); + /** * typec_set_pwr_role - Report power role change * @port: The USB Type-C Port where the role was changed diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 252af3f77039..309251572e2e 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -337,6 +337,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, void typec_unregister_plug(struct typec_plug *plug); void typec_set_data_role(struct typec_port *port, enum typec_data_role role); +enum typec_data_role typec_get_data_role(struct typec_port *port); void typec_set_pwr_role(struct typec_port *port, enum typec_role role); void typec_set_vconn_role(struct typec_port *port, enum typec_role role); void typec_set_pwr_opmode(struct typec_port *port, enum typec_pwr_opmode mode); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index b3c0866ea70f..f7db3bd4c90e 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -172,6 +172,19 @@ typec_altmode_get_svdm_version(struct typec_altmode *altmode) return typec_get_negotiated_svdm_version(typec_altmode2port(altmode)); } +/** + * typec_altmode_get_data_role - Get port data role + * @altmode: Handle to the alternate mode + * + * Alt Mode drivers should only issue Enter Mode through the port if they are + * the DFP. + */ +static inline enum typec_data_role +typec_altmode_get_data_role(struct typec_altmode *altmode) +{ + return typec_get_data_role(typec_altmode2port(altmode)); +} + /** * struct typec_altmode_driver - USB Type-C alternate mode device driver * @id_table: Null terminated array of SVIDs From 41294342fad7d7fe907d8707a4efcafdb66b7940 Mon Sep 17 00:00:00 2001 From: RD Babiera Date: Tue, 23 Sep 2025 18:16:08 +0000 Subject: [PATCH 020/161] usb: typec: altmodes/displayport: do not enter mode if port is the UFP Nothing currently stops the DisplayPort Alt Mode driver from sending Enter Mode if the port is the Data Device. Utilize typec_altmode_get_data_role to prevent mode entry. Signed-off-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20250923181606.1583584-6-rdbabiera@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 1dcb77faf85d..8d111ad3b71b 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -758,7 +758,9 @@ int dp_altmode_probe(struct typec_altmode *alt) struct fwnode_handle *fwnode; struct dp_altmode *dp; - /* FIXME: Port can only be DFP_U. */ + /* Port can only be DFP_U. */ + if (typec_altmode_get_data_role(alt) != TYPEC_HOST) + return -EPROTO; /* Make sure we have compatible pin configurations */ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & From b9f1c762a4de17d93017fbd12b9941caff6d3078 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 29 Sep 2025 10:24:14 -0400 Subject: [PATCH 021/161] dt-bindings: usb: add missed compatible string for arm64 layerscape Add missed compatible string for arm64 layerscape platform. Allow these fallback to fsl,ls1028a-dwc3. Remove fallback snps,dwc3 because layerscape dwc3 is not full compatible with common snps,dwc3 device, a special value gsburstcfg0 need be set when dma coherence enabled. Allow iommus property. Change ref to snps,dwc3-common.yaml to use dwc3 flatten library. Reviewed-by: Rob Herring (Arm) Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20250929-ls_dma_coherence-v5-1-2ebee578eb7e@nxp.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/fsl,ls1028a.yaml | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml index a44bdf391887..4784f057264a 100644 --- a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml @@ -9,21 +9,19 @@ title: Freescale layerscape SuperSpeed DWC3 USB SoC controller maintainers: - Frank Li -select: - properties: - compatible: - contains: - enum: - - fsl,ls1028a-dwc3 - required: - - compatible - properties: compatible: - items: - - enum: - - fsl,ls1028a-dwc3 - - const: snps,dwc3 + oneOf: + - items: + - enum: + - fsl,ls1012a-dwc3 + - fsl,ls1043a-dwc3 + - fsl,ls1046a-dwc3 + - fsl,ls1088a-dwc3 + - fsl,ls208xa-dwc3 + - fsl,lx2160a-dwc3 + - const: fsl,ls1028a-dwc3 + - const: fsl,ls1028a-dwc3 reg: maxItems: 1 @@ -31,6 +29,11 @@ properties: interrupts: maxItems: 1 + iommus: + maxItems: 1 + + dma-coherent: true + unevaluatedProperties: false required: @@ -39,14 +42,14 @@ required: - interrupts allOf: - - $ref: snps,dwc3.yaml# + - $ref: snps,dwc3-common.yaml# examples: - | #include usb@fe800000 { - compatible = "fsl,ls1028a-dwc3", "snps,dwc3"; + compatible = "fsl,ls1028a-dwc3"; reg = <0xfe800000 0x100000>; interrupts = ; }; From 7298c06d58e23c1c6e60180ab1ce069087ae38e2 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 29 Sep 2025 10:24:15 -0400 Subject: [PATCH 022/161] usb: dwc3: Add software-managed properties for flattened model Add software-managed properties for the flattened model, which does not need to use device tree properties to pass down information to the common DWC3 core. Add 'properties' in dwc3_probe_data and set default values for existing users (dwc3-qcom, dwc3-generic-plat). No functional changes. Acked-by: Thinh Nguyen Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20250929-ls_dma_coherence-v5-2-2ebee578eb7e@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 12 ++++++++++-- drivers/usb/dwc3/dwc3-generic-plat.c | 1 + drivers/usb/dwc3/dwc3-qcom.c | 1 + drivers/usb/dwc3/glue.h | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ae140c356295..c5076580616e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1666,7 +1666,8 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); } -static void dwc3_get_software_properties(struct dwc3 *dwc) +static void dwc3_get_software_properties(struct dwc3 *dwc, + const struct dwc3_properties *properties) { struct device *tmpdev; u16 gsbuscfg0_reqinfo; @@ -1674,6 +1675,12 @@ static void dwc3_get_software_properties(struct dwc3 *dwc) dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; + if (properties->gsbuscfg0_reqinfo != + DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { + dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo; + return; + } + /* * Iterate over all parent nodes for finding swnode properties * and non-DT (non-ABI) properties. @@ -2206,7 +2213,7 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_get_properties(dwc); - dwc3_get_software_properties(dwc); + dwc3_get_software_properties(dwc, &data->properties); dwc->usb_psy = dwc3_get_usb_power_supply(dwc); if (IS_ERR(dwc->usb_psy)) @@ -2356,6 +2363,7 @@ static int dwc3_probe(struct platform_device *pdev) probe_data.dwc = dwc; probe_data.res = res; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; return dwc3_core_probe(&probe_data); } diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c index d96b20570002..af95a527dcc2 100644 --- a/drivers/usb/dwc3/dwc3-generic-plat.c +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -75,6 +75,7 @@ static int dwc3_generic_probe(struct platform_device *pdev) probe_data.dwc = &dwc3g->dwc; probe_data.res = res; probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; ret = dwc3_core_probe(&probe_data); if (ret) return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index ded2ca86670c..9ac75547820d 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -704,6 +704,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) probe_data.dwc = &qcom->dwc; probe_data.res = &res; probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; ret = dwc3_core_probe(&probe_data); if (ret) { ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index 2efd00e763be..cc6e138bd9ef 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h @@ -9,17 +9,31 @@ #include #include "core.h" +/** + * dwc3_properties: DWC3 core properties + * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field + */ +struct dwc3_properties { + u32 gsbuscfg0_reqinfo; +}; + +#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \ + .gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \ + }) + /** * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe() * @dwc: Reference to dwc3 context structure * @res: resource for the DWC3 core mmio region * @ignore_clocks_and_resets: clocks and resets defined for the device should * be ignored by the DWC3 core, as they are managed by the glue + * @properties: dwc3 software manage properties */ struct dwc3_probe_data { struct dwc3 *dwc; struct resource *res; bool ignore_clocks_and_resets; + struct dwc3_properties properties; }; int dwc3_core_probe(const struct dwc3_probe_data *data); From 1c97fc901fb6318aca0160da96736d0bc136ddcd Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 29 Sep 2025 10:24:16 -0400 Subject: [PATCH 023/161] usb: dwc3: dwc3-generic-plat: Add layerscape dwc3 support Add layerscape dwc3 support by using flatten dwc3 core library. Layerscape dwc3 need set gsbuscfg0-reqinfo as 0x2222 when dma-coherence set. Signed-off-by: Frank Li Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20250929-ls_dma_coherence-v5-3-2ebee578eb7e@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-generic-plat.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c index af95a527dcc2..770fedc16bb8 100644 --- a/drivers/usb/dwc3/dwc3-generic-plat.c +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -29,6 +29,7 @@ static void dwc3_generic_reset_control_assert(void *data) static int dwc3_generic_probe(struct platform_device *pdev) { + const struct dwc3_properties *properties; struct dwc3_probe_data probe_data = {}; struct device *dev = &pdev->dev; struct dwc3_generic *dwc3g; @@ -75,7 +76,13 @@ static int dwc3_generic_probe(struct platform_device *pdev) probe_data.dwc = &dwc3g->dwc; probe_data.res = res; probe_data.ignore_clocks_and_resets = true; - probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + properties = of_device_get_match_data(dev); + if (properties) + probe_data.properties = *properties; + else + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + ret = dwc3_core_probe(&probe_data); if (ret) return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); @@ -146,8 +153,13 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { dwc3_generic_runtime_idle) }; +static const struct dwc3_properties fsl_ls1028_dwc3 = { + .gsbuscfg0_reqinfo = 0x2222, +}; + static const struct of_device_id dwc3_generic_of_match[] = { { .compatible = "spacemit,k1-dwc3", }, + { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); From 5570ad1423ee60f6e972dadb63fb2e5f90a54cbe Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Sun, 28 Sep 2025 14:56:11 -0400 Subject: [PATCH 024/161] USB: Fix descriptor count when handling invalid MBIM extended descriptor In cdc_parse_cdc_header(), the check for the USB_CDC_MBIM_EXTENDED_TYPE descriptor was using 'break' upon detecting an invalid length. This was incorrect because 'break' only exits the switch statement, causing the code to fall through to cnt++, thus incorrectly incrementing the count of parsed descriptors for a descriptor that was actually invalid and being discarded. This patch changes 'break' to 'goto next_desc;' to ensure that the logic skips the counter increment and correctly proceeds to the next descriptor in the buffer. This maintains an accurate count of only the successfully parsed descriptors. Fixes: e4c6fb7794982 ("usbnet: move the CDC parser into USB core") Signed-off-by: Seungjin Bae Link: https://lore.kernel.org/r/20250928185611.764589-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index d2b2787be409..6138468c67c4 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -2431,7 +2431,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, break; case USB_CDC_MBIM_EXTENDED_TYPE: if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; + goto next_desc; hdr->usb_cdc_mbim_extended_desc = (struct usb_cdc_mbim_extended_desc *)buffer; break; From 30cd2cb1abf4c4acdb1ddb468c946f68939819fb Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Thu, 21 Aug 2025 14:53:07 -0400 Subject: [PATCH 025/161] usb: typec: ucsi: Handle incorrect num_connectors capability The UCSI spec states that the num_connectors field is 7 bits, and the 8th bit is reserved and should be set to zero. Some buggy FW has been known to set this bit, and it can lead to a system not booting. Flag that the FW is not behaving correctly, and auto-fix the value so that the system boots correctly. Found on Lenovo P1 G8 during Linux enablement program. The FW will be fixed, but seemed worth addressing in case it hit platforms that aren't officially Linux supported. Signed-off-by: Mark Pearson Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20250821185319.2585023-1-mpearson-lenovo@squebb.ca Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 3f568f790f39..3995483a0aa0 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1807,6 +1807,12 @@ static int ucsi_init(struct ucsi *ucsi) ret = -ENODEV; goto err_reset; } + /* Check if reserved bit set. This is out of spec but happens in buggy FW */ + if (ucsi->cap.num_connectors & 0x80) { + dev_warn(ucsi->dev, "UCSI: Invalid num_connectors %d. Likely buggy FW\n", + ucsi->cap.num_connectors); + ucsi->cap.num_connectors &= 0x7f; // clear bit and carry on + } /* Allocate the connectors. Released in ucsi_unregister() */ connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); From 9a55e0079258f5c054f469e08c2dc349bbfd1943 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Oct 2025 14:10:28 +0200 Subject: [PATCH 026/161] Revert "USB: disable rust bindings from the build for now" This reverts commit c584a1c7c8a192c13637bc51c7b63a9f15fe6474. It brings the rust bindings for USB back into the build so that we can work off of this for future kernel releases. Link: https://lore.kernel.org/r/2025100827-divorcee-steadier-b40b@gregkh Signed-off-by: Greg Kroah-Hartman --- rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/kernel/lib.rs | 2 ++ samples/rust/Kconfig | 2 +- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 2e43c66635a2..d6906465e17e 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 551da6c9b506..16049d6e713f 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -56,6 +56,7 @@ #include "task.c" #include "time.c" #include "uaccess.c" +#include "usb.c" #include "vmalloc.c" #include "wait.c" #include "workqueue.c" diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3dd7bebe7888..9cf4ca0ae7a1 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -138,6 +138,8 @@ pub mod time; pub mod tracepoint; pub mod transmute; pub mod types; +#[cfg(CONFIG_USB = "y")] +pub mod usb; pub mod uaccess; pub mod workqueue; pub mod xarray; diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c376eb899b7a..c1cc787a9add 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -107,7 +107,7 @@ config SAMPLE_RUST_DRIVER_PLATFORM config SAMPLE_RUST_DRIVER_USB tristate "USB Driver" - depends on USB = y && BROKEN + depends on USB = y help This option builds the Rust USB driver sample. From 082c8dc13a3b63713ca6f3b2b5f31a47fb345e17 Mon Sep 17 00:00:00 2001 From: Ivaylo Ivanov Date: Sun, 14 Sep 2025 16:56:52 +0300 Subject: [PATCH 027/161] dt-bindings: usb: samsung,exynos-dwc3 add exynos8890 compatible Add a compatible for the exynos8890-dwusb3 node. It features the same clocks and regulators as exynos7, so reuse its compatible. Signed-off-by: Ivaylo Ivanov Reviewed-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250914135652.2626066-1-ivo.ivanov.ivanov1@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index 3098845a90f3..8af0143c3e47 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -21,6 +21,9 @@ properties: - samsung,exynos7870-dwusb3 - samsung,exynos850-dwusb3 - samsung,exynosautov920-dwusb3 + - items: + - const: samsung,exynos8890-dwusb3 + - const: samsung,exynos7-dwusb3 - items: - const: samsung,exynos990-dwusb3 - const: samsung,exynos850-dwusb3 From e361b2bf54308593158e2abd0645740eee95a9a9 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 8 Oct 2025 19:54:26 +0300 Subject: [PATCH 028/161] usb: vhci-hcd: Switch to dev_err_probe() in probe path Replace pr_err() calls in vhci_hcd_probe() with dev_err_probe(), to simplify error handling a bit and improve consistency. Acked-by: Shuah Khan Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20251008-vhci-hcd-cleanup-v2-1-b6acc4dd6e44@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index f7e405abe608..c3f24bf8610a 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1372,10 +1372,9 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Our private data is also allocated automatically. */ hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd_hs) { - pr_err("create primary hcd failed\n"); - return -ENOMEM; - } + if (!hcd_hs) + return dev_err_probe(&pdev->dev, -ENOMEM, "create primary hcd failed\n"); + hcd_hs->has_tt = 1; /* @@ -1383,22 +1382,21 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Call the driver's reset() and start() routines. */ ret = usb_add_hcd(hcd_hs, 0, 0); - if (ret != 0) { - pr_err("usb_add_hcd hs failed %d\n", ret); + if (ret) { + dev_err_probe(&pdev->dev, ret, "usb_add_hcd hs failed\n"); goto put_usb2_hcd; } hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev), hcd_hs); if (!hcd_ss) { - ret = -ENOMEM; - pr_err("create shared hcd failed\n"); + ret = dev_err_probe(&pdev->dev, -ENOMEM, "create shared hcd failed\n"); goto remove_usb2_hcd; } ret = usb_add_hcd(hcd_ss, 0, 0); if (ret) { - pr_err("usb_add_hcd ss failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "usb_add_hcd ss failed\n"); goto put_usb3_hcd; } From a3a8c9c18f6904a777ff21f300d3da8c2b214c80 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 8 Oct 2025 19:54:27 +0300 Subject: [PATCH 029/161] usb: vhci-hcd: Replace pr_*() with dev_*() logging Where possible, use driver model logging helpers dev_*() instead of pr_*() to ensure the messages are always associated with the corresponding device/driver. While at it, join the split strings to make checkpatch happy and regain ability to grep for those log messages in the source code: WARNING: quoted string split across lines Acked-by: Shuah Khan Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20251008-vhci-hcd-cleanup-v2-2-b6acc4dd6e44@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 74 +++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index c3f24bf8610a..e55690da19e5 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -346,9 +346,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (wIndex < 1 || wIndex > VHCI_HC_PORTS) { invalid_rhport = true; if (wIndex > VHCI_HC_PORTS) - pr_err("invalid port number %d\n", wIndex); - } else + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); + } else { rhport = wIndex - 1; + } vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; @@ -368,14 +369,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case ClearPortFeature: if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed >= HCD_USB3) { - pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "ClearPortFeature: USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } usbip_dbg_vhci_rh( @@ -408,7 +409,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed >= HCD_USB3 && (wLength < USB_DT_SS_HUB_SIZE || wValue != (USB_DT_SS_HUB << 8))) { - pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n"); + dev_err(hcd_dev(hcd), + "Wrong hub descriptor type for USB 3.0 roothub.\n"); goto error; } if (hcd->speed >= HCD_USB3) @@ -433,7 +435,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortStatus: usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); retval = -EPIPE; goto error; } @@ -483,7 +485,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, USB_PORT_STAT_LOW_SPEED; break; default: - pr_err("vhci_device speed not set\n"); + dev_err(hcd_dev(hcd), "vhci_device speed not set\n"); break; } } @@ -505,8 +507,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_LINK_STATE req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_LINK_STATE req not supported for USB 2.0 roothub\n"); goto error; } /* @@ -523,8 +525,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); /* TODO: add suspend/resume support! */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_U1/2_TIMEOUT req not supported for USB 2.0 roothub\n"); goto error; } break; @@ -533,13 +535,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); /* Applicable only for USB2.0 hub */ if (hcd->speed >= HCD_USB3) { - pr_err("USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } @@ -549,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_POWER\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (hcd->speed >= HCD_USB3) @@ -561,13 +563,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* Applicable only for USB3.0 hub */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_BH_PORT_RESET req not supported for USB 2.0 roothub\n"); goto error; } fallthrough; @@ -575,7 +577,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* if it's already enabled, disable */ @@ -598,7 +600,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", wValue); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (wValue >= 32) @@ -618,8 +620,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortErrorCount: usbip_dbg_vhci_rh(" GetPortErrorCount\n"); if (hcd->speed < HCD_USB3) { - pr_err("GetPortErrorCount req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "GetPortErrorCount req not supported for USB 2.0 roothub\n"); goto error; } /* We'll always return 0 since this is a dummy hub */ @@ -628,13 +630,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetHubDepth: usbip_dbg_vhci_rh(" SetHubDepth\n"); if (hcd->speed < HCD_USB3) { - pr_err("SetHubDepth req not supported for " - "USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "SetHubDepth req not supported for USB 2.0 roothub\n"); goto error; } break; default: - pr_err("default hub control req: %04x v%04x i%04x l%d\n", + dev_err(hcd_dev(hcd), + "default hub control req: %04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); error: /* "protocol stall" on error */ @@ -642,7 +645,7 @@ error: } if (usbip_dbg_flag_vhci_rh) { - pr_debug("port %d\n", rhport); + dev_dbg(hcd_dev(hcd), "%s port %d\n", __func__, rhport); /* Only dump valid port status */ if (!invalid_rhport) { dump_port_status_diff(prev_port_status[rhport], @@ -702,7 +705,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag unsigned long flags; if (portnum > VHCI_HC_PORTS) { - pr_err("invalid port number %d\n", portnum); + dev_err(hcd_dev(hcd), "invalid port number %d\n", portnum); return -ENODEV; } vdev = &vhci_hcd->vdev[portnum-1]; @@ -958,7 +961,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink->seqnum = atomic_inc_return(&vhci_hcd->seqnum); if (unlink->seqnum == 0xffff) - pr_info("seqnum max\n"); + dev_info(hcd_dev(hcd), "seqnum max\n"); unlink->unlink_seqnum = priv->seqnum; @@ -1036,10 +1039,11 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) static void vhci_shutdown_connection(struct usbip_device *ud) { struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + struct usb_hcd *hcd = vhci_hcd_to_hcd(vdev_to_vhci_hcd(vdev)); /* need this? see stub_dev.c */ if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %d\n", ud->sockfd); + dev_dbg(hcd_dev(hcd), "shutdown tcp_socket %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } @@ -1052,7 +1056,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) kthread_stop_put(vdev->ud.tcp_tx); vdev->ud.tcp_tx = NULL; } - pr_info("stop threads\n"); + dev_info(hcd_dev(hcd), "stop threads\n"); /* active connection is closed */ if (vdev->ud.tcp_socket) { @@ -1060,7 +1064,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) vdev->ud.tcp_socket = NULL; vdev->ud.sockfd = -1; } - pr_info("release socket\n"); + dev_info(hcd_dev(hcd), "release socket\n"); vhci_device_unlink_cleanup(vdev); @@ -1086,7 +1090,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) */ rh_port_disconnect(vdev); - pr_info("disconnect device\n"); + dev_info(hcd_dev(hcd), "disconnect device\n"); } static void vhci_device_reset(struct usbip_device *ud) @@ -1222,7 +1226,7 @@ static int vhci_start(struct usb_hcd *hcd) id = hcd_name_to_id(hcd_name(hcd)); if (id < 0) { - pr_err("invalid vhci name %s\n", hcd_name(hcd)); + dev_err(hcd_dev(hcd), "invalid vhci name %s\n", hcd_name(hcd)); return -EINVAL; } @@ -1239,7 +1243,7 @@ static int vhci_start(struct usb_hcd *hcd) vhci_finish_attr_group(); return err; } - pr_info("created sysfs %s\n", hcd_name(hcd)); + dev_info(hcd_dev(hcd), "created sysfs %s\n", hcd_name(hcd)); } return 0; From 9f0b086cd51c5a66e7483e06665103a2aa417cdc Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 10 Oct 2025 08:56:24 +0300 Subject: [PATCH 030/161] usb: ljca: Order ACPI hardware IDs alphabetically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver has three lists of ACPI hardware IDs, for GPIO, I²C and SPI. Order them alphabetically. Signed-off-by: Sakari Ailus Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20251010055625.4147844-1-sakari.ailus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb-ljca.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index 1846156c0800..dc2ad31ae152 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -165,26 +165,26 @@ struct ljca_match_ids_walk_data { }; static const struct acpi_device_id ljca_gpio_hids[] = { + { "INTC100B" }, { "INTC1074" }, { "INTC1096" }, - { "INTC100B" }, - { "INTC10D1" }, { "INTC10B5" }, + { "INTC10D1" }, {}, }; static const struct acpi_device_id ljca_i2c_hids[] = { + { "INTC100C" }, { "INTC1075" }, { "INTC1097" }, - { "INTC100C" }, { "INTC10D2" }, {}, }; static const struct acpi_device_id ljca_spi_hids[] = { + { "INTC100D" }, { "INTC1091" }, { "INTC1098" }, - { "INTC100D" }, { "INTC10D3" }, {}, }; From 877c80dfbf788e57a3338627899033b7007037ee Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 10 Oct 2025 08:56:25 +0300 Subject: [PATCH 031/161] usb: ljca: Improve ACPI hardware ID documentation Document the differences between the LJCA client device ACPI hardware IDs, including the USBIO IDs used for LJCA devices. Signed-off-by: Sakari Ailus Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20251010055625.4147844-2-sakari.ailus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb-ljca.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index dc2ad31ae152..9e65bb9577ea 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data { struct acpi_device *adev; }; +/* + * ACPI hardware IDs for LJCA client devices. + * + * [1] Some BIOS implementations use these IDs for denoting LJCA client devices + * even though the IDs have been allocated for USBIO. This isn't a problem + * as the usb-ljca driver is probed based on the USB device's vendor and + * product IDs and its client drivers are probed based on auxiliary device + * names, not these ACPI _HIDs. List of such systems: + * + * Dell Precision 5490 + */ static const struct acpi_device_id ljca_gpio_hids[] = { - { "INTC100B" }, - { "INTC1074" }, - { "INTC1096" }, - { "INTC10B5" }, - { "INTC10D1" }, + { "INTC100B" }, /* RPL LJCA GPIO */ + { "INTC1074" }, /* CVF LJCA GPIO */ + { "INTC1096" }, /* ADL LJCA GPIO */ + { "INTC10B5" }, /* LNL LJCA GPIO */ + { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */ {}, }; static const struct acpi_device_id ljca_i2c_hids[] = { - { "INTC100C" }, - { "INTC1075" }, - { "INTC1097" }, - { "INTC10D2" }, + { "INTC100C" }, /* RPL LJCA I2C */ + { "INTC1075" }, /* CVF LJCA I2C */ + { "INTC1097" }, /* ADL LJCA I2C */ + { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */ {}, }; static const struct acpi_device_id ljca_spi_hids[] = { - { "INTC100D" }, - { "INTC1091" }, - { "INTC1098" }, - { "INTC10D3" }, + { "INTC100D" }, /* RPL LJCA SPI */ + { "INTC1091" }, /* TGL/ADL LJCA SPI */ + { "INTC1098" }, /* ADL LJCA SPI */ + { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */ {}, }; From 56bcf64b613127cbccae91d55becf5e5da235cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Karol=20Pi=C4=85tkowski?= Date: Thu, 2 Oct 2025 17:35:33 +0000 Subject: [PATCH 032/161] thunderbolt: Fix typo in tb_eeprom_ctl_read documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix presumed copy-paste typo of tb_eeprom_ctl_read documentation referring to tb_eeprom_ctl_write. Signed-off-by: Dominik Karol Piątkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 1af65fece495..5477b9437048 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -21,7 +21,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) } /* - * tb_eeprom_ctl_write() - read control word + * tb_eeprom_ctl_read() - read control word */ static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) { From c05ebd0ec91e27408767c46dd16c291cbff2213d Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Wed, 15 Oct 2025 03:50:51 +0800 Subject: [PATCH 033/161] usb: core: Centralize device state update logic Introduce a new static function, update_usb_device_state(), to centralize the process of changing a device's state, including the management of active_duration during suspend/resume transitions. This change prepares for adding tracepoints, allowing tracing logic to be added in a single, central location within the new helper function. Signed-off-by: Kuen-Han Tsai Reviewed-by: Alan Stern Link: https://patch.msgid.link/20251015-usbcore-tracing-v2-1-5a14b5b9d4e0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c8a543af3ad9..f2314da01159 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2142,6 +2142,20 @@ static void update_port_device_state(struct usb_device *udev) } } +static void update_usb_device_state(struct usb_device *udev, + enum usb_device_state new_state) +{ + if (udev->state == USB_STATE_SUSPENDED && + new_state != USB_STATE_SUSPENDED) + udev->active_duration -= jiffies; + else if (new_state == USB_STATE_SUSPENDED && + udev->state != USB_STATE_SUSPENDED) + udev->active_duration += jiffies; + + udev->state = new_state; + update_port_device_state(udev); +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); @@ -2151,10 +2165,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (hub->ports[i]->child) recursively_mark_NOTATTACHED(hub->ports[i]->child); } - if (udev->state == USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - udev->state = USB_STATE_NOTATTACHED; - update_port_device_state(udev); + update_usb_device_state(udev, USB_STATE_NOTATTACHED); } /** @@ -2204,14 +2215,7 @@ void usb_set_device_state(struct usb_device *udev, else wakeup = 0; } - if (udev->state == USB_STATE_SUSPENDED && - new_state != USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - else if (new_state == USB_STATE_SUSPENDED && - udev->state != USB_STATE_SUSPENDED) - udev->active_duration += jiffies; - udev->state = new_state; - update_port_device_state(udev); + update_usb_device_state(udev, new_state); } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); From 071786e27d81d3e8ae11f078357c9373fc4ce2e8 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Wed, 15 Oct 2025 03:50:52 +0800 Subject: [PATCH 034/161] usb: core: Add tracepoints for device allocation and state changes Introduce new tracepoints to the USB core to improve debuggability of USB device lifecycle events. The following tracepoints are added: - usb_alloc_dev: Triggered when a new USB device structure is allocated, providing insights into early device setup. - usb_set_device_state: Triggered when the USB device state changes, allowing observation of the device's state transitions. These tracepoints capture detailed information about the USB device, including its name, speed, state, bus current value, and authorized flag. This will aid developers in diagnosing issues related to device enumeration within the USB subsystem. Examples: usb_alloc_dev: usb 1-1 speed UNKNOWN state attached 0mA [authorized] usb_set_device_state: usb 1-1 speed UNKNOWN state powered 0mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state default 500mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state default 500mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state addressed 500mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state configured 500mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state suspended 500mA [authorized] usb_set_device_state: usb 1-1 speed full-speed state not attached 500mA [authorized] Signed-off-by: Kuen-Han Tsai Reviewed-by: Alan Stern Link: https://patch.msgid.link/20251015-usbcore-tracing-v2-2-5a14b5b9d4e0@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Makefile | 5 +++- drivers/usb/core/hub.c | 2 ++ drivers/usb/core/trace.c | 6 ++++ drivers/usb/core/trace.h | 61 +++++++++++++++++++++++++++++++++++++++ drivers/usb/core/usb.c | 2 ++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/core/trace.c create mode 100644 drivers/usb/core/trace.h diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 766000b4939e..60ea76160122 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -3,10 +3,13 @@ # Makefile for USB Core files and filesystem # +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o -usbcore-y += phy.o port.o +usbcore-y += phy.o port.o trace.o usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f2314da01159..af3565e541cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -41,6 +41,7 @@ #include "hub.h" #include "phy.h" #include "otg_productlist.h" +#include "trace.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define USB_VENDOR_SMSC 0x0424 @@ -2154,6 +2155,7 @@ static void update_usb_device_state(struct usb_device *udev, udev->state = new_state; update_port_device_state(udev); + trace_usb_set_device_state(udev); } static void recursively_mark_NOTATTACHED(struct usb_device *udev) diff --git a/drivers/usb/core/trace.c b/drivers/usb/core/trace.c new file mode 100644 index 000000000000..607bcf639d27 --- /dev/null +++ b/drivers/usb/core/trace.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Google LLC + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h new file mode 100644 index 000000000000..903e57dc273a --- /dev/null +++ b/drivers/usb/core/trace.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Google LLC + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM usbcore + +#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _USB_CORE_TRACE_H + +#include +#include +#include + +DECLARE_EVENT_CLASS(usb_core_log_usb_device, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev), + TP_STRUCT__entry( + __string(name, dev_name(&udev->dev)) + __field(enum usb_device_speed, speed) + __field(enum usb_device_state, state) + __field(unsigned short, bus_mA) + __field(unsigned, authorized) + ), + TP_fast_assign( + __assign_str(name); + __entry->speed = udev->speed; + __entry->state = udev->state; + __entry->bus_mA = udev->bus_mA; + __entry->authorized = udev->authorized; + ), + TP_printk("usb %s speed %s state %s %dmA [%s]", + __get_str(name), + usb_speed_string(__entry->speed), + usb_state_string(__entry->state), + __entry->bus_mA, + __entry->authorized ? "authorized" : "unauthorized") +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + + +#endif /* _USB_CORE_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b6b0b8489523..e740f7852bcd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -46,6 +46,7 @@ #include #include "hub.h" +#include "trace.h" const char *usbcore_name = "usbcore"; @@ -746,6 +747,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #endif dev->authorized = usb_dev_authorized(dev, usb_hcd); + trace_usb_alloc_dev(dev); return dev; } EXPORT_SYMBOL_GPL(usb_alloc_dev); From 8ad194699fc17bec5307b59d36676f43ea460b05 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 17 Oct 2025 11:19:23 +0200 Subject: [PATCH 035/161] usbip: Use min to simplify stub_send_ret_submit Use min() to improve stub_send_ret_submit(). Change the local variable 'size' from 'int' to 'unsigned int' to prevent a signedness error and to match the resulting type. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20251017091923.1694-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_tx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index 7eb2e074012a..55919c3762ba 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -239,17 +240,13 @@ static int stub_send_ret_submit(struct stub_device *sdev) urb->actual_length > 0) { if (urb->num_sgs) { unsigned int copy = urb->actual_length; - int size; + unsigned int size; for_each_sg(urb->sg, sg, urb->num_sgs, i) { if (copy == 0) break; - if (copy < sg->length) - size = copy; - else - size = sg->length; - + size = min(copy, sg->length); iov[iovnum].iov_base = sg_virt(sg); iov[iovnum].iov_len = size; From f82890c98f3e3fd61983e9021354c632ecd47427 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 15 Oct 2025 04:30:13 +0000 Subject: [PATCH 036/161] tcpm: Parse and log AVS APDO The USB PD specification introduced new Adjustable Voltage Supply (AVS) types for both Standard Power Range (SPR) and Extended Power Range (EPR) sources. Add definitions to correctly parse and handle the new AVS APDO. Use bitfield macros to add inline helper functions to extract voltage, current, power, and peak current fields to parse and log the details of the new EPR AVS and SPR AVS APDO. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Amit Sunil Dhamne Reviewed-by: Kyle Tso Reviewed-by: RD Babiera Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251015043017.3382908-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 15 +++++++- include/linux/usb/pd.h | 69 ++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index b2a568a5bc9b..c65aa8104950 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -823,10 +823,23 @@ static void tcpm_log_source_caps(struct tcpm_port *port) case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) scnprintf(msg, sizeof(msg), - "%u-%u mV, %u mA", + "PPS %u-%u mV, %u mA", pdo_pps_apdo_min_voltage(pdo), pdo_pps_apdo_max_voltage(pdo), pdo_pps_apdo_max_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_EPR_AVS) + scnprintf(msg, sizeof(msg), + "EPR AVS %u-%u mV %u W peak_current: %u", + pdo_epr_avs_apdo_min_voltage_mv(pdo), + pdo_epr_avs_apdo_max_voltage_mv(pdo), + pdo_epr_avs_apdo_pdp_w(pdo), + pdo_epr_avs_apdo_src_peak_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) + scnprintf(msg, sizeof(msg), + "SPR AVS 9-15 V: %u mA 15-20 V: %u mA peak_current: %u", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo), + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo), + pdo_spr_avs_apdo_src_peak_current(pdo)); else strcpy(msg, "undefined APDO"); break; diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 3068c3084eb6..6ccd1b2af993 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -6,6 +6,7 @@ #ifndef __LINUX_USB_PD_H #define __LINUX_USB_PD_H +#include #include #include #include @@ -271,9 +272,11 @@ enum pd_pdo_type { enum pd_apdo_type { APDO_TYPE_PPS = 0, + APDO_TYPE_EPR_AVS = 1, + APDO_TYPE_SPR_AVS = 2, }; -#define PDO_APDO_TYPE_SHIFT 28 /* Only valid value currently is 0x0 - PPS */ +#define PDO_APDO_TYPE_SHIFT 28 #define PDO_APDO_TYPE_MASK 0x3 #define PDO_APDO_TYPE(t) ((t) << PDO_APDO_TYPE_SHIFT) @@ -297,6 +300,35 @@ enum pd_apdo_type { PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) +/* + * Applicable only to EPR AVS APDO source cap as per + * Table 6.15 EPR Adjustable Voltage Supply APDO – Source + */ +#define PDO_EPR_AVS_APDO_PEAK_CURRENT GENMASK(27, 26) + +/* + * Applicable to both EPR AVS APDO source and sink cap as per + * Table 6.15 EPR Adjustable Voltage Supply APDO – Source + * Table 6.22 EPR Adjustable Voltage Supply APDO – Sink + */ +#define PDO_EPR_AVS_APDO_MAX_VOLT GENMASK(25, 17) /* 100mV unit */ +#define PDO_EPR_AVS_APDO_MIN_VOLT GENMASK(15, 8) /* 100mV unit */ +#define PDO_EPR_AVS_APDO_PDP GENMASK(7, 0) /* 1W unit */ + +/* + * Applicable only SPR AVS APDO source cap as per + * Table 6.14 SPR Adjustable Voltage Supply APDO – Source + */ +#define PDO_SPR_AVS_APDO_PEAK_CURRENT GENMASK(27, 26) + +/* + * Applicable to both SPR AVS APDO source and sink cap as per + * Table 6.14 SPR Adjustable Voltage Supply APDO – Source + * Table 6.21 SPR Adjustable Voltage Supply APDO – Sink + */ +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR GENMASK(19, 10) /* 10mA unit */ +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ + static inline enum pd_pdo_type pdo_type(u32 pdo) { return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; @@ -350,6 +382,41 @@ static inline unsigned int pdo_pps_apdo_max_current(u32 pdo) PDO_PPS_APDO_CURR_MASK) * 50; } +static inline unsigned int pdo_epr_avs_apdo_src_peak_current(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_PEAK_CURRENT, pdo); +} + +static inline unsigned int pdo_epr_avs_apdo_min_voltage_mv(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_MIN_VOLT, pdo) * 100; +} + +static inline unsigned int pdo_epr_avs_apdo_max_voltage_mv(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_MIN_VOLT, pdo) * 100; +} + +static inline unsigned int pdo_epr_avs_apdo_pdp_w(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_PDP, pdo); +} + +static inline unsigned int pdo_spr_avs_apdo_src_peak_current(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_PEAK_CURRENT, pdo); +} + +static inline unsigned int pdo_spr_avs_apdo_9v_to_15v_max_current_ma(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR, pdo) * 10; +} + +static inline unsigned int pdo_spr_avs_apdo_15v_to_20v_max_current_ma(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR, pdo) * 10; +} + /* RDO: Request Data Object */ #define RDO_OBJ_POS_SHIFT 28 #define RDO_OBJ_POS_MASK 0x7 From b4528e1dbe679bd9c0457bd263456a60859f8161 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 15 Oct 2025 04:30:14 +0000 Subject: [PATCH 037/161] usb: typec: pd: Register SPR AVS caps with usb_power_delivery class usb_power_delivery class will now display AVS cap as `spr_adjustable_voltage_supply`. `maximum_current_9V_to_15V` and `maximum_current_15V_to_20V` shows the corresponding current limits in mA. `peak_current` follows the same convention as fixed_supply where the value reported in the capabilities message is displayed as is. Sample output with an SPR AVS capable PD charger: $cat /sys/class/usb_power_delivery/pd1/source-capabilities/5:spr_adjustable_voltage_supply/maximum_current_9V_to_15V 4000mA $cat /sys/class/usb_power_delivery/pd1/source-capabilities/5:spr_adjustable_voltage_supply/maximum_current_15V_to_20V 3350mA $cat /sys/class/usb_power_delivery/pd1/source-capabilities/5:spr_adjustable_voltage_supply/peak_current 0 Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251015043017.3382908-2-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- .../testing/sysfs-class-usb_power_delivery | 28 ++++++ drivers/usb/typec/pd.c | 95 ++++++++++++++++++- 2 files changed, 118 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-usb_power_delivery b/Documentation/ABI/testing/sysfs-class-usb_power_delivery index 61d233c320ea..c754458a527e 100644 --- a/Documentation/ABI/testing/sysfs-class-usb_power_delivery +++ b/Documentation/ABI/testing/sysfs-class-usb_power_delivery @@ -254,3 +254,31 @@ Contact: Heikki Krogerus Description: The PPS Power Limited bit indicates whether or not the source supply will exceed the rated output power if requested. + +Standard Power Range (SPR) Adjustable Voltage Supplies + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Adjustable Voltage Supply (AVS) Augmented PDO (APDO). + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/maximum_current_9V_to_15V +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Maximum Current for 9V to 15V range in milliamperes. + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/maximum_current_15V_to_20V +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Maximum Current for greater than 15V till 20V range in + milliamperes. + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/peak_current +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + This file shows the value of the Adjustable Voltage Supply Peak Current + Capability field. diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index d78c04a421bc..67f20b5ffdf4 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -359,6 +359,84 @@ static const struct device_type sink_pps_type = { .groups = sink_pps_groups, }; +/* -------------------------------------------------------------------------- */ +/* Standard Power Range (SPR) Adjustable Voltage Supply (AVS) */ + +static ssize_t +spr_avs_9v_to_15v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_15v_to_20v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_src_peak_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", + pdo_spr_avs_apdo_src_peak_current(to_pdo(dev)->pdo)); +} + +static struct device_attribute spr_avs_9v_to_15v_max_current_attr = { + .attr = { + .name = "maximum_current_9V_to_15V", + .mode = 0444, + }, + .show = spr_avs_9v_to_15v_max_current_show, +}; + +static struct device_attribute spr_avs_15v_to_20v_max_current_attr = { + .attr = { + .name = "maximum_current_15V_to_20V", + .mode = 0444, + }, + .show = spr_avs_15v_to_20v_max_current_show, +}; + +static struct device_attribute spr_avs_src_peak_current_attr = { + .attr = { + .name = "peak_current", + .mode = 0444, + }, + .show = spr_avs_src_peak_current_show, +}; + +static struct attribute *source_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + &spr_avs_src_peak_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_spr_avs); + +static const struct device_type source_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_spr_avs_groups, +}; + +static struct attribute *sink_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_spr_avs); + +static const struct device_type sink_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_spr_avs_groups, +}; + /* -------------------------------------------------------------------------- */ static const char * const supply_name[] = { @@ -368,7 +446,8 @@ static const char * const supply_name[] = { }; static const char * const apdo_supply_name[] = { - [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_SPR_AVS] = "spr_adjustable_voltage_supply", }; static const struct device_type *source_type[] = { @@ -378,7 +457,8 @@ static const struct device_type *source_type[] = { }; static const struct device_type *source_apdo_type[] = { - [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_SPR_AVS] = &source_spr_avs_type, }; static const struct device_type *sink_type[] = { @@ -388,7 +468,8 @@ static const struct device_type *sink_type[] = { }; static const struct device_type *sink_apdo_type[] = { - [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_SPR_AVS] = &sink_spr_avs_type, }; /* REVISIT: Export when EPR_*_Capabilities need to be supported. */ @@ -407,8 +488,12 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos p->object_position = position; if (pdo_type(pdo) == PDO_TYPE_APDO) { - /* FIXME: Only PPS supported for now! Skipping others. */ - if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) { + /* + * FIXME: Only PPS, SPR_AVS supported for now! + * Skipping others. + */ + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS && + pdo_apdo_type(pdo) != APDO_TYPE_SPR_AVS) { dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n", pdo); kfree(p); return 0; From 71e13cc1c3411c3f43305ac2fd2296d9ee6deab3 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 15 Oct 2025 15:40:41 +0000 Subject: [PATCH 038/161] dt-bindings: usb: Add Apple dwc3 Apple Silicon uses Synopsys DesignWare dwc3 based USB controllers for their Type-C ports. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-1-cbd65a2d511a@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/apple,dwc3.yaml | 80 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 81 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/apple,dwc3.yaml diff --git a/Documentation/devicetree/bindings/usb/apple,dwc3.yaml b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml new file mode 100644 index 000000000000..f70c33f32c5d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/apple,dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Silicon DWC3 USB controller + +maintainers: + - Sven Peter + +description: + Apple Silicon SoCs use a Synopsys DesignWare DWC3 based controller for each of + their Type-C ports. + +allOf: + - $ref: snps,dwc3-common.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - apple,t6000-dwc3 + - apple,t6020-dwc3 + - apple,t8112-dwc3 + - const: apple,t8103-dwc3 + - const: apple,t8103-dwc3 + + reg: + items: + - description: Core DWC3 region + - description: Apple-specific DWC3 region + + reg-names: + items: + - const: dwc3-core + - const: dwc3-apple + + interrupts: + maxItems: 1 + + iommus: + maxItems: 2 + + resets: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - iommus + - resets + - power-domains + - usb-role-switch + +unevaluatedProperties: false + +examples: + - | + #include + #include + + usb@82280000 { + compatible = "apple,t8103-dwc3"; + reg = <0x82280000 0xcd00>, <0x8228cd00 0x3200>; + reg-names = "dwc3-core", "dwc3-apple"; + interrupts = ; + iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; + + power-domains = <&ps_atc0_usb>; + resets = <&atcphy0>; + + usb-role-switch; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968..fa238b5371b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2437,6 +2437,7 @@ F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml +F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: arch/arm64/boot/dts/apple/ F: drivers/bluetooth/hci_bcm4377.c From 5ed9cc71432a8adf3c42223c935f714aac29901b Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 15 Oct 2025 15:40:42 +0000 Subject: [PATCH 039/161] usb: dwc3: dwc3_power_off_all_roothub_ports: Use ioremap_np when required On Apple Silicon machines we can't use ioremap() / Device-nGnRE to map most regions but must use ioremap_np() / Device-nGnRnE whenever IORESOURCE_MEM_NONPOSTED is set. Make sure this is also done inside dwc3_power_off_all_roothub_ports to prevent SErrors. Fixes: 2d2a3349521d ("usb: dwc3: Add workaround for host mode VBUS glitch when boot") Cc: stable@kernel.org Acked-by: Thinh Nguyen Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-2-cbd65a2d511a@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/host.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 1c513bf8002e..e77fd86d09cf 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -37,7 +37,10 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) /* xhci regs are not mapped yet, do it temporarily here */ if (dwc->xhci_resources[0].start) { - xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); + if (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED) + xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); + else + xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { dev_err(dwc->dev, "Failed to ioremap xhci_regs\n"); return; From e10bc7964635c5ada5d69d53bf8173501fc03a26 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 15 Oct 2025 15:40:43 +0000 Subject: [PATCH 040/161] usb: dwc3: glue: Add documentation We're about to add more exported functions to be used inside glue driver which will need more detailed documentation explaining how they must be used. Let's also add documentation for the functions already available. Acked-by: Thinh Nguyen Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-3-cbd65a2d511a@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/glue.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index cc6e138bd9ef..bef4d9d68558 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h @@ -36,9 +36,36 @@ struct dwc3_probe_data { struct dwc3_properties properties; }; +/** + * dwc3_core_probe - Initialize the core dwc3 driver + * @data: Initialization and configuration parameters for the controller + * + * Initializes the DesignWare USB3 core driver by setting up resources, + * registering interrupts, performing hardware setup, and preparing + * the controller for operation in the appropriate mode (host, gadget, + * or OTG). This is the main initialization function called by glue + * layer drivers to set up the core controller. + * + * Return: 0 on success, negative error code on failure + */ int dwc3_core_probe(const struct dwc3_probe_data *data); + +/** + * dwc3_core_remove - Deinitialize and remove the core dwc3 driver + * @dwc: Pointer to DWC3 controller context + * + * Cleans up resources and disables the dwc3 core driver. This should be called + * during driver removal or when the glue layer needs to shut down the + * controller completely. + */ void dwc3_core_remove(struct dwc3 *dwc); +/* + * The following callbacks are provided for glue drivers to call from their + * own pm callbacks provided in struct dev_pm_ops. Glue drivers can perform + * platform-specific work before or after calling these functions and delegate + * the core suspend/resume operations to the core driver. + */ int dwc3_runtime_suspend(struct dwc3 *dwc); int dwc3_runtime_resume(struct dwc3 *dwc); int dwc3_runtime_idle(struct dwc3 *dwc); From f854920e8f9af234570b4c5cdfdceb87527def39 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 15 Oct 2025 15:40:44 +0000 Subject: [PATCH 041/161] usb: dwc3: glue: Allow more fine grained control over mode switches We need fine grained control over mode switched on the DWC3 controller present on Apple Silicon. Export core, host and gadget init and exit, ptrcap and susphy control functions. Also introduce an additional parameter to probe_data that allows to skip the final initialization step that would bring up host or gadget mode. Acked-by: Thinh Nguyen Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-4-cbd65a2d511a@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 16 ++++-- drivers/usb/dwc3/gadget.c | 2 + drivers/usb/dwc3/glue.h | 116 ++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/host.c | 2 + 4 files changed, 131 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c5076580616e..96f85eada047 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -132,6 +132,7 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); } } +EXPORT_SYMBOL_GPL(dwc3_enable_susphy); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) { @@ -158,6 +159,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) dwc->current_dr_role = mode; trace_dwc3_set_prtcap(mode); } +EXPORT_SYMBOL_GPL(dwc3_set_prtcap); static void __dwc3_set_mode(struct work_struct *work) { @@ -975,7 +977,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc) clk_disable_unprepare(dwc->bus_clk); } -static void dwc3_core_exit(struct dwc3 *dwc) +void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_phy_power_off(dwc); @@ -983,6 +985,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_clk_disable(dwc); reset_control_assert(dwc->reset); } +EXPORT_SYMBOL_GPL(dwc3_core_exit); static bool dwc3_core_is_valid(struct dwc3 *dwc) { @@ -1328,7 +1331,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_core_init(struct dwc3 *dwc) +int dwc3_core_init(struct dwc3 *dwc) { unsigned int hw_mode; u32 reg; @@ -1528,6 +1531,7 @@ err_exit_ulpi: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_init); static int dwc3_core_get_phy(struct dwc3 *dwc) { @@ -2306,9 +2310,11 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_check_params(dwc); dwc3_debugfs_init(dwc); - ret = dwc3_core_init_mode(dwc); - if (ret) - goto err_exit_debugfs; + if (!data->skip_core_init_mode) { + ret = dwc3_core_init_mode(dwc); + if (ret) + goto err_exit_debugfs; + } pm_runtime_put(dev); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6f18b4840a25..1f67fb6aead5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -4810,6 +4810,7 @@ err1: err0: return ret; } +EXPORT_SYMBOL_GPL(dwc3_gadget_init); /* -------------------------------------------------------------------------- */ @@ -4828,6 +4829,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); } +EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index bef4d9d68558..df86e14cb706 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h @@ -27,12 +27,15 @@ struct dwc3_properties { * @res: resource for the DWC3 core mmio region * @ignore_clocks_and_resets: clocks and resets defined for the device should * be ignored by the DWC3 core, as they are managed by the glue + * @skip_core_init_mode: Skip the finial initialization of the target mode, as + * it must be managed by the glue * @properties: dwc3 software manage properties */ struct dwc3_probe_data { struct dwc3 *dwc; struct resource *res; bool ignore_clocks_and_resets; + bool skip_core_init_mode; struct dwc3_properties properties; }; @@ -74,4 +77,117 @@ int dwc3_pm_resume(struct dwc3 *dwc); void dwc3_pm_complete(struct dwc3 *dwc); int dwc3_pm_prepare(struct dwc3 *dwc); + +/* All of the following functions must only be used with skip_core_init_mode */ + +/** + * dwc3_core_init - Initialize DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Configures and initializes the core hardware, usually done by dwc3_core_probe. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the core initialization after some platform-specific setup. + * It must only be called when using skip_core_init_mode and before + * dwc3_host_init or dwc3_gadget_init. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_core_init(struct dwc3 *dwc); + +/** + * dwc3_core_exit - Shut down DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up the core hardware state. This is usually handled + * internally by dwc3 and must only be called when using skip_core_init_mode + * and only after dwc3_core_init. Afterwards, dwc3_core_init may be called + * again. + */ +void dwc3_core_exit(struct dwc3 *dwc); + +/** + * dwc3_host_init - Initialize host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB host mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the host initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_host_init(struct dwc3 *dwc); + +/** + * dwc3_host_exit - Shut down host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up host mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_host_init. + */ +void dwc3_host_exit(struct dwc3 *dwc); + +/** + * dwc3_gadget_init - Initialize gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB gadget mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. This + * function is provided for platforms that use skip_core_init_mode and need to + * finalize the gadget initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_gadget_init(struct dwc3 *dwc); + +/** + * dwc3_gadget_exit - Shut down gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up gadget mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_gadget_init. + */ +void dwc3_gadget_exit(struct dwc3 *dwc); + +/** + * dwc3_enable_susphy - Control SUSPHY status for all USB ports + * @dwc: Pointer to DWC3 controller context + * @enable: True to enable SUSPHY, false to disable + * + * Enables or disables the USB3 PHY SUSPEND and USB2 PHY SUSPHY feature for + * all available ports. + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage SUSPHY + * settings itself, e.g., due to platform-specific requirements during mode + * switches. + */ +void dwc3_enable_susphy(struct dwc3 *dwc, bool enable); + +/** + * dwc3_set_prtcap - Set the USB controller PRTCAP mode + * @dwc: Pointer to DWC3 controller context + * @mode: Target mode, must be one of DWC3_GCTL_PRTCAP_{HOST,DEVICE,OTG} + * @ignore_susphy: If true, skip disabling the SUSPHY and keep the current state + * + * Updates PRTCAP of the controller and current_dr_role inside the dwc3 + * structure. For DRD controllers, this also disables SUSPHY unless explicitly + * told to skip via the ignore_susphy parameter. + * + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage mode + * transitions itself due to platform-specific requirements. It must be called + * with the correct mode before calling dwc3_host_init or dwc3_gadget_init. + */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy); + #endif diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index e77fd86d09cf..cf6512ed17a6 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -220,6 +220,7 @@ err: platform_device_put(xhci); return ret; } +EXPORT_SYMBOL_GPL(dwc3_host_init); void dwc3_host_exit(struct dwc3 *dwc) { @@ -230,3 +231,4 @@ void dwc3_host_exit(struct dwc3 *dwc) platform_device_unregister(dwc->xhci); dwc->xhci = NULL; } +EXPORT_SYMBOL_GPL(dwc3_host_exit); From 0ec946d32ef7b1f58070cbef06bd7bdc8193c94a Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Wed, 15 Oct 2025 15:40:45 +0000 Subject: [PATCH 042/161] usb: dwc3: Add Apple Silicon DWC3 glue layer driver The dwc3 controller present on Apple Silicon SoCs like the M1 requires a specific order of operations synchronized between its PHY and its Type-C controller. Specifically, the PHY first has to go through initial bringup (which requires knowledge of the lane mode and orientation) before dwc3 itself can be brought up and can then finalize the PHY configuration. Additionally, dwc3 has to be teared down and re-initialized whenever the cable is changed due to hardware quirks that prevent a new device from being recognized and due to the PHY being unable to switch lane mode or orientation while dwc3 is up and running. These controllers also have a Apple-specific MMIO region after the common dwc3 region where some controls have to be updated. PHY bringup and shutdown also requires SUSPHY to be enabled for the ports to work correctly. In the future, this driver will also gain support for USB3-via-USB4 tunneling which will require additional tweaks. Add a glue driver that takes of all of these constraints. Reviewed-by: Neal Gompa Acked-by: Thinh Nguyen Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-5-cbd65a2d511a@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + drivers/usb/dwc3/Kconfig | 11 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-apple.c | 489 ++++++++++++++++++++++++++++++++++ 4 files changed, 502 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-apple.c diff --git a/MAINTAINERS b/MAINTAINERS index fa238b5371b9..28bfefd7ecb8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2462,6 +2462,7 @@ F: drivers/pwm/pwm-apple.c F: drivers/soc/apple/* F: drivers/spi/spi-apple.c F: drivers/spmi/spmi-apple-controller.c +F: drivers/usb/dwc3/dwc3-apple.c F: drivers/video/backlight/apple_dwi_bl.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 4925d15084f8..bf3e04635131 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -200,4 +200,15 @@ config USB_DWC3_GENERIC_PLAT the dwc3 child node in the device tree. Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way. +config USB_DWC3_APPLE + tristate "Apple Silicon DWC3 Platform Driver" + depends on OF && ARCH_APPLE + default USB_DWC3 + select USB_ROLE_SWITCH + help + Support Apple Silicon SoCs with DesignWare Core USB3 IP. + The DesignWare Core USB3 IP has to be used in dual-role + mode on these machines. + Say 'Y' or 'M' if you have such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 96469e48ff9d..89d46ab50068 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -43,6 +43,7 @@ endif ## obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o +obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c new file mode 100644 index 000000000000..6e41bd0e34f4 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple Silicon DWC3 Glue driver + * Copyright (C) The Asahi Linux Contributors + * + * Based on: + * - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved. + * - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include +#include +#include + +#include "glue.h" + +/* + * This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY: + * + * 1) The PHY itself has to be brought up; for this we need to know the mode (USB3, + * USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set. + * 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet. + * 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the + * USB3 PHY, this is done inside phy_set_mode. + * 4) We can now initialize xhci or gadget mode. + * + * We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3. + * + * And then to bring this all down again: + * + * 1) DWC3 has to exit host or gadget mode and must no longer touch those registers + * 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend + * 3) The PHY itself can be shut down, this happens from typec_mux_set + * + * We also can't transition the PHY from one mode to another while dwc3 is up and running (this is + * slightly wrong, some transitions are possible, others aren't but because we have no documentation + * for this I'd rather play it safe). + * + * After both the PHY and dwc3 are initialized we will only ever see a single "new device connected" + * event. If we just keep them running only the first device plugged in will ever work. XHCI's port + * status register actually does show the correct state but no interrupt ever comes in. In gadget + * mode we don't even get a USBDisconnected event and everything looks like there's still something + * connected on the other end. + * This can be partially explained because the USB2 D+/D- lines are connected through a stateful + * eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which + * resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a + * PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again. + * + * And to make this all extra fun: If we get the order of some of this wrong either the port is just + * broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't + * reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC + * reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates + * invalid states). + * + * Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable + * because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode + * it was until the next cable is connected we'd need to tear it all down and bring it back up again + * anyway to detect and use the next device. + * + * Instead, we just shut down everything when a cable is disconnected and transition to + * DWC3_APPLE_NO_CABLE. + * During initial probe we don't have any information about the connected cable and can't bring up + * the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off + * and defer the first dwc3 probe until we get the first cable connected event. Until then we stay + * in DWC3_APPLE_PROBE_PENDING. + * Once a cable is connected we then keep track of the controller mode here by transitioning to + * DWC3_APPLE_HOST or DWC3_APPLE_DEVICE. + */ +enum dwc3_apple_state { + DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */ + DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */ + DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */ + DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */ +}; + +/** + * struct dwc3_apple - Apple-specific DWC3 USB controller + * @dwc: Core DWC3 structure + * @dev: Pointer to the device structure + * @mmio_resource: Resource to be passed to dwc3_core_probe + * @apple_regs: Apple-specific DWC3 registers + * @resets: Reset control + * @role_sw: USB role switch + * @lock: Mutex for synchronizing access + * @state: Current state of the controller, see documentation for the enum for details + */ +struct dwc3_apple { + struct dwc3 dwc; + + struct device *dev; + struct resource *mmio_resource; + void __iomem *apple_regs; + + struct reset_control *resets; + struct usb_role_switch *role_sw; + + struct mutex lock; + + enum dwc3_apple_state state; +}; + +#define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc) + +/* + * Apple Silicon dwc3 vendor-specific registers + * + * These registers were identified by tracing XNU's memory access patterns and correlating them with + * debug output over serial to determine their names. We don't exactly know what these do but + * without these USB3 devices sometimes don't work. + */ +#define APPLE_DWC3_REGS_START 0xcd00 +#define APPLE_DWC3_REGS_END 0xcdff + +#define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38 +#define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80 + +#define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c +#define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0 + +#define APPLE_DWC3_CIO_LINK_TIMER 0xcd40 +#define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16) +#define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14 +#define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8) +#define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0) +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10 + +static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value) +{ + writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset) +{ + return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value) +{ + u32 reg; + + reg = dwc3_apple_readl(appledwc, offset); + reg &= ~mask; + reg |= value; + dwc3_apple_writel(appledwc, offset, reg); +} + +static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc) +{ + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE); + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET, + APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER, + APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER, + APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE)); +} + +static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode) +{ + guard(spinlock_irqsave)(&appledwc->dwc.lock); + dwc3_set_prtcap(&appledwc->dwc, mode, false); +} + +static int dwc3_apple_core_probe(struct dwc3_apple *appledwc) +{ + struct dwc3_probe_data probe_data = {}; + int ret; + + lockdep_assert_held(&appledwc->lock); + WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING); + + appledwc->dwc.dev = appledwc->dev; + probe_data.dwc = &appledwc->dwc; + probe_data.res = appledwc->mmio_resource; + probe_data.ignore_clocks_and_resets = true; + probe_data.skip_core_init_mode = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + ret = dwc3_core_probe(&probe_data); + if (ret) + return ret; + + appledwc->state = DWC3_APPLE_NO_CABLE; + return 0; +} + +static int dwc3_apple_core_init(struct dwc3_apple *appledwc) +{ + int ret; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + ret = dwc3_apple_core_probe(appledwc); + if (ret) + dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret); + break; + case DWC3_APPLE_NO_CABLE: + ret = dwc3_core_init(&appledwc->dwc); + if (ret) + dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret); + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + break; + } + + return ret; +} + +static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) +{ + lockdep_assert_held(&appledwc->lock); + + /* + * This platform requires SUSPHY to be enabled here already in order to properly configure + * the PHY and switch dwc3's PIPE interface to USB3 PHY. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode); +} + +static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) +{ + int ret, ret_reset; + + lockdep_assert_held(&appledwc->lock); + + ret = reset_control_deassert(appledwc->resets); + if (ret) { + dev_err(appledwc->dev, "Failed to deassert resets, err=%d\n", ret); + return ret; + } + + ret = dwc3_apple_core_init(appledwc); + if (ret) + goto reset_assert; + + /* + * Now that the core is initialized and already went through dwc3_core_soft_reset we can + * configure some unknown Apple-specific settings and then bring up xhci or gadget mode. + */ + dwc3_apple_setup_cio(appledwc); + + switch (state) { + case DWC3_APPLE_HOST: + appledwc->dwc.dr_mode = USB_DR_MODE_HOST; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST); + ret = dwc3_host_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); + goto core_exit; + } + + break; + case DWC3_APPLE_DEVICE: + appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret); + goto core_exit; + } + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + goto core_exit; + } + + appledwc->state = state; + return 0; + +core_exit: + dwc3_core_exit(&appledwc->dwc); +reset_assert: + ret_reset = reset_control_assert(appledwc->resets); + if (ret_reset) + dev_warn(appledwc->dev, "Failed to assert resets, err=%d\n", ret_reset); + + return ret; +} + +static int dwc3_apple_exit(struct dwc3_apple *appledwc) +{ + int ret = 0; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + case DWC3_APPLE_NO_CABLE: + /* Nothing to do if we're already off */ + return 0; + case DWC3_APPLE_DEVICE: + dwc3_gadget_exit(&appledwc->dwc); + break; + case DWC3_APPLE_HOST: + dwc3_host_exit(&appledwc->dwc); + break; + } + + /* + * This platform requires SUSPHY to be enabled in order to properly power down the PHY + * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via + * a different PHY connected through ULPI). + */ + dwc3_enable_susphy(&appledwc->dwc, true); + dwc3_core_exit(&appledwc->dwc); + appledwc->state = DWC3_APPLE_NO_CABLE; + + ret = reset_control_assert(appledwc->resets); + if (ret) { + dev_err(appledwc->dev, "Failed to assert resets, err=%d\n", ret); + return ret; + } + + return 0; +} + +static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + int ret; + + guard(mutex)(&appledwc->lock); + + /* + * We need to tear all of dwc3 down and re-initialize it every time a cable is + * connected or disconnected or when the mode changes. See the documentation for enum + * dwc3_apple_state for details. + */ + ret = dwc3_apple_exit(appledwc); + if (ret) + return ret; + + switch (role) { + case USB_ROLE_NONE: + /* Nothing to do if no cable is connected */ + return 0; + case USB_ROLE_HOST: + return dwc3_apple_init(appledwc, DWC3_APPLE_HOST); + case USB_ROLE_DEVICE: + return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE); + default: + dev_err(appledwc->dev, "Invalid target role: %d\n", role); + return -EINVAL; + } +} + +static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + + guard(mutex)(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_HOST: + return USB_ROLE_HOST; + case DWC3_APPLE_DEVICE: + return USB_ROLE_DEVICE; + case DWC3_APPLE_NO_CABLE: + case DWC3_APPLE_PROBE_PENDING: + return USB_ROLE_NONE; + default: + /* Unreachable unless there's a bug in this driver */ + dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state); + return USB_ROLE_NONE; + } +} + +static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc) +{ + struct usb_role_switch_desc dwc3_role_switch = { NULL }; + + dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev); + dwc3_role_switch.set = dwc3_usb_role_switch_set; + dwc3_role_switch.get = dwc3_usb_role_switch_get; + dwc3_role_switch.driver_data = appledwc; + appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch); + if (IS_ERR(appledwc->role_sw)) + return PTR_ERR(appledwc->role_sw); + + return 0; +} + +static int dwc3_apple_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_apple *appledwc; + int ret; + + appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL); + if (!appledwc) + return -ENOMEM; + + appledwc->dev = &pdev->dev; + mutex_init(&appledwc->lock); + + appledwc->resets = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(appledwc->resets)) + return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->resets), + "Failed to get resets\n"); + + ret = reset_control_assert(appledwc->resets); + if (ret) { + dev_err(&pdev->dev, "Failed to assert resets, err=%d\n", ret); + return ret; + } + + appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core"); + if (!appledwc->mmio_resource) { + dev_err(dev, "Failed to get DWC3 MMIO\n"); + return -EINVAL; + } + + appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple"); + if (IS_ERR(appledwc->apple_regs)) + return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs), + "Failed to map Apple-specific MMIO\n"); + + /* + * On this platform, DWC3 can only be brought up after parts of the PHY have been + * initialized with knowledge of the target mode and cable orientation from typec_set_mux. + * Since this has not happened here we cannot setup DWC3 yet and instead defer this until + * the first cable is connected. See the documentation for enum dwc3_apple_state for + * details. + */ + appledwc->state = DWC3_APPLE_PROBE_PENDING; + ret = dwc3_apple_setup_role_switch(appledwc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n"); + + return 0; +} + +static void dwc3_apple_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_apple *appledwc = to_dwc3_apple(dwc); + + guard(mutex)(&appledwc->lock); + + usb_role_switch_unregister(appledwc->role_sw); + + /* + * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and + * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove. + * dwc3_apple_exit can be called unconditionally because it checks the state itself. + */ + dwc3_apple_exit(appledwc); + if (appledwc->state != DWC3_APPLE_PROBE_PENDING) + dwc3_core_remove(&appledwc->dwc); +} + +static const struct of_device_id dwc3_apple_of_match[] = { + { .compatible = "apple,t8103-dwc3" }, + {} +}; +MODULE_DEVICE_TABLE(of, dwc3_apple_of_match); + +static struct platform_driver dwc3_apple_driver = { + .probe = dwc3_apple_probe, + .remove = dwc3_apple_remove, + .driver = { + .name = "dwc3-apple", + .of_match_table = dwc3_apple_of_match, + }, +}; + +module_platform_driver(dwc3_apple_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver"); From ef7204a9aada8be607a1b7d7bed0c0280d0bce89 Mon Sep 17 00:00:00 2001 From: Jameson Thies Date: Thu, 16 Oct 2025 23:59:07 +0000 Subject: [PATCH 043/161] usb: typec: ucsi: psy: Add power supply status Add support for power supply status. If a port is acting as a sink with the sink path enabled, report it is charging. If a port is source, report it is discharging. If there is no connection or the port hasn't enabled the sink path, report not charging. Signed-off-by: Jameson Thies Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Tested-By: Kenneth R. Crudup Link: https://patch.msgid.link/20251016235909.2092917-2-jthies@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/psy.c | 26 ++++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 62a9d68bb66d..2b0225821502 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -29,6 +29,7 @@ static enum power_supply_property ucsi_psy_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, }; static int ucsi_psy_get_scope(struct ucsi_connector *con, @@ -51,6 +52,29 @@ static int ucsi_psy_get_scope(struct ucsi_connector *con, return 0; } +static int ucsi_psy_get_status(struct ucsi_connector *con, + union power_supply_propval *val) +{ + bool is_sink = UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK; + bool sink_path_enabled = true; + + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (con->ucsi->version >= UCSI_VERSION_2_0) + sink_path_enabled = + UCSI_CONSTAT(con, SINK_PATH_STATUS_V2_0) == + UCSI_CONSTAT_SINK_PATH_ENABLED; + + if (UCSI_CONSTAT(con, CONNECTED)) { + if (is_sink && sink_path_enabled) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (!is_sink) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + + return 0; +} + static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { @@ -245,6 +269,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy, return ucsi_psy_get_current_now(con, val); case POWER_SUPPLY_PROP_SCOPE: return ucsi_psy_get_scope(con, val); + case POWER_SUPPLY_PROP_STATUS: + return ucsi_psy_get_status(con, val); default: return -EINVAL; } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index e301d9012936..cce93af7461b 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -360,6 +360,9 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 #define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) +#define UCSI_CONSTAT_SINK_PATH_STATUS_V2_0 UCSI_DECLARE_BITFIELD_V2_0(87, 1) +#define UCSI_CONSTAT_SINK_PATH_DISABLED 0 +#define UCSI_CONSTAT_SINK_PATH_ENABLED 1 #define UCSI_CONSTAT_PWR_READING_READY_V2_1 UCSI_DECLARE_BITFIELD_V2_1(89, 1) #define UCSI_CONSTAT_CURRENT_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(90, 3) #define UCSI_CONSTAT_PEAK_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(93, 16) From e397f446afcffd225f9c95d3e6471595421f21c2 Mon Sep 17 00:00:00 2001 From: Jameson Thies Date: Thu, 16 Oct 2025 23:59:08 +0000 Subject: [PATCH 044/161] usb: typec: ucsi: Report power supply changes on power opmode changes Report opmode changes from the PPM to the power supply class by calling ucsi_port_psy_changed(). If the current opmode is USB PD, do not call ucsi_port_psy_changed(). The power supply class will be updated after requesting partner source PDOs. Signed-off-by: Jameson Thies Reviewed-by: Benson Leung Reviewed-by: Heikki Krogerus Tested-By: Kenneth R. Crudup Link: https://patch.msgid.link/20251016235909.2092917-3-jthies@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 3995483a0aa0..04047038720e 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1022,14 +1022,17 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A); + ucsi_port_psy_changed(con); break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A); + ucsi_port_psy_changed(con); break; default: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB); + ucsi_port_psy_changed(con); break; } } From 26c3af0cd8b44c591f27be373266765a8606ad69 Mon Sep 17 00:00:00 2001 From: Jameson Thies Date: Thu, 16 Oct 2025 23:59:09 +0000 Subject: [PATCH 045/161] usb: typec: ucsi: Report power supply change on sink path change Update the UCSI interface driver to report a power supply change when the PPM sets the Sink Path Change bit. Signed-off-by: Jameson Thies Reviewed-by: Benson Leung Tested-By: Kenneth R. Crudup Link: https://patch.msgid.link/20251016235909.2092917-4-jthies@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 2 +- drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 04047038720e..ed23edab7763 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1293,7 +1293,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (change & UCSI_CONSTAT_BC_CHANGE) + if (change & (UCSI_CONSTAT_BC_CHANGE | UCSI_CONSTAT_SINK_PATH_CHANGE)) ucsi_port_psy_changed(con); if (con->ucsi->version >= UCSI_VERSION_2_1 && diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index cce93af7461b..35993bc34d4d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -382,6 +382,7 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_CHANGE BIT(9) #define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) #define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) +#define UCSI_CONSTAT_SINK_PATH_CHANGE BIT(13) #define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) #define UCSI_CONSTAT_ERROR BIT(15) From f83cb615cb7a615f9c15787f914a8eee1c6e93d4 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 14 Oct 2025 18:06:45 +0200 Subject: [PATCH 046/161] usb: typec: ps883x: Cache register settings, not Type-C mode Certain Type-C mode configurations may result in identical settings of the PS8830. Check if the latter have changed instead of assuming there's always a difference. ps883x_set() is changed to accept a typec_retimer_state in preparation for more work and the ps883x_sw_set() (which only handles orientation switching) is changed to use regmap_assign_bits(), which itself does not perform any writes if the desired value is already set. Reviewed-by: Jack Pham Signed-off-by: Konrad Dybcio Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251014-topic-ps883x_usb4-v1-1-e6adb1a4296e@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 41 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index ad59babf7cce..68f172df7be3 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -54,8 +54,9 @@ struct ps883x_retimer { struct mutex lock; /* protect non-concurrent retimer & switch */ enum typec_orientation orientation; - unsigned long mode; - unsigned int svid; + u8 cfg0; + u8 cfg1; + u8 cfg2; }; static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, @@ -64,6 +65,9 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, struct device *dev = &retimer->client->dev; int ret; + if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2) + return 0; + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); if (ret) { dev_err(dev, "failed to write conn_status_0: %d\n", ret); @@ -82,27 +86,31 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, return ret; } + retimer->cfg0 = cfg0; + retimer->cfg1 = cfg1; + retimer->cfg2 = cfg2; + return 0; } -static int ps883x_set(struct ps883x_retimer *retimer) +static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) { int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; int cfg1 = 0x00; int cfg2 = 0x00; if (retimer->orientation == TYPEC_ORIENTATION_NONE || - retimer->mode == TYPEC_STATE_SAFE) { + state->mode == TYPEC_STATE_SAFE) { return ps883x_configure(retimer, cfg0, cfg1, cfg2); } - if (retimer->mode != TYPEC_STATE_USB && retimer->svid != USB_TYPEC_DP_SID) + if (state->alt && state->alt->svid != USB_TYPEC_DP_SID) return -EINVAL; if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; - switch (retimer->mode) { + switch (state->mode) { case TYPEC_STATE_USB: cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; @@ -149,7 +157,13 @@ static int ps883x_sw_set(struct typec_switch_dev *sw, if (retimer->orientation != orientation) { retimer->orientation = orientation; - ret = ps883x_set(retimer); + ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + CONN_STATUS_0_ORIENTATION_REVERSED, + orientation == TYPEC_ORIENTATION_REVERSE); + if (ret) { + dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); + return ret; + } } mutex_unlock(&retimer->lock); @@ -165,18 +179,7 @@ static int ps883x_retimer_set(struct typec_retimer *rtmr, int ret = 0; mutex_lock(&retimer->lock); - - if (state->mode != retimer->mode) { - retimer->mode = state->mode; - - if (state->alt) - retimer->svid = state->alt->svid; - else - retimer->svid = 0; - - ret = ps883x_set(retimer); - } - + ret = ps883x_set(retimer, state); mutex_unlock(&retimer->lock); if (ret) From 6bebd9b77726a27e37834acd8f9c0f2cbbe2618b Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 14 Oct 2025 18:06:46 +0200 Subject: [PATCH 047/161] usb: typec: ps883x: Rework ps883x_set() In preparation to extend it with new alt/USB modes, rework the code a bit by changing the flow into a pair of switch statements. Reviewed-by: Jack Pham Signed-off-by: Konrad Dybcio Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251014-topic-ps883x_usb4-v1-2-e6adb1a4296e@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 71 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 68f172df7be3..72f1e737ca4b 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -99,44 +99,47 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state int cfg1 = 0x00; int cfg2 = 0x00; - if (retimer->orientation == TYPEC_ORIENTATION_NONE || - state->mode == TYPEC_STATE_SAFE) { - return ps883x_configure(retimer, cfg0, cfg1, cfg2); - } - - if (state->alt && state->alt->svid != USB_TYPEC_DP_SID) - return -EINVAL; - if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; - switch (state->mode) { - case TYPEC_STATE_USB: - cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - break; + if (state->alt) { + switch (state->alt->svid) { + case USB_TYPEC_DP_SID: + cfg1 |= CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; - case TYPEC_DP_STATE_C: - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | - CONN_STATUS_1_DP_HPD_LEVEL; - break; - - case TYPEC_DP_STATE_D: - cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | - CONN_STATUS_1_DP_HPD_LEVEL; - break; - - case TYPEC_DP_STATE_E: - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_HPD_LEVEL; - break; - - default: - return -EOPNOTSUPP; + switch (state->mode) { + case TYPEC_DP_STATE_C: + cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; + fallthrough; + case TYPEC_DP_STATE_D: + cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + default: /* MODE_E */ + break; + } + break; + default: + dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", + state->alt->svid); + return -EOPNOTSUPP; + } + } else { + switch (state->mode) { + case TYPEC_STATE_SAFE: + /* USB2 pins don't even go through this chip */ + case TYPEC_MODE_USB2: + break; + case TYPEC_STATE_USB: + case TYPEC_MODE_USB3: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", + state->mode); + return -EOPNOTSUPP; + } } return ps883x_configure(retimer, cfg0, cfg1, cfg2); From 832c8d3fce77cf03cc225fc555c1bffa1c547ba1 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 14 Oct 2025 18:06:47 +0200 Subject: [PATCH 048/161] usb: typec: ps883x: Add USB4 mode and TBT3 altmode support This chip can do some more than the driver currently describes. Add support for configuring it for various flavors of TBT3/USB4 operation. Reviewed-by: Jack Pham Signed-off-by: Konrad Dybcio Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251014-topic-ps883x_usb4-v1-3-e6adb1a4296e@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 29 +++++++++++++++++++++++++++++ include/linux/usb/typec_tbt.h | 1 + 2 files changed, 30 insertions(+) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 72f1e737ca4b..7c61629b36d6 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -14,15 +14,18 @@ #include #include #include +#include #include #include #include #include +#include #define REG_USB_PORT_CONN_STATUS_0 0x00 #define CONN_STATUS_0_CONNECTION_PRESENT BIT(0) #define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1) +#define CONN_STATUS_0_ACTIVE_CABLE BIT(2) #define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5) #define REG_USB_PORT_CONN_STATUS_1 0x01 @@ -34,6 +37,10 @@ #define REG_USB_PORT_CONN_STATUS_2 0x02 +#define CONN_STATUS_2_TBT_CONNECTED BIT(0) +#define CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT BIT(4) +#define CONN_STATUS_2_USB4_CONNECTED BIT(7) + struct ps883x_retimer { struct i2c_client *client; struct gpio_desc *reset_gpio; @@ -95,6 +102,8 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) { + struct typec_thunderbolt_data *tb_data; + const struct enter_usb_data *eudo_data; int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; int cfg1 = 0x00; int cfg2 = 0x00; @@ -120,6 +129,18 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state break; } break; + case USB_TYPEC_TBT_SID: + tb_data = state->data; + + /* Unconditional */ + cfg2 |= CONN_STATUS_2_TBT_CONNECTED; + + if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + + if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX) + cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT; + break; default: dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", state->alt->svid); @@ -135,6 +156,14 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state case TYPEC_MODE_USB3: cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; + case TYPEC_MODE_USB4: + eudo_data = state->data; + + cfg2 |= CONN_STATUS_2_USB4_CONNECTED; + + if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + break; default: dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", state->mode); diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index 55dcea12082c..0b570f1b8bc8 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -55,6 +55,7 @@ struct typec_thunderbolt_data { /* TBT3 Device Enter Mode VDO bits */ #define TBT_ENTER_MODE_CABLE_SPEED(s) TBT_SET_CABLE_SPEED(s) +#define TBT_ENTER_MODE_UNI_DIR_LSRX BIT(23) #define TBT_ENTER_MODE_ACTIVE_CABLE BIT(24) #endif /* __USB_TYPEC_TBT_H */ From 93741bd104ce07a790519ecee3a331ee7cf61ae3 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 15 Oct 2025 11:43:09 -0700 Subject: [PATCH 049/161] usb: uhci: Work around bogus clang shift overflow warning from DMA_BIT_MASK(64) After commit 18a9ec886d32 ("usb: uhci: Add Aspeed AST2700 support"), clang incorrectly warns: In file included from drivers/usb/host/uhci-hcd.c:855: drivers/usb/host/uhci-platform.c:69:32: error: shift count >= width of type [-Werror,-Wshift-count-overflow] 69 | static const u64 dma_mask_64 = DMA_BIT_MASK(64); | ^~~~~~~~~~~~~~~~ include/linux/dma-mapping.h:93:54: note: expanded from macro 'DMA_BIT_MASK' 93 | #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) | ^ ~~~ clang has a long outstanding and complicated problem [1] with generating a proper control flow graph at global scope, resulting in it being unable to understand that this shift can never happen due to the 'n == 64' check. Restructure the code to only use DMA_BIT_MASK() within uhci_hcd_platform_probe() (i.e., function scope) to avoid this global scope issue, similar to the approach of commit 274f2232a94f ("usb: ehci: Add Aspeed AST2700 support"). Closes: https://github.com/ClangBuiltLinux/linux/issues/2136 Link: https://github.com/ClangBuiltLinux/linux/issues/92 [1] Reviewed-by: Alan Stern Signed-off-by: Nathan Chancellor Acked-by: Ryan Chen Link: https://patch.msgid.link/20251015-usb-uhci-avoid-bogus-clang-shift-warning-v2-1-68532d2f6114@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 37607f985cc0..5e02f2ceafb6 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -65,13 +65,10 @@ static const struct hc_driver uhci_platform_hc_driver = { .hub_control = uhci_hub_control, }; -static const u64 dma_mask_32 = DMA_BIT_MASK(32); -static const u64 dma_mask_64 = DMA_BIT_MASK(64); - static int uhci_hcd_platform_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const u64 *dma_mask_ptr; + bool dma_mask_64 = false; struct usb_hcd *hcd; struct uhci_hcd *uhci; struct resource *res; @@ -85,11 +82,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev); - if (!dma_mask_ptr) - dma_mask_ptr = &dma_mask_32; + if (of_device_get_match_data(&pdev->dev)) + dma_mask_64 = true; - ret = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr); + ret = dma_coerce_mask_and_coherent(&pdev->dev, + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (ret) return ret; @@ -200,7 +197,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) static const struct of_device_id platform_uhci_ids[] = { { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, - { .compatible = "aspeed,ast2700-uhci", .data = &dma_mask_64}, + { .compatible = "aspeed,ast2700-uhci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, platform_uhci_ids); From d227a8b3e715963b7c034971c3b467d5430a2cab Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 23 Oct 2025 10:13:42 +0200 Subject: [PATCH 050/161] usb: typec: ps883x: Fix missing mutex_unlock() There's a missing mutex_unlock() in the error-return path inside ps883x_sw_set(). Simply delete that return since there's another one 3 lines below. Fixes: f83cb615cb7a ("usb: typec: ps883x: Cache register settings, not Type-C mode") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202510231023.aJ09O6pk-lkp@intel.com/ Signed-off-by: Konrad Dybcio Link: https://patch.msgid.link/20251023-topic-ps883x_fixup-v1-1-2afb5b85f09b@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/ps883x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 7c61629b36d6..5f2879749769 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -192,10 +192,8 @@ static int ps883x_sw_set(struct typec_switch_dev *sw, ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, CONN_STATUS_0_ORIENTATION_REVERSED, orientation == TYPEC_ORIENTATION_REVERSE); - if (ret) { + if (ret) dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); - return ret; - } } mutex_unlock(&retimer->lock); From 30a34716562ee7871593158684d4495aa2aebd4a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 21 Oct 2025 10:39:54 +0530 Subject: [PATCH 051/161] dt-bindings: usb: qcom,snps-dwc3: Add the SM8750 compatible Add qcom,sm8750-dwc3 compatible to flattened implementation binding. Signed-off-by: Konrad Dybcio Signed-off-by: Krishna Kurapati Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251021050954.3462613-1-krishna.kurapati@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index dfd084ed9024..a6c1bfb33c39 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -67,6 +67,7 @@ properties: - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 + - qcom,sm8750-dwc3 - qcom,x1e80100-dwc3 - const: qcom,snps-dwc3 @@ -212,6 +213,7 @@ allOf: - qcom,sdx65-dwc3 - qcom,sdx75-dwc3 - qcom,sm6350-dwc3 + - qcom,sm8750-dwc3 then: properties: clocks: @@ -498,6 +500,7 @@ allOf: - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 + - qcom,sm8750-dwc3 then: properties: interrupts: From 389597581e3ef46940476f2d59c9ac7f9a26b113 Mon Sep 17 00:00:00 2001 From: Pritam Manohar Sutar Date: Fri, 24 Oct 2025 14:24:55 +0530 Subject: [PATCH 052/161] usb: dwc3: Allow usb role swich control from userspace There is a possibility of user needs for USB mode switching on boards that lack external hardware support for dynamic host/device role detection. This is particularly relevant in automotive applications where userspace applications need to switch USB roles (host to device) at runtime for CarPlay/Android Auto integration. Add an `allow_userspace_control` flag to handle such cases. When enabled, it exposes a sysfs attribute that allows userspace to switch the USB role manually between host and device. This provides flexibility for platforms that cannot rely on hardware-based mode detection. The role switch can be done as below echo host > /sys/class/usb_role/.usb-role-switch/role echo device > /sys/class/usb_role/.usb-role-switch/role Acked-by: Thinh Nguyen Signed-off-by: Pritam Manohar Sutar Link: https://patch.msgid.link/20251024085455.789555-1-pritam.sutar@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/drd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 4c91240eb429..589bbeb27454 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -515,6 +515,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) dwc3_role_switch.set = dwc3_usb_role_switch_set; dwc3_role_switch.get = dwc3_usb_role_switch_get; dwc3_role_switch.driver_data = dwc; + dwc3_role_switch.allow_userspace_control = true; dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); if (IS_ERR(dwc->role_sw)) return PTR_ERR(dwc->role_sw); From dacd0ca666461e3b79059b7830403ae436157bf1 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Tue, 21 Oct 2025 17:45:53 -0700 Subject: [PATCH 053/161] usb: typec: ucsi_glink: Update request/response buffers to be packed Update the ucsi request/response buffers to be packed to ensure there are no "holes" in memory while we read/write these structs. Signed-off-by: Anjelique Melendez Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251022004554.1956729-2-anjelique.melendez@oss.qualcomm.com --- drivers/usb/typec/ucsi/ucsi_glink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 8af79101a2fc..1f9f0d942c1a 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -30,24 +30,24 @@ struct ucsi_read_buf_req_msg { struct pmic_glink_hdr hdr; }; -struct ucsi_read_buf_resp_msg { +struct __packed ucsi_read_buf_resp_msg { struct pmic_glink_hdr hdr; u8 buf[UCSI_BUF_SIZE]; u32 ret_code; }; -struct ucsi_write_buf_req_msg { +struct __packed ucsi_write_buf_req_msg { struct pmic_glink_hdr hdr; u8 buf[UCSI_BUF_SIZE]; u32 reserved; }; -struct ucsi_write_buf_resp_msg { +struct __packed ucsi_write_buf_resp_msg { struct pmic_glink_hdr hdr; u32 ret_code; }; -struct ucsi_notify_ind_msg { +struct __packed ucsi_notify_ind_msg { struct pmic_glink_hdr hdr; u32 notification; u32 receiver; From 4dad394ce3b60a903452f45c4bd90cb220362ba9 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Tue, 21 Oct 2025 17:45:54 -0700 Subject: [PATCH 054/161] usb: typec: ucsi_glink: Increase buffer size to support UCSI v2 UCSI v2 specification has increased the MSG_IN and MSG_OUT size from 16 bytes to 256 bytes each for the message exchange between OPM and PPM This makes the total buffer size increase from 48 bytes to 528 bytes. Update the buffer size to support this increase. Signed-off-by: Anjelique Melendez Reviewed-by: Heikki Krogerus Reviewed-by: Dmitry Baryshkov Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20251022004554.1956729-3-anjelique.melendez@oss.qualcomm.com --- drivers/usb/typec/ucsi/ucsi_glink.c | 80 +++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 1f9f0d942c1a..11b3e24e34e2 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -16,10 +16,10 @@ #define PMIC_GLINK_MAX_PORTS 3 -#define UCSI_BUF_SIZE 48 +#define UCSI_BUF_V1_SIZE (UCSI_MESSAGE_OUT + (UCSI_MESSAGE_OUT - UCSI_MESSAGE_IN)) +#define UCSI_BUF_V2_SIZE (UCSIv2_MESSAGE_OUT + (UCSIv2_MESSAGE_OUT - UCSI_MESSAGE_IN)) #define MSG_TYPE_REQ_RESP 1 -#define UCSI_BUF_SIZE 48 #define UC_NOTIFY_RECEIVER_UCSI 0x0 #define UC_UCSI_READ_BUF_REQ 0x11 @@ -32,13 +32,19 @@ struct ucsi_read_buf_req_msg { struct __packed ucsi_read_buf_resp_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 ret_code; }; struct __packed ucsi_write_buf_req_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 reserved; }; @@ -72,7 +78,7 @@ struct pmic_glink_ucsi { bool ucsi_registered; bool pd_running; - u8 read_buf[UCSI_BUF_SIZE]; + u8 read_buf[UCSI_BUF_V2_SIZE]; }; static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset, @@ -132,17 +138,35 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i const void *val, size_t val_len) { struct ucsi_write_buf_req_msg req = {}; + size_t req_len, buf_len; unsigned long left; int ret; + u8 *buf; req.hdr.owner = PMIC_GLINK_OWNER_USBC; req.hdr.type = MSG_TYPE_REQ_RESP; req.hdr.opcode = UC_UCSI_WRITE_BUF_REQ; - memcpy(&req.buf[offset], val, val_len); + + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf_len = UCSI_BUF_V2_SIZE; + buf = req.buf.v2_buf; + } else if (ucsi->ucsi->version) { + buf_len = UCSI_BUF_V1_SIZE; + buf = req.buf.v1_buf; + } else { + dev_err(ucsi->dev, "UCSI version unknown\n"); + return -EINVAL; + } + req_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (offset + val_len > buf_len) + return -EINVAL; + + memcpy(&buf[offset], val, val_len); reinit_completion(&ucsi->write_ack); - ret = pmic_glink_send(ucsi->client, &req, sizeof(req)); + ret = pmic_glink_send(ucsi->client, &req, req_len); if (ret < 0) { dev_err(ucsi->dev, "failed to send UCSI write request: %d\n", ret); return ret; @@ -216,12 +240,48 @@ static const struct ucsi_operations pmic_glink_ucsi_ops = { static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len) { - const struct ucsi_read_buf_resp_msg *resp = data; + u32 ret_code, resp_len, buf_len = 0; + u8 *buf; - if (resp->ret_code) + if (ucsi->ucsi->version) { + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else if (!ucsi->ucsi_registered) { + /* + * If UCSI version is not known yet because device is not registered, choose buffer + * size which best fits incoming data + */ + if (len > sizeof(struct pmic_glink_hdr) + UCSI_BUF_V2_SIZE) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else { + dev_err(ucsi->dev, "Device has been registered but UCSI version is still unknown\n"); + return; + } + + resp_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (len > resp_len) return; - memcpy(ucsi->read_buf, resp->buf, UCSI_BUF_SIZE); + /* Ensure that buffer_len leaves space for ret_code to be read back from memory */ + if (buf_len > len - sizeof(struct pmic_glink_hdr) - sizeof(u32)) + buf_len = len - sizeof(struct pmic_glink_hdr) - sizeof(u32); + + memcpy(&ret_code, buf + buf_len, sizeof(u32)); + if (ret_code) + return; + + memcpy(ucsi->read_buf, buf, buf_len); complete(&ucsi->read_ack); } From b6e0b3016187446ddef9edac03cd9d544ac63f11 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:33 +0200 Subject: [PATCH 055/161] USB: serial: belkin_sa: fix TIOCMBIS and TIOCMBIC Asserting or deasserting a modem control line using TIOCMBIS or TIOCMBIC should not deassert any lines that are not in the mask. Fix this long-standing regression dating back to 2003 when the tiocmset() callback was introduced. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/belkin_sa.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 44f5b58beec9..aa6b4c4ad5ec 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -435,7 +435,7 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; - int retval; + int retval = 0; int rts = 0; int dtr = 0; @@ -452,26 +452,32 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, } if (clear & TIOCM_RTS) { control_state &= ~TIOCM_RTS; - rts = 0; + rts = 1; } if (clear & TIOCM_DTR) { control_state &= ~TIOCM_DTR; - dtr = 0; + dtr = 1; } priv->control_state = control_state; spin_unlock_irqrestore(&priv->lock, flags); - retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts); - if (retval < 0) { - dev_err(&port->dev, "Set RTS error %d\n", retval); - goto exit; + if (rts) { + retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, + !!(control_state & TIOCM_RTS)); + if (retval < 0) { + dev_err(&port->dev, "Set RTS error %d\n", retval); + goto exit; + } } - retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr); - if (retval < 0) { - dev_err(&port->dev, "Set DTR error %d\n", retval); - goto exit; + if (dtr) { + retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, + !!(control_state & TIOCM_DTR)); + if (retval < 0) { + dev_err(&port->dev, "Set DTR error %d\n", retval); + goto exit; + } } exit: return retval; From d432df758f92c4c28aac409bc807fd1716167577 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:34 +0200 Subject: [PATCH 056/161] USB: serial: kobil_sct: fix TIOCMBIS and TIOCMBIC Asserting or deasserting a modem control line using TIOCMBIS or TIOCMBIC should not deassert any lines that are not in the mask. Fix this long-standing issue dating back to 2003 when the support for these ioctls was added with the introduction of the tiocmset() callback. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 464433be2034..96ea571c436a 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -418,7 +418,7 @@ static int kobil_tiocmset(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct device *dev = &port->dev; struct kobil_private *priv; - int result; + int result = 0; int dtr = 0; int rts = 0; @@ -435,12 +435,12 @@ static int kobil_tiocmset(struct tty_struct *tty, if (set & TIOCM_DTR) dtr = 1; if (clear & TIOCM_RTS) - rts = 0; + rts = 1; if (clear & TIOCM_DTR) - dtr = 0; + dtr = 1; - if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { - if (dtr != 0) + if (dtr && priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { + if (set & TIOCM_DTR) dev_dbg(dev, "%s - Setting DTR\n", __func__); else dev_dbg(dev, "%s - Clearing DTR\n", __func__); @@ -448,13 +448,13 @@ static int kobil_tiocmset(struct tty_struct *tty, usb_sndctrlpipe(port->serial->dev, 0), SUSBCRequest_SetStatusLinesOrQueues, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), + ((set & TIOCM_DTR) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), 0, NULL, 0, KOBIL_TIMEOUT); - } else { - if (rts != 0) + } else if (rts) { + if (set & TIOCM_RTS) dev_dbg(dev, "%s - Setting RTS\n", __func__); else dev_dbg(dev, "%s - Clearing RTS\n", __func__); @@ -462,7 +462,7 @@ static int kobil_tiocmset(struct tty_struct *tty, usb_sndctrlpipe(port->serial->dev, 0), SUSBCRequest_SetStatusLinesOrQueues, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), + ((set & TIOCM_RTS) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), 0, NULL, 0, From 66b1c554a83dbe30db382c527890fead49e777f5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:35 +0200 Subject: [PATCH 057/161] USB: serial: belkin_sa: clean up tiocmset() Clean up the tiocmset() implementation by dropping the dtr and rts flags to make the logic a little easier to follow. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/belkin_sa.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index aa6b4c4ad5ec..5c41c1c82c3f 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -436,33 +436,23 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, unsigned long control_state; unsigned long flags; int retval = 0; - int rts = 0; - int dtr = 0; spin_lock_irqsave(&priv->lock, flags); control_state = priv->control_state; - if (set & TIOCM_RTS) { + if (set & TIOCM_RTS) control_state |= TIOCM_RTS; - rts = 1; - } - if (set & TIOCM_DTR) { + if (set & TIOCM_DTR) control_state |= TIOCM_DTR; - dtr = 1; - } - if (clear & TIOCM_RTS) { + if (clear & TIOCM_RTS) control_state &= ~TIOCM_RTS; - rts = 1; - } - if (clear & TIOCM_DTR) { + if (clear & TIOCM_DTR) control_state &= ~TIOCM_DTR; - dtr = 1; - } priv->control_state = control_state; spin_unlock_irqrestore(&priv->lock, flags); - if (rts) { + if ((set | clear) & TIOCM_RTS) { retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, !!(control_state & TIOCM_RTS)); if (retval < 0) { @@ -471,7 +461,7 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, } } - if (dtr) { + if ((set | clear) & TIOCM_DTR) { retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, !!(control_state & TIOCM_DTR)); if (retval < 0) { From ddf81605809652228469275f8598cf7d55450bdc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:36 +0200 Subject: [PATCH 058/161] USB: serial: kobil_sct: clean up tiocmset() Clean up the tiocmset() implementation by simplifying the flag check, dropping some dev_dbg(), logging errors using dev_err() and using a common control message call for both DTR and RTS to make the existing logic easier to follow. Note that the modem control lines are currently only manipulated in this function, which therefore does not require any locking. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 63 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 96ea571c436a..b8169783f6f0 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -418,11 +418,10 @@ static int kobil_tiocmset(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct device *dev = &port->dev; struct kobil_private *priv; - int result = 0; - int dtr = 0; - int rts = 0; + int dtr, rts; + int result; + u16 val = 0; - /* FIXME: locking ? */ priv = usb_get_serial_port_data(port); if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { @@ -430,46 +429,38 @@ static int kobil_tiocmset(struct tty_struct *tty, return -EINVAL; } - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 1; - if (clear & TIOCM_DTR) - dtr = 1; + dtr = (set | clear) & TIOCM_DTR; + rts = (set | clear) & TIOCM_RTS; if (dtr && priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { if (set & TIOCM_DTR) - dev_dbg(dev, "%s - Setting DTR\n", __func__); + val = SUSBCR_SSL_SETDTR; else - dev_dbg(dev, "%s - Clearing DTR\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((set & TIOCM_DTR) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), - 0, - NULL, - 0, - KOBIL_TIMEOUT); + val = SUSBCR_SSL_CLRDTR; } else if (rts) { if (set & TIOCM_RTS) - dev_dbg(dev, "%s - Setting RTS\n", __func__); + val = SUSBCR_SSL_SETRTS; else - dev_dbg(dev, "%s - Clearing RTS\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((set & TIOCM_RTS) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), - 0, - NULL, - 0, - KOBIL_TIMEOUT); + val = SUSBCR_SSL_CLRRTS; } - dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result); - return (result < 0) ? result : 0; + + if (val) { + result = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + SUSBCRequest_SetStatusLinesOrQueues, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + val, + 0, + NULL, + 0, + KOBIL_TIMEOUT); + if (result < 0) { + dev_err(dev, "failed to set status lines: %d\n", result); + return result; + } + } + + return 0; } static void kobil_set_termios(struct tty_struct *tty, From e41de6124e0aac03e7c83f6972810e59ccb27fba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:37 +0200 Subject: [PATCH 059/161] USB: serial: kobil_sct: clean up device type checks Clean up the driver device type checks by moving logical operators to the previous line and using consistent indentation of continuation lines. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index b8169783f6f0..e1015cab2770 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -239,8 +239,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result); } if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* start reading (Adapter B 'cause PNP string) */ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -318,9 +318,10 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) { /* stop reading (except TWIN and KAAN SIM) */ - if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) - || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID)) + if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { usb_kill_urb(port->interrupt_in_urb); + } todo = priv->filled - priv->cur_pos; @@ -347,7 +348,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, /* start reading (except TWIN and KAAN SIM) */ if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -373,8 +374,8 @@ static int kobil_tiocmget(struct tty_struct *tty) int transfer_buffer_length = 8; priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } @@ -423,8 +424,8 @@ static int kobil_tiocmset(struct tty_struct *tty, u16 val = 0; priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } From 754640d85566ffccfae489cd0c16de57bdf88140 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:38 +0200 Subject: [PATCH 060/161] USB: serial: kobil_sct: add control request helpers Refactor by adding two control request helpers to make the code more readable. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 108 +++++++++------------------------ 1 file changed, 27 insertions(+), 81 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index e1015cab2770..3c13410520ec 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -109,6 +109,21 @@ struct kobil_private { __u16 device_type; }; +static int kobil_ctrl_send(struct usb_serial_port *port, u8 req, u16 val) +{ + return usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + val, 0, NULL, 0, KOBIL_TIMEOUT); +} + +static int kobil_ctrl_recv(struct usb_serial_port *port, u8 req, u16 val, void *buf, u16 size) +{ + return usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + val, 0, buf, size, KOBIL_TIMEOUT); +} static int kobil_port_probe(struct usb_serial_port *port) { @@ -176,16 +191,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENOMEM; /* get hardware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetHWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetHWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], @@ -193,16 +200,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) } /* get firmware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetFWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetFWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], @@ -212,30 +211,12 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { /* Setting Baudrate, Parity and Stopbits */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | - SUSBCR_SPASB_1StopBit, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, + SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit); dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result); /* reset all queues */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result); } if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || @@ -385,16 +366,8 @@ static int kobil_tiocmget(struct tty_struct *tty) if (!transfer_buffer) return -ENOMEM; - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetStatusLineState, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - 0, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT); - + result = kobil_ctrl_recv(port, SUSBCRequest_GetStatusLineState, 0, + transfer_buffer, transfer_buffer_length); dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n", result); if (result < 1) { @@ -446,15 +419,7 @@ static int kobil_tiocmset(struct tty_struct *tty, } if (val) { - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - val, - 0, - NULL, - 0, - KOBIL_TIMEOUT); + result = kobil_ctrl_send(port, SUSBCRequest_SetStatusLinesOrQueues, val); if (result < 0) { dev_err(dev, "failed to set status lines: %d\n", result); return result; @@ -506,16 +471,7 @@ static void kobil_set_termios(struct tty_struct *tty, tty->termios.c_cflag &= ~CMSPAR; tty_encode_baud_rate(tty, speed, speed); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - urb_val, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, urb_val); if (result) { dev_err(&port->dev, "failed to update line settings: %d\n", result); @@ -536,17 +492,7 @@ static int kobil_ioctl(struct tty_struct *tty, switch (cmd) { case TCFLSH: - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); - + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(&port->dev, "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", __func__, result); From d99bdbb0d3e4928dfc5c8dfd017483055ed792c1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:39 +0200 Subject: [PATCH 061/161] USB: serial: kobil_sct: clean up set_termios() Clean up set_termios() by using a shorter identifier for the control request value, replacing a ternary operator and adding some missing braces to make it more readable. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 3c13410520ec..cad3cfc63ce7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -435,9 +435,9 @@ static void kobil_set_termios(struct tty_struct *tty, { struct kobil_private *priv; int result; - unsigned short urb_val = 0; int c_cflag = tty->termios.c_cflag; speed_t speed; + u16 val; priv = usb_get_serial_port_data(port); if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || @@ -450,28 +450,34 @@ static void kobil_set_termios(struct tty_struct *tty, speed = tty_get_baud_rate(tty); switch (speed) { case 1200: - urb_val = SUSBCR_SBR_1200; + val = SUSBCR_SBR_1200; break; default: speed = 9600; fallthrough; case 9600: - urb_val = SUSBCR_SBR_9600; + val = SUSBCR_SBR_9600; break; } - urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : - SUSBCR_SPASB_1StopBit; + + if (c_cflag & CSTOPB) + val |= SUSBCR_SPASB_2StopBits; + else + val |= SUSBCR_SPASB_1StopBit; + if (c_cflag & PARENB) { if (c_cflag & PARODD) - urb_val |= SUSBCR_SPASB_OddParity; + val |= SUSBCR_SPASB_OddParity; else - urb_val |= SUSBCR_SPASB_EvenParity; - } else - urb_val |= SUSBCR_SPASB_NoParity; + val |= SUSBCR_SPASB_EvenParity; + } else { + val |= SUSBCR_SPASB_NoParity; + } + tty->termios.c_cflag &= ~CMSPAR; tty_encode_baud_rate(tty, speed, speed); - result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, urb_val); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, val); if (result) { dev_err(&port->dev, "failed to update line settings: %d\n", result); From 53002803832be8cfea1466ce8d568014e9de29d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Oct 2025 17:26:40 +0200 Subject: [PATCH 062/161] USB: serial: kobil_sct: drop unnecessary initialisations Drop unnecessary initialisation of variables that are always assigned before being used. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index cad3cfc63ce7..3a1343d88386 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -178,10 +178,10 @@ static void kobil_init_termios(struct tty_struct *tty) static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { struct device *dev = &port->dev; - int result = 0; struct kobil_private *priv; unsigned char *transfer_buffer; int transfer_buffer_length = 8; + int result; priv = usb_get_serial_port_data(port); @@ -272,10 +272,8 @@ static void kobil_write_int_callback(struct urb *urb) static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - int length = 0; - int result = 0; - int todo = 0; struct kobil_private *priv; + int length, todo, result; if (count == 0) { dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__); From e2d2bd6d61a4f288bf38d9236c3ca3e34b84d3dd Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 17 Oct 2025 01:13:50 +0200 Subject: [PATCH 063/161] rust: usb: fix formatting We do our best to keep the repository `rustfmt`-clean, thus run the tool to fix the formatting issue. Link: https://docs.kernel.org/rust/coding-guidelines.html#style-formatting Link: https://rust-for-linux.com/contributing#submit-checklist-addendum Fixes: 9a55e0079258 ("Revert "USB: disable rust bindings from the build for now"") Signed-off-by: Miguel Ojeda Link: https://patch.msgid.link/20251016231350.1418501-1-ojeda@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9cf4ca0ae7a1..cd191686fef6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -138,9 +138,9 @@ pub mod time; pub mod tracepoint; pub mod transmute; pub mod types; +pub mod uaccess; #[cfg(CONFIG_USB = "y")] pub mod usb; -pub mod uaccess; pub mod workqueue; pub mod xarray; From b9d87b41f96fa2993bc86f3613ea57c5e5e5f389 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Fri, 24 Oct 2025 17:10:14 -0700 Subject: [PATCH 064/161] dt-bindings: usb: qcom,snps-dwc3: Add Glymur compatible Introduce the compatible definition for Glymur QCOM SNPS DWC3. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wesley Cheng Link: https://patch.msgid.link/20251024-glymur_usb_subsystem-v1-1-bf6faf63b566@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/qcom,snps-dwc3.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index 301e873684ae..f88ad2e96f34 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -24,6 +24,8 @@ properties: compatible: items: - enum: + - qcom,glymur-dwc3 + - qcom,glymur-dwc3-mp - qcom,ipq4019-dwc3 - qcom,ipq5018-dwc3 - qcom,ipq5332-dwc3 @@ -389,6 +391,28 @@ allOf: - const: mock_utmi - const: xo + - if: + properties: + compatible: + contains: + enum: + - qcom,glymur-dwc3 + - qcom,glymur-dwc3-mp + + then: + properties: + clocks: + maxItems: 7 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: noc_aggr_north + - const: noc_aggr_south + - if: properties: compatible: @@ -458,6 +482,7 @@ allOf: compatible: contains: enum: + - qcom,glymur-dwc3 - qcom,milos-dwc3 - qcom,x1e80100-dwc3 then: @@ -524,6 +549,7 @@ allOf: compatible: contains: enum: + - qcom,glymur-dwc3-mp - qcom,sc8180x-dwc3-mp - qcom,x1e80100-dwc3-mp then: From abf640a2436f3cd21f3d63eeccb332b4056adb9e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 26 Oct 2025 11:51:04 +0100 Subject: [PATCH 065/161] xen/usb: Constify struct hc_driver 'struct hc_driver' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 52065 23176 256 75497 126e9 drivers/usb/host/xen-hcd.o After: ===== text data bss dec hex filename 52897 22344 256 75497 126e9 drivers/usb/host/xen-hcd.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/63241c9e857646d895ce615b998d41ee4829f9e3.1761475831.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xen-hcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c index 1c2a95fe41e5..0a94d302911a 100644 --- a/drivers/usb/host/xen-hcd.c +++ b/drivers/usb/host/xen-hcd.c @@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd) return 0; } -static struct hc_driver xenhcd_usb20_hc_driver = { +static const struct hc_driver xenhcd_usb20_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB2.0 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), @@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = { #endif }; -static struct hc_driver xenhcd_usb11_hc_driver = { +static const struct hc_driver xenhcd_usb11_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB1.1 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), From 7915d513e22db3226bfc7d5428b6dd6faa3ce181 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 26 Oct 2025 12:21:36 +0000 Subject: [PATCH 066/161] usb: dwc3: apple: Only support a single reset controller As pointed out by Philipp, Apple's dwc3 controller only uses a single reset line and there's thus no need to use reset controller array functions. The only functional change here is replacing devm_reset_control_array_get_exclusive with devm_reset_control_get_exclusive. The rest are only cosmetic changes to replace "resets" with "reset". Reported-by: Philipp Zabel Closes: https://lore.kernel.org/asahi/47112ace39ea096242e68659d67a401e931abf3a.camel@pengutronix.de/ Fixes: 0ec946d32ef7 ("usb: dwc3: Add Apple Silicon DWC3 glue layer driver") Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251026-b4-dwc3-apple-reset-array-fix-v1-1-ccdbacd63f78@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-apple.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index 6e41bd0e34f4..cc47cad232e3 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -81,7 +81,7 @@ enum dwc3_apple_state { * @dev: Pointer to the device structure * @mmio_resource: Resource to be passed to dwc3_core_probe * @apple_regs: Apple-specific DWC3 registers - * @resets: Reset control + * @reset: Reset control * @role_sw: USB role switch * @lock: Mutex for synchronizing access * @state: Current state of the controller, see documentation for the enum for details @@ -93,7 +93,7 @@ struct dwc3_apple { struct resource *mmio_resource; void __iomem *apple_regs; - struct reset_control *resets; + struct reset_control *reset; struct usb_role_switch *role_sw; struct mutex lock; @@ -237,9 +237,9 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st lockdep_assert_held(&appledwc->lock); - ret = reset_control_deassert(appledwc->resets); + ret = reset_control_deassert(appledwc->reset); if (ret) { - dev_err(appledwc->dev, "Failed to deassert resets, err=%d\n", ret); + dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret); return ret; } @@ -288,9 +288,9 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st core_exit: dwc3_core_exit(&appledwc->dwc); reset_assert: - ret_reset = reset_control_assert(appledwc->resets); + ret_reset = reset_control_assert(appledwc->reset); if (ret_reset) - dev_warn(appledwc->dev, "Failed to assert resets, err=%d\n", ret_reset); + dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset); return ret; } @@ -323,9 +323,9 @@ static int dwc3_apple_exit(struct dwc3_apple *appledwc) dwc3_core_exit(&appledwc->dwc); appledwc->state = DWC3_APPLE_NO_CABLE; - ret = reset_control_assert(appledwc->resets); + ret = reset_control_assert(appledwc->reset); if (ret) { - dev_err(appledwc->dev, "Failed to assert resets, err=%d\n", ret); + dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret); return ret; } @@ -411,14 +411,14 @@ static int dwc3_apple_probe(struct platform_device *pdev) appledwc->dev = &pdev->dev; mutex_init(&appledwc->lock); - appledwc->resets = devm_reset_control_array_get_exclusive(dev); - if (IS_ERR(appledwc->resets)) - return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->resets), - "Failed to get resets\n"); + appledwc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(appledwc->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset), + "Failed to get reset control\n"); - ret = reset_control_assert(appledwc->resets); + ret = reset_control_assert(appledwc->reset); if (ret) { - dev_err(&pdev->dev, "Failed to assert resets, err=%d\n", ret); + dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret); return ret; } From e67a353d6b6f4636f044993f9c624ea67d7df071 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 26 Oct 2025 15:45:57 -0700 Subject: [PATCH 067/161] usb: mtu3: fix misspelled words Fix spelling errors as reported by codespell. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251026224558.826143-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3.h | 4 ++-- drivers/usb/mtu3/mtu3_core.c | 2 +- drivers/usb/mtu3/mtu3_qmu.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index c11840b9a6f1..5e59f58940b5 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -76,7 +76,7 @@ struct mtu3_request; /** * Normally the device works on HS or SS, to simplify fifo management, - * devide fifo into some 512B parts, use bitmap to manage it; And + * divide fifo into some 512B parts, use bitmap to manage it; And * 128 bits size of bitmap is large enough, that means it can manage * up to 64KB fifo size. * NOTE: MTU3_EP_FIFO_UNIT should be power of two @@ -121,7 +121,7 @@ enum mtu3_g_ep0_state { }; /** - * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + * MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode * by IDPIN signal. * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * IDPIN signal. diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index a3a6282893d0..3a25ee18f144 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu) /* delay about 0.1us from detecting reset to send chirp-K */ mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); - /* enable automatical HWRW from L1 */ + /* enable automatic HWRW from L1 */ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 3d77408e3133..03f26589b056 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) return ring->dequeue; } -/* check if a ring is emtpy */ +/* check if a ring is empty */ static bool gpd_ring_empty(struct mtu3_gpd_ring *ring) { struct qmu_gpd *enq = ring->enqueue; From bf3371dfe3c4e735fd5a1e0c5506bce1bd418a56 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 26 Oct 2025 15:45:58 -0700 Subject: [PATCH 068/161] usb: mtu3: fix invalid kernel-doc in mtu3.h Fix unconventional kernel-doc lines to avoid warnings. Summary of changes: - change non-kernel-doc comments from "/**" to "/*" - add "enum - short description" for undescribed enums - use leading '@' for describing enum members and struct members - add "struct - short description" for undescribed structs Repair and remove invalid or unconventional lines in mtu3.h: (This list is a combination of warnings from the old kernel-doc.pl and the new kernel-doc.py scripts. This combo provides better coverage of all problems.) mtu3.h:69: warning: This comment starts with '/**', but isn't a kernel-doc comment. * IP TRUNK version mtu3.h:69: warning: missing initial short description on line: * IP TRUNK version mtu3.h:78: warning: This comment starts with '/**', but isn't a kernel-doc comment. * Normally the device works on HS or SS, to simplify fifo management, mtu3.h:78: warning: missing initial short description on line: * Normally the device works on HS or SS, to simplify fifo management, mtu3.h:89: warning: This comment starts with '/**', but isn't a kernel-doc comment. * Maximum size of ep0 response buffer for ch9 requests, mtu3.h:89: warning: missing initial short description on line: * Maximum size of ep0 response buffer for ch9 requests, mtu3.h:106: warning: Cannot understand * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP on line 106 - I thought it was a doc line mtu3.h:130: warning: cannot understand function prototype: 'enum mtu3_dr_force_mode' mtu3.h:137: warning: Cannot understand * @base: the base address of fifo on line 137 - I thought it was a doc line mtu3.h:148: warning: missing initial short description on line: * General Purpose Descriptor (GPD): mtu3.h:174: warning: cannot understand function prototype: 'struct qmu_gpd' mtu3.h:189: warning: cannot understand function prototype: 'struct mtu3_gpd_ring' mtu3.h:198: warning: Cannot understand * @vbus: vbus 5V used by host mode on line 198 - I thought it was a doc line mtu3.h:225: warning: Cannot understand * @mac_base: register base address of device MAC, exclude xHCI's on line 225 - I thought it was a doc line mtu3.h:275: warning: cannot understand function prototype: 'struct mtu3_ep' Warning: drivers/usb/mtu3/mtu3.h:135 Enum value 'MTU3_DR_FORCE_NONE' not described in enum 'mtu3_dr_force_mode' Warning: drivers/usb/mtu3/mtu3.h:135 Enum value 'MTU3_DR_FORCE_HOST' not described in enum 'mtu3_dr_force_mode' Warning: drivers/usb/mtu3/mtu3.h:135 Enum value 'MTU3_DR_FORCE_DEVICE' not described in enum 'mtu3_dr_force_mode' Warning: drivers/usb/mtu3/mtu3.h:270 Cannot find identifier on line: * @fifo_size: it is (@slot + 1) * @fifo_seg_size Warning: drivers/usb/mtu3/mtu3.h:271 Cannot find identifier on line: * @fifo_seg_size: it is roundup_pow_of_two(@maxp) Warning: drivers/usb/mtu3/mtu3.h:272 Cannot find identifier on line: */ Warning: drivers/usb/mtu3/mtu3.h:273 Cannot find identifier on line: struct mtu3_ep { Warning: drivers/usb/mtu3/mtu3.h:274 Cannot find identifier on line: struct usb_ep ep; Warning: drivers/usb/mtu3/mtu3.h:275 Cannot find identifier on line: char name[12]; Warning: drivers/usb/mtu3/mtu3.h:276 missing initial short description on line: struct mtu3 *mtu; This removes all of the invalid/unconventional kernel-doc attempts but still leaves quite a few struct members in structs ssusb_mtk, mtu3_ep, and mtu3 without kernel-doc descriptions. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251026224558.826143-2-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3.h | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 5e59f58940b5..ba5a63669e5f 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -65,7 +65,7 @@ struct mtu3_request; #define MTU3_U3_IP_SLOT_DEFAULT 2 #define MTU3_U2_IP_SLOT_DEFAULT 1 -/** +/* * IP TRUNK version * from 0x1003 version, USB3 Gen2 is supported, two changes affect driver: * 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted, @@ -74,7 +74,7 @@ struct mtu3_request; */ #define MTU3_TRUNK_VERS_1003 0x1003 -/** +/* * Normally the device works on HS or SS, to simplify fifo management, * divide fifo into some 512B parts, use bitmap to manage it; And * 128 bits size of bitmap is large enough, that means it can manage @@ -85,7 +85,7 @@ struct mtu3_request; #define MTU3_FIFO_BIT_SIZE 128 #define MTU3_U2_IP_EP0_FIFO_SIZE 64 -/** +/* * Maximum size of ep0 response buffer for ch9 requests, * the SET_SEL request uses 6 so far, and GET_STATUS is 2 */ @@ -103,6 +103,7 @@ enum mtu3_speed { }; /** + * enum mtu3_g_ep0_state - endpoint 0 states * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP * without data stage. * @MU3D_EP0_STATE_TX: IN data stage @@ -121,11 +122,12 @@ enum mtu3_g_ep0_state { }; /** - * MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode + * enum mtu3_dr_force_mode - indicates host/OTG operating mode + * @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode * by IDPIN signal. - * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * IDPIN signal. - * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. */ enum mtu3_dr_force_mode { MTU3_DR_FORCE_NONE = 0, @@ -134,6 +136,7 @@ enum mtu3_dr_force_mode { }; /** + * struct mtu3_fifo_info - HW FIFO description and management data * @base: the base address of fifo * @limit: the bitmap size in bits * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -145,7 +148,7 @@ struct mtu3_fifo_info { }; /** - * General Purpose Descriptor (GPD): + * struct qmu_gpd - General Purpose Descriptor (GPD): * The format of TX GPD is a little different from RX one. * And the size of GPD is 16 bytes. * @@ -179,11 +182,13 @@ struct qmu_gpd { } __packed; /** -* dma: physical base address of GPD segment -* start: virtual base address of GPD segment -* end: the last GPD element -* enqueue: the first empty GPD to use -* dequeue: the first completed GPD serviced by ISR +* struct mtu3_gpd_ring - GPD ring descriptor +* @dma: physical base address of GPD segment +* @start: virtual base address of GPD segment +* @end: the last GPD element +* @enqueue: the first empty GPD to use +* @dequeue: the first completed GPD serviced by ISR +* * NOTE: the size of GPD ring should be >= 2 */ struct mtu3_gpd_ring { @@ -195,6 +200,7 @@ struct mtu3_gpd_ring { }; /** +* struct otg_switch_mtk - OTG/dual-role switch management * @vbus: vbus 5V used by host mode * @edev: external connector used to detect vbus and iddig changes * @id_nb : notifier for iddig(idpin) detection @@ -222,6 +228,7 @@ struct otg_switch_mtk { }; /** + * struct ssusb_mtk - SuperSpeed USB descriptor (MTK) * @mac_base: register base address of device MAC, exclude xHCI's * @ippc_base: register base address of IP Power and Clock interface (IPPC) * @vusb33: usb3.3V shared by device/host IP @@ -268,6 +275,7 @@ struct ssusb_mtk { }; /** + * struct mtu3_ep - common mtu3 endpoint description * @fifo_size: it is (@slot + 1) * @fifo_seg_size * @fifo_seg_size: it is roundup_pow_of_two(@maxp) */ From 14934421dc8be364e2a7506dccf1222c45ecca24 Mon Sep 17 00:00:00 2001 From: Venkat Jayaraman Date: Sun, 26 Oct 2025 16:58:30 -0700 Subject: [PATCH 069/161] usb: typec: ucsi: Add SET_POWER_LEVEL UCSI command to debugfs Add SET_POWER_LEVEL to list of commands supported by UCSI debugfs. Signed-off-by: Venkat Jayaraman Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251026235830.936477-1-venkat.jayaraman@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 1 + drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index f73f2b54554e..f3684ab787fe 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -35,6 +35,7 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_SINK_PATH: case UCSI_SET_NEW_CAM: case UCSI_SET_USB: + case UCSI_SET_POWER_LEVEL: case UCSI_READ_POWER_LEVEL: ret = ucsi_send_command(ucsi, val, NULL, 0); break; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 35993bc34d4d..700d353f9bb1 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -127,6 +127,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_CONNECTOR_STATUS 0x12 #define UCSI_GET_CONNECTOR_STATUS_SIZE 152 #define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_SET_POWER_LEVEL 0x14 #define UCSI_GET_ATTENTION_VDO 0x16 #define UCSI_GET_PD_MESSAGE 0x15 #define UCSI_GET_CAM_CS 0x18 From 18514fd70ea4ca9de137bb3bceeac1bac4bcad75 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 28 Oct 2025 14:52:12 +0200 Subject: [PATCH 070/161] usb: typec: ucsi: Add support for orientation According to UCSI 2.0 specification, the orientation is part of the connector status payload. So tie up the port orientation. Reviewed-by: Heikki Krogerus Signed-off-by: Abel Vesa Link: https://patch.msgid.link/20251028-usb-typec-ucsi-orientation-v2-1-9330478bb6c1@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 24 ++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ed23edab7763..ec6c8f928dda 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1008,6 +1008,28 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } +static void ucsi_orientation(struct ucsi_connector *con) +{ + if (con->ucsi->version < UCSI_VERSION_2_0) + return; + + if (!UCSI_CONSTAT(con, CONNECTED)) { + typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE); + return; + } + + switch (UCSI_CONSTAT(con, ORIENTATION)) { + case UCSI_CONSTAT_ORIENTATION_NORMAL: + typec_set_orientation(con->port, TYPEC_ORIENTATION_NORMAL); + break; + case UCSI_CONSTAT_ORIENTATION_REVERSE: + typec_set_orientation(con->port, TYPEC_ORIENTATION_REVERSE); + break; + default: + break; + } +} + static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { switch (UCSI_CONSTAT(con, PWR_OPMODE)) { @@ -1261,6 +1283,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); + ucsi_orientation(con); if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); @@ -1693,6 +1716,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) 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); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 700d353f9bb1..410389ef173a 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -361,6 +361,9 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 #define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) +#define UCSI_CONSTAT_ORIENTATION UCSI_DECLARE_BITFIELD_V2_0(86, 1) +#define UCSI_CONSTAT_ORIENTATION_NORMAL 0 +#define UCSI_CONSTAT_ORIENTATION_REVERSE 1 #define UCSI_CONSTAT_SINK_PATH_STATUS_V2_0 UCSI_DECLARE_BITFIELD_V2_0(87, 1) #define UCSI_CONSTAT_SINK_PATH_DISABLED 0 #define UCSI_CONSTAT_SINK_PATH_ENABLED 1 From d776e805f197cb055c759d190bcc249072244d1c Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Wed, 27 Aug 2025 13:56:47 +0200 Subject: [PATCH 071/161] thunderbolt: Update NVM firmware upgrade documentation Update guide about firmware upgrade of Thunderbolt devices, replacing outdated recommendations with the use of modern "fwupd" tool. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- Documentation/admin-guide/thunderbolt.rst | 50 ++++++++++++++++------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 102c693c8f81..07303c1346fb 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -203,10 +203,10 @@ host controller or a device, it is important that the firmware can be upgraded to the latest where possible bugs in it have been fixed. Typically OEMs provide this firmware from their support site. -There is also a central site which has links where to download firmware -for some machines: - - `Thunderbolt Updates `_ +Currently, recommended method of updating firmware is through "fwupd" tool. +It uses LVFS (Linux Vendor Firmware Service) portal by default to get the +latest firmware from hardware vendors and updates connected devices if found +compatible. For details refer to: https://github.com/fwupd/fwupd. Before you upgrade firmware on a device, host or retimer, please make sure it is a suitable upgrade. Failing to do that may render the device @@ -215,18 +215,40 @@ tools! Host NVM upgrade on Apple Macs is not supported. -Once the NVM image has been downloaded, you need to plug in a -Thunderbolt device so that the host controller appears. It does not -matter which device is connected (unless you are upgrading NVM on a -device - then you need to connect that particular device). +Fwupd is installed by default. If you don't have it on your system, simply +use your distro package manager to get it. + +To see possible updates through fwupd, you need to plug in a Thunderbolt +device so that the host controller appears. It does not matter which +device is connected (unless you are upgrading NVM on a device - then you +need to connect that particular device). Note an OEM-specific method to power the controller up ("force power") may be available for your system in which case there is no need to plug in a Thunderbolt device. -After that we can write the firmware to the non-active parts of the NVM -of the host or device. As an example here is how Intel NUC6i7KYK (Skull -Canyon) Thunderbolt controller NVM is upgraded:: +Updating firmware using fwupd is straightforward - refer to official +readme on fwupd github. + +If firmware image is written successfully, the device shortly disappears. +Once it comes back, the driver notices it and initiates a full power +cycle. After a while device appears again and this time it should be +fully functional. + +Device of interest should display new version under "Current version" +and "Update State: Success" in fwupd's interface. + +Upgrading firmware manually +--------------------------------------------------------------- +If possible, use fwupd to updated the firmware. However, if your device OEM +has not uploaded the firmware to LVFS, but it is available for download +from their side, you can use method below to directly upgrade the +firmware. + +Manual firmware update can be done with 'dd' tool. To update firmware +using this method, you need to write it to the non-active parts of NVM +of the host or device. Example on how to update Intel NUC6i7KYK +(Skull Canyon) Thunderbolt controller NVM:: # dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem @@ -235,10 +257,8 @@ upgrade process as follows:: # echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate -If no errors are returned, the host controller shortly disappears. Once -it comes back the driver notices it and initiates a full power cycle. -After a while the host controller appears again and this time it should -be fully functional. +If no errors are returned, device should behave as described in previous +section. We can verify that the new NVM firmware is active by running the following commands:: From 8ab64d711b93306b2b0f1bd50a85816ad3b05dab Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Mon, 8 Sep 2025 14:41:50 +0200 Subject: [PATCH 072/161] thunderbolt: Update deprecated firmware update site in icm.c Update leftover reference to Thunderbolt firmware upgrade site (thunderbolttechnology.net) with the preferred method described in Documentation/admin-guide/thunderbolt.rst. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index f213d9174dc5..66092febb40d 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2000,7 +2000,7 @@ static int icm_driver_ready(struct tb *tb) if (icm->safe_mode) { tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); - tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); + tb_info(tb, "Use fwupd tool to apply update. Check Documentation/admin-guide/thunderbolt.rst for details.\n"); return 0; } From 9393a3a4207f35a2cd1ff5994ed17e8fbd9ce787 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Wed, 5 Nov 2025 17:27:36 +0100 Subject: [PATCH 073/161] thunderbolt: Replace use of system_wq with system_percpu_wq Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. This patch continues the effort to refactor worqueue APIs, which has begun with the change introducing new workqueues and a new alloc_workqueue flag: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") Replace system_wq with system_percpu_wq, keeping the old behavior. The old wq (system_wq) will be kept for a few release cycles. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 4a94cb406bdf..7a3f76a852de 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -2636,7 +2636,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, * the 10s already expired and we should * give the reserved back to others). */ - mod_delayed_work(system_wq, &group->release_work, + mod_delayed_work(system_percpu_wq, &group->release_work, msecs_to_jiffies(TB_RELEASE_BW_TIMEOUT)); } } From d0d7c4062d321bd8cf187db6d9b0d4eb8b0650ef Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:34 +0200 Subject: [PATCH 074/161] thunderbolt: Fix typos in ctl.c Fix typos in ctl.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index f92175ee3841..d7a535671404 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -412,7 +412,7 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg) * We ignore failures during stop. * All rx packets are referenced * from ctl->rx_packets, so we do - * not loose them. + * not lose them. */ } From b719f112d250d56b5b019c8414393d7e068efbb6 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:35 +0200 Subject: [PATCH 075/161] thunderbolt: Fix typos in debugfs.c Fix typos in debugfs.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 46a2a3550be7..45266ec72f88 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -201,7 +201,7 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len, #if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE) /* * Path registers need to be written in double word pairs and they both must be - * read before written. This writes one double word in patch config space + * read before written. This writes one double word in path config space * following the spec flow. */ static int path_write_one(struct tb_port *port, u32 val, u32 offset) @@ -1196,7 +1196,7 @@ static int validate_margining(struct tb_margining *margining) { /* * For running on RX2 the link must be asymmetric with 3 - * receivers. Because this is can change dynamically, check it + * receivers. Because this can change dynamically, check it * here before we start the margining and report back error if * expectations are not met. */ From 4994e9a711a9de736c0d898e86e5b10b860d7e4f Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:36 +0200 Subject: [PATCH 076/161] thunderbolt: Fix typos in domain.c Fix typos in domain.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 83defc915d33..3ced37b4a869 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -376,7 +376,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize struct tb *tb; /* - * Make sure the structure sizes map with that the hardware + * Make sure the structure sizes map with what the hardware * expects because bit-fields are being used. */ BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4); From 91c7b372a31751a2cc79cc13fe67b7942b4156ef Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:38 +0200 Subject: [PATCH 077/161] thunderbolt: Fix typos in icm.c Fix typos in icm.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 66092febb40d..d339ba835376 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -787,7 +787,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) * information might have changed for example by the * fact that a switch on a dual-link connection might * have been enumerated using the other link now. Make - * sure our book keeping matches that. + * sure our bookkeeping matches that. */ if (sw->depth == depth && sw_phy_port == phy_port && !!sw->authorized == authorized) { @@ -969,7 +969,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) /* * Look if there already exists an XDomain in the same place - * than the new one and in that case remove it because it is + * as the new one and in that case remove it because it is * most likely another host that got disconnected. */ xd = tb_xdomain_find_by_link_depth(tb, link, depth); @@ -2171,7 +2171,7 @@ static int icm_runtime_resume_switch(struct tb_switch *sw) static int icm_runtime_resume(struct tb *tb) { /* - * We can reuse the same resume functionality than with system + * We can reuse the same resume functionality as with system * suspend. */ icm_complete(tb); From 0370b14fd55cb9590d76cdea20a6ce0e45d6d3c3 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:39 +0200 Subject: [PATCH 078/161] thunderbolt: Fix typos in lc.c Fix typos in lc.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/lc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index 0891d51ac2e9..4449c28cc5f1 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -558,7 +558,7 @@ static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink) return ret; /* - * Sink is available for CM/SW to use if the allocation valie is + * Sink is available for CM/SW to use if the allocation value is * either 0 or 1. */ if (!sink) { From 1c9ad530d36c2890dabb66c81b77e067f2c6100b Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:40 +0200 Subject: [PATCH 079/161] thunderbolt: Fix typos in nhi.c Fix typos in nhi.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 5f63f9b9cf40..ac3b83a05dbc 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -712,7 +712,7 @@ void tb_ring_start(struct tb_ring *ring) ring_iowrite64desc(ring, ring->descriptors_dma, 0); if (ring->is_tx) { ring_iowrite32desc(ring, ring->size, 12); - ring_iowrite32options(ring, 0, 4); /* time releated ? */ + ring_iowrite32options(ring, 0, 4); ring_iowrite32options(ring, flags, 0); } else { u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask; From 6c1e5744e68935047bfe3bb1dc5d41619eb4feda Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:41 +0200 Subject: [PATCH 080/161] thunderbolt: Fix typos in retimer.c Fix typos in retimer.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 3a0f486a24d5..13d64dbd2bc5 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -501,7 +501,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) * @add: If true also registers found retimers * * Brings the sideband into a state where retimers can be accessed. - * Then Tries to enumerate on-board retimers connected to @port. Found + * Then tries to enumerate on-board retimers connected to @port. Found * retimers are registered as children of @port if @add is set. Does * not scan for cable retimers for now. * From cafe5dd8bba426b92f233678f166b2295679a4d4 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:42 +0200 Subject: [PATCH 081/161] thunderbolt: Fix typos in switch.c Fix typos in switch.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 0e07904aa73b..b3948aad0b95 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -736,9 +736,9 @@ static int tb_init_port(struct tb_port *port) port->cap_usb4 = cap; /* - * USB4 ports the buffers allocated for the control path + * USB4 port buffers allocated for the control path * can be read from the path config space. Legacy - * devices we use hard-coded value. + * devices use hard-coded value. */ if (port->cap_usb4) { struct tb_regs_hop hop; @@ -3221,7 +3221,7 @@ int tb_switch_configure_link(struct tb_switch *sw) * @sw: Switch whose link is unconfigured * * Sets the link unconfigured so the @sw will be disconnected if the - * domain exists sleep. + * domain exits sleep. */ void tb_switch_unconfigure_link(struct tb_switch *sw) { From 6cdbf50107cc4697eb65152b3832c0fce12a79e6 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:43 +0200 Subject: [PATCH 082/161] thunderbolt: Fix typos in tb.c Fix typos in tb.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 7a3f76a852de..4f5f1dfc0fbf 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -322,7 +322,7 @@ static int tb_enable_tmu(struct tb_switch *sw) /* * If both routers at the end of the link are v2 we simply - * enable the enhanched uni-directional mode. That covers all + * enable the enhanced uni-directional mode. That covers all * the CL states. For v1 and before we need to use the normal * rate to allow CL1 (when supported). Otherwise we keep the TMU * running at the highest accuracy. @@ -538,7 +538,7 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb, * @src_port: Source protocol adapter * @dst_port: Destination protocol adapter * @port: USB4 port the consumed bandwidth is calculated - * @consumed_up: Consumed upsream bandwidth (Mb/s) + * @consumed_up: Consumed upstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed USB3 and PCIe bandwidth at @port between path @@ -589,7 +589,7 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, * @src_port: Source protocol adapter * @dst_port: Destination protocol adapter * @port: USB4 port the consumed bandwidth is calculated - * @consumed_up: Consumed upsream bandwidth (Mb/s) + * @consumed_up: Consumed upstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed DP bandwidth at @port between path from @src_port @@ -1115,7 +1115,7 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, /* * Here requested + consumed > threshold so we need to - * transtion the link into asymmetric now. + * transition the link into asymmetric now. */ ret = tb_switch_set_link_width(up->sw, width_up); if (ret) { @@ -1936,7 +1936,7 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) */ tb_recalc_estimated_bandwidth(tb); /* - * In case of DP tunnel exists, change host + * In case DP tunnel exists, change host * router's 1st children TMU mode to HiFi for * CL0s to work. */ @@ -2786,8 +2786,8 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work) * There is no request active so this means the * BW allocation mode was enabled from graphics * side. At this point we know that the graphics - * driver has read the DRPX capabilities so we - * can offer an better bandwidth estimatation. + * driver has read the DPRX capabilities so we + * can offer better bandwidth estimation. */ tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n"); tb_recalc_estimated_bandwidth(tb); From 20b2af31286b265a2b8cc4bfa237bae3658e0127 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:44 +0200 Subject: [PATCH 083/161] thunderbolt: Fix typos in tb.h Fix typos in tb.h. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8e2762ff8d51..e96474f17067 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -308,7 +308,7 @@ struct tb_port { * struct usb4_port - USB4 port device * @dev: Device for the port * @port: Pointer to the lane 0 adapter - * @can_offline: Does the port have necessary platform support to moved + * @can_offline: Does the port have necessary platform support to move * it into offline mode and back * @offline: The port is currently in offline mode * @margining: Pointer to margining structure if enabled @@ -355,7 +355,7 @@ struct tb_retimer { * struct tb_path_hop - routing information for a tb_path * @in_port: Ingress port of a switch * @out_port: Egress port of a switch where the packet is routed out - * (must be on the same switch than @in_port) + * (must be on the same switch as @in_port) * @in_hop_index: HopID where the path configuration entry is placed in * the path config space of @in_port. * @in_counter_index: Used counter index (not used in the driver @@ -499,9 +499,9 @@ struct tb_path { * performed. If this returns %-EOPNOTSUPP then the * native USB4 router operation is called. * @usb4_switch_nvm_authenticate_status: Optional callback that the CM - * implementation can be used to - * return status of USB4 NVM_AUTH - * router operation. + * implementation can use to return + * status of USB4 NVM_AUTH router + * operation. */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -1109,7 +1109,7 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, struct tb_port *prev); /** - * tb_port_path_direction_downstream() - Checks if path directed downstream + * tb_port_path_direction_downstream() - Checks if path is directed downstream * @src: Source adapter * @dst: Destination adapter * @@ -1141,7 +1141,7 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port) (p) = tb_next_port_on_path((src), (dst), (p))) /** - * tb_for_each_upstream_port_on_path() - Iterate over each upstreamm port on path + * tb_for_each_upstream_port_on_path() - Iterate over each upstream port on path * @src: Source port * @dst: Destination port * @p: Port used as iterator From 836fe732db6068e6d3909c32bfebcf7572a2bbc6 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:45 +0200 Subject: [PATCH 084/161] thunderbolt: Fix typos in tb_regs.h Fix typos in tb_regs.h. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb_regs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 4e43b47f9f11..c0bf136236e6 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -99,7 +99,7 @@ struct tb_cap_extended_long { } __packed; /** - * struct tb_cap_any - Structure capable of hold every capability + * struct tb_cap_any - Structure capable of holding every capability * @basic: Basic capability * @extended_short: Vendor specific capability * @extended_long: Vendor specific extended capability @@ -534,8 +534,8 @@ struct tb_regs_hop { /* * Used for Titan Ridge only. Bits are part of the same register: TMU_ADP_CS_6 - * (see above) as in USB4 spec, but these specific bits used for Titan Ridge - * only and reserved in USB4 spec. + * (see above) as in USB4 spec, but these specific bits are used for Titan Ridge + * only and are reserved in USB4 spec. */ #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK GENMASK(3, 2) #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 BIT(2) From 5d463af9818cd4d6206abac59ba32bcd42bf5d7c Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:46 +0200 Subject: [PATCH 085/161] thunderbolt: Fix typos in tmu.c Fix typos in tmu.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index b22831b41ec0..cf779874c675 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -400,10 +400,10 @@ static int tmu_mode_init(struct tb_switch *sw) /** * tb_switch_tmu_init() - Initialize switch TMU structures - * @sw: Switch to initialized + * @sw: Switch to be initialized * * This function must be called before other TMU related functions to - * makes the internal structures are filled in correctly. Does not + * make sure the internal structures are filled in correctly. Does not * change any hardware configuration. * * Return: %0 on success, negative errno otherwise. From c3d53000d2a59c0922615aa10e97161a28a094f2 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:47 +0200 Subject: [PATCH 086/161] thunderbolt: Fix typos in tunnel.c Fix typos in tunnel.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index bfa0607b5574..9fa95c595ecc 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -301,7 +301,7 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); int ret; - /* Only supported of both routers are at least USB4 v2 */ + /* Only supported if both routers are at least USB4 v2 */ if ((usb4_switch_version(tunnel->src_port->sw) < 2) || (usb4_switch_version(tunnel->dst_port->sw) < 2)) return 0; @@ -1170,8 +1170,8 @@ static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel, /* * 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. + * read parameter values so we can use this to determine the + * maximum possible bandwidth over this link. * * See USB4 v2 spec 1.0 10.4.4.5. */ @@ -1783,8 +1783,8 @@ static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits) /* * First lane adapter is the one connected to the remote host. - * We don't tunnel other traffic over this link so can use all - * the credits (except the ones reserved for control traffic). + * We don't tunnel other traffic over this link so we can use + * all the credits (except the ones reserved for control traffic). */ hop = &path->hops[0]; tmp = min(tb_usable_credits(hop->in_port), credits); @@ -2044,7 +2044,7 @@ static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel, /* * PCIe tunneling, if enabled, affects the USB3 bandwidth so - * take that it into account here. + * take that into account here. */ *consumed_up = tunnel->allocated_up * (TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT; @@ -2605,7 +2605,7 @@ int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, * @tunnel: Tunnel whose unused bandwidth to release * * If tunnel supports dynamic bandwidth management (USB3 tunnels at the - * moment) this function makes it to release all the unused bandwidth. + * moment) this function makes it release all the unused bandwidth. * * Return: %0 on success, negative errno otherwise. */ From 9527d0c5436c02905cb1befe74ff764f6b4ea5b8 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:48 +0200 Subject: [PATCH 087/161] thunderbolt: Fix typos in usb4.c Fix typos in usb4.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/usb4.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 76f01713a875..9e810b2ae0b5 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -284,7 +284,7 @@ int usb4_switch_setup(struct tb_switch *sw) val |= ROUTER_CS_5_PTO; /* * xHCI can be enabled if PCIe tunneling is supported - * and the parent does not have any USB3 dowstream + * and the parent does not have any USB3 downstream * adapters (so we cannot do USB 3.x tunneling). */ if (xhci) @@ -1342,7 +1342,7 @@ static int usb4_port_write_data(struct tb_port *port, const void *data, * usb4_port_sb_read() - Read from sideband register * @port: USB4 port to read * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @reg: Sideband register index * @buf: Buffer where the sideband data is copied * @size: Size of @buf @@ -1395,7 +1395,7 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index * usb4_port_sb_write() - Write to sideband register * @port: USB4 port to write * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @reg: Sideband register index * @buf: Data to write * @size: Size of @buf @@ -1527,7 +1527,7 @@ int usb4_port_router_offline(struct tb_port *port) } /** - * usb4_port_router_online() - Put the USB4 port back to online + * usb4_port_router_online() - Put the USB4 port back online * @port: USB4 port * * Makes the USB4 port functional again. @@ -1692,10 +1692,10 @@ int usb4_port_asym_start(struct tb_port *port) } /** - * usb4_port_margining_caps() - Read USB4 port marginig capabilities + * usb4_port_margining_caps() - Read USB4 port margining capabilities * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @caps: Array with at least two elements to hold the results * @ncaps: Number of elements in the caps array * @@ -1721,7 +1721,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, * usb4_port_hw_margin() - Run hardware lane margining on port * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @params: Parameters for USB4 hardware margining * @results: Array to hold the results * @nresults: Number of elements in the results array @@ -1769,7 +1769,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * usb4_port_sw_margin() - Run software lane margining on port * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @params: Parameters for USB4 software margining * @results: Data word for the operation completion data * @@ -1819,7 +1819,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, * usb4_port_sw_margin_errors() - Read the software margining error counters * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @errors: Error metadata is copied here. * * This reads back the software margining error counters from the port. @@ -1853,7 +1853,7 @@ static inline int usb4_port_retimer_op(struct tb_port *port, u8 index, * @port: USB4 port * @index: Retimer index * - * Enables sideband channel transations on SBTX. Can be used when USB4 + * Enables sideband channel transactions on SBTX. Can be used when USB4 * link does not go up, for example if there is no device connected. * * Return: %0 on success, negative errno otherwise. @@ -1882,7 +1882,7 @@ int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index) * @port: USB4 port * @index: Retimer index * - * Disables sideband channel transations on SBTX. The reverse of + * Disables sideband channel transactions on SBTX. The reverse of * usb4_port_retimer_set_inbound_sbtx(). * * Return: %0 on success, negative errno otherwise. @@ -1981,7 +1981,7 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index) * @index: Retimer index * @address: Start offset * - * Exlicitly sets NVM write offset. Normally when writing to NVM this is + * Explicitly sets NVM write offset. Normally when writing to NVM this is * done automatically by usb4_port_retimer_nvm_write(). * * Return: %0 on success, negative errno otherwise. @@ -2190,7 +2190,7 @@ usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw) } /** - * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate + * usb4_usb3_port_max_link_rate() - Maximum supported USB3 link rate * @port: USB3 adapter port * * Return: Maximum supported link rate of a USB3 adapter in Mb/s. From 479d186fc946b16c440f57995d5cb2151bfe61c0 Mon Sep 17 00:00:00 2001 From: Alan Borzeszkowski Date: Tue, 2 Sep 2025 16:38:49 +0200 Subject: [PATCH 088/161] thunderbolt: Fix typos in xdomain.c Fix typos in xdomain.c. No functional changes. Signed-off-by: Alan Borzeszkowski Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 9d220ba544ec..63c7be818b2c 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1951,8 +1951,8 @@ static void tb_xdomain_link_exit(struct tb_xdomain *xd) /** * tb_xdomain_alloc() - Allocate new XDomain object * @tb: Domain where the XDomain belongs - * @parent: Parent device (the switch through the connection to the - * other domain is reached). + * @parent: Parent device (the switch through which the other domain + * is reached). * @route: Route string used to reach the other domain * @local_uuid: Our local domain UUID * @remote_uuid: UUID of the other domain (optional) From 4e31a5d0a9ee672f708fc993c1d5520643f769fd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:05 +0100 Subject: [PATCH 089/161] USB: serial: ftdi_sio: match on interface number for jtag Some FTDI devices have the first port reserved for JTAG and have been using a dedicated quirk to prevent binding to it. As can be inferred directly or indirectly from the commit messages, almost all of these devices are dual port devices which means that the more recently added macro for matching on interface number can be used instead (and some such devices do so already). This avoids probing interfaces that will never be bound and cleans up the match table somewhat. Note that the JTAG quirk is kept for quad port devices, which would otherwise require three match entries. Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 72 ++++++++++++----------------------- 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 49666c33b41f..36c8830281b9 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -628,10 +628,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TIAO_UMPA_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_NT_ORIONLXM_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLX_PLUS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORION_IO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONMX_PID) }, @@ -842,24 +840,17 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, - { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, CYBER_CORTEX_AV_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) }, - { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FIC_VID, FIC_NEO1973_DEBUG_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_OOCDLINK_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) }, { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, @@ -901,17 +892,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ATMEL_VID, STK541_PID) }, { USB_DEVICE(DE_VID, STB_PID) }, { USB_DEVICE(DE_VID, WHT_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICEPLUS_PID, 1) }, { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0x00) }, { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, - { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(MARVELL_VID, MARVELL_SHEEVAPLUG_PID, 1) }, { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, { USB_DEVICE(FTDI_VID, PI_C865_PID) }, @@ -934,10 +922,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(PI_VID, PI_1016_PID) }, { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, - { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, MARVELL_OPENRD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, TI_XDS100V2_PID, 1) }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, @@ -946,18 +932,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_ST_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID, 1) }, { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, - { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(IONICS_VID, IONICS_PLUGCOMPUTER_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, @@ -972,15 +954,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, - { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ST_VID, ST_STMCLT_2232_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ST_VID, ST_STMCLT_2232_PID, 1) }, { USB_DEVICE(ST_VID, ST_STMCLT_4232_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, - { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, /* Crucible Devices */ { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, @@ -1055,8 +1034,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) }, { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) }, { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) }, - { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(TI_VID, TI_CC3200_LAUNCHPAD_PID, 1) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) }, { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) }, @@ -1075,10 +1053,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ZED_PID) }, { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) }, /* FreeCalypso USB adapters */ - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID, 1) }, /* GMC devices */ { USB_DEVICE(GMC_VID, GMC_Z216C_PID) }, /* Altera USB Blaster 3 */ From 448016e3265410ee61006da1ffa3478854baba4b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:06 +0100 Subject: [PATCH 090/161] USB: serial: ftdi_sio: silence jtag probe Probe of a device should generally be silent unless errors are encountered. Stop logging that the JTAG port is ignored when probing devices with such a reserved port. This also maintains consistency with devices that match on interface number to avoid binding to reserved ports. Note that the message is not even correct for the second port of the ST Micro Connect Lite. Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 36c8830281b9..05b3c558d1e2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2294,10 +2294,8 @@ static int ftdi_jtag_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum == 0) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum == 0) return -ENODEV; - } return 0; } @@ -2330,10 +2328,8 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum < 2) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum < 2) return -ENODEV; - } return 0; } From 73de1ddaf4e6f851eb3f67751c3aadb62229094b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:07 +0100 Subject: [PATCH 091/161] USB: serial: ftdi_sio: rewrite 8u2232c quirk Rewrite the 8u2232c quirk to avoid the manufacturer and product string comparisons for the second port which will always be bound. Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 05b3c558d1e2..3f224e3c3322 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2302,16 +2302,21 @@ static int ftdi_jtag_probe(struct usb_serial *serial) static int ftdi_8u2232c_probe(struct usb_serial *serial) { + struct usb_interface *intf = serial->interface; struct usb_device *udev = serial->dev; + int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) - return ftdi_jtag_probe(serial); + if (ifnum == 0) { + if (udev->manufacturer && + !strcmp(udev->manufacturer, "CALAO Systems")) + return -ENODEV; - if (udev->product && - (!strcmp(udev->product, "Arrow USB Blaster") || - !strcmp(udev->product, "BeagleBone/XDS100V2") || - !strcmp(udev->product, "SNAP Connect E10"))) - return ftdi_jtag_probe(serial); + if (udev->product && + (!strcmp(udev->product, "Arrow USB Blaster") || + !strcmp(udev->product, "BeagleBone/XDS100V2") || + !strcmp(udev->product, "SNAP Connect E10"))) + return -ENODEV; + } return 0; } From 47ed918ececc7cad21268ae32bf69be80014e04f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:08 +0100 Subject: [PATCH 092/161] USB: serial: ftdi_sio: clean up quirk comments Clean up the quirk function comments that were using odd formatting and were referring to a non-existing function. Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3f224e3c3322..622452763456 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2230,9 +2230,10 @@ err_free: return result; } -/* Setup for the USB-UIRT device, which requires hardwired - * baudrate (38400 gets mapped to 312500) */ -/* Called from usbserial:serial_probe */ +/* + * Setup for the USB-UIRT device, which requires hardwired baudrate + * (38400 gets mapped to 312500). + */ static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; @@ -2240,9 +2241,10 @@ static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) priv->force_baud = 38400; } -/* Setup for the HE-TIRA1 device, which requires hardwired - * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ - +/* + * Setup for the HE-TIRA1 device, which requires hardwired baudrate + * (38400 gets mapped to 100000) and RTS-CTS enabled. + */ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; @@ -2258,10 +2260,9 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) */ static int ndi_latency_timer = 1; -/* Setup for the NDI FTDI-based USB devices, which requires hardwired +/* + * Setup for the NDI FTDI-based USB devices, which requires hardwired * baudrate (19200 gets mapped to 1200000). - * - * Called from usbserial:serial_probe. */ static int ftdi_NDI_device_setup(struct usb_serial *serial) { From f5fef0c5f641435dbd69b9b1f1795ceec67cfe06 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:09 +0100 Subject: [PATCH 093/161] USB: serial: ftdi_sio: rename quirk symbols Use lower case names for the quirk symbols and rename the NDI quirk probe function for consistency. Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 622452763456..7e6e7b7c21ea 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -107,26 +107,26 @@ struct ftdi_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_NDI_device_setup(struct usb_serial *serial); +static int ftdi_ndi_probe(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); +static void ftdi_usb_uirt_setup(struct ftdi_private *priv); +static void ftdi_he_tira1_setup(struct ftdi_private *priv); static const struct ftdi_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static const struct ftdi_quirk ftdi_NDI_device_quirk = { - .probe = ftdi_NDI_device_setup, +static const struct ftdi_quirk ftdi_ndi_quirk = { + .probe = ftdi_ndi_probe, }; -static const struct ftdi_quirk ftdi_USB_UIRT_quirk = { - .port_probe = ftdi_USB_UIRT_setup, +static const struct ftdi_quirk ftdi_usb_uirt_quirk = { + .port_probe = ftdi_usb_uirt_setup, }; -static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = { - .port_probe = ftdi_HE_TIRA1_setup, +static const struct ftdi_quirk ftdi_he_tira1_quirk = { + .port_probe = ftdi_he_tira1_setup, }; static const struct ftdi_quirk ftdi_stmclite_quirk = { @@ -590,9 +590,9 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(OCT_VID, OCT_US101_PID) }, { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID), - .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_he_tira1_quirk }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID), - .driver_info = (kernel_ulong_t)&ftdi_USB_UIRT_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_usb_uirt_quirk }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, @@ -792,17 +792,17 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_NDI_VID, FTDI_NDI_EMGUIDE_GEMINI_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) }, @@ -2234,7 +2234,7 @@ err_free: * Setup for the USB-UIRT device, which requires hardwired baudrate * (38400 gets mapped to 312500). */ -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) +static void ftdi_usb_uirt_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; @@ -2245,7 +2245,7 @@ static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) * Setup for the HE-TIRA1 device, which requires hardwired baudrate * (38400 gets mapped to 100000) and RTS-CTS enabled. */ -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) +static void ftdi_he_tira1_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 240; @@ -2264,7 +2264,7 @@ static int ndi_latency_timer = 1; * Setup for the NDI FTDI-based USB devices, which requires hardwired * baudrate (19200 gets mapped to 1200000). */ -static int ftdi_NDI_device_setup(struct usb_serial *serial) +static int ftdi_ndi_probe(struct usb_serial *serial) { struct usb_device *udev = serial->dev; int latency = ndi_latency_timer; From 96e5d1b1e69097cb89f8002770cccf464c0dfa1c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:10 +0100 Subject: [PATCH 094/161] USB: serial: ftdi_sio: enable NDI speed hack consistently The original submission adding support for NDI EMGUIDE Gemini enabled the existing NDI speed hack which remaps the 19200 line speed to 1.2 Mbps, but this silently fell out during resubmission. Enable the speed hack also for the new NDI product for consistency. This will also allow for cleaning up the implementation without resorting to overengineering. Link: https://lore.kernel.org/all/YQXPR01MB49870CB7B3075ADDF88A3FD4DF43A@YQXPR01MB4987.CANPRD01.PROD.OUTLOOK.COM/ Link: https://lore.kernel.org/all/YQXPR01MB4987F1E0DA41E689779E6958DF48A@YQXPR01MB4987.CANPRD01.PROD.OUTLOOK.COM/ Cc: Ryan Mann Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 7e6e7b7c21ea..58dbf922208c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1311,7 +1311,8 @@ static u32 get_ftdi_divisor(struct tty_struct *tty, (product_id == FTDI_NDI_SPECTRA_SCU_PID) || (product_id == FTDI_NDI_FUTURE_2_PID) || (product_id == FTDI_NDI_FUTURE_3_PID) || - (product_id == FTDI_NDI_AURORA_SCU_PID)) && + (product_id == FTDI_NDI_AURORA_SCU_PID) || + (product_id == FTDI_NDI_EMGUIDE_GEMINI_PID)) && (baud == 19200)) { baud = 1200000; } From cde24373724bd3a0937b7af9453dfc6d7433c726 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:11 +0100 Subject: [PATCH 095/161] USB: serial: ftdi_sio: clean up NDI speed hack NDI devices remap the 19200 line speed to 1.2 Mbps. Use the quirk pointer from the match table to enable the quirk instead of comparing PIDs on every speed change. Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 58dbf922208c..d77c1ccafe05 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1246,6 +1246,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, static u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { + const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial); struct ftdi_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; u32 div_value = 0; @@ -1305,17 +1306,8 @@ static u32 get_ftdi_divisor(struct tty_struct *tty, case FT232R: case FTX: if (baud <= 3000000) { - u16 product_id = le16_to_cpu( - port->serial->dev->descriptor.idProduct); - if (((product_id == FTDI_NDI_HUC_PID) || - (product_id == FTDI_NDI_SPECTRA_SCU_PID) || - (product_id == FTDI_NDI_FUTURE_2_PID) || - (product_id == FTDI_NDI_FUTURE_3_PID) || - (product_id == FTDI_NDI_AURORA_SCU_PID) || - (product_id == FTDI_NDI_EMGUIDE_GEMINI_PID)) && - (baud == 19200)) { + if (quirk == &ftdi_ndi_quirk && baud == 19200) baud = 1200000; - } div_value = ftdi_232bm_baud_to_divisor(baud); } else { dev_dbg(dev, "%s - Baud rate too high!\n", __func__); From 4d822b0a4a272902039c77e68c9b12bdb20c233f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 10 Nov 2025 12:12:12 +0100 Subject: [PATCH 096/161] USB: serial: ftdi_sio: drop NDI quirk module parameter NDI devices have been using a latency timer of 1 ms since commit b760dac290c3 ("USB: ftdi: support NDI devices"), which also added a vendor specific module parameter that could be used to override the default value for these devices. Module parameters should generally be avoided as they apply to all devices managed by a driver and vendor specific hacks should be kept out of mainline. Drop the module parameter in favour of the generic sysfs interface for setting the latency timer (e.g. using udev rules) while keeping the default 1 ms timer for NDI devices. Note that there seems to be no (correct) public references to the module parameter and most likely no one is using it. Cc: Ryan Mann Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 41 +++-------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d77c1ccafe05..be79a8746098 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -107,7 +107,6 @@ struct ftdi_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_ndi_probe(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); static void ftdi_usb_uirt_setup(struct ftdi_private *priv); @@ -118,7 +117,6 @@ static const struct ftdi_quirk ftdi_jtag_quirk = { }; static const struct ftdi_quirk ftdi_ndi_quirk = { - .probe = ftdi_ndi_probe, }; static const struct ftdi_quirk ftdi_usb_uirt_quirk = { @@ -2204,7 +2202,9 @@ static int ftdi_port_probe(struct usb_serial_port *port) goto err_free; ftdi_set_max_packet_size(port); - if (read_latency_timer(port) < 0) + if (quirk == &ftdi_ndi_quirk) + priv->latency = 1; + else if (read_latency_timer(port) < 0) priv->latency = 16; write_latency_timer(port); @@ -2246,38 +2246,6 @@ static void ftdi_he_tira1_setup(struct ftdi_private *priv) priv->force_rtscts = 1; } -/* - * Module parameter to control latency timer for NDI FTDI-based USB devices. - * If this value is not set in /etc/modprobe.d/ its value will be set - * to 1ms. - */ -static int ndi_latency_timer = 1; - -/* - * Setup for the NDI FTDI-based USB devices, which requires hardwired - * baudrate (19200 gets mapped to 1200000). - */ -static int ftdi_ndi_probe(struct usb_serial *serial) -{ - struct usb_device *udev = serial->dev; - int latency = ndi_latency_timer; - - if (latency == 0) - latency = 1; - if (latency > 99) - latency = 99; - - dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency); - dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency); - - /* FIXME: errors are not returned */ - usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - FTDI_SIO_SET_LATENCY_TIMER_REQUEST, - FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, - latency, 0, NULL, 0, WDR_TIMEOUT); - return 0; -} - /* * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from @@ -2905,6 +2873,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); - -module_param(ndi_latency_timer, int, 0644); -MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); From 2a06ffc3f192280c96df95953465cd0a5f777ee9 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Sat, 1 Nov 2025 04:24:40 +0000 Subject: [PATCH 097/161] dt-bindings: usb: renesas,rzg3e-xhci: Add RZ/V2H(P) and RZ/V2N support Add device tree binding support for the USB3.2 Gen2 controller on Renesas RZ/V2H(P) and RZ/V2N SoCs. The USB3.2 IP on these SoCs is identical to that found on the RZ/G3E SoC. Add new compatible strings "renesas,r9a09g056-xhci" for RZ/V2N and "renesas,r9a09g057-xhci" for RZ/V2H(P). Both variants use "renesas,r9a09g047-xhci" as a fallback compatible to indicate hardware compatibility with the RZ/G3E implementation. Update the title to be more generic as it now covers multiple SoC families beyond just RZ/G3E. Signed-off-by: Lad Prabhakar Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251101042440.648321-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/renesas,rzg3e-xhci.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml b/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml index 98260f9fb442..3f4b09e48ce0 100644 --- a/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml @@ -4,14 +4,22 @@ $id: http://devicetree.org/schemas/usb/renesas,rzg3e-xhci.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/G3E USB 3.2 Gen2 Host controller +title: Renesas USB 3.2 Gen2 Host controller maintainers: - Biju Das properties: compatible: - const: renesas,r9a09g047-xhci + oneOf: + - items: + - enum: + - renesas,r9a09g056-xhci # RZ/V2N + - renesas,r9a09g057-xhci # RZ/V2H(P) + - const: renesas,r9a09g047-xhci + + - items: + - const: renesas,r9a09g047-xhci # RZ/G3E reg: maxItems: 1 From e7ab90c8675f0de4572969b80160d97ed11d955b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 3 Nov 2025 23:02:16 -0800 Subject: [PATCH 098/161] usb: xhci-mtk: correct most kernel-doc problems in xhci-mtk.h Correct the kernel-doc notation in xhck-mtk.h to avoid most kernel-doc warnings. Summary of changes: - don't use /** to begin comments that are not in kernel-doc format - add missing "struct mu3h_sch_tt" kernel-doc line - convert several "struct mu3h_...:" to using " - " to separate the struct name from its short description - add a missing @speed: struct member description Warning messages that are fixed: xhci-mtk.h:25: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * To simplify scheduler algorithm, set a upper limit for ESIT, xhci-mtk.h:25: warning: missing initial short description on line: * To simplify scheduler algorithm, set a upper limit for ESIT, Warning: drivers/usb/host/xhci-mtk.h:36 Cannot find identifier on line: * @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes Warning: drivers/usb/host/xhci-mtk.h:37 Cannot find identifier on line: * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes Warning: drivers/usb/host/xhci-mtk.h:38 Cannot find identifier on line: * @ls_bus_bw: save bandwidth used by LS eps in each uframes Warning: drivers/usb/host/xhci-mtk.h:39 Cannot find identifier on line: * @fs_frame_bw: save bandwidth used by FS/LS eps in each FS frames Warning: drivers/usb/host/xhci-mtk.h:40 Cannot find identifier on line: * @in_ss_cnt: the count of Start-Split for IN eps Warning: drivers/usb/host/xhci-mtk.h:41 Cannot find identifier on line: * @ep_list: Endpoints using this TT Warning: drivers/usb/host/xhci-mtk.h:42 Cannot find identifier on line: */ Warning: drivers/usb/host/xhci-mtk.h:43 Cannot find identifier on line: struct mu3h_sch_tt { Warning: drivers/usb/host/xhci-mtk.h:44 Cannot find identifier on line: u16 fs_bus_bw_out[XHCI_MTK_MAX_ESIT]; Warning: drivers/usb/host/xhci-mtk.h:45 Cannot find identifier on line: u16 fs_bus_bw_in[XHCI_MTK_MAX_ESIT]; Warning: drivers/usb/host/xhci-mtk.h:46 Cannot find identifier on line: u8 ls_bus_bw[XHCI_MTK_MAX_ESIT]; Warning: drivers/usb/host/xhci-mtk.h:47 Cannot find identifier on line: u16 fs_frame_bw[XHCI_MTK_FRAMES_CNT]; Warning: drivers/usb/host/xhci-mtk.h:48 Cannot find identifier on line: u8 in_ss_cnt[XHCI_MTK_MAX_ESIT]; Warning: drivers/usb/host/xhci-mtk.h:49 Cannot find identifier on line: struct list_head ep_list; Warning: drivers/usb/host/xhci-mtk.h:50 Cannot find identifier on line: }; Warning: drivers/usb/host/xhci-mtk.h:51 Cannot find identifier on line: Warning: drivers/usb/host/xhci-mtk.h:52 Cannot find identifier on line: /** Warning: drivers/usb/host/xhci-mtk.h:121 struct member 'speed' not described in 'mu3h_sch_ep_info' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251104070216.907540-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f5e2bd66bb1b..2274f5995171 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -21,7 +21,7 @@ /* support at most 64 ep, use 32 size hash table */ #define SCH_EP_HASH_BITS 5 -/** +/* * To simplify scheduler algorithm, set a upper limit for ESIT, * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT, * round down to the limit value, that means allocating more @@ -34,6 +34,7 @@ #define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME) /** + * struct mu3h_sch_tt - TT scheduling data * @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes * @ls_bus_bw: save bandwidth used by LS eps in each uframes @@ -51,7 +52,7 @@ struct mu3h_sch_tt { }; /** - * struct mu3h_sch_bw_info: schedule information for bandwidth domain + * struct mu3h_sch_bw_info - schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes * @@ -63,7 +64,7 @@ struct mu3h_sch_bw_info { }; /** - * struct mu3h_sch_ep_info: schedule information for endpoint + * struct mu3h_sch_ep_info - schedule information for endpoint * * @esit: unit is 125us, equal to 2 << Interval field in ep-context * @num_esit: number of @esit in a period @@ -77,6 +78,7 @@ struct mu3h_sch_bw_info { * @ep_type: endpoint type * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct + * @speed: usb device speed * @allocated: the bandwidth is aready allocated from bus_bw * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -125,7 +127,7 @@ struct mu3h_sch_ep_info { #define MU3C_U2_PORT_MAX 5 /** - * struct mu3c_ippc_regs: MTK ssusb ip port control registers + * struct mu3c_ippc_regs - MTK ssusb ip port control registers * @ip_pw_ctr0~3: ip power and clock control registers * @ip_pw_sts1~2: ip power and clock status registers * @ip_xhci_cap: ip xHCI capability register From 23bba7f33bb12d78110d53277feae8d063df61bf Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Tue, 11 Nov 2025 14:59:19 +0800 Subject: [PATCH 099/161] dt-bindings: usb: Support MediaTek MT8189 xhci modify dt-binding for support mt8189 dts node of xhci Signed-off-by: Jack Hsu Acked-by: Conor Dooley Link: https://patch.msgid.link/20251111070031.305281-6-jh.hsu@mediatek.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index 004d3ebec091..231e6f35a986 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -34,6 +34,7 @@ properties: - mediatek,mt8183-xhci - mediatek,mt8186-xhci - mediatek,mt8188-xhci + - mediatek,mt8189-xhci - mediatek,mt8192-xhci - mediatek,mt8195-xhci - mediatek,mt8365-xhci @@ -168,7 +169,8 @@ properties: 104 - used by mt8195, IP1, specific 1.04; 105 - used by mt8195, IP2, specific 1.05; 106 - used by mt8195, IP3, specific 1.06; - enum: [1, 2, 101, 102, 103, 104, 105, 106] + 110 - used by mt8189, IP4, specific 1.10; + enum: [1, 2, 101, 102, 103, 104, 105, 106, 110] mediatek,u3p-dis-msk: $ref: /schemas/types.yaml#/definitions/uint32 From 8d34983720155b8f05de765f0183d9b0e1345cc0 Mon Sep 17 00:00:00 2001 From: Hongyu Xie Date: Wed, 19 Nov 2025 16:23:55 +0200 Subject: [PATCH 100/161] usb: xhci: limit run_graceperiod for only usb 3.0 devices run_graceperiod blocks usb 2.0 devices from auto suspending after xhci_start for 500ms. Log shows: [ 13.387170] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100 [ 13.387177] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000 [ 13.387182] hub_suspend:3903: hub 7-0:1.0: hub_suspend [ 13.387188] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1 [ 13.387191] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event [ 13.387193] hcd_bus_resume:2303: usb usb7: usb auto-resume [ 13.387296] hub_event:5779: hub 3-0:1.0: state 7 ports 1 chg 0000 evt 0000 [ 13.393343] handle_port_status:2034: xhci-hcd PNP0D10:02: handle_port_status: starting usb5 port polling. [ 13.393353] xhci_hub_control:1271: xhci-hcd PNP0D10:02: Get port status 5-1 read: 0x206e1, return 0x10101 [ 13.400047] hub_suspend:3903: hub 3-0:1.0: hub_suspend [ 13.403077] hub_resume:3948: hub 7-0:1.0: hub_resume [ 13.403080] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100 [ 13.403085] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000 [ 13.403087] hub_suspend:3903: hub 7-0:1.0: hub_suspend [ 13.403090] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1 [ 13.403093] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event [ 13.403095] hcd_bus_resume:2303: usb usb7: usb auto-resume [ 13.405002] handle_port_status:1913: xhci-hcd PNP0D10:04: Port change event, 9-1, id 1, portsc: 0x6e1 [ 13.405016] hub_activate:1169: usb usb5-port1: status 0101 change 0001 [ 13.405026] xhci_clear_port_change_bit:658: xhci-hcd PNP0D10:02: clear port1 connect change, portsc: 0x6e1 [ 13.413275] hcd_bus_suspend:2250: usb usb3: bus auto-suspend, wakeup 1 [ 13.419081] hub_resume:3948: hub 7-0:1.0: hub_resume [ 13.419086] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100 [ 13.419095] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000 [ 13.419100] hub_suspend:3903: hub 7-0:1.0: hub_suspend [ 13.419106] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1 [ 13.419110] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event [ 13.419112] hcd_bus_resume:2303: usb usb7: usb auto-resume [ 13.420455] handle_port_status:2034: xhci-hcd PNP0D10:04: handle_port_status: starting usb9 port polling. [ 13.420493] handle_port_status:1913: xhci-hcd PNP0D10:05: Port change event, 10-1, id 1, portsc: 0x6e1 [ 13.425332] hcd_bus_suspend:2279: usb usb3: suspend raced with wakeup event [ 13.431931] handle_port_status:2034: xhci-hcd PNP0D10:05: handle_port_status: starting usb10 port polling. [ 13.435080] hub_resume:3948: hub 7-0:1.0: hub_resume [ 13.435084] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100 [ 13.435092] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000 [ 13.435096] hub_suspend:3903: hub 7-0:1.0: hub_suspend [ 13.435102] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1 [ 13.435106] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event usb7 and other usb 2.0 root hub were rapidly toggling between suspend and resume states. More, "suspend raced with wakeup event" confuses people. So, limit run_graceperiod for only usb 3.0 devices Signed-off-by: Hongyu Xie Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b3a59ce1b3f4..5e1442e91743 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1671,7 +1671,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) * SS devices are only visible to roothub after link training completes. * Keep polling roothubs for a grace period after xHC start */ - if (xhci->run_graceperiod) { + if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) { if (time_before(jiffies, xhci->run_graceperiod)) status = 1; else From fad902d6709e349babe8fc79550552512facac30 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 19 Nov 2025 16:23:56 +0200 Subject: [PATCH 101/161] xhci: Add helper to find trb from its dma address Add a xhci_dma_to_trb() helper, and use it to find the transfer TRB early in handle_tx_event() based on the dma address found in the event TRB. With this helper we can avoid using 'ep_seg' transfer TRB segment variable as both a a boolean to indicate if the transfer TRB is part of the next queued TD, and to actually find the transfer TRB based on ep_seg and ep_trb_dma. This is a first step in reworking and cleaning up trb_in_td() and handle_tx_event() Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 8e209aa33ea7..7fccca7e1c62 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, return seg->dma + (segment_offset * sizeof(*trb)); } +static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg, + dma_addr_t dma, + struct xhci_segment **match_seg) +{ + struct xhci_segment *seg; + + xhci_for_each_ring_seg(start_seg, seg) { + if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) { + if (match_seg) + *match_seg = seg; + return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)]; + } + } + + return NULL; +} + static bool trb_is_noop(union xhci_trb *trb) { return TRB_TYPE_NOOP_LE32(trb->generic.field[3]); @@ -2658,7 +2675,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, int ep_index; struct xhci_td *td = NULL; dma_addr_t ep_trb_dma; - struct xhci_segment *ep_seg; union xhci_trb *ep_trb; int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; @@ -2689,6 +2705,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!ep_ring) return handle_transferless_tx_event(xhci, ep, trb_comp_code); + /* find the transfer trb this events points to */ + ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL); + /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2862,10 +2881,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list); - /* Is this a TRB in the currently executing TD? */ - ep_seg = trb_in_td(td, ep_trb_dma); - - if (!ep_seg) { + /* Is this TRB not part of the currently executing TD? */ + if (!trb_in_td(td, ep_trb_dma)) { if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { /* this event is unlikely to match any TD, don't skip them all */ @@ -2948,7 +2965,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (ring_xrun_event) return 0; - ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); /* From 86dcf43be869fe683923f577ddc3d04ec37ff581 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 19 Nov 2025 16:23:57 +0200 Subject: [PATCH 102/161] xhci: simplify and rework trb_in_td() The trb_in_td() checking is quite complex, especially when checking for TRBs in ranges that can span several segments. Simplify the search by creating a position index for each TRB on the ring, and just compare the position indexes. Add a more generic dma_in_range() helper that checks if a trb dma address is in the range between a start and end trb and call it from trb_in_td() Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 70 ++++++++++++++---------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7fccca7e1c62..aa7fc4d6f97c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -160,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) } } +static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb) +{ + return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs); +} + /* Updates trb to point to the next TRB in the ring, and updates seg if the next * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. @@ -299,55 +304,34 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, inc_enq_past_link(xhci, ring, chain); } -/* - * If the suspect DMA address is a TRB in this TD, this function returns that - * TRB's segment. Otherwise it returns 0. - */ -static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +static bool dma_in_range(dma_addr_t dma, + struct xhci_segment *start_seg, union xhci_trb *start_trb, + struct xhci_segment *end_seg, union xhci_trb *end_trb) { - dma_addr_t start_dma; - dma_addr_t end_seg_dma; - dma_addr_t end_trb_dma; - struct xhci_segment *cur_seg; + unsigned int pos, start, end; + struct xhci_segment *pos_seg; + union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg); - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); - cur_seg = td->start_seg; + /* Is the trb dma address even part of the whole ring? */ + if (!pos_trb) + return false; - do { - if (start_dma == 0) - return NULL; - /* We may get an event for a Link TRB in the middle of a TD */ - end_seg_dma = xhci_trb_virt_to_dma(cur_seg, - &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); - /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); + pos = trb_to_pos(pos_seg, pos_trb); + start = trb_to_pos(start_seg, start_trb); + end = trb_to_pos(end_seg, end_trb); - if (end_trb_dma > 0) { - /* The end TRB is in this segment, so suspect should be here */ - if (start_dma <= end_trb_dma) { - if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma) - return cur_seg; - } else { - /* Case for one segment with - * a TD wrapped around to the top - */ - if ((suspect_dma >= start_dma && - suspect_dma <= end_seg_dma) || - (suspect_dma >= cur_seg->dma && - suspect_dma <= end_trb_dma)) - return cur_seg; - } - return NULL; - } - /* Might still be somewhere in this segment */ - if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma) - return cur_seg; + /* end position is smaller than start, search range wraps around */ + if (end < start) + return !(pos > end && pos < start); - cur_seg = cur_seg->next; - start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (cur_seg != td->start_seg); + return (pos >= start && pos <= end); +} - return NULL; +/* If the suspect DMA address is a TRB in this TD, this function returns true */ +static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +{ + return dma_in_range(suspect_dma, td->start_seg, td->start_trb, + td->end_seg, td->end_trb); } /* From 2f751709463b235ae0f0790210a70ff48b33493a Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:23:58 +0200 Subject: [PATCH 103/161] usb: xhci: rework xhci_decode_portsc() Rework xhci_decode_portsc(), which is used for PORTSC tracing, to make the output more compact and general. The function now first prints the multi-bit fields (port speed and link state), followed by the abbreviated names of each individual bit as defined in the xHCI specification. This reduces message length and makes the output easier to read. This change prepares for upcoming patches that will trace all PORTSC writes, requiring the same decoding logic to handle both reads and writes. This is particularly important for Read-Write-1-to-Clear (RW1C) bits, where the semantics differ between read and write operations. For example, when reading the Port Enabled bit, a set bit means the port is enabled; when writing, a set bit indicates the port is being disabled. The decoder now also includes the following fields: Port Link State Write Strobe (LWS) Device Removable (DR) Warm Port Reset (WPR) ==== Examples Traces ==== Before: 0x00201201 Powered Connected Disabled Link:U0 PortSpeed:4 Change: PRC Wake: 0x0a0002a0 Powered Not-connected Disabled Link:RxDetect PortSpeed:0 \ Change: Wake: WCE WOE After: 0x00201201 Speed=4 Link=U0 CCS PP PRC 0x0a0002a0 Speed=0 Link=RxDetect PP WCE WOE Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 59 +++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 58a51f09cceb..8e1311f90fdb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2399,25 +2399,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc == ~(u32)0) return str; - ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ", - portsc & PORT_POWER ? "Powered" : "Powered-off", - portsc & PORT_CONNECT ? "Connected" : "Not-connected", - portsc & PORT_PE ? "Enabled" : "Disabled", - xhci_portsc_link_state_string(portsc), - DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc)); + /* RO/ROS: Read-only */ + if (portsc & PORT_CONNECT) + ret += sprintf(str + ret, "CCS "); if (portsc & PORT_OC) - ret += sprintf(str + ret, "OverCurrent "); - if (portsc & PORT_RESET) - ret += sprintf(str + ret, "In-Reset "); + ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */ + if (portsc & PORT_CAS) + ret += sprintf(str + ret, "CAS "); + if (portsc & PORT_DEV_REMOVE) + ret += sprintf(str + ret, "DR "); - ret += sprintf(str + ret, "Change: "); + /* RWS; writing 1 sets the bit, writing 0 clears the bit. */ + if (portsc & PORT_POWER) + ret += sprintf(str + ret, "PP "); + if (portsc & PORT_WKCONN_E) + ret += sprintf(str + ret, "WCE "); + if (portsc & PORT_WKDISC_E) + ret += sprintf(str + ret, "WDE "); + if (portsc & PORT_WKOC_E) + ret += sprintf(str + ret, "WOE "); + + /* RW; writing 1 sets the bit, writing 0 clears the bit */ + if (portsc & PORT_LINK_STROBE) + ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */ + + /* RW1S; writing 1 sets the bit, writing 0 has no effect */ + if (portsc & PORT_RESET) + ret += sprintf(str + ret, "PR "); + if (portsc & PORT_WR) + ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */ + + /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */ + if (portsc & PORT_PE) + ret += sprintf(str + ret, "PED "); if (portsc & PORT_CSC) ret += sprintf(str + ret, "CSC "); if (portsc & PORT_PEC) - ret += sprintf(str + ret, "PEC "); + ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */ if (portsc & PORT_WRC) - ret += sprintf(str + ret, "WRC "); + ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */ if (portsc & PORT_OCC) ret += sprintf(str + ret, "OCC "); if (portsc & PORT_RC) @@ -2425,17 +2448,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc & PORT_PLC) ret += sprintf(str + ret, "PLC "); if (portsc & PORT_CEC) - ret += sprintf(str + ret, "CEC "); - if (portsc & PORT_CAS) - ret += sprintf(str + ret, "CAS "); - - ret += sprintf(str + ret, "Wake: "); - if (portsc & PORT_WKCONN_E) - ret += sprintf(str + ret, "WCE "); - if (portsc & PORT_WKDISC_E) - ret += sprintf(str + ret, "WDE "); - if (portsc & PORT_WKOC_E) - ret += sprintf(str + ret, "WOE "); + ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */ return str; } From 829738e59f1fad90ef7b63d6a1a4de9d5d22544a Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:23:59 +0200 Subject: [PATCH 104/161] usb: xhci: add tracing for PORTSC register writes Introduce a dedicated write function for the USB Port Register Set (PORTSC) that includes tracing capabilities for values written to the PORTSC register. This enhancement minimizes code duplication and improves debugging. The PORTSC register is part of the Host Controller USB Port Register Set, comprising 4 x 32-bit registers. As the first register, PORTSC is accessed directly via 'port->addr'. Future commits will introduce a dedicated Port register struct to further streamline access. By adding the xhci_portsc_writel() function prior to these changes, we significantly reduce the number of same line modifications required. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-hub.c | 33 ++++++++++++++++----------------- drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci-trace.h | 5 +++++ drivers/usb/host/xhci.c | 9 ++++++++- drivers/usb/host/xhci.h | 1 + 6 files changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index c6d44977193f..df99fffc6120 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, portsc = xhci_port_state_to_neutral(portsc); portsc &= ~PORT_PLS_MASK; portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); spin_unlock_irqrestore(&xhci->lock, flags); } else { return -EINVAL; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5e1442e91743..d0300c097803 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -570,7 +570,7 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) portsc = xhci_port_state_to_neutral(portsc); /* Write 1 to disable the port */ - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); portsc = readl(port->addr); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", @@ -578,7 +578,7 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, __le32 __iomem *addr, u32 port_status) + u16 wIndex, struct xhci_port *port, u32 port_status) { char *port_change_bit; u32 status; @@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - writel(port_status | status, addr); - port_status = readl(addr); + xhci_portsc_writel(port, port_status | status); + port_status = readl(port->addr); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", wIndex + 1, port_change_bit, port_status); @@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, if (on) { /* Power on */ - writel(temp | PORT_POWER, port->addr); + xhci_portsc_writel(port, temp | PORT_POWER); readl(port->addr); } else { /* Power off */ - writel(temp & ~PORT_POWER, port->addr); + xhci_portsc_writel(port, temp & ~PORT_POWER); } spin_unlock_irqrestore(&xhci->lock, *flags); @@ -805,7 +805,7 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", port->rhub->hcd->self.busnum, port->hcd_portnum + 1, @@ -835,7 +835,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, else temp &= ~PORT_WKOC_E; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } /* Test and clear port RWC bit */ @@ -848,7 +848,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } } @@ -1371,7 +1371,7 @@ 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, port->addr); + xhci_portsc_writel(port, temp | PORT_PE); temp = readl(port->addr); break; } @@ -1500,7 +1500,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - writel(temp, port->addr); + xhci_portsc_writel(port, temp); temp = readl(port->addr); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", @@ -1514,7 +1514,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); temp = readl(port->addr); break; case USB_PORT_FEAT_U1_TIMEOUT: @@ -1603,8 +1603,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: - xhci_clear_port_change_bit(xhci, wValue, wIndex, - port->addr, temp); + xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(xhci, port); @@ -1829,7 +1828,7 @@ retry: spin_lock_irqsave(&xhci->lock, flags); } } - writel(portsc_buf[port_index], ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc_buf[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1863,7 +1862,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) /* clear wakeup/change bits, and do a warm port reset */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc |= PORT_WR; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); /* flush write */ readl(port->addr); return true; @@ -1942,7 +1941,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) } /* disable wake for all ports, write new link state if needed */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); - writel(portsc, ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc); } /* USB2 specific resume signaling delay and U0 link state transition */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index f67a4d956204..b1192648aee7 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -919,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n", port->rhub->hcd->self.busnum, port->hcd_portnum + 1); portsc = xhci_port_state_to_neutral(portsc); - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); } return 0; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 9abc904f1749..481becbcbf81 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -575,6 +575,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, TP_ARGS(port, portsc) ); +DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel, + TP_PROTO(struct xhci_port *port, u32 portsc), + TP_ARGS(port, portsc) +); + DECLARE_EVENT_CLASS(xhci_log_doorbell, TP_PROTO(u32 slot, u32 doorbell), TP_ARGS(slot, doorbell), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0cb45b95e4f5..84e109dbabe8 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -41,6 +41,13 @@ static unsigned long long quirks; module_param(quirks, ullong, S_IRUGO); MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); +void xhci_portsc_writel(struct xhci_port *port, u32 val) +{ + trace_xhci_portsc_writel(port, val); + writel(val, port->addr); +} +EXPORT_SYMBOL_GPL(xhci_portsc_writel); + static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { struct xhci_segment *seg; @@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, t2 |= PORT_CSC; if (t1 != t2) { - writel(t2, rhub->ports[i]->addr); + xhci_portsc_writel(rhub->ports[i], t2); xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n", rhub->hcd->self.busnum, i + 1, portsc, t2); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8e1311f90fdb..3b6b2d0d4c60 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1961,6 +1961,7 @@ void xhci_update_erst_dequeue(struct xhci_hcd *xhci, void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); int xhci_usb_endpoint_maxp(struct usb_device *udev, struct usb_host_endpoint *host_ep); +void xhci_portsc_writel(struct xhci_port *port, u32 val); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, From 511afe80b82d2b21086e459906e6c97b4eaeed20 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:00 +0200 Subject: [PATCH 105/161] usb: xhci: add helper to read PORTSC register Add a dedicated helper function to read the USB Port Status and Control (PORTSC) register. This complements xhci_portsc_writel() and improves code clarity by providing a clear counterpart for reading the register. Suggested-by: Peter Chen Reviewed-by: Peter Chen Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 4 +- drivers/usb/host/xhci-hub.c | 68 ++++++++++++++++----------------- drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci-tegra.c | 12 +++--- drivers/usb/host/xhci.c | 14 +++++-- drivers/usb/host/xhci.h | 1 + 7 files changed, 55 insertions(+), 48 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index df99fffc6120..d32ac8f84691 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused) u32 portsc; char str[XHCI_MSG_MAX]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc)); return 0; @@ -359,7 +359,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, return count; spin_lock_irqsave(&xhci->lock, flags); /* compliance mode can only be enabled on ports in RxDetect */ - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) { spin_unlock_irqrestore(&xhci->lock, flags); return -EPERM; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d0300c097803..2927b8a80327 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -566,13 +566,13 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) return; } - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); portsc = xhci_port_state_to_neutral(portsc); /* Write 1 to disable the port */ xhci_portsc_writel(port, portsc | PORT_PE); - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, portsc); } @@ -622,7 +622,7 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, } /* Change bits are all write 1 to clear */ xhci_portsc_writel(port, port_status | status); - port_status = readl(port->addr); + port_status = xhci_portsc_readl(port); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", wIndex + 1, port_change_bit, port_status); @@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; hcd = port->rhub->hcd; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); @@ -660,7 +660,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, if (on) { /* Power on */ xhci_portsc_writel(port, temp | PORT_POWER); - readl(port->addr); + xhci_portsc_readl(port); } else { /* Power off */ xhci_portsc_writel(port, temp & ~PORT_POWER); @@ -801,7 +801,7 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; @@ -817,7 +817,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -844,7 +844,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; @@ -1002,7 +1002,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } xhci_ring_device(xhci, port->slot_id); } else { - int port_status = readl(port->addr); + int port_status = xhci_portsc_readl(port); xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); @@ -1263,7 +1263,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wIndex--; port = ports[portnum1 - 1]; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1309,7 +1309,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port = ports[portnum1 - 1]; wIndex--; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1319,7 +1319,7 @@ 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(port->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port, XDEV_U0); @@ -1331,7 +1331,7 @@ 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(port->addr); + temp = xhci_portsc_readl(port); 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", @@ -1354,11 +1354,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(port->addr); + temp = xhci_portsc_readl(port); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", @@ -1372,7 +1372,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; xhci_portsc_writel(port, temp | PORT_PE); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } @@ -1381,7 +1381,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Enable port %d-%d\n", hcd->self.busnum, portnum1); xhci_set_link_state(xhci, port, link_state); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } @@ -1414,7 +1414,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, hcd->self.busnum, portnum1); xhci_set_link_state(xhci, port, link_state); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } /* Port must be enabled */ @@ -1462,7 +1462,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", hcd->self.busnum, portnum1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } @@ -1480,12 +1480,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; } break; @@ -1502,20 +1502,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = (temp | PORT_RESET); xhci_portsc_writel(port, temp); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: xhci_set_remote_wake_mask(xhci, port, wake_mask); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; xhci_portsc_writel(port, temp); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) @@ -1547,7 +1547,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* unblock any posted writes */ - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; case ClearPortFeature: if (!portnum1 || portnum1 > max_ports) @@ -1556,7 +1556,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port = ports[portnum1 - 1]; wIndex--; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1566,7 +1566,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(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1681,7 +1681,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = readl(ports[i]->addr); + temp = xhci_portsc_readl(ports[i]); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1750,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 t1, t2; int retries = 10; retry: - t1 = readl(ports[port_index]->addr); + t1 = xhci_portsc_readl(ports[port_index]); t2 = xhci_port_state_to_neutral(t1); portsc_buf[port_index] = 0; @@ -1849,7 +1849,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) { u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); /* if any of these are set we are not stuck */ if (portsc & (PORT_CONNECT | PORT_CAS)) @@ -1864,7 +1864,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) portsc |= PORT_WR; xhci_portsc_writel(port, portsc); /* flush write */ - readl(port->addr); + xhci_portsc_readl(port); return true; } @@ -1911,7 +1911,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) } port_index = max_ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); /* warm reset CAS limited ports stuck in polling/compliance */ if ((xhci->quirks & XHCI_MISSING_CAS) && diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index b1192648aee7..2ba0261a29c1 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -898,7 +898,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { port = &xhci->hw_ports[i]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_U3) continue; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index aa7fc4d6f97c..88022d221c70 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2027,7 +2027,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) hcd = port->rhub->hcd; bus_state = &port->rhub->bus_state; hcd_portnum = port->hcd_portnum; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", hcd->self.busnum, hcd_portnum + 1, port_id, portsc); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 5255b1002893..1e23f198a005 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2036,7 +2036,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) u32 value; for (i = 0; i < hub->num_ports; i++) { - value = readl(hub->ports[i]->addr); + value = xhci_portsc_readl(hub->ports[i]); if ((value & PORT_PE) == 0) continue; @@ -2162,7 +2162,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra) if (!is_host_mode_phy(tegra, i, j)) continue; - portsc = readl(rhub->ports[index]->addr); + portsc = xhci_portsc_readl(rhub->ports[index]); speed = tegra_xhci_portsc_to_speed(tegra, portsc); tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed); tegra_xusb_padctl_enable_phy_wake(padctl, phy); @@ -2257,7 +2257,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume) for (i = 0; i < xhci->usb2_rhub.num_ports; i++) { if (!xhci->usb2_rhub.ports[i]) continue; - portsc = readl(xhci->usb2_rhub.ports[i]->addr); + portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]); tegra->lp0_utmi_pad_mask &= ~BIT(i); if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS)) tegra->lp0_utmi_pad_mask |= BIT(i); @@ -2790,7 +2790,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, while (i--) { if (!test_bit(i, &bus_state->resuming_ports)) continue; - portsc = readl(ports[i]->addr); + portsc = xhci_portsc_readl(ports[i]); if ((portsc & PORT_PLS_MASK) == XDEV_RESUME) tegra_phy_xusb_utmi_pad_power_on( tegra_xusb_get_phy(tegra, "usb2", (int) i)); @@ -2808,7 +2808,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if (!index || index > rhub->num_ports) return -EPIPE; ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (portsc & PORT_CONNECT) tegra_phy_xusb_utmi_pad_power_on(phy); } @@ -2827,7 +2827,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) { ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (!(portsc & PORT_CONNECT)) { /* We don't suspend the PAD while HNP role swap happens on the OTG * port diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 84e109dbabe8..6b47b218cb24 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -48,6 +48,12 @@ void xhci_portsc_writel(struct xhci_port *port, u32 val) } EXPORT_SYMBOL_GPL(xhci_portsc_writel); +u32 xhci_portsc_readl(struct xhci_port *port) +{ + return readl(port->addr); +} +EXPORT_SYMBOL_GPL(xhci_portsc_readl); + static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { struct xhci_segment *seg; @@ -380,7 +386,7 @@ static void compliance_mode_recovery(struct timer_list *t) return; for (i = 0; i < rhub->num_ports; i++) { - temp = readl(rhub->ports[i]->addr); + temp = xhci_portsc_readl(rhub->ports[i]); if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { /* * Compliance Mode Detected. Letting USB Core @@ -903,7 +909,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < rhub->num_ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); t1 = xhci_port_state_to_neutral(portsc); t2 = t1; @@ -943,7 +949,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb2_rhub.num_ports; ports = xhci->usb2_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & PORT_CHANGE_MASK || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; @@ -951,7 +957,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb3_rhub.num_ports; ports = xhci->usb3_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & (PORT_CHANGE_MASK | PORT_CAS) || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3b6b2d0d4c60..bddf9c15d813 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1962,6 +1962,7 @@ void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); int xhci_usb_endpoint_maxp(struct usb_device *udev, struct usb_host_endpoint *host_ep); void xhci_portsc_writel(struct xhci_port *port, u32 val); +u32 xhci_portsc_readl(struct xhci_port *port); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, From 377a91594e008848363641d07f51d2e48f4bdde5 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:01 +0200 Subject: [PATCH 106/161] usb: xhci: add USB Port Register Set struct Introduce a new struct for the Host Controller USB Port Register Set to enhance readability and maintainability. The Host Controller Operational Registers (struct 'xhci_op_regs') span from offset 0x0 to 0x3FF and consist of fixed fields. Following these fixed fields are the Host Controller USB Port Register Sets, which are dynamic and repeat from 1 to MaxPorts, as defined by HCSPARAMS1. Currently, the struct 'xhci_op_regs' includes: __le32 port_status_base; The first PORTSC __le32 port_power_base; The first PORTPMSC __le32 port_link_base; The first PORTLI __le32 reserved5; The first PORTHLPMC, not reserved __le32 reserved6[NUM_PORT_REGS*254]; Port registers 2 to MaxPorts Replace this with the simpler: struct xhci_port_regs port_regs[]; Port registers 1 to MaxPorts Host Controller USB Port Register Set: | Offset | Mnemonic | Register Name -------------------------------------------------------------------------- | 0x0 | PORTSC | Port Status and Control | 0x4 | PORTPMSC | Port Power Management Status and Control | 0x8 | PORTLI | Port Link Info | 0xC | PORTHLPMC | Port Hardware LPM Control Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 3 +-- drivers/usb/host/xhci.h | 36 ++++++++++++++++-------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6e5b6057de79..ea3cfc229cd0 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2201,8 +2201,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return -ENOMEM; for (i = 0; i < num_ports; i++) { - xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * i; + xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc; xhci->hw_ports[i].hw_portnum = i; init_completion(&xhci->hw_ports[i].rexit_done); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index bddf9c15d813..d3ba50462589 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -66,14 +66,25 @@ struct xhci_cap_regs { /* Reserved up to (CAPLENGTH - 0x1C) */ }; -/* Number of registers per port */ -#define NUM_PORT_REGS 4 - #define PORTSC 0 #define PORTPMSC 1 #define PORTLI 2 #define PORTHLPMC 3 +/* + * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8 + * @portsc: Port Status and Control + * @portpmsc: Port Power Management Status and Control + * @portli: Port Link Info + * @porthlmpc: Port Hardware LPM Control + */ +struct xhci_port_regs { + __le32 portsc; + __le32 portpmsc; + __le32 portli; + __le32 porthlmpc; +}; + /** * struct xhci_op_regs - xHCI Host Controller Operational Registers. * @command: USBCMD - xHC command register @@ -85,16 +96,7 @@ struct xhci_cap_regs { * @cmd_ring: CRP - 64-bit Command Ring Pointer * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer * @config_reg: CONFIG - Configure Register - * @port_status_base: PORTSCn - base address for Port Status and Control - * Each port has a Port Status and Control register, - * followed by a Port Power Management Status and Control - * register, a Port Link Info register, and a reserved - * register. - * @port_power_base: PORTPMSCn - base address for - * Port Power Management Status and Control - * @port_link_base: PORTLIn - base address for Port Link Info (current - * Link PM state and control) for USB 2.1 and USB 3.0 - * devices. + * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1). */ struct xhci_op_regs { __le32 command; @@ -110,13 +112,7 @@ struct xhci_op_regs { __le32 config_reg; /* rsvd: offset 0x3C-3FF */ __le32 reserved4[241]; - /* port 1 registers, which serve as a base address for other ports */ - __le32 port_status_base; - __le32 port_power_base; - __le32 port_link_base; - __le32 reserved5; - /* registers for ports 2-255 */ - __le32 reserved6[NUM_PORT_REGS*254]; + struct xhci_port_regs port_regs[]; }; /* USBCMD - USB command - command bitmasks */ From f2469d89a70cc6eb3d8141770995a3b251afa6ff Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:02 +0200 Subject: [PATCH 107/161] usb: xhci: implement USB Port Register Set struct Previously, each port's 'addr' field pointed to the base of the Host Controller USB Port Register Set, and specific registers were accessed using macros such as (port->addr + PORTPMSC). This patch replaces the raw '__le32 __iomem *addr' pointer with a typed 'struct xhci_port_regs __iomem *port_reg' pointer. With this change, individual registers can be accessed directly through the structure fields: Before: port->addr port->addr + PORTPMSC port->addr + PORTLI port->addr + PORTHLPMC After: port->port_reg->portsc port->port_reg->portpmsc port->port_reg->portli port->port_reg->porthlpmc This improves code readability and makes register access more intuitive by using named struct members instead of pointer arithmetic and macros. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 16 ++++++++-------- drivers/usb/host/xhci-mem.c | 2 +- drivers/usb/host/xhci.c | 29 ++++++++++++++--------------- drivers/usb/host/xhci.h | 7 +------ 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2927b8a80327..c4c85312b04c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -683,9 +683,9 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci, /* xhci only supports test mode for usb2 ports */ port = xhci->usb2_rhub.ports[wIndex]; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp |= test_mode << PORT_TEST_MODE_SHIFT; - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); xhci->test_mode = test_mode; if (test_mode == USB_TEST_FORCE_ENABLE) xhci_start(xhci); @@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(port->addr + PORTLI); + port_li = readl(&port->port_reg->portli); status = xhci_get_ext_port_status(temp, port_li); put_unaligned_le32(status, &buf[4]); } @@ -1520,18 +1520,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ @@ -1962,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* poll for U0 link state complete, both USB2 and USB3 */ for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) { - sret = xhci_handshake(ports[port_index]->addr, PORT_PLC, + sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC, PORT_PLC, 10 * 1000); if (sret) { xhci_warn(xhci, "port %d-%d resume PLC timeout\n", diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ea3cfc229cd0..9a6a8d9f3770 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2201,7 +2201,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return -ENOMEM; for (i = 0; i < num_ports; i++) { - xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc; + xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i]; xhci->hw_ports[i].hw_portnum = i; init_completion(&xhci->hw_ports[i].rexit_done); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6b47b218cb24..593b9d3aa9b6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -44,13 +44,13 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); void xhci_portsc_writel(struct xhci_port *port, u32 val) { trace_xhci_portsc_writel(port, val); - writel(val, port->addr); + writel(val, &port->port_reg->portsc); } EXPORT_SYMBOL_GPL(xhci_portsc_writel); u32 xhci_portsc_readl(struct xhci_port *port) { - return readl(port->addr); + return readl(&port->port_reg->portsc); } EXPORT_SYMBOL_GPL(xhci_portsc_readl); @@ -4649,7 +4649,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_port **ports; - __le32 __iomem *pm_addr, *hlpm_addr; + struct xhci_port_regs __iomem *port_reg; u32 pm_val, hlpm_val, field; unsigned int port_num; unsigned long flags; @@ -4674,9 +4674,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, ports = xhci->usb2_rhub.ports; port_num = udev->portnum - 1; - pm_addr = ports[port_num]->addr + PORTPMSC; - pm_val = readl(pm_addr); - hlpm_addr = ports[port_num]->addr + PORTHLPMC; + port_reg = ports[port_num]->port_reg; + pm_val = readl(&port_reg->portpmsc); xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", str_enable_disable(enable), port_num + 1); @@ -4705,30 +4704,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, spin_lock_irqsave(&xhci->lock, flags); hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); - writel(hlpm_val, hlpm_addr); + writel(hlpm_val, &port_reg->porthlmpc); /* flush write */ - readl(hlpm_addr); + readl(&port_reg->porthlmpc); } else { hird = xhci_calculate_hird_besl(xhci, udev); } pm_val &= ~PORT_HIRD_MASK; pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); - writel(pm_val, pm_addr); - pm_val = readl(pm_addr); + writel(pm_val, &port_reg->portpmsc); + pm_val = readl(&port_reg->portpmsc); pm_val |= PORT_HLE; - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); } else { pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); if (udev->usb2_hw_lpm_besl_capable) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_change_max_exit_latency(xhci, udev, 0); - readl_poll_timeout(ports[port_num]->addr, pm_val, + readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val, (pm_val & PORT_PLS_MASK) == XDEV_U0, 100, 10000); return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index d3ba50462589..adabe26b413b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -66,11 +66,6 @@ struct xhci_cap_regs { /* Reserved up to (CAPLENGTH - 0x1C) */ }; -#define PORTSC 0 -#define PORTPMSC 1 -#define PORTLI 2 -#define PORTHLPMC 3 - /* * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8 * @portsc: Port Status and Control @@ -1470,7 +1465,7 @@ struct xhci_port_cap { }; struct xhci_port { - __le32 __iomem *addr; + struct xhci_port_regs __iomem *port_reg; int hw_portnum; int hcd_portnum; struct xhci_hub *rhub; From f7812977456c3c1efc69ecf79ea88302bf472f4b Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 19 Nov 2025 16:24:03 +0200 Subject: [PATCH 108/161] usb: xhci: Assume that endpoints halt as specified xHCI 4.8.3 recommends that software should simply assume endpoints to halt after certain events, without looking at the Endpoint Context for confirmation, because HCs may be slow to update that. While no cases of such "slowness" appear to be known, different problem exists on AMD Promontory chipsets: they may halt and generate a transfer event, but fail to ever update the Endpoint Context at all, at least not until some command is queued and fails with Context State Error. This is easily triggered by disconnecting D- of a full speed serial device. Possibly similar bug in non-AMD hardware has been reported to linux-usb. In such case, failed TD is given back without erasing from the ring and endpoint isn't reset. If some URB is unlinked later, Stop Endpoint fails and its handler resets the endpoint. On next submission it will restart on the stale TD. Outcome is UAF on success, or another halt on error and then Dequeue doesn't move and URBs are stuck. Unlinking and resubmitting the URBs causes unlimited ring expansion if the situation repeats. This can be solved by ignoring Endpoint Context State and trusting that endpoints halt when required, except one known case in ancient hardware. The check for "Already resolving halted ep" becomes redundant, because for these completion codes we now jump to xhci_handle_halted_endpoint() which deals with pending EP_HALTED internally. Link: https://lore.kernel.org/linux-usb/20250311234139.0e73e138@foxbook/ Link: https://lore.kernel.org/linux-usb/20250918055527.4157212-1-zhangjinpeng@kylinos.cn/ Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 73 ++++++++++++------------------------ 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 88022d221c70..95005f9a3504 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2180,24 +2180,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td, * External device side is also halted in functional stall cases. Class driver * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later. */ -static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code) +static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx, + unsigned int comp_code) { - /* Stall halts both internal and device side endpoint */ - if (comp_code == COMP_STALL_ERROR) - return true; + int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); - /* TRB completion codes that may require internal halt cleanup */ - if (comp_code == COMP_USB_TRANSACTION_ERROR || - comp_code == COMP_BABBLE_DETECTED_ERROR || - comp_code == COMP_SPLIT_TRANSACTION_ERROR) + switch (comp_code) { + case COMP_STALL_ERROR: + /* on xHCI this always halts, including protocol stall */ + return true; + case COMP_BABBLE_DETECTED_ERROR: /* * The 0.95 spec says a babbling control endpoint is not halted. * The 0.96 spec says it is. Some HW claims to be 0.95 * compliant, but it halts the control endpoint anyway. * Check endpoint context if endpoint is halted. */ - if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED) - return true; + if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP) + return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED; + + fallthrough; + case COMP_USB_TRANSACTION_ERROR: + case COMP_SPLIT_TRANSACTION_ERROR: + /* these errors halt all non-isochronous endpoints */ + return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP; + } return false; } @@ -2234,41 +2241,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * the ring dequeue pointer or take this TD off any lists yet. */ return; - case COMP_USB_TRANSACTION_ERROR: - case COMP_BABBLE_DETECTED_ERROR: - case COMP_SPLIT_TRANSACTION_ERROR: - /* - * If endpoint context state is not halted we might be - * racing with a reset endpoint command issued by a unsuccessful - * stop endpoint completion (context error). In that case the - * td should be on the cancelled list, and EP_HALTED flag set. - * - * Or then it's not halted due to the 0.95 spec stating that a - * babbling control endpoint should not halt. The 0.96 spec - * again says it should. Some HW claims to be 0.95 compliant, - * but it halts the control endpoint anyway. - */ - if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) { - /* - * If EP_HALTED is set and TD is on the cancelled list - * the TD and dequeue pointer will be handled by reset - * ep command completion - */ - if ((ep->ep_state & EP_HALTED) && - !list_empty(&td->cancelled_td_list)) { - xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n", - (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->start_trb)); - return; - } - /* endpoint not halted, don't reset it */ - break; - } - /* Almost same procedure as for STALL_ERROR below */ - xhci_clear_hub_tt_buffer(xhci, td, ep); - xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return; - case COMP_STALL_ERROR: + } + + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) { /* * xhci internal endpoint state will go to a "halt" state for * any stall, including default control pipe protocol stall. @@ -2279,14 +2254,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stall later. Hub TT buffer should only be cleared for FS/LS * devices behind HS hubs for functional stalls. */ - if (ep->ep_index != 0) + if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR)) xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); return; /* xhci_handle_halted_endpoint marked td cancelled */ - default: - break; } xhci_dequeue_td(xhci, td, ep_ring, td->status); @@ -2363,7 +2336,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_STOPPED_LENGTH_INVALID: goto finish_td; default: - if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) break; xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n", trb_comp_code, ep->ep_index); @@ -2973,7 +2946,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; check_endpoint_halted: - if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); return 0; From e6aec6d9f5794e85d2312497a5d81296d885090e Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Wed, 19 Nov 2025 16:24:04 +0200 Subject: [PATCH 109/161] usb: xhci: Don't unchain link TRBs on quirky HCs Some old HCs ignore transfer ring link TRBs whose chain bit is unset. This breaks endpoint operation and sometimes makes it execute other ring's TDs, which may corrupt their buffers or cause unwanted device action. We avoid this by chaining all link TRBs on affected rings. Fix an omission which allows them to be unchained by cancelling TDs. The patch was tested by reproducing this condition on an isochronous endpoint (non-power-of-two TDs are sometimes split not to cross 64K) and printing link TRBs in trb_to_noop() on good and buggy HCs. Actual hardware malfunction is rare since it requires Missed Service Error shortly before the unchained link TRB, at least on NEC and AMD. I have never seen it after commit bb0ba4cb1065 ("usb: xhci: Apply the link chain quirk on NEC isoc endpoints"), but it's Russian roulette and I can't test all affected hosts and workloads. Fairly often MSEs happen after cancellation because the endpoint was stopped. Signed-off-by: Michal Pecio Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-11-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 95005f9a3504..5acec9143811 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -145,11 +145,11 @@ static void inc_td_cnt(struct urb *urb) urb_priv->num_tds_done++; } -static void trb_to_noop(union xhci_trb *trb, u32 noop_type) +static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links) { if (trb_is_link(trb)) { - /* unchain chained link TRBs */ - trb->link.control &= cpu_to_le32(~TRB_CHAIN); + if (unchain_links) + trb->link.control &= cpu_to_le32(~TRB_CHAIN); } else { trb->generic.field[0] = 0; trb->generic.field[1] = 0; @@ -466,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, xhci_dbg(xhci, "Turn aborted command %p to no-op\n", i_cmd->command_trb); - trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP); + trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false); /* * caller waiting for completion is called when command @@ -798,13 +798,18 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, * (The last TRB actually points to the ring enqueue pointer, which is not part * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. */ -static void td_to_noop(struct xhci_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_td *td, bool flip_cycle) { + bool unchain_links; struct xhci_segment *seg = td->start_seg; union xhci_trb *trb = td->start_trb; + /* link TRBs should now be unchained, but some old HCs expect otherwise */ + unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM); + while (1) { - trb_to_noop(trb, TRB_TR_NOOP); + trb_to_noop(trb, TRB_TR_NOOP, unchain_links); /* flip cycle if asked to */ if (flip_cycle && trb != td->start_trb && trb != td->end_trb) @@ -1092,16 +1097,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) "Found multiple active URBs %p and %p in stream %u?\n", td->urb, cached_td->urb, td->urb->stream_id); - td_to_noop(cached_td, false); + td_to_noop(xhci, ep, cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -1126,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) continue; xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", td->urb); - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -4241,7 +4246,7 @@ cleanup: */ urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(&urb_priv->td[0], true); + td_to_noop(xhci, xep, &urb_priv->td[0], true); /* Reset the ring enqueue back to the first TRB and its cycle bit. */ ep_ring->enqueue = urb_priv->td[0].start_trb; From 1ebf363fcdf6b0394d0190468824bd55a0e96fc1 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Wed, 19 Nov 2025 16:24:05 +0200 Subject: [PATCH 110/161] usb: xhci: replace use of system_wq with system_percpu_wq Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") Switch to using system_percpu_wq because system_wq is going away as part of a workqueue restructuring. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 8 ++++---- drivers/usb/host/xhci-ring.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index ecda964e018a..9da4f3b452cb 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -374,7 +374,7 @@ int dbc_ep_queue(struct dbc_request *req) ret = dbc_ep_do_queue(req); spin_unlock_irqrestore(&dbc->lock, flags); - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); trace_xhci_dbc_queue_request(req); @@ -677,7 +677,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) return ret; } - return mod_delayed_work(system_wq, &dbc->event_work, + return mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(dbc->poll_interval)); } @@ -1023,7 +1023,7 @@ static void xhci_dbc_handle_events(struct work_struct *work) return; } - mod_delayed_work(system_wq, &dbc->event_work, + mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(poll_interval)); } @@ -1274,7 +1274,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev, dbc->poll_interval = value; - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); return size; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5acec9143811..f1582360d96a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -435,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, + return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer, msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } From 6c2689712177ec1ceb1bf40558c2a957e5c494c9 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:06 +0200 Subject: [PATCH 111/161] usb: xhci: remove deprecated TODO comment The Device Context Base Address Array (DCBAA) contains pointers to device contexts. These fields are 64-bit registers, capable of holding 64-bit addresses. When struct 'xhci_device_context_array' was introduced in commit [1], the entries were represented as pairs of 'u32', requiring a custom helper function to set 64-bit addresses. This was later made redundant by commit [2], which changed the representation to a single 'u64', allowing direct assignment. The associated TODO comment referencing the old 32-bit representation is no longer relevant and is removed. Link: https://git.kernel.org/torvalds/c/a74588f94655 [1] Link: https://git.kernel.org/torvalds/c/8e595a5d30a5 [2] Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-13-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index adabe26b413b..3d644d16d9fb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -791,7 +791,6 @@ struct xhci_device_context_array { /* private xHCD pointers */ dma_addr_t dma; }; -/* TODO: write function to set the 64-bit device DMA address */ /* * TODO: change this to be dynamically sized at HC mem init time since the HC * might not be able to handle the maximum number of devices possible. From 2085fa6c0f337dc30bb869bceceb641a48f03f0a Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:07 +0200 Subject: [PATCH 112/161] usb: xhci: remove unused trace operation and argument Remove endpoint number 'ep_num' argument and memory operation from xhci_log_ctx() trace function. These changes were added in commit 1d27fabec068 ("xhci: add xhci_address_ctx trace event") on Aug 14, 2013 and have never been used. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-14-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-trace.h | 18 ++++-------------- drivers/usb/host/xhci.c | 11 ++++------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 481becbcbf81..8e5b8e1282f7 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -71,18 +71,13 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion, ); DECLARE_EVENT_CLASS(xhci_log_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num), + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx), TP_STRUCT__entry( __field(int, ctx_64) __field(unsigned, ctx_type) __field(dma_addr_t, ctx_dma) __field(u8 *, ctx_va) - __field(unsigned, ctx_ep_num) - __dynamic_array(u32, ctx_data, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)) ), TP_fast_assign( @@ -90,10 +85,6 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, __entry->ctx_type = ctx->type; __entry->ctx_dma = ctx->dma; __entry->ctx_va = ctx->bytes; - __entry->ctx_ep_num = ep_num; - memcpy(__get_dynamic_array(ctx_data), ctx->bytes, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)); ), TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p", __entry->ctx_64, __entry->ctx_type, @@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, ); DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num) + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx) ); DECLARE_EVENT_CLASS(xhci_log_trb, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 593b9d3aa9b6..7ac8198d0d7b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4379,8 +4379,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); ctrl_ctx->drop_flags = 0; - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); trace_xhci_address_ctrl_ctx(ctrl_ctx); spin_lock_irqsave(&xhci->lock, flags); @@ -4440,7 +4439,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_err(xhci, "ERROR: unexpected setup %s command completion code 0x%x.\n", act, command->status); - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); ret = -EINVAL; break; } @@ -4458,14 +4457,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); /* * USB core uses address 1 for the roothubs, so we add one to the * address given back to us by the HC. */ - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; From 70651cc3f5a4c7cec529f121e36ea3b45ea84778 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:08 +0200 Subject: [PATCH 113/161] usb: xhci: use cached HCSPARAMS1 value The Structural Parameters 1 (HCSPARAMS1) register is read and cached in 'xhci->hcs_params1' during host controller initialization. Since this register is read-only and its value remains constant for the lifetime of the controller, re-reading it later is unnecessary. Replace subsequent register reads with the cached 'xhci->hcs_params1' value to avoid redundant MMIO access. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-15-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7ac8198d0d7b..dbe64ea47936 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4235,8 +4235,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) xhci_err(xhci, "Error while assigning device slot ID: %s\n", xhci_trb_comp_code_string(command->status)); xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", - HCS_MAX_SLOTS( - readl(&xhci->cap_regs->hcs_params1))); + HCS_MAX_SLOTS(xhci->hcs_params1)); xhci_free_command(xhci, command); return 0; } From df08973556851b29bd78e79db696d992ed1b43f0 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:09 +0200 Subject: [PATCH 114/161] usb: xhci: simplify handling of Structural Parameters 1 values The 32-bit read-only HCSPARAMS1 register contains the following fields: Bits 7:0 - Number of Device Slots (MaxSlots) Bits 18:8 - Number of Interrupters (MaxIntrs) Bits 23:19 - Reserved Bits 31:24 - Number of Ports (MaxPorts) Since the register value is constant for the lifetime of the controller, it is cached in 'xhci->hcs_params1'. However, platform drivers may override the number of interrupters through a separate variable, 'xhci->max_interrupters', leaving only the maximum slots and ports values still derived from the cached register. To simplify the code and improve readability, replace 'xhci->hcs_params1' with two dedicated 'u8' fields: 'xhci->max_slots' and 'xhci->max_ports'. These values are initialized once and used directly instead of calling 'HCS_MAX_SLOTS()' and 'HCS_MAX_PORTS()' macros. This change reduces code clutter without increasing memory usage. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-16-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 15 ++++----------- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci-mem.c | 31 +++++++++++++------------------ drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci-ring.c | 6 ++---- drivers/usb/host/xhci.c | 21 ++++++++++----------- drivers/usb/host/xhci.h | 3 ++- 7 files changed, 33 insertions(+), 47 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index d32ac8f84691..ae50b667a548 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -613,20 +613,16 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, struct dentry *parent) { - unsigned int num_ports; char port_name[8]; struct xhci_port *port; struct dentry *dir; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - parent = debugfs_create_dir("ports", parent); - while (num_ports--) { - scnprintf(port_name, sizeof(port_name), "port%02d", - num_ports + 1); + for (int i = 0; i < xhci->max_ports; i++) { + scnprintf(port_name, sizeof(port_name), "port%02d", i + 1); dir = debugfs_create_dir(port_name, parent); - port = &xhci->hw_ports[num_ports]; + port = &xhci->hw_ports[i]; debugfs_create_file("portsc", 0644, dir, port, &port_fops); } } @@ -634,7 +630,6 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, struct seq_file *s) { - unsigned int num_ports; unsigned int i; int ret; struct xhci_container_ctx *ctx; @@ -645,8 +640,6 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, if (ret < 0) return ret; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - ctx = xhci_alloc_port_bw_ctx(xhci, 0); if (!ctx) { pm_runtime_put_sync(dev); @@ -661,7 +654,7 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, /* print all roothub ports available bandwidth * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved */ - for (i = 1; i < num_ports+1; i++) + for (i = 1; i <= xhci->max_ports; i++) seq_printf(s, "port[%d] available bw: %d%%.\n", i, ctx->bytes[i]); err_out: diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index c4c85312b04c..cf358e5c6642 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable all Device Slots */ xhci_dbg(xhci, "Disable all slots\n"); spin_unlock_irqrestore(&xhci->lock, *flags); - for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 9a6a8d9f3770..2bbbf64a32c8 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -951,7 +951,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i /* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) { /* are any devices using this tt_info? */ - for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i < xhci->max_slots; i++) { vdev = xhci->devs[i]; if (vdev && (vdev->tt_info == tt_info)) xhci_free_virt_devices_depth_first( @@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int i, j, num_ports; + int i, j; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1918,8 +1918,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < num_ports && xhci->rh_bw; i++) { + for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) { struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; for (j = 0; j < XHCI_MAX_INTERVAL; j++) { struct list_head *ep = &bwt->interval_bw[j].endpoints; @@ -1928,7 +1927,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } } - for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) + for (i = xhci->max_slots; i > 0; i--) xhci_free_virt_devices_depth_first(xhci, i); dma_pool_destroy(xhci->segment_pool); @@ -1964,7 +1963,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (!xhci->rh_bw) goto no_bw; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { struct xhci_tt_bw_info *tt, *n; list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) { list_del(&tt->tt_list); @@ -2165,7 +2164,7 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, if (!rhub->ports) return; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) continue; @@ -2188,19 +2187,17 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) { void __iomem *base; u32 offset; - unsigned int num_ports; int i, j; int cap_count = 0; u32 cap_start; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports), - flags, dev_to_node(dev)); + xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports), + flags, dev_to_node(dev)); if (!xhci->hw_ports) return -ENOMEM; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i]; xhci->hw_ports[i].hw_portnum = i; @@ -2208,11 +2205,10 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) init_completion(&xhci->hw_ports[i].u3exit_done); } - xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, - dev_to_node(dev)); + xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev)); if (!xhci->rh_bw) return -ENOMEM; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { struct xhci_interval_bw_table *bw_table; INIT_LIST_HEAD(&xhci->rh_bw[i].tts); @@ -2244,9 +2240,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) offset = cap_start; while (offset) { - xhci_add_in_port(xhci, num_ports, base + offset, cap_count); - if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == - num_ports) + xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count); + if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports) break; offset = xhci_find_next_ext_cap(base, offset, XHCI_EXT_CAPS_PROTOCOL); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2ba0261a29c1..585b2f3117b0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -896,7 +896,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT)) return 0; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { port = &xhci->hw_ports[i]; portsc = xhci_portsc_readl(port); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f1582360d96a..0ac7f9870d3d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1394,7 +1394,7 @@ void xhci_hc_died(struct xhci_hcd *xhci) xhci_cleanup_command_queue(xhci); /* return any pending urbs, remove may be waiting for them */ - for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 0; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; for (j = 0; j < 31; j++) @@ -1994,7 +1994,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) struct usb_hcd *hcd; u32 port_id; u32 portsc, cmd_reg; - int max_ports; unsigned int hcd_portnum; struct xhci_bus_state *bus_state; bool bogus_port_status = false; @@ -2006,9 +2005,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) "WARN: xHC returned failed port status event\n"); port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); - if ((port_id <= 0) || (port_id > max_ports)) { + if ((port_id <= 0) || (port_id > xhci->max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); return; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index dbe64ea47936..ad5ef294d4f3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -291,8 +291,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) if (upper_32_bits(val)) xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); - intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1), - ARRAY_SIZE(xhci->run_regs->ir_set)); + intrs = min_t(u32, xhci->max_interrupters, ARRAY_SIZE(xhci->run_regs->ir_set)); for (i = 0; i < intrs; i++) { struct xhci_intr_reg __iomem *ir; @@ -484,15 +483,13 @@ static void xhci_hcd_page_size(struct xhci_hcd *xhci) static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci) { u32 config_reg; - u32 max_slots; - max_slots = HCS_MAX_SLOTS(xhci->hcs_params1); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots", - max_slots); + xhci->max_slots); config_reg = readl(&xhci->op_regs->config_reg); config_reg &= ~HCS_SLOTS_MASK; - config_reg |= max_slots; + config_reg |= xhci->max_slots; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x", config_reg); @@ -4235,7 +4232,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) xhci_err(xhci, "Error while assigning device slot ID: %s\n", xhci_trb_comp_code_string(command->status)); xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", - HCS_MAX_SLOTS(xhci->hcs_params1)); + xhci->max_slots); xhci_free_command(xhci, command); return 0; } @@ -5416,6 +5413,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) */ struct device *dev = hcd->self.sysdev; int retval; + u32 hcs_params1; /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; @@ -5441,7 +5439,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->run_regs = hcd->regs + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ - xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + hcs_params1 = readl(&xhci->cap_regs->hcs_params1); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase)); @@ -5449,10 +5447,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->max_slots = HCS_MAX_SLOTS(hcs_params1); + xhci->max_ports = HCS_MAX_PORTS(hcs_params1); /* 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); + if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); xhci->quirks |= quirks; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3d644d16d9fb..fface54dbc7a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1500,7 +1500,6 @@ struct xhci_hcd { struct xhci_doorbell_array __iomem *dba; /* Cached register copies of read-only HC data */ - __u32 hcs_params1; __u32 hcs_params2; __u32 hcs_params3; __u32 hcc_params; @@ -1511,6 +1510,8 @@ struct xhci_hcd { /* packed release number */ u16 hci_version; u16 max_interrupters; + u8 max_slots; + u8 max_ports; /* imod_interval in ns (I * 250ns) */ u32 imod_interval; u32 page_size; From 1668263a13ae08c812d6da70690ee61caea73bce Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:10 +0200 Subject: [PATCH 115/161] usb: xhci: limit number of ports to 127 The xHCI driver allocates various port-related structures based on the maximum number of ports reported by the controller. The Number of Ports (MaxPorts) field occupies bits 31:24 of the HCSPARAMS1 register and can represent values up to 255. However, the 'HCS_MAX_PORTS()' macro currently reads bits 30:24, effectively limiting the maximum to 127. Fixing the macro increases the reported port limit to 255, which in turn increases memory usage regardless of how many ports are actually used. To maintain compatibility and control memory consumption, set 'xhci->max_ports' to the minimum of the value read from 'HCS_MAX_PORTS()' and 127 (MAX_HC_PORTS). This preserves the existing limit while making the restriction explicit and easier to adjust in the future. Summary: * Port allocations are now limited to 127. * HC max ports macro now correctly reads the MaxPorts value. * Macro 'MAX_HC_PORTS' can be modified to set the port limit. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-17-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 4 ++-- drivers/usb/host/xhci.c | 2 +- drivers/usb/host/xhci.h | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 89bc83e4f1eb..8390c969389e 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -12,8 +12,8 @@ #define HCS_SLOTS_MASK 0xff /* bits 8:18, Max Interrupters */ #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) -/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ -#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) +/* bits 31:24, Max Ports - max value is 255 */ +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) /* HCSPARAMS2 - hcs_params2 - bitmasks */ /* bits 0:3, frames or uframes that SW needs to queue transactions diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ad5ef294d4f3..cb82b15fc203 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5448,7 +5448,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); - xhci->max_ports = HCS_MAX_PORTS(hcs_params1); + xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index fface54dbc7a..e04bc491a22b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -34,7 +34,10 @@ /* Max number of USB devices for any host controller - limit in section 6.1 */ #define MAX_HC_SLOTS 256 -/* Section 5.3.3 - MaxPorts */ +/* + * Max Number of Ports. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 255. + */ #define MAX_HC_PORTS 127 /* From 8e9a3a1ea1b6e95e4503b258e7d6b1c4790c2e82 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:11 +0200 Subject: [PATCH 116/161] usb: xhci: limit number of interrupts to 128 The xHCI driver defines only 128 interrupter register slots, yet allows up to 2047 interrupters. According to the xHCI specification, the maximum valid number of interrupters is 1024. These mismatches can lead to out-of-range accesses and excessive memory use. The Number of Interrupters (MaxIntrs) field occupies bits 18:8 of the HCSPARAMS1 register, which can yield a value up to 2047, although the specification limits it to 1024. Cap the value using the 'MAX_HC_INTRS' macro. Set 'xhci->max_intrs' to the minimum of the value reported by the HCSPARAMS1 register and 'MAX_HC_INTRS'. The interrupter register slot array is defined for 1024 entries, serving only as a structural template and not increasing memory usage. Although the xHCI specification allows up to 1024 interrupters, raising 'MAX_HC_INTRS' above 128 provides no practical benefit. The driver only uses the primary interrupter (0), and secondary interrupters (1+) are rarely, if ever, used in practice. No reports exist of usage beyond 128. Therefore, I have limited it to 128. Summary: * Interrupter allocations are now limited to 128 from 2047. * Interrupter Register template slots are set to 1024 from 128. * Macro 'MAX_HC_INTRS' can be modified to set the interrupter limit. ==== Detailed interrupter explanation ==== There are two relevant components: Interrupter array: This holds the software interrupter structures and is allocated by the xhci driver. The number of interrupters allocated is determined by the HCSPARAMS1 register field, which specifies the supported interrupter count. Interrupter register slots: This is a template struct used to access the hardware's runtime registers. It is not allocated by the driver, the hardware defines and owns this memory region, and the driver only maps it for MMIO access. Each entry in the interrupter array points to its corresponding interrupter register slot in the hardware region once that interrupter is enabled. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-18-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 13 ++++++------- drivers/usb/host/xhci.h | 7 ++++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index cb82b15fc203..1e4032bf1223 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -250,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) struct iommu_domain *domain; int err, i; u64 val; - u32 intrs; /* * Some Renesas controllers get into a weird state if they are @@ -291,9 +290,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) if (upper_32_bits(val)) xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); - intrs = min_t(u32, xhci->max_interrupters, ARRAY_SIZE(xhci->run_regs->ir_set)); - - for (i = 0; i < intrs; i++) { + for (i = 0; i < xhci->max_interrupters; i++) { struct xhci_intr_reg __iomem *ir; ir = &xhci->run_regs->ir_set[i]; @@ -5450,7 +5447,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ - if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) + if (!xhci->max_interrupters) + xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); + else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); xhci->quirks |= quirks; @@ -5670,8 +5669,8 @@ static int __init xhci_hcd_init(void) BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8); - /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ - BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); + /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */ + BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8); if (usb_disabled()) return -ENODEV; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e04bc491a22b..2b0796f6d00e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -39,6 +39,11 @@ * Valid values are in the range of 1 to 255. */ #define MAX_HC_PORTS 127 +/* + * Max number of Interrupter Register Sets. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 1024. + */ +#define MAX_HC_INTRS 128 /* * xHCI register interface. @@ -278,7 +283,7 @@ struct xhci_intr_reg { struct xhci_run_regs { __le32 microframe_index; __le32 rsvd[7]; - struct xhci_intr_reg ir_set[128]; + struct xhci_intr_reg ir_set[1024]; }; /** From edab00902be0001b65be934ce3e748fcc36cc220 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:12 +0200 Subject: [PATCH 117/161] usb: xhci: improve xhci-caps.h comments No functional changes. This patch updates comments in xhci-caps.h for better readability and consistency. Each Capability Register bit field now includes a brief description of its name and valid range, following a uniform comment format across the file. These updates are based on the xHCI specification, revision 1.2. Bit field comment format: /* - , */ Why print the bit range? The bit range aids in identifying missing macros and reserved bit ranges. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-19-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 100 ++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 8390c969389e..8a435786f950 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -1,93 +1,107 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * xHCI Host Controller Capability Registers. + * xHCI Specification Section 5.3, Revision 1.2. + */ -/* hc_capbase bitmasks */ -/* bits 7:0 - how long is the Capabilities register */ +/* hc_capbase - bitmasks */ +/* bits 7:0 - Capability Registers Length */ #define HC_LENGTH(p) XHCI_HC_LENGTH(p) -/* bits 31:16 */ +/* bits 15:8 - Rsvd */ +/* bits 31:16 - Host Controller Interface Version Number */ #define HC_VERSION(p) (((p) >> 16) & 0xffff) /* HCSPARAMS1 - hcs_params1 - bitmasks */ -/* bits 0:7, Max Device Slots */ +/* bits 7:0 - Number of Device Slots */ #define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) #define HCS_SLOTS_MASK 0xff -/* bits 8:18, Max Interrupters */ +/* bits 18:8 - Number of Interrupters, max values is 1024 */ #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) /* bits 31:24, Max Ports - max value is 255 */ #define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) /* HCSPARAMS2 - hcs_params2 - bitmasks */ -/* bits 0:3, frames or uframes that SW needs to queue transactions - * ahead of the HW to meet periodic deadlines */ +/* + * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW + * needs to queue transactions ahead of the HW to meet periodic deadlines. + */ #define HCS_IST(p) (((p) >> 0) & 0xf) -/* bits 4:7, max number of Event Ring segments */ +/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) -/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ -/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ +/* bits 20:8 - Rsvd */ +/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +/* bit 26 - Scratchpad restore, for save/restore HW state */ +/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ #define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ -/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ #define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) -/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +/* bits 15:8 - Rsvd */ +/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ #define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) -/* HCCPARAMS - hcc_params - bitmasks */ -/* true: HC can use 64-bit address pointers */ +/* HCCPARAMS1 - hcc_params - bitmasks */ +/* bit 0 - 64-bit Addressing Capability */ #define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) -/* true: HC can do bandwidth negotiation */ +/* bit 1 - BW Negotiation Capability */ #define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) -/* true: HC uses 64-byte Device Context structures - * FIXME 64-byte context structures aren't supported yet. - */ +/* bit 2 - Context Size */ #define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) -/* true: HC has port power switches */ +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) +/* bit 3 - Port Power Control */ #define HCC_PPC(p) ((p) & (1 << 3)) -/* true: HC has port indicators */ +/* bit 4 - Port Indicators */ #define HCS_INDICATOR(p) ((p) & (1 << 4)) -/* true: HC has Light HC Reset Capability */ +/* bit 5 - Light HC Reset Capability */ #define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) -/* true: HC supports latency tolerance messaging */ +/* bit 6 - Latency Tolerance Messaging Capability */ #define HCC_LTC(p) ((p) & (1 << 6)) -/* true: no secondary Stream ID Support */ +/* bit 7 - No Secondary Stream ID Support */ #define HCC_NSS(p) ((p) & (1 << 7)) -/* true: HC supports Stopped - Short Packet */ +/* bit 8 - Parse All Event Data */ +/* bit 9 - Short Packet Capability */ #define HCC_SPC(p) ((p) & (1 << 9)) -/* true: HC has Contiguous Frame ID Capability */ +/* bit 10 - Stopped EDTLA Capability */ +/* bit 11 - Contiguous Frame ID Capability */ #define HCC_CFC(p) ((p) & (1 << 11)) -/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) -/* Extended Capabilities pointer from PCI base - section 5.3.6 */ +/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ #define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) -#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) - -/* db_off bitmask - bits 31:2 Doorbell Array Offset */ +/* DBOFF - db_off - bitmasks */ +/* bits 1:0 - Rsvd */ +/* bits 31:2 - Doorbell Array Offset */ #define DBOFF_MASK (0xfffffffc) -/* run_regs_off bitmask - bits 0:4 reserved */ +/* RTSOFF - run_regs_off - bitmasks */ +/* bits 4:0 - Rsvd */ +/* bits 31:5 - Runtime Register Space Offse */ #define RTSOFF_MASK (~0x1f) /* HCCPARAMS2 - hcc_params2 - bitmasks */ -/* true: HC supports U3 entry Capability */ +/* bit 0 - U3 Entry Capability */ #define HCC2_U3C(p) ((p) & (1 << 0)) -/* true: HC supports Configure endpoint command Max exit latency too large */ +/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */ #define HCC2_CMC(p) ((p) & (1 << 1)) -/* true: HC supports Force Save context Capability */ +/* bit 2 - Force Save Context Capabilitu */ #define HCC2_FSC(p) ((p) & (1 << 2)) -/* true: HC supports Compliance Transition Capability */ +/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */ #define HCC2_CTC(p) ((p) & (1 << 3)) -/* true: HC support Large ESIT payload Capability > 48k */ +/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */ #define HCC2_LEC(p) ((p) & (1 << 4)) -/* true: HC support Configuration Information Capability */ +/* bit 5 - Configuration Information Capability */ #define HCC2_CIC(p) ((p) & (1 << 5)) -/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */ +/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */ #define HCC2_ETC(p) ((p) & (1 << 6)) -/* true: HC support Extended TBC TRB Status Capability */ +/* bit 7 - Extended TBC TRB Status Capability */ #define HCC2_ETC_TSC(p) ((p) & (1 << 7)) -/* true: HC support Get/Set Extended Property Capability */ +/* bit 8 - Get/Set Extended Property Capability */ #define HCC2_GSC(p) ((p) & (1 << 8)) -/* true: HC support Virtualization Based Trusted I/O Capability */ +/* bit 9 - Virtualization Based Trusted I/O Capability */ #define HCC2_VTC(p) ((p) & (1 << 9)) -/* true: HC support Double BW on a eUSB2 HS ISOC EP */ +/* bit 10 - Rsvd */ +/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */ #define HCC2_EUSB2_DIC(p) ((p) & (1 << 11)) +/* bits 31:12 - Rsvd */ From f724e34719f03133e6c1ebf7386070f0b9a3209f Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:13 +0200 Subject: [PATCH 118/161] usb: xhci: simplify Isochronous Scheduling Threshold handling The IST is represented by bits 2:0, with bit 3 indicating the unit of measurement, Frames or Microframes. Introduce xhci_ist_microframes(), which returns the IST value in Microframes, simplifying the code and reducing duplication. Improve documentation in xhci-caps.h to clarify the IST register specifics, including the unit conversion details. These change removes the need to explain it each time the IST values is retrieved. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-20-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 9 ++++++++- drivers/usb/host/xhci-ring.c | 26 ++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 8a435786f950..e772d5f30d36 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -24,8 +24,15 @@ /* * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW * needs to queue transactions ahead of the HW to meet periodic deadlines. + * - Bits 2:0: Threshold value + * - Bit 3: Unit indicator + * - '1': Threshold in Frames + * - '0': Threshold in Microframes (uframes) + * Note: 1 Frame = 8 Microframes + * xHCI specification section 5.3.4. */ -#define HCS_IST(p) (((p) >> 0) & 0xf) +#define HCS_IST_VALUE(p) ((p) & 0x7) +#define HCS_IST_UNIT(p) ((p) & (1 << 3)) /* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) /* bits 20:8 - Rsvd */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0ac7f9870d3d..104fd6f83265 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3961,6 +3961,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, return total_packet_count - 1; } +/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ +static int xhci_ist_microframes(struct xhci_hcd *xhci) +{ + int ist = HCS_IST_VALUE(xhci->hcs_params2); + + if (HCS_IST_UNIT(xhci->hcs_params2)) + ist *= 8; + return ist; +} + /* * Calculates Frame ID field of the isochronous TRB identifies the * target frame that the Interval associated with this Isochronous @@ -3980,17 +3990,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, else start_frame = (urb->start_frame + index * urb->interval) >> 3; - /* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2): - * - * If bit [3] of IST is cleared to '0', software can add a TRB no - * later than IST[2:0] Microframes before that TRB is scheduled to - * be executed. - * If bit [3] of IST is set to '1', software can add a TRB no later - * than IST[2:0] Frames before that TRB is scheduled to be executed. - */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); /* Software shall not schedule an Isoch TD with a Frame ID value that * is less than the Start Frame ID or greater than the End Frame ID, @@ -4311,9 +4311,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Round up to the next frame and consider the time before trb really * gets scheduled by hardare. */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); start_frame += ist + XHCI_CFC_DELAY; start_frame = roundup(start_frame, 8); From 9936909099cc13a50d7ed1a12370eb03d72ab492 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:14 +0200 Subject: [PATCH 119/161] usb: xhci: simplify Max Scratchpad buffer macros Max Scratchpad Buffers consist of two bit-fields: bits 25:21 - Max Scratchpad Buffers High, 5 Most significant bits bits 27:31 - Max Scratchpad Buffers Low, 5 Least significant bits Combined they create the Max Scratchpad Buffers value. Add two new macros, 'HCS_MAX_SP_HI' and 'HCS_MAX_SP_LO', to separately extract the high and low parts of the Max Scratchpad Buffers. These are then combined using 'HCS_MAX_SCRATCHPAD' macro. This change simplifies the code and makes it similar to other split value register macros in the xhci driver. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-21-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index e772d5f30d36..af47aebc5ba8 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -37,9 +37,11 @@ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) /* bits 20:8 - Rsvd */ /* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) /* bit 26 - Scratchpad restore, for save/restore HW state */ /* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ -#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) +#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) +#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ From 2282ab38d87e63010195be5569a751448724d14b Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:15 +0200 Subject: [PATCH 120/161] usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h Drop the dependency of xhci-caps.h on xhci-ext-caps.h by eliminating 2 instances where macros in xhci-caps.h were redefined from xhci-ext-caps.h. Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-22-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index af47aebc5ba8..99557df89f88 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -6,7 +6,7 @@ /* hc_capbase - bitmasks */ /* bits 7:0 - Capability Registers Length */ -#define HC_LENGTH(p) XHCI_HC_LENGTH(p) +#define HC_LENGTH(p) ((p) & 0xff) /* bits 15:8 - Rsvd */ /* bits 31:16 - Host Controller Interface Version Number */ #define HC_VERSION(p) (((p) >> 16) & 0xffff) @@ -77,7 +77,7 @@ /* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ -#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) +#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) /* DBOFF - db_off - bitmasks */ /* bits 1:0 - Rsvd */ From 757508d6d7714c4ffa7a3b6bc95b7cddb992b291 Mon Sep 17 00:00:00 2001 From: Niklas Neronin Date: Wed, 19 Nov 2025 16:24:16 +0200 Subject: [PATCH 121/161] usb: xhci: standardize single bit-field macros Convert single bit-field macros to simple masks. The change makes the masks more universal. Multi bit-field macros are changed in the next commit. After both changes, all masks in xhci-caps.h will follow the same format. I plan to introduce this change to all xhci macros. Bit shift operations on a 32-bit signed can be problematic on some architectures. Instead use BIT() macro, which returns a 64-bit unsigned value. This ensures that the shift operation is performed on an unsigned type, which is safer and more portable across different architectures. Using unsigned integers for bit shifts avoids issues related to sign bits and ensures consistent behavior. Switch from 32-bit to 64-bit? As far as I am aware, this does not cause any issues. Performing bitwise operations between 32 and 64 bit values, the smaller operand is promoted to match the size of the larger one, resulting in a 64-bit operation. This promotion extends the 32-bit value to 64 bits, by zero-padding (for unsigned). Will the change to 64-bit slow down the xhci driver? On a 64-bit architecture - No. On a 32-bit architecture, yes? but in my opinion the performance decrease does not outweigh the readability and other benefits of using BIT() macro. Why not use FIELD_GET() and FIELD_PREP()? While they can be used for single bit macros, I prefer to use simple bitwise operation directly. Because, it takes less space, is less overhead and is as clear as if using FIELD_GET() and FIELD_PREP(). Why not use test_bit() macro? Same reason as with FIELD_GET() and FIELD_PREP(). Suggested-by: Sakari Ailus Reviewed-by: Sakari Ailus Signed-off-by: Niklas Neronin Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-23-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 48 +++++++++++++++++---------------- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-hub.c | 6 ++--- drivers/usb/host/xhci-mem.c | 7 +++-- drivers/usb/host/xhci-ring.c | 8 +++--- drivers/usb/host/xhci-trace.h | 2 +- drivers/usb/host/xhci.c | 2 +- 7 files changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 99557df89f88..52153c4a43a8 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -4,6 +4,8 @@ * xHCI Specification Section 5.3, Revision 1.2. */ +#include + /* hc_capbase - bitmasks */ /* bits 7:0 - Capability Registers Length */ #define HC_LENGTH(p) ((p) & 0xff) @@ -32,7 +34,7 @@ * xHCI specification section 5.3.4. */ #define HCS_IST_VALUE(p) ((p) & 0x7) -#define HCS_IST_UNIT(p) ((p) & (1 << 3)) +#define HCS_IST_UNIT BIT(3) /* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) /* bits 20:8 - Rsvd */ @@ -52,28 +54,28 @@ /* HCCPARAMS1 - hcc_params - bitmasks */ /* bit 0 - 64-bit Addressing Capability */ -#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) +#define HCC_64BIT_ADDR BIT(0) /* bit 1 - BW Negotiation Capability */ -#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) +#define HCC_BANDWIDTH_NEG BIT(1) /* bit 2 - Context Size */ -#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) -#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) +#define HCC_64BYTE_CONTEXT BIT(2) +#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32) /* bit 3 - Port Power Control */ -#define HCC_PPC(p) ((p) & (1 << 3)) +#define HCC_PPC BIT(3) /* bit 4 - Port Indicators */ -#define HCS_INDICATOR(p) ((p) & (1 << 4)) +#define HCS_INDICATOR BIT(4) /* bit 5 - Light HC Reset Capability */ -#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) +#define HCC_LIGHT_RESET BIT(5) /* bit 6 - Latency Tolerance Messaging Capability */ -#define HCC_LTC(p) ((p) & (1 << 6)) +#define HCC_LTC BIT(6) /* bit 7 - No Secondary Stream ID Support */ -#define HCC_NSS(p) ((p) & (1 << 7)) +#define HCC_NSS BIT(7) /* bit 8 - Parse All Event Data */ /* bit 9 - Short Packet Capability */ -#define HCC_SPC(p) ((p) & (1 << 9)) +#define HCC_SPC BIT(9) /* bit 10 - Stopped EDTLA Capability */ /* bit 11 - Contiguous Frame ID Capability */ -#define HCC_CFC(p) ((p) & (1 << 11)) +#define HCC_CFC BIT(11) /* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ @@ -91,26 +93,26 @@ /* HCCPARAMS2 - hcc_params2 - bitmasks */ /* bit 0 - U3 Entry Capability */ -#define HCC2_U3C(p) ((p) & (1 << 0)) +#define HCC2_U3C BIT(0) /* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */ -#define HCC2_CMC(p) ((p) & (1 << 1)) +#define HCC2_CMC BIT(1) /* bit 2 - Force Save Context Capabilitu */ -#define HCC2_FSC(p) ((p) & (1 << 2)) +#define HCC2_FSC BIT(2) /* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */ -#define HCC2_CTC(p) ((p) & (1 << 3)) +#define HCC2_CTC BIT(3) /* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */ -#define HCC2_LEC(p) ((p) & (1 << 4)) +#define HCC2_LEC BIT(4) /* bit 5 - Configuration Information Capability */ -#define HCC2_CIC(p) ((p) & (1 << 5)) +#define HCC2_CIC BIT(5) /* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */ -#define HCC2_ETC(p) ((p) & (1 << 6)) +#define HCC2_ETC BIT(6) /* bit 7 - Extended TBC TRB Status Capability */ -#define HCC2_ETC_TSC(p) ((p) & (1 << 7)) +#define HCC2_ETC_TSC BIT(7) /* bit 8 - Get/Set Extended Property Capability */ -#define HCC2_GSC(p) ((p) & (1 << 8)) +#define HCC2_GSC BIT(8) /* bit 9 - Virtualization Based Trusted I/O Capability */ -#define HCC2_VTC(p) ((p) & (1 << 9)) +#define HCC2_VTC BIT(9) /* bit 10 - Rsvd */ /* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */ -#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11)) +#define HCC2_EUSB2_DIC BIT(11) /* bits 31:12 - Rsvd */ diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index ae50b667a548..5322911576eb 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -355,7 +355,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, if (!strncmp(buf, "compliance", 10)) { /* If CTC is clear, compliance is enabled by default */ - if (!HCC2_CTC(xhci->hcc_params2)) + if (!(xhci->hcc_params2 & HCC2_CTC)) return count; spin_lock_irqsave(&xhci->lock, flags); /* compliance mode can only be enabled on ports in RxDetect */ diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index cf358e5c6642..04cc3d681495 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, ss_cap->bU2DevExitLat = 0; /* set later */ reg = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(reg)) + if (reg & HCC_LTC) ss_cap->bmAttributes |= USB_LTM_SUPPORT; if ((xhci->quirks & XHCI_LPM_SUPPORT)) { @@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bNbrPorts = ports; temp = 0; /* Bits 1:0 - support per-port power switching, or power always on */ - if (HCC_PPC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_PPC) temp |= HUB_CHAR_INDV_PORT_LPSM; else temp |= HUB_CHAR_NO_LPSM; @@ -1400,7 +1400,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * automatically entered as on 1.0 and prior. */ if (link_state == USB_SS_PORT_LS_COMP_MOD) { - if (!HCC2_CTC(xhci->hcc_params2)) { + if (!(xhci->hcc_params2 & HCC2_CTC)) { xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n"); break; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 2bbbf64a32c8..c708bdd69f16 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -463,7 +463,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, return NULL; ctx->type = type; - ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024; if (type == XHCI_CTX_TYPE_INPUT) ctx->size += CTX_SIZE(xhci->hcc_params); @@ -1344,7 +1344,7 @@ static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci, bool lec; /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */ - lec = xhci->hci_version > 0x100 && HCC2_LEC(xhci->hcc_params2); + lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC); /* eUSB2 double isoc bw devices are the only USB2 devices using mult */ if (usb_endpoint_is_hs_isoc_double(udev, ep) && @@ -1433,8 +1433,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ring_type = usb_endpoint_type(&ep->desc); /* Ensure host supports double isoc bandwidth for eUSB2 devices */ - if (usb_endpoint_is_hs_isoc_double(udev, ep) && - !HCC2_EUSB2_DIC(xhci->hcc_params2)) { + if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) { dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n"); return -EINVAL; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 104fd6f83265..2247fd9dd163 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3966,7 +3966,7 @@ static int xhci_ist_microframes(struct xhci_hcd *xhci) { int ist = HCS_IST_VALUE(xhci->hcs_params2); - if (HCS_IST_UNIT(xhci->hcs_params2)) + if (xhci->hcs_params2 & HCS_IST_UNIT) ist *= 8; return ist; } @@ -4135,7 +4135,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* use SIA as default, if frame id is used overwrite it */ sia_frame_id = TRB_SIA; if (!(urb->transfer_flags & URB_ISO_ASAP) && - HCC_CFC(xhci->hcc_params)) { + (xhci->hcc_params & HCC_CFC)) { frame_id = xhci_get_isoc_frame_id(xhci, urb, i); if (frame_id >= 0) sia_frame_id = TRB_FRAME_ID(frame_id); @@ -4219,7 +4219,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* store the next frame id */ - if (HCC_CFC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_CFC) xep->next_frame_id = urb->start_frame + num_tds * urb->interval; if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { @@ -4298,7 +4298,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, check_interval(urb, ep_ctx); /* Calculate the start frame and put it in urb->start_frame. */ - if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) { + if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { urb->start_frame = xep->next_frame_id; goto skip_start_over; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 8e5b8e1282f7..724cba2dbb78 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -81,7 +81,7 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, ), TP_fast_assign( - __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); + __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT; __entry->ctx_type = ctx->type; __entry->ctx_dma = ctx->dma; __entry->ctx_va = ctx->bytes; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1e4032bf1223..57a74ffadc24 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5495,7 +5495,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Set dma_mask and coherent_dma_mask to 64-bits, * if xHC supports 64-bit addressing */ - if (HCC_64BIT_ADDR(xhci->hcc_params) && + if ((xhci->hcc_params & HCC_64BIT_ADDR) && !dma_set_mask(dev, DMA_BIT_MASK(64))) { xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); From 384c57ec720597f8104f69082cdd261abb998b80 Mon Sep 17 00:00:00 2001 From: "Rai, Amardeep" Date: Wed, 19 Nov 2025 16:24:17 +0200 Subject: [PATCH 122/161] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register. Each xHCI roothub port has a Port Link Info (PORTLI) register that is used by USB3 and eUSB2V2 ports. USB3 ports show link error count, rx lane count, and tx lane count. eUSB2V2 ports show Rx Data Rate (RDR) and Tx Data Rate (TDR). Rx/Tx Data Rate is a multiple of USB2 2.0 HS 480 Mb/s data rates, and is only valid if a eUSB2V2 device is connected (CCS=1). 0 = "USB 2.0 HS" normal HS 480 Mb/s, no eUSB2V2 in use 1 = "HS1" Assymetric eUSB2V2 where this direction runs normal 480Mb/s 2 = "HS2" 960Mb/s ... 10 = "HS10" 4.8 Gb/s, max eUSB2V2 rate PORTLI is Reserved and preserve "RsvdP" for normal USB2 ports Sample output of USB3 port PORTLI: cat /sys/kernel/debug/usb/xhci/0000:00:14.0/ports/port14/portli 0x00000000 LEC=0 RLC=0 TLC=0 Signed-off-by: Rai, Amardeep Co-developed-by: Mathias Nyman Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-24-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-caps.h | 4 +++- drivers/usb/host/xhci-debugfs.c | 34 +++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-port.h | 5 +++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 52153c4a43a8..2f59b6ab1e45 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -115,4 +115,6 @@ /* bit 10 - Rsvd */ /* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */ #define HCC2_EUSB2_DIC BIT(11) -/* bits 31:12 - Rsvd */ +/* bit 12 - HC support eUSB2V2 capability */ +#define HCC2_E2V2C BIT(12) +/* bits 31:13 - Rsvd */ diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 5322911576eb..c1eb1036ede9 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -383,6 +383,39 @@ static const struct file_operations port_fops = { .release = single_release, }; +static int xhci_portli_show(struct seq_file *s, void *unused) +{ + struct xhci_port *port = s->private; + struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd); + u32 portli; + + portli = readl(&port->port_reg->portli); + + /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */ + if (port->rhub == &xhci->usb3_rhub) + seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli, + PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli)); + else if (xhci->hcc_params2 & HCC2_E2V2C) + seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli, + PORTLI_RDR(portli), PORTLI_TDR(portli)); + else + seq_printf(s, "0x%08x RsvdP\n", portli); + + return 0; +} + +static int xhci_portli_open(struct inode *inode, struct file *file) +{ + return single_open(file, xhci_portli_show, inode->i_private); +} + +static const struct file_operations portli_fops = { + .open = xhci_portli_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void xhci_debugfs_create_files(struct xhci_hcd *xhci, struct xhci_file_map *files, size_t nentries, void *data, @@ -624,6 +657,7 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, dir = debugfs_create_dir(port_name, parent); port = &xhci->hw_ports[i]; debugfs_create_file("portsc", 0644, dir, port, &port_fops); + debugfs_create_file("portli", 0444, dir, port, &portli_fops); } } diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h index f19efb966d18..889b5fb0fcd8 100644 --- a/drivers/usb/host/xhci-port.h +++ b/drivers/usb/host/xhci-port.h @@ -144,9 +144,14 @@ #define PORT_TEST_MODE_SHIFT 28 /* USB3 Protocol PORTLI Port Link Information */ +#define PORT_LEC(p) ((p) & 0xffff) #define PORT_RX_LANES(p) (((p) >> 16) & 0xf) #define PORT_TX_LANES(p) (((p) >> 20) & 0xf) +/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */ +#define PORTLI_RDR(p) ((p) & 0xf) +#define PORTLI_TDR(p) (((p) >> 4) & 0xf) + /* USB2 Protocol PORTHLPMC */ #define PORT_HIRDM(p)((p) & 3) #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) From 7ebbd0a5a9e2e94e77fed3f324978e8bc4721f45 Mon Sep 17 00:00:00 2001 From: Ronak Raheja Date: Wed, 29 Oct 2025 01:39:17 -0700 Subject: [PATCH 123/161] dt-bindings: usb: qcom,snps-dwc3: Add Kaanapali compatible Kaanapali uses a single-node USB controller architecture with the Synopsys DWC3 controller. Add this to the compatibles list to utilize the DWC3 QCOM and DWC3 core framework. Signed-off-by: Ronak Raheja Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jingyi Wang Link: https://patch.msgid.link/20251029-knp-usb-dwc3-v3-1-6d3a72783336@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index f88ad2e96f34..8cee7c5582f2 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -34,6 +34,7 @@ properties: - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 - qcom,ipq9574-dwc3 + - qcom,kaanapali-dwc3 - qcom,milos-dwc3 - qcom,msm8953-dwc3 - qcom,msm8994-dwc3 @@ -203,6 +204,7 @@ allOf: contains: enum: - qcom,ipq9574-dwc3 + - qcom,kaanapali-dwc3 - qcom,msm8953-dwc3 - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 @@ -506,6 +508,7 @@ allOf: enum: - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 + - qcom,kaanapali-dwc3 - qcom,msm8994-dwc3 - qcom,qcs615-dwc3 - qcom,qcs8300-dwc3 From a2fa8a12e6bc9d89c0505b8dd7ae38ec173d25de Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 30 Oct 2025 10:39:06 +0100 Subject: [PATCH 124/161] usb: chaoskey: fix locking for O_NONBLOCK A failure to take a lock with O_NONBLOCK needs to result in -EAGAIN. Change it. Fixes: 66e3e591891da ("usb: Add driver for Altus Metrum ChaosKey device (v2)") Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20251030093918.2248104-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/chaoskey.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 225863321dc4..45cff32656c6 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file, goto bail; mutex_unlock(&dev->rng_lock); - result = mutex_lock_interruptible(&dev->lock); - if (result) - goto bail; + if (file->f_flags & O_NONBLOCK) { + result = mutex_trylock(&dev->lock); + if (result == 0) { + result = -EAGAIN; + goto bail; + } else { + result = 0; + } + } else { + result = mutex_lock_interruptible(&dev->lock); + if (result) + goto bail; + } if (dev->valid == dev->used) { result = _chaoskey_fill(dev); if (result < 0) { From 86a35865fefff806d2674982017a47ff082aee0b Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 28 Oct 2025 17:43:03 +0200 Subject: [PATCH 125/161] usb: typec: ucsi: Set orientation_aware if UCSI version is 2.x and above For UCSI 2.0 and above, since the orientation is part of the paylad, set the orientation_aware by default and let the implementation specific update_connector op override if necessary. Signed-off-by: Abel Vesa Reviewed-by: Heikki Krogerus Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20251028-b4-ucsi-set-orientation-aware-on-version-2-and-above-v1-1-d3425f5679af@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index ec6c8f928dda..a7b388dc7fa0 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1660,6 +1660,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->driver_data = con; cap->ops = &ucsi_ops; + if (ucsi->version >= UCSI_VERSION_2_0) + con->typec_cap.orientation_aware = true; + if (ucsi->ops->update_connector) ucsi->ops->update_connector(con); From b6ebcfdcac40a27953f052e4269ce75a18825ffc Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 4 Nov 2025 08:25:02 +0800 Subject: [PATCH 126/161] usb: dwc2: fix hang during shutdown if set as peripheral dwc2 on most platforms needs phy controller, clock and power supply. All of them must be enabled/activated to properly operate. If dwc2 is configured as peripheral mode, then all the above three hardware resources are disabled at the end of the probe: /* Gadget code manages lowlevel hw on its own */ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) dwc2_lowlevel_hw_disable(hsotg); But dwc2_driver_shutdown() tries to disable the interrupts on HW IP level. This would result in hang during shutdown if dwc2 is configured as peripheral mode. Fix this hang by only disable and sync irq when lowlevel hw is enabled. Fixes: 4fdf228cdf69 ("usb: dwc2: Fix shutdown callback in platform") Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20251104002503.17158-2-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3f83ecc9fc23..b07bdf16326a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -369,11 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_disable_global_interrupts(hsotg); - synchronize_irq(hsotg->irq); - - if (hsotg->ll_hw_enabled) + if (hsotg->ll_hw_enabled) { + dwc2_disable_global_interrupts(hsotg); + synchronize_irq(hsotg->irq); dwc2_lowlevel_hw_disable(hsotg); + } } /** From 2b94b054ac4974ad2f89f7f7461840c851933adb Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 4 Nov 2025 08:25:03 +0800 Subject: [PATCH 127/161] usb: dwc2: fix hang during suspend if set as peripheral dwc2 on most platforms needs phy controller, clock and power supply. All of them must be enabled/activated to properly operate. If dwc2 is configured as peripheral mode, then all the above three hardware resources are disabled at the end of the probe: /* Gadget code manages lowlevel hw on its own */ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) dwc2_lowlevel_hw_disable(hsotg); But the dwc2_suspend() tries to read the dwc2's reg to check whether is_device_mode or not, this would result in hang during suspend if dwc2 is configured as peripheral mode. Fix this hang by bypassing suspend/resume if lowlevel hw isn't enabled. Fixes: 09a75e857790 ("usb: dwc2: refactor common low-level hw code to platform.c") Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20251104002503.17158-3-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/platform.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b07bdf16326a..ef0d73077034 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -649,9 +649,13 @@ error: static int __maybe_unused dwc2_suspend(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); - bool is_device_mode = dwc2_is_device_mode(dwc2); + bool is_device_mode; int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + + is_device_mode = dwc2_is_device_mode(dwc2); if (is_device_mode) dwc2_hsotg_suspend(dwc2); @@ -728,6 +732,9 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) From 12a8f543250cc3e84a29bf0f28a5aa28b6dfc707 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Thu, 6 Nov 2025 16:27:12 +0100 Subject: [PATCH 128/161] usb: dwc3: replace use of system_wq with system_percpu_wq Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") Switch to using system_percpu_wq because system_wq is going away as part of a workqueue restructuring. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20251106152712.279042-1-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1f67fb6aead5..3830aa2c10a9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3872,7 +3872,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, case DEPEVT_STREAM_NOSTREAM: dep->flags &= ~DWC3_EP_STREAM_PRIMED; if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) - queue_delayed_work(system_wq, &dep->nostream_work, + queue_delayed_work(system_percpu_wq, &dep->nostream_work, msecs_to_jiffies(100)); break; } From 71f91b401c334cd486b29798128aea315269de90 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 7 Nov 2025 16:42:36 +0100 Subject: [PATCH 129/161] usb: uas: add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251107154236.306620-1-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 4ed0dc19afe0..0657f5f7a51f 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1265,7 +1265,7 @@ static int __init uas_init(void) { int rv; - workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0); + workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!workqueue) return -ENOMEM; From 66371878dfe3376a55cb44bc0918a56232522590 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Fri, 7 Nov 2025 16:13:10 +0100 Subject: [PATCH 130/161] usb: typec: tipd: mark as orientation aware The driver contains orientation detection logic and correctly calls typec_set_orientation(), but forgets to set the orientation_aware capability, so the orientation value is not visible in sysfs - Fix that. Signed-off-by: Peter Korsgaard Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251107151311.2089806-1-peter@korsgaard.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index d0c86347251c..e2b26af2b84a 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1701,6 +1701,7 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) typec_cap.data = ret; typec_cap.revision = USB_TYPEC_REV_1_3; typec_cap.pd_revision = 0x300; + typec_cap.orientation_aware = true; typec_cap.driver_data = tps; typec_cap.ops = &tps6598x_ops; typec_cap.fwnode = fwnode; From a7d5fe02059af66c9759cbf9199e7ed381cc592a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 6 Nov 2025 16:36:22 +0200 Subject: [PATCH 131/161] usb: host: Do not check priv->clks[clk] There is no need to check the entries in priv->clks[] array before passing it to clk_disable_unprepare() as the clk_disable_unprepare() already check if it receives a NULL or error pointer as argument. Remove this check. This makes the code simpler. Signed-off-by: Claudiu Beznea Acked-by: Alan Stern Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251106143625.3050119-2-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 3 +-- drivers/usb/host/ohci-platform.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index bcd1c9073515..57d5a7ddac5f 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -112,8 +112,7 @@ static void ehci_platform_power_off(struct platform_device *dev) int clk; for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ehci_platform_hc_driver; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index f47ae12cde6a..af26f1449bc2 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -69,8 +69,7 @@ static void ohci_platform_power_off(struct platform_device *dev) int clk; for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ohci_platform_hc_driver; From c31a401fe7abedabb5c05b5cbf36e2fdb3e6be63 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 6 Nov 2025 16:36:23 +0200 Subject: [PATCH 132/161] usb: host: ehci-platform: Call reset assert/deassert on suspend/resume The Renesas RZ/G3S SoC supports a power-saving mode in which power to most of the SoC components is turned off, including the USB blocks. On the resume path, the reset signal must be de-asserted before applying any settings to the USB registers. To handle this properly, call reset_control_assert() and reset_control_deassert() during suspend and resume, respectively. Signed-off-by: Claudiu Beznea Acked-by: Alan Stern Link: https://patch.msgid.link/20251106143625.3050119-3-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 57d5a7ddac5f..f61f095cedab 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -454,6 +454,17 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->rsts); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ehci_resume(hcd, false); + + if (priv->quirk_poll) + quirk_poll_init(priv); + } + return ret; } @@ -464,11 +475,18 @@ static int __maybe_unused ehci_platform_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); struct device *companion_dev; + int err; + + err = reset_control_deassert(priv->rsts); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->rsts); return err; + } } companion_dev = usb_of_get_companion_dev(hcd->self.controller); From e4d9da32bf6059cb485caac4c9c0a2e36cdd5573 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 6 Nov 2025 16:36:24 +0200 Subject: [PATCH 133/161] usb: host: ohci-platform: Call reset assert/deassert on suspend/resume The Renesas RZ/G3S SoC supports a power-saving mode in which power to most of the SoC components is turned off, including the USB blocks. On the resume path, the reset signal must be de-asserted before applying any settings to the USB registers. To handle this properly, call reset_control_assert() and reset_control_deassert() during suspend and resume, respectively. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20251106143625.3050119-4-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index af26f1449bc2..2e4bb5cc2165 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -270,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -280,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->resets); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ohci_resume(hcd, false); + } + return ret; } @@ -288,11 +297,19 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int err; + + err = reset_control_deassert(priv->resets); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->resets); return err; + } } ohci_resume(hcd, hibernated); From 3578b1cde59496efc4625ba3fbd14eb2918807a2 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 6 Nov 2025 16:36:25 +0200 Subject: [PATCH 134/161] usb: renesas_usbhs: Assert/de-assert reset signals on suspend/resume The Renesas RZ/G3S SoC supports a power-saving mode in which power to most SoC components is turned off, including the USB subsystem. To properly restore from such a state, the reset signal needs to be asserted/de-asserted during suspend/resume. Add reset assert/de-assert on suspend/resume. The resume code has been moved into a separate function to allow reusing it in case reset_control_assert() from suspend fails. Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20251106143625.3050119-5-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 51 ++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 8f536f2c500f..8df68261a320 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -827,23 +827,7 @@ static void usbhs_remove(struct platform_device *pdev) usbhs_pipe_remove(priv); } -static int usbhsc_suspend(struct device *dev) -{ - struct usbhs_priv *priv = dev_get_drvdata(dev); - struct usbhs_mod *mod = usbhs_mod_get_current(priv); - - if (mod) { - usbhs_mod_call(priv, stop, priv); - usbhs_mod_change(priv, -1); - } - - if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) - usbhsc_power_ctrl(priv, 0); - - return 0; -} - -static int usbhsc_resume(struct device *dev) +static void usbhsc_restore(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct platform_device *pdev = usbhs_priv_to_pdev(priv); @@ -856,6 +840,39 @@ static int usbhsc_resume(struct device *dev) usbhs_platform_call(priv, phy_reset, pdev); usbhsc_schedule_notify_hotplug(pdev); +} + +static int usbhsc_suspend(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + int ret; + + if (mod) { + usbhs_mod_call(priv, stop, priv); + usbhs_mod_change(priv, -1); + } + + if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) + usbhsc_power_ctrl(priv, 0); + + ret = reset_control_assert(priv->rsts); + if (ret) + usbhsc_restore(dev); + + return ret; +} + +static int usbhsc_resume(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(priv->rsts); + if (ret) + return ret; + + usbhsc_restore(dev); return 0; } From 8d3c283ef80950532500f017a80a00a83e8cce0f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 11 Nov 2025 11:51:17 +0200 Subject: [PATCH 135/161] usb: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Acked-by: Thierry Reding Reviewed-by: AngeloGioacchino Del Regno Acked-by: Peter Chen Link: https://patch.msgid.link/20251111095117.95023-1-sakari.ailus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 1 - drivers/usb/cdns3/cdnsp-gadget.c | 1 - drivers/usb/chipidea/core.c | 1 - drivers/usb/chipidea/otg_fsm.c | 1 - drivers/usb/dwc3/core.c | 2 -- drivers/usb/dwc3/dwc3-am62.c | 1 - drivers/usb/dwc3/dwc3-imx8mp.c | 1 - drivers/usb/dwc3/dwc3-pci.c | 1 - drivers/usb/dwc3/dwc3-xilinx.c | 1 - drivers/usb/gadget/udc/cdns2/cdns2-gadget.c | 1 - drivers/usb/host/xhci-mtk.c | 1 - drivers/usb/host/xhci-tegra.c | 1 - drivers/usb/misc/apple-mfi-fastcharge.c | 1 - drivers/usb/mtu3/mtu3_plat.c | 1 - drivers/usb/musb/musb_core.c | 5 ----- drivers/usb/musb/musb_debugfs.c | 5 ----- drivers/usb/musb/musb_dsps.c | 1 - drivers/usb/musb/musb_gadget.c | 4 ---- drivers/usb/musb/omap2430.c | 1 - 19 files changed, 31 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d9d8dc05b235..168707213ed9 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3251,7 +3251,6 @@ static void cdns3_gadget_exit(struct cdns *cdns) priv_dev = cdns->gadget_dev; - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget(&priv_dev->gadget); diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 0252560cbc80..d37c29a253dd 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -1999,7 +1999,6 @@ static void cdnsp_gadget_exit(struct cdns *cdns) struct cdnsp_device *pdev = cdns->gadget_dev; devm_free_irq(pdev->dev, cdns->dev_irq, pdev); - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget(&pdev->gadget); cdnsp_gadget_free_endpoints(pdev); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 694b4a8e4e1d..a6ce73dcc871 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1372,7 +1372,6 @@ static int ci_controller_resume(struct device *dev) ci->in_lpm = false; if (ci->wakeup_int) { ci->wakeup_int = false; - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); enable_irq(ci->irq); if (ci_otg_is_fsm_mode(ci)) diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index a093544482d5..929536dc96ec 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -629,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ci_otg_queue_work(ci); } } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); return 0; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 96f85eada047..d7f340f22595 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -282,7 +282,6 @@ static void __dwc3_set_mode(struct work_struct *work) } out: - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); mutex_unlock(&dwc->mutex); } @@ -2658,7 +2657,6 @@ int dwc3_runtime_idle(struct dwc3 *dwc) break; } - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index 9db8f3ca493d..e11d7643f966 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -292,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev) /* Setting up autosuspend */ pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index bce6af82f54c..050da327f785 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -312,7 +312,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) if (dwc3_imx->wakeup_pending) { dwc3_imx->wakeup_pending = false; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); } else { /* diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 39c72cb52ce7..ce7b76d24fb2 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -322,7 +322,6 @@ static void dwc3_pci_resume_work(struct work_struct *work) return; } - pm_runtime_mark_last_busy(&dwc3->dev); pm_runtime_put_sync_autosuspend(&dwc3->dev); } #endif diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 1e28d6f50ed0..0a8c47876ff9 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -383,7 +383,6 @@ static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev) static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) { - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 7e69944ef18a..9b53daf76583 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated) void cdns2_gadget_remove(struct cdns2_device *pdev) { - pm_runtime_mark_last_busy(pdev->dev); pm_runtime_put_autosuspend(pdev->dev); usb_del_gadget(&pdev->gadget); diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 208558cf822d..06043c7c3100 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 1e23f198a005..31ccced5125e 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1399,7 +1399,6 @@ static void tegra_xhci_id_work(struct work_struct *work) } tegra_xhci_set_port_power(tegra, true, true); - pm_runtime_mark_last_busy(tegra->dev); } else { if (tegra->otg_usb3_port >= 0) diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index 8e852f4b8262..47b38dcc2992 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -134,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy, ret = -EINVAL; } - pm_runtime_mark_last_busy(&mfi->udev->dev); pm_runtime_put_autosuspend(&mfi->udev->dev); return ret; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 7b5a431acb56..cc8a864dbd63 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c7234b236971..0acc62569ae5 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) if (!musb->session) break; trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); musb->session = false; return; @@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) msecs_to_jiffies(3000)); } else { trace_musb_state(musb, devctl, "Allow PM with no session"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2090,7 +2088,6 @@ static void musb_irq_work(struct work_struct *data) sysfs_notify(&musb->controller->kobj, NULL, "mode"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2564,7 +2561,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_init_debugfs(musb); musb->is_initialized = 1; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev) error); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 2d623284edf6..5092d62c2062 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -106,7 +106,6 @@ static int musb_regdump_show(struct seq_file *s, void *unused) } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; } @@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused) pm_runtime_get_sync(musb->controller); test = musb_readb(musb->mregs, MUSB_TESTMODE); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) @@ -216,7 +214,6 @@ static ssize_t musb_test_mode_write(struct file *file, musb_writeb(musb->mregs, MUSB_TESTMODE, test); ret: - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } @@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused) reg = musb_readb(musb->mregs, MUSB_DEVCTL); connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); break; default: @@ -304,7 +300,6 @@ static ssize_t musb_softconnect_write(struct file *file, } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a08ce96c08d3..e3935f18dd56 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t) if (err < 0) dev_err(dev, "%s resume work: %i\n", __func__, err); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index caf4d4cd4b75..d666c4292753 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, unlock: spin_unlock_irqrestore(&musb->lock, lockflags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return status; @@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work) spin_lock_irqsave(&musb->lock, flags); musb_pullup(musb, musb->softconnect); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -1862,7 +1860,6 @@ static int musb_gadget_start(struct usb_gadget *g, if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID) musb_platform_set_vbus(musb, 1); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -1916,7 +1913,6 @@ static int musb_gadget_stop(struct usb_gadget *g) usb_gadget_set_state(g, USB_STATE_NOTATTACHED); /* Force check of devctl register for PM runtime */ - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c35c07b7488c..48bb9bfb2204 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) default: dev_dbg(musb->controller, "ID float\n"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); atomic_notifier_call_chain(&musb->xceiv->notifier, musb->xceiv->last_event, NULL); From a67df6d1b939ca98e1ad403f53e3ee57299b8c44 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 11 Nov 2025 14:46:10 +0100 Subject: [PATCH 136/161] uapi: cdc.h: cleanly provide for more interfaces and countries The spec requires at least one interface respectively country. It allows multiple ones. This needs to be clearly said in the UAPI. This is subject to sanity checking in cdc_parse_cdc_header(), thus we can trust the length. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20251111134641.4118827-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- include/uapi/linux/usb/cdc.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 73f9476774ae..54be4aa1dcb2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1475,7 +1475,7 @@ made_compressed_probe: if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + memcpy(acm->country_codes, cfd->wCountryCodes, cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index 1924cf665448..7bd5d12d8b26 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -104,8 +104,10 @@ struct usb_cdc_union_desc { __u8 bDescriptorSubType; __u8 bMasterInterface0; - __u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ + union { + __u8 bSlaveInterface0; + __DECLARE_FLEX_ARRAY(__u8, bSlaveInterfaces); + }; } __attribute__ ((packed)); /* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */ @@ -115,8 +117,10 @@ struct usb_cdc_country_functional_desc { __u8 bDescriptorSubType; __u8 iCountryCodeRelDate; - __le16 wCountyCode0; - /* ... and there can be a lot of country codes */ + union { + __le16 wCountryCode0; + __DECLARE_FLEX_ARRAY(__le16, wCountryCodes); + }; } __attribute__ ((packed)); /* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ From 3e082978c33151d576694deac8abde021ea669a8 Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Thu, 30 Oct 2025 07:48:55 -0700 Subject: [PATCH 137/161] usb: typec: ucsi: Update UCSI structure to have message in and message out fields Update UCSI structure by adding fields for incoming and outgoing messages. Update .sync_control function and other related functions to use these new fields within the UCSI structure, instead of handling them as separate parameters. Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/214b0a90c3220db33084ab714f4f33a004f70a41.1761773881.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- 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, 112 insertions(+), 71 deletions(-) diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index eed2a7d0ebc6..d753f2188e25 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -105,13 +105,12 @@ 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, - void *data, size_t size) +static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci) { struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); + ret = ucsi_sync_control_common(ucsi, cmd, cci); 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 f3684ab787fe..924f93027553 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -37,7 +37,8 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_USB: case UCSI_SET_POWER_LEVEL: case UCSI_READ_POWER_LEVEL: - ret = ucsi_send_command(ucsi, val, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, val); break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: @@ -52,9 +53,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: - ret = ucsi_send_command(ucsi, val, - &ucsi->debugfs->response, - sizeof(ucsi->debugfs->response)); + 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)); break; default: ret = -EOPNOTSUPP; diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..a09b4900ec76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -67,11 +67,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) } command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); - ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); + ucsi->message_in_size = sizeof(cur); + ret = ucsi_send_command(ucsi, command); 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) { @@ -126,7 +129,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) } command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); - ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); + dp->con->ucsi->message_in_size = 0; + ret = ucsi_send_command(dp->con->ucsi, command); if (ret < 0) goto out_unlock; @@ -193,7 +197,8 @@ static int ucsi_displayport_configure(struct ucsi_dp *dp) command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); - return ucsi_send_command(dp->con->ucsi, command, NULL, 0); + dp->con->ucsi->message_in_size = 0; + return ucsi_send_command(dp->con->ucsi, command); } 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 a7b388dc7fa0..819540713150 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -55,8 +55,7 @@ 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, - void *data, size_t size) +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -84,9 +83,10 @@ out_clear_bit: if (!ret && cci) ret = ucsi->ops->read_cci(ucsi, cci); - if (!ret && data && + if (!ret && ucsi->message_in_size > 0 && (*cci & UCSI_CCI_COMMAND_COMPLETE)) - ret = ucsi->ops->read_message_in(ucsi, data, size); + ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in, + ucsi->message_in_size); return ret; } @@ -103,23 +103,25 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); + ucsi->message_in_size = 0; + return ucsi->ops->sync_control(ucsi, ctrl, NULL); } -static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size, bool conn_ack) +static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, bool conn_ack) { int ret, err; *cci = 0; - if (size > UCSI_MAX_DATA_LENGTH(ucsi)) + if (ucsi->message_in_size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); + ret = ucsi->ops->sync_control(ucsi, command, cci); - if (*cci & UCSI_CCI_BUSY) - return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; + if (*cci & UCSI_CCI_BUSY) { + ucsi->message_in_size = 0; + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, false) ?: -EBUSY; + } if (ret) return ret; @@ -151,10 +153,13 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) int ret; command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num); - ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); + ucsi->message_in_size = sizeof(error); + ret = ucsi_run_command(ucsi, command, &cci, false); if (ret < 0) return ret; + memcpy(&error, ucsi->message_in, sizeof(error)); + switch (error) { case UCSI_ERROR_INCOMPATIBLE_PARTNER: return -EOPNOTSUPP; @@ -200,8 +205,7 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) return -EIO; } -static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, - void *data, size_t size, bool conn_ack) +static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack) { u8 connector_num; u32 cci; @@ -229,7 +233,7 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack); + ret = ucsi_run_command(ucsi, cmd, &cci, conn_ack); if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); @@ -238,10 +242,9 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, return ret; } -int ucsi_send_command(struct ucsi *ucsi, u64 command, - void *data, size_t size) +int ucsi_send_command(struct ucsi *ucsi, u64 command) { - return ucsi_send_command_common(ucsi, command, data, size, false); + return ucsi_send_command_common(ucsi, command, false); } EXPORT_SYMBOL_GPL(ucsi_send_command); @@ -319,7 +322,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) int i; command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); + con->ucsi->message_in_size = sizeof(cur); + ret = ucsi_send_command(con->ucsi, command); if (ret < 0) { if (con->ucsi->version > 0x0100) { dev_err(con->ucsi->dev, @@ -327,6 +331,8 @@ 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) @@ -510,7 +516,8 @@ 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); - len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt)); + ucsi->message_in_size = sizeof(alt); + len = ucsi_send_command(con->ucsi, command); /* * We are collecting all altmodes first and then registering. * Some type-C device will return zero length data beyond last @@ -519,6 +526,8 @@ 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; @@ -586,12 +595,15 @@ 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); - len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); + con->ucsi->message_in_size = sizeof(alt); + len = ucsi_send_command(con->ucsi, command); 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 @@ -659,7 +671,9 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) UCSI_MAX_DATA_LENGTH(con->ucsi)); int ret; - ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + con->ucsi->message_in_size = size; + ret = ucsi_send_command_common(con->ucsi, command, conn_ack); + memcpy(&con->status, con->ucsi->message_in, size); return ret < 0 ? ret : 0; } @@ -682,8 +696,9 @@ 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; - ret = ucsi_send_command(ucsi, command, pdos + offset, - num_pdos * sizeof(u32)); + ucsi->message_in_size = num_pdos * sizeof(u32); + ret = ucsi_send_command(ucsi, command); + memcpy(pdos + offset, ucsi->message_in, num_pdos * sizeof(u32)); if (ret < 0 && ret != -ETIMEDOUT) dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); @@ -770,7 +785,9 @@ 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); - ret = ucsi_send_command(con->ucsi, command, data + offset, len); + con->ucsi->message_in_size = len; + ret = ucsi_send_command(con->ucsi, command); + memcpy(data + offset, con->ucsi->message_in, len); if (ret < 0) return ret; } @@ -935,7 +952,9 @@ static int ucsi_register_cable(struct ucsi_connector *con) int ret; command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &cable_prop, sizeof(cable_prop)); + 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)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n", ret); return ret; @@ -996,7 +1015,9 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return 0; command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap)); + 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)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret); return ret; @@ -1380,7 +1401,8 @@ 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; - return ucsi_send_command(con->ucsi, command, NULL, 0); + con->ucsi->message_in_size = 0; + return ucsi_send_command(con->ucsi, command); } static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -1461,7 +1483,8 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) { int ret; - ret = ucsi_send_command(con->ucsi, command, NULL, 0); + con->ucsi->message_in_size = 0; + ret = ucsi_send_command(con->ucsi, command); if (ret == -ETIMEDOUT) { u64 c; @@ -1469,7 +1492,8 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) ucsi_reset_ppm(con->ucsi); c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; - ucsi_send_command(con->ucsi, c, NULL, 0); + con->ucsi->message_in_size = 0; + ucsi_send_command(con->ucsi, c); ucsi_reset_connector(con, true); } @@ -1622,10 +1646,13 @@ 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); - ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap)); + ucsi->message_in_size = sizeof(con->cap); + ret = ucsi_send_command(ucsi, command); 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)) @@ -1822,17 +1849,20 @@ 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; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto err_reset; /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ret = ucsi_send_command(ucsi, command, &ucsi->cap, - BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); + ucsi->message_in_size = BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE); + ret = ucsi_send_command(ucsi, command); 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; @@ -1862,7 +1892,8 @@ static int ucsi_init(struct ucsi *ucsi) /* Enable all supported notifications */ ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto err_unregister; @@ -1913,7 +1944,8 @@ static void ucsi_resume_work(struct work_struct *work) /* Restore UCSI notification enable mask after system resume */ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); 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 410389ef173a..479bf1f69c72 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -29,6 +29,10 @@ 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 @@ -80,8 +84,7 @@ 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, - void *data, size_t size); + 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, struct ucsi_altmode *orig, @@ -493,6 +496,12 @@ 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) @@ -555,15 +564,13 @@ struct ucsi_connector { struct usb_pd_identity cable_identity; }; -int ucsi_send_command(struct ucsi *ucsi, u64 command, - void *retval, size_t size); +int ucsi_send_command(struct ucsi *ucsi, u64 command); 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, - void *data, size_t size); +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci); #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 6b92f296e985..f1d1f6917b09 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -105,15 +105,14 @@ 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, - void *val, size_t len) +static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) { 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, val, len); + ret = ucsi_sync_control_common(ucsi, command, cci); if (ret < 0) return ret; @@ -125,8 +124,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 *)val == bogus_change) - *(u16 *)val = 0; + if (*(u16 *)ucsi->message_in == bogus_change) + *(u16 *)ucsi->message_in = 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 d83a0051c737..ead1b2a25c79 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -606,8 +606,7 @@ 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, - void *data, size_t size) +static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -629,16 +628,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, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci); switch (UCSI_COMMAND(command)) { case UCSI_GET_CURRENT_CAM: if (uc->has_multiple_dp) - ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data); + ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)ucsi->message_in); break; case UCSI_GET_ALTERNATE_MODES: if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) { - struct ucsi_altmode *alt = data; + struct ucsi_altmode *alt = (struct ucsi_altmode *)ucsi->message_in; if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) ucsi_ccg_nvidia_altmode(uc, alt, command); @@ -646,7 +645,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 = data; + struct ucsi_capability *cap = (struct ucsi_capability *)ucsi->message_in; 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 0187c1c4b21a..299081444caa 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -88,8 +88,7 @@ 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, - void *data, size_t size) + u32 *cci) { int ret; @@ -107,8 +106,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, }; dev_dbg(ucsi->dev, "faking DP altmode for con1\n"); - memset(data, 0, size); - memcpy(data, &alt, min(sizeof(alt), size)); + memset(ucsi->message_in, 0, ucsi->message_in_size); + memcpy(ucsi->message_in, &alt, min(sizeof(alt), ucsi->message_in_size)); *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt)); return 0; } @@ -121,18 +120,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(data, 0, size); + memset(ucsi->message_in, 0, ucsi->message_in_size); *cci = UCSI_CCI_COMMAND_COMPLETE; return 0; } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci); if (ret < 0) return ret; /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */ - if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data) - ((u8 *)data)[0]--; + if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && ucsi->message_in_size > 0) + ucsi->message_in[0]--; return ret; } From db0028637cc832add6d87564fcc2ebb12781b046 Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Thu, 30 Oct 2025 07:48:56 -0700 Subject: [PATCH 138/161] usb: typec: ucsi: Add support for message out data structure Add support for updating message out data structure for UCSI ACPI interface for UCSI 2.1 and UCSI 3.0 commands such as Set PDOs and LPM Firmware Update. Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/5bb1f367e44c9fc5244c3e10e513f02d62fe8166.1761773881.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- 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 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 819540713150..9b3df776137a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -67,6 +67,20 @@ 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 479bf1f69c72..d01b796a8d23 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -69,6 +69,7 @@ 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 @@ -84,6 +85,7 @@ 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 f1d1f6917b09..f9beeb835238 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -86,6 +86,21 @@ 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); @@ -101,6 +116,7 @@ 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 775fae520e6ae62c393a8daf42dc534f09692f3f Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Thu, 30 Oct 2025 07:48:57 -0700 Subject: [PATCH 139/161] usb: typec: ucsi: Enable debugfs for message_out data structure Add debugfs entry for writing message_out data structure to handle UCSI 2.1 and 3.0 commands through debugfs interface. Users writing to the message_out debugfs file should ensure the input data adheres to the following format: 1. Input must be a non-empty valid hexadecimal string. 2. Input length of hexadecimal string must not exceed 256 bytes of length to be in alignment with the message out data structure size as per the UCSI specification v2.1. 3. If the input string length is odd, then user needs to prepend a '0' to the first character for proper hex conversion. Below are examples of valid hex strings. Note that these values are just examples. The exact values depend on specific command use case. #echo 1A2B3C4D > message_out #echo 01234567 > message_out Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/0a81c2209eb299c1af191cd7ce758a92d5adf81b.1761773881.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 924f93027553..90d11b79d2c0 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -110,6 +110,30 @@ 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); @@ -122,6 +146,8 @@ 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 1b474ee01fbb73b1365adbf9b3067f7375e471ee Mon Sep 17 00:00:00 2001 From: Pooja Katiyar Date: Thu, 30 Oct 2025 07:48:58 -0700 Subject: [PATCH 140/161] usb: typec: ucsi: Add support for SET_PDOS command Add support for UCSI SET_PDOS command as per UCSI specification v2.1 and above to debugfs. Reviewed-by: Heikki Krogerus Signed-off-by: Pooja Katiyar Link: https://patch.msgid.link/b4ccc1e75746b04a8b48c8998b42b019afb934f1.1761773881.git.pooja.katiyar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 1 + drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 90d11b79d2c0..174f4d53b777 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -37,6 +37,7 @@ 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 d01b796a8d23..f946b728c373 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -137,6 +137,7 @@ 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 c640a4239db53e077dd5fd20db52fbc8b64f290b Mon Sep 17 00:00:00 2001 From: Hang Cao Date: Wed, 12 Nov 2025 13:53:21 +0800 Subject: [PATCH 141/161] dt-bindings: usb: Add ESWIN EIC7700 USB controller Add Device Tree binding documentation for the ESWIN EIC7700 usb controller module. Signed-off-by: Senchuan Zhang Signed-off-by: Hang Cao Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20251112055321.1638-1-caohang@eswincomputing.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/eswin,eic7700-usb.yaml | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml diff --git a/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml new file mode 100644 index 000000000000..41c3b1b98991 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/eswin,eic7700-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 SoC Usb Controller + +maintainers: + - Wei Yang + - Senchuan Zhang + - Hang Cao + +description: + The Usb controller on EIC7700 SoC. + +allOf: + - $ref: snps,dwc3-common.yaml# + +properties: + compatible: + const: eswin,eic7700-dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: peripheral + + clocks: + maxItems: 3 + + clock-names: + items: + - const: aclk + - const: cfg + - const: usb_en + + resets: + maxItems: 2 + + reset-names: + items: + - const: vaux + - const: usb_rst + + eswin,hsp-sp-csr: + description: + HSP CSR is to control and get status of different high-speed peripherals + (such as Ethernet, USB, SATA, etc.) via register, which can tune + board-level's parameters of PHY, etc. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to HSP Register Controller hsp_sp_csr node. + - description: USB bus register offset. + - description: AXI low power register offset. + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - resets + - reset-names + - eswin,hsp-sp-csr + +unevaluatedProperties: false + +examples: + - | + usb@50480000 { + compatible = "eswin,eic7700-dwc3"; + reg = <0x50480000 0x10000>; + clocks = <&clock 135>, + <&clock 136>, + <&hspcrg 18>; + clock-names = "aclk", "cfg", "usb_en"; + interrupt-parent = <&plic>; + interrupts = <85>; + interrupt-names = "peripheral"; + resets = <&reset 84>, <&hspcrg 2>; + reset-names = "vaux", "usb_rst"; + dr_mode = "peripheral"; + maximum-speed = "high-speed"; + phy_type = "utmi"; + eswin,hsp-sp-csr = <&hsp_sp_csr 0x800 0x818>; + }; From e05d28b759c28660c28a36bf0add178edcc3466e Mon Sep 17 00:00:00 2001 From: Hang Cao Date: Wed, 12 Nov 2025 13:53:45 +0800 Subject: [PATCH 142/161] usb: dwc3: eic7700: Add EIC7700 USB driver The EIC7700 instantiates two USB 3.0 DWC3 IPs, each of which is backward compatible with USB interfaces. It supports Super-speed (5Gb/s), DRD mode, and compatible with xHCI 1.1, etc. Each of instances supports 16 endpoints in device's mode and max 64 devices in host's mode. This module needs to interact with the NOC via the AXI master bus, thus requiring some HSP configuration operations to achieve this. Ops include bus filter, pm signal or status to usb bus and so on. Acked-by: Thinh Nguyen Signed-off-by: Senchuan Zhang Signed-off-by: Hang Cao Link: https://patch.msgid.link/20251112055346.1655-1-caohang@eswincomputing.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-generic-plat.c | 71 +++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c index e869c7de7bc8..e846844e0023 100644 --- a/drivers/usb/dwc3/dwc3-generic-plat.c +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -10,8 +10,16 @@ #include #include #include +#include +#include #include "glue.h" +#define EIC7700_HSP_BUS_FILTER_EN BIT(0) +#define EIC7700_HSP_BUS_CLKEN_GM BIT(9) +#define EIC7700_HSP_BUS_CLKEN_GS BIT(16) +#define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0) +#define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16) + struct dwc3_generic { struct device *dev; struct dwc3 dwc; @@ -20,6 +28,11 @@ struct dwc3_generic { struct reset_control *resets; }; +struct dwc3_generic_config { + int (*init)(struct dwc3_generic *dwc3g); + struct dwc3_properties properties; +}; + #define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) static void dwc3_generic_reset_control_assert(void *data) @@ -27,9 +40,38 @@ static void dwc3_generic_reset_control_assert(void *data) reset_control_assert(data); } +static int dwc3_eic7700_init(struct dwc3_generic *dwc3g) +{ + struct device *dev = dwc3g->dev; + struct regmap *regmap; + u32 hsp_usb_axi_lp; + u32 hsp_usb_bus; + u32 args[2]; + u32 val; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "eswin,hsp-sp-csr", + ARRAY_SIZE(args), args); + if (IS_ERR(regmap)) { + dev_err(dev, "No hsp-sp-csr phandle specified\n"); + return PTR_ERR(regmap); + } + + hsp_usb_bus = args[0]; + hsp_usb_axi_lp = args[1]; + + regmap_read(regmap, hsp_usb_bus, &val); + regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN | + EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS); + + regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ | + EIC7700_HSP_AXI_LP_XS_CSYSREQ); + return 0; +} + static int dwc3_generic_probe(struct platform_device *pdev) { - const struct dwc3_properties *properties; + const struct dwc3_generic_config *plat_config; struct dwc3_probe_data probe_data = {}; struct device *dev = &pdev->dev; struct dwc3_generic *dwc3g; @@ -77,12 +119,21 @@ static int dwc3_generic_probe(struct platform_device *pdev) probe_data.res = res; probe_data.ignore_clocks_and_resets = true; - properties = of_device_get_match_data(dev); - if (properties) - probe_data.properties = *properties; - else + plat_config = of_device_get_match_data(dev); + if (!plat_config) { probe_data.properties = DWC3_DEFAULT_PROPERTIES; + goto core_probe; + } + probe_data.properties = plat_config->properties; + if (plat_config->init) { + ret = plat_config->init(dwc3g); + if (ret) + return dev_err_probe(dev, ret, + "failed to init platform\n"); + } + +core_probe: ret = dwc3_core_probe(&probe_data); if (ret) return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); @@ -150,13 +201,19 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { dwc3_generic_runtime_idle) }; -static const struct dwc3_properties fsl_ls1028_dwc3 = { - .gsbuscfg0_reqinfo = 0x2222, +static const struct dwc3_generic_config fsl_ls1028_dwc3 = { + .properties.gsbuscfg0_reqinfo = 0x2222, +}; + +static const struct dwc3_generic_config eic7700_dwc3 = { + .init = dwc3_eic7700_init, + .properties = DWC3_DEFAULT_PROPERTIES, }; static const struct of_device_id dwc3_generic_of_match[] = { { .compatible = "spacemit,k1-dwc3", }, { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, + { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); From 24b040fe50308f7695f33da21c15a3019a1224f1 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 12 Nov 2025 14:55:05 +0100 Subject: [PATCH 143/161] usb: uas: reduce time under spinlock Drop the lock before freeing memory. Signed-off-by: Oliver Neukum Link: https://patch.msgid.link/20251112135543.31081-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 0657f5f7a51f..8f24b81d6c12 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -309,18 +309,18 @@ static void uas_stat_cmplt(struct urb *urb) int status = urb->status; bool success; + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + dev_err(&urb->dev->dev, "stat urb: status %d\n", status); + goto bail; + } + + idx = be16_to_cpup(&iu->tag) - 1; + spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->resetting) goto out; - - if (status) { - if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) - dev_err(&urb->dev->dev, "stat urb: status %d\n", status); - goto out; - } - - idx = be16_to_cpup(&iu->tag) - 1; if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { dev_err(&urb->dev->dev, "stat urb: no pending cmd for uas-tag %d\n", idx + 1); @@ -375,9 +375,8 @@ static void uas_stat_cmplt(struct urb *urb) default: uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); } -out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); /* Unlinking of data urbs must be done without holding the lock */ if (data_in_urb) { @@ -388,6 +387,12 @@ out: usb_unlink_urb(data_out_urb); usb_put_urb(data_out_urb); } + return; + +out: + spin_unlock_irqrestore(&devinfo->lock, flags); +bail: + usb_free_urb(urb); } static void uas_data_cmplt(struct urb *urb) @@ -429,8 +434,8 @@ static void uas_data_cmplt(struct urb *urb) } uas_try_complete(cmnd, __func__); out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); } static void uas_cmd_cmplt(struct urb *urb) From 363eb9bfdea537c456cec62c0560ab7d386c555c Mon Sep 17 00:00:00 2001 From: Liang Jie Date: Fri, 14 Nov 2025 16:42:44 +0800 Subject: [PATCH 144/161] usb: gadget: functionfs: use dma_buf_unmap_attachment_unlocked() helper Replace the open-coded dma_resv_lock()/dma_resv_unlock() around dma_buf_unmap_attachment() in ffs_dmabuf_release() with the dma_buf_unmap_attachment_unlocked() helper. This aligns FunctionFS DMABUF unmap handling with the standard DMA-BUF API, avoids duplicating locking logic and eases future maintenance. No functional change. Reviewed-by: fanggeng Signed-off-by: Liang Jie Link: https://patch.msgid.link/20251114084246.2064845-1-buaajxlj@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 47cfbe41fdff..7f8e566b1c57 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1306,9 +1306,7 @@ static void ffs_dmabuf_release(struct kref *ref) struct dma_buf *dmabuf = attach->dmabuf; pr_vdebug("FFS DMABUF release\n"); - dma_resv_lock(dmabuf->resv, NULL); - dma_buf_unmap_attachment(attach, priv->sgt, priv->dir); - dma_resv_unlock(dmabuf->resv); + dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir); dma_buf_detach(attach->dmabuf, attach); dma_buf_put(dmabuf); From a75a5b148b4e1d7c0525359be455d5a54024b714 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 14 Nov 2025 19:37:55 +0100 Subject: [PATCH 145/161] usb: ohci-da8xx: remove unused platform data We no longer support any board files for DaVinci in mainline and so struct da8xx_ohci_root_hub is no longer used. Remove it together with all the code it's used for. Signed-off-by: Bartosz Golaszewski Acked-by: Alan Stern Link: https://patch.msgid.link/20251114-davinci-usb-v1-1-737380353a74@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-da8xx.c | 17 ----------------- include/linux/platform_data/usb-davinci.h | 22 ---------------------- 2 files changed, 39 deletions(-) delete mode 100644 include/linux/platform_data/usb-davinci.h diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 3c5ca2d7c92e..0938c0e7a8b6 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd) return 0; } -static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) -{ - struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); - - if (hub && hub->potpgt) - return 1; - - return 0; -} - static int ohci_da8xx_regulator_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int result; u32 rh_a; @@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd) rh_a &= ~RH_A_NOCP; rh_a |= RH_A_OCPM; } - if (ohci_da8xx_has_potpgt(hcd)) { - rh_a &= ~RH_A_POTPGT; - rh_a |= hub->potpgt << 24; - } ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); return result; diff --git a/include/linux/platform_data/usb-davinci.h b/include/linux/platform_data/usb-davinci.h deleted file mode 100644 index 879f5c78b91a..000000000000 --- a/include/linux/platform_data/usb-davinci.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * USB related definitions - * - * Copyright (C) 2009 MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __ASM_ARCH_USB_H -#define __ASM_ARCH_USB_H - -/* Passed as the platform data to the OHCI driver */ -struct da8xx_ohci_root_hub { - /* Time from power on to power good (in 2 ms units) */ - u8 potpgt; -}; - -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); - -#endif /* ifndef __ASM_ARCH_USB_H */ From a5160af78be7fcf3ade6caab0a14e349560c96d7 Mon Sep 17 00:00:00 2001 From: Gopi Krishna Menon Date: Tue, 28 Oct 2025 22:26:57 +0530 Subject: [PATCH 146/161] usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE The previous commit removed the PAGE_SIZE limit on transfer length of raw_io buffer in order to avoid any problems with emulating USB devices whose full configuration descriptor exceeds PAGE_SIZE in length. However this also removes the upperbound on user supplied length, allowing very large values to be passed to the allocator. syzbot on fuzzing the transfer length with very large value (1.81GB) results in kmalloc() to fall back to the page allocator, which triggers a kernel warning as the page allocator cannot handle allocations more than MAX_PAGE_ORDER/KMALLOC_MAX_SIZE. Since there is no limit imposed on the size of buffer for both control and non control transfers, cap the raw_io transfer length to KMALLOC_MAX_SIZE and return -EINVAL for larger transfer length to prevent any warnings from the page allocator. Fixes: 37b9dd0d114a ("usb: raw-gadget: do not limit transfer length") Tested-by: syzbot+d8fd35fa6177afa8c92b@syzkaller.appspotmail.com Reported-by: syzbot+d8fd35fa6177afa8c92b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/68fc07a0.a70a0220.3bf6c6.01ab.GAE@google.com/ Signed-off-by: Gopi Krishna Menon Reviewed-by: Andrey Konovalov Link: https://patch.msgid.link/20251028165659.50962-1-krishnagopi487@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/raw_gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index b71680c58de6..46f343ba48b3 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -40,6 +40,7 @@ MODULE_LICENSE("GPL"); static DEFINE_IDA(driver_id_numbers); #define DRIVER_DRIVER_NAME_LENGTH_MAX 32 +#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE #define RAW_EVENT_QUEUE_SIZE 16 @@ -667,6 +668,8 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, return ERR_PTR(-EINVAL); if (!usb_raw_io_flags_valid(io->flags)) return ERR_PTR(-EINVAL); + if (io->length > USB_RAW_IO_LENGTH_MAX) + return ERR_PTR(-EINVAL); if (get_from_user) data = memdup_user(ptr + sizeof(*io), io->length); else { From de7275cbc6171ce777fae7af444cf8efaf14258b Mon Sep 17 00:00:00 2001 From: David Laight Date: Wed, 19 Nov 2025 22:41:24 +0000 Subject: [PATCH 147/161] drivers/usb/storage: use min() instead of min_t() min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'. Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long' and so cannot discard significant bits. In this case the 'unsigned long' value is small enough that the result is ok. Detected by an extra check added to min_t(). Signed-off-by: David Laight Link: https://patch.msgid.link/20251119224140.8616-29-david.laight.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/protocol.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 9033e505db7f..0cff54ad90fa 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -139,8 +139,7 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, return cnt; while (sg_miter_next(&miter) && cnt < buflen) { - unsigned int len = min_t(unsigned int, miter.length, - buflen - cnt); + unsigned int len = min(miter.length, buflen - cnt); if (dir == FROM_XFER_BUF) memcpy(buffer + cnt, miter.addr, len); From b43889fcae25c247f2ad8e4a304a04b22532767c Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Fri, 14 Nov 2025 18:02:39 +0530 Subject: [PATCH 148/161] dt-bindings: usb: dwc3-xilinx: Describe the reset constraint for the versal platform AMD Versal platform USB 2.0 IP controller receives one reset input from the SoC controlled by the CRL.RST_USB [RESET] register so accordingly describe reset constraints. Signed-off-by: Radhey Shyam Pandey Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251114123239.1929255-1-radhey.shyam.pandey@amd.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/dwc3-xilinx.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml index 36f5c644d959..d6823ef5f9a7 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml +++ b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml @@ -47,6 +47,7 @@ properties: - const: ref_clk resets: + minItems: 1 description: A list of phandles for resets listed in reset-names. @@ -56,6 +57,7 @@ properties: - description: USB APB reset reset-names: + minItems: 1 items: - const: usb_crst - const: usb_hibrst @@ -95,6 +97,26 @@ required: - resets - reset-names +allOf: + - if: + properties: + compatible: + contains: + enum: + - xlnx,versal-dwc3 + then: + properties: + resets: + maxItems: 1 + reset-names: + maxItems: 1 + else: + properties: + resets: + minItems: 3 + reset-names: + minItems: 3 + additionalProperties: false examples: From e91bbe082878c9e9bcfddd68c5f823cf1f757f15 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 7 Nov 2025 16:37:30 +0100 Subject: [PATCH 149/161] USB: add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251107153737.301413-2-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- drivers/usb/gadget/function/f_hid.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index af3565e541cd..be50d03034a9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -6078,7 +6078,7 @@ int usb_hub_init(void) * device was gone before the EHCI controller had handed its port * over to the companion full-speed controller. */ - hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); + hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0); if (hub_wq) return 0; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 307ea563af95..3ddfd4f66f0b 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1272,8 +1272,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) INIT_WORK(&hidg->work, get_report_workqueue_handler); hidg->workqueue = alloc_workqueue("report_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!hidg->workqueue) { From 1052864d7d628f7c1f51cffea5ada554def31314 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 7 Nov 2025 16:37:31 +0100 Subject: [PATCH 150/161] usb: typec: anx7411: add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Cc: Heikki Krogerus Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251107153737.301413-3-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/anx7411.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 0ae0a5ee3fae..2e8ae1d2faf9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1516,8 +1516,7 @@ static int anx7411_i2c_probe(struct i2c_client *client) INIT_WORK(&plat->work, anx7411_work_func); plat->workqueue = alloc_workqueue("anx7411_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!plat->workqueue) { dev_err(dev, "fail to create work queue\n"); From d53bdaae894768eccff55327d379e8c033ce30d8 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Tue, 11 Nov 2025 12:50:24 +0530 Subject: [PATCH 151/161] dt-bindings: usb: ti,hd3ss3220: Add support for VBUS based on ID state Update the bindings to support reading ID state and VBUS, as per the HD3SS3220 data sheet. The ID pin is kept high if VBUS is not at VSafe0V and asserted low once VBUS is at VSafe0V, enforcing the Type-C requirement that VBUS must be at VSafe0V before re-enabling VBUS. Add id-gpios property to describe the input gpio for USB ID pin. Reviewed-by: Rob Herring (Arm) Signed-off-by: Krishna Kurapati Link: https://patch.msgid.link/20251111072025.2199142-2-krishna.kurapati@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml b/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml index bec1c8047bc0..06099e93c6c3 100644 --- a/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml +++ b/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml @@ -25,6 +25,14 @@ properties: interrupts: maxItems: 1 + id-gpios: + description: + An input gpio for USB ID pin. Upon detecting a UFP device, HD3SS3220 + will keep ID pin high if VBUS is not at VSafe0V. Once VBUS is at VSafe0V, + the HD3SS3220 will assert ID pin low. This is done to enforce Type-C + requirement that VBUS must be at VSafe0V before re-enabling VBUS. + maxItems: 1 + ports: $ref: /schemas/graph.yaml#/properties/ports description: OF graph bindings (specified in bindings/graph.txt) that model From f8d2bf7c0c5d2e9eb1792587c3d29a4c5201634e Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Tue, 11 Nov 2025 12:50:25 +0530 Subject: [PATCH 152/161] usb: typec: hd3ss3220: Enable VBUS based on ID pin state There is a ID pin present on HD3SS3220 controller that can be routed to SoC. As per the datasheet: "Upon detecting a UFP device, HD3SS3220 will keep ID pin high if VBUS is not at VSafe0V. Once VBUS is at VSafe0V, the HD3SS3220 will assert ID pin low. This is done to enforce Type-C requirement that VBUS must be at VSafe0V before re-enabling VBUS" Add support to read the ID pin state and enable VBUS accordingly. Signed-off-by: Krishna Kurapati Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251111072025.2199142-3-krishna.kurapati@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/hd3ss3220.c | 75 ++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 3ecc688dda82..3876f4faead6 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #define HD3SS3220_REG_CN_STAT 0x08 #define HD3SS3220_REG_CN_STAT_CTRL 0x09 @@ -54,6 +57,11 @@ struct hd3ss3220 { struct delayed_work output_poll_work; enum usb_role role_state; bool poll; + + struct gpio_desc *id_gpiod; + int id_irq; + + struct regulator *vbus; }; static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) @@ -319,13 +327,33 @@ static const struct regmap_config config = { .max_register = 0x0A, }; +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id) +{ + struct hd3ss3220 *hd3ss3220 = dev_id; + int ret; + int id; + + id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod); + if (!id) + ret = regulator_enable(hd3ss3220->vbus); + else + ret = regulator_disable(hd3ss3220->vbus); + + if (ret) + dev_err(hd3ss3220->dev, + "vbus regulator %s failed: %d\n", id ? "disable" : "enable", ret); + + return IRQ_HANDLED; +} + static int hd3ss3220_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; - struct hd3ss3220 *hd3ss3220; struct fwnode_handle *connector, *ep; - int ret; + struct hd3ss3220 *hd3ss3220; + struct regulator *vbus; unsigned int data; + int ret; hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220), GFP_KERNEL); @@ -359,6 +387,49 @@ static int hd3ss3220_probe(struct i2c_client *client) goto err_put_fwnode; } + vbus = devm_of_regulator_get_optional(hd3ss3220->dev, + to_of_node(connector), + "vbus"); + if (IS_ERR(vbus) && vbus != ERR_PTR(-ENODEV)) { + ret = PTR_ERR(vbus); + dev_err(hd3ss3220->dev, "failed to get vbus: %d", ret); + goto err_put_fwnode; + } + + hd3ss3220->vbus = (vbus == ERR_PTR(-ENODEV) ? NULL : vbus); + + if (hd3ss3220->vbus) { + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, + "id", + GPIOD_IN); + if (IS_ERR(hd3ss3220->id_gpiod)) { + ret = PTR_ERR(hd3ss3220->id_gpiod); + goto err_put_fwnode; + } + } + + if (hd3ss3220->id_gpiod) { + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod); + if (hd3ss3220->id_irq < 0) { + ret = hd3ss3220->id_irq; + dev_err(hd3ss3220->dev, + "failed to get ID gpio: %d\n", + hd3ss3220->id_irq); + goto err_put_fwnode; + } + + ret = devm_request_threaded_irq(hd3ss3220->dev, + hd3ss3220->id_irq, NULL, + hd3ss3220_id_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(hd3ss3220->dev), hd3ss3220); + if (ret < 0) { + dev_err(hd3ss3220->dev, "failed to get ID irq: %d\n", ret); + goto err_put_fwnode; + } + } + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; typec_cap.driver_data = hd3ss3220; typec_cap.type = TYPEC_PORT_DRP; From 955a48a5353f4fe009704a9a4272a3adf627cd35 Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Fri, 21 Nov 2025 14:40:20 +0800 Subject: [PATCH 153/161] usb: usb-storage: No additional quirks need to be added to the EL-R12 optical drive. The optical drive of EL-R12 has the same vid and pid as INIC-3069, as follows: T: Bus=02 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0 D: Ver= 3.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=13fd ProdID=3940 Rev= 3.10 S: Manufacturer=HL-DT-ST S: Product= DVD+-RW GT80N S: SerialNumber=423349524E4E38303338323439202020 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=144mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=02 Prot=50 Driver=usb-storage E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=0a(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms This will result in the optical drive device also adding the quirks of US_FL_NO_ATA_1X. When performing an erase operation, it will fail, and the reason for the failure is as follows: [ 388.967742] sr 5:0:0:0: [sr0] tag#0 Send: scmd 0x00000000d20c33a7 [ 388.967742] sr 5:0:0:0: [sr0] tag#0 CDB: ATA command pass through(12)/Blank a1 11 00 00 00 00 00 00 00 00 00 00 [ 388.967773] sr 5:0:0:0: [sr0] tag#0 Done: SUCCESS Result: hostbyte=DID_TARGET_FAILURE driverbyte=DRIVER_OK cmd_age=0s [ 388.967773] sr 5:0:0:0: [sr0] tag#0 CDB: ATA command pass through(12)/Blank a1 11 00 00 00 00 00 00 00 00 00 00 [ 388.967803] sr 5:0:0:0: [sr0] tag#0 Sense Key : Illegal Request [current] [ 388.967803] sr 5:0:0:0: [sr0] tag#0 Add. Sense: Invalid field in cdb [ 388.967803] sr 5:0:0:0: [sr0] tag#0 scsi host busy 1 failed 0 [ 388.967803] sr 5:0:0:0: Notifying upper driver of completion (result 8100002) [ 388.967834] sr 5:0:0:0: [sr0] tag#0 0 sectors total, 0 bytes done. For the EL-R12 standard optical drive, all operational commands and usage scenarios were tested without adding the IGNORE_RESIDUE quirks, and no issues were encountered. It can be reasonably concluded that removing the IGNORE_RESIDUE quirks has no impact. Signed-off-by: Chen Changcheng Link: https://patch.msgid.link/20251121064020.29332-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 1477e31d7763..b695f5ba9a40 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, 0x0000, 0x9999, +UNUSUAL_DEV(0x13fd, 0x3940, 0x0309, 0x0309, "Initio Corporation", "INIC-3069", USB_SC_DEVICE, USB_PR_DEVICE, NULL, From 7970b4969c4c99bcdaf105f9f39c6d2021f6d244 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Tue, 18 Nov 2025 14:45:28 +0800 Subject: [PATCH 154/161] USB: serial: option: add Foxconn T99W760 T99W760 is designed based on Qualcomm SDX35 (5G redcap) chip. There are three serial ports to be enumerated: Modem, NMEA and Diag. test evidence as below: T: Bus=03 Lev=01 Prnt=01 Port=03 Cnt=01 Dev#= 4 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e123 Rev=05.15 S: Manufacturer=QCOM S: Product=SDXBAAGHA-IDP _SN:39A8D3E4 S: SerialNumber=39a8d3e4 C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=896mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=85(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=88(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms 0&1: MBIM, 2:Modem, 3:GNSS(non-serial port), 4: NMEA, 5:Diag Signed-off-by: Slark Xiao Cc: stable@vger.kernel.org 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 5de856f65f0d..8fcac5c91bcb 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2376,6 +2376,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */ .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe123, 0xff), /* Foxconn T99W760 MBIM */ + .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe145, 0xff), /* Foxconn T99W651 RNDIS */ .driver_info = RSVD(5) | RSVD(6) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe15f, 0xff), /* Foxconn T99W709 */ From c69ff68b097b0f53333114f1b2c3dc128f389596 Mon Sep 17 00:00:00 2001 From: Diogo Ivo Date: Fri, 21 Nov 2025 18:16:36 +0000 Subject: [PATCH 155/161] usb: phy: Initialize struct usb_phy list_head As part of the registration of a new 'struct usb_phy' with the USB PHY core via either usb_add_phy(struct usb_phy *x, ...) or usb_add_phy_dev(struct usb_phy *x) these functions call list_add_tail(&x->head, phy_list) in order for the new instance x to be stored in phy_list, a static list kept internally by the core. After 7d21114dc6a2 ("usb: phy: Introduce one extcon device into usb phy") when executing either of the registration functions above it is possible that usb_add_extcon() fails, leading to either function returning before the call to list_add_tail(), leaving x->head uninitialized. Then, when a driver tries to undo the failed registration by calling usb_remove_phy(struct usb_phy *x) there will be an unconditional call to list_del(&x->head) acting on an uninitialized variable, and thus a possible NULL pointer dereference. Fix this by initializing x->head before usb_add_extcon() has a chance to fail. Note that this was not needed before 7d21114dc6a2 since list_add_phy() was executed unconditionally and it guaranteed that x->head was initialized. Fixes: 7d21114dc6a2 ("usb: phy: Introduce one extcon device into usb phy") Cc: stable Signed-off-by: Diogo Ivo Link: https://patch.msgid.link/20251121-diogo-smaug_typec-v2-1-5c37c1169d57@tecnico.ulisboa.pt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index e1435bc59662..5a9b9353f343 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) @@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) From 6d935ce213bd9d3760947e0743f30bfa63c8404f Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 16 Nov 2025 18:00:33 +0530 Subject: [PATCH 156/161] usb: dwc3: core: Remove redundant comment in core init Remove redundant comment which was put in to address LLUCTL register modifications for all applicable ports of multiport controller. Although the support was added, the todo comment wasn't removed then. Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20251116123033.131004-1-krishna.kurapati@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d7f340f22595..5ea8bf056d37 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1483,10 +1483,6 @@ int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); - /* - * Modify this for all supported Super Speed ports when - * multiport support is added. - */ if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { From 6b120ef99fbcba9e413783561f8cc160719db589 Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Tue, 25 Nov 2025 18:36:26 +0800 Subject: [PATCH 157/161] usb: typec: ucsi: fix probe failure in gaokun_ucsi_probe() The gaokun_ucsi_probe() uses ucsi_create() to allocate a UCSI instance. The ucsi_create() validates whether ops->poll_cci is defined, and if not, it directly returns -EINVAL. However, the gaokun_ucsi_ops structure does not define the poll_cci, causing ucsi_create() always fail with -EINVAL. This issue can be observed in the kernel log with the following error: ucsi_huawei_gaokun.ucsi huawei_gaokun_ec.ucsi.0: probe with driver ucsi_huawei_gaokun.ucsi failed with error -22 Fix the issue by adding the missing poll_cci callback to gaokun_ucsi_ops. Fixes: 00327d7f2c8c ("usb: typec: ucsi: add Huawei Matebook E Go ucsi driver") Cc: stable Signed-off-by: Duoming Zhou Reviewed-by: Heikki Krogerus Reviewed-by: Pengyu Luo Link: https://patch.msgid.link/4d077d6439d728be68646bb8c8678436a3a0885e.1764065838.git.duoming@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index 7b5222081bbb..8401ab414bd9 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -196,6 +196,7 @@ static void gaokun_ucsi_connector_status(struct ucsi_connector *con) const struct ucsi_operations gaokun_ucsi_ops = { .read_version = gaokun_ucsi_read_version, .read_cci = gaokun_ucsi_read_cci, + .poll_cci = gaokun_ucsi_read_cci, .read_message_in = gaokun_ucsi_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = gaokun_ucsi_async_control, From 2b7a0f47aaf2439d517ba0a6b29c66a535302154 Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Tue, 25 Nov 2025 18:36:27 +0800 Subject: [PATCH 158/161] usb: typec: ucsi: fix use-after-free caused by uec->work The delayed work uec->work is scheduled in gaokun_ucsi_probe() but never properly canceled in gaokun_ucsi_remove(). This creates use-after-free scenarios where the ucsi and gaokun_ucsi structure are freed after ucsi_destroy() completes execution, while the gaokun_ucsi_register_worker() might be either currently executing or still pending in the work queue. The already-freed gaokun_ucsi or ucsi structure may then be accessed. Furthermore, the race window is 3 seconds, which is sufficiently long to make this bug easily reproducible. The following is the trace captured by KASAN: ================================================================== BUG: KASAN: slab-use-after-free in __run_timers+0x5ec/0x630 Write of size 8 at addr ffff00000ec28cc8 by task swapper/0/0 ... Call trace: show_stack+0x18/0x24 (C) dump_stack_lvl+0x78/0x90 print_report+0x114/0x580 kasan_report+0xa4/0xf0 __asan_report_store8_noabort+0x20/0x2c __run_timers+0x5ec/0x630 run_timer_softirq+0xe8/0x1cc handle_softirqs+0x294/0x720 __do_softirq+0x14/0x20 ____do_softirq+0x10/0x1c call_on_irq_stack+0x30/0x48 do_softirq_own_stack+0x1c/0x28 __irq_exit_rcu+0x27c/0x364 irq_exit_rcu+0x10/0x1c el1_interrupt+0x40/0x60 el1h_64_irq_handler+0x18/0x24 el1h_64_irq+0x6c/0x70 arch_local_irq_enable+0x4/0x8 (P) do_idle+0x334/0x458 cpu_startup_entry+0x60/0x70 rest_init+0x158/0x174 start_kernel+0x2f8/0x394 __primary_switched+0x8c/0x94 Allocated by task 72 on cpu 0 at 27.510341s: kasan_save_stack+0x2c/0x54 kasan_save_track+0x24/0x5c kasan_save_alloc_info+0x40/0x54 __kasan_kmalloc+0xa0/0xb8 __kmalloc_node_track_caller_noprof+0x1c0/0x588 devm_kmalloc+0x7c/0x1c8 gaokun_ucsi_probe+0xa0/0x840 auxiliary_bus_probe+0x94/0xf8 really_probe+0x17c/0x5b8 __driver_probe_device+0x158/0x2c4 driver_probe_device+0x10c/0x264 __device_attach_driver+0x168/0x2d0 bus_for_each_drv+0x100/0x188 __device_attach+0x174/0x368 device_initial_probe+0x14/0x20 bus_probe_device+0x120/0x150 device_add+0xb3c/0x10fc __auxiliary_device_add+0x88/0x130 ... Freed by task 73 on cpu 1 at 28.910627s: kasan_save_stack+0x2c/0x54 kasan_save_track+0x24/0x5c __kasan_save_free_info+0x4c/0x74 __kasan_slab_free+0x60/0x8c kfree+0xd4/0x410 devres_release_all+0x140/0x1f0 device_unbind_cleanup+0x20/0x190 device_release_driver_internal+0x344/0x460 device_release_driver+0x18/0x24 bus_remove_device+0x198/0x274 device_del+0x310/0xa84 ... The buggy address belongs to the object at ffff00000ec28c00 which belongs to the cache kmalloc-512 of size 512 The buggy address is located 200 bytes inside of freed 512-byte region The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4ec28 head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x3fffe0000000040(head|node=0|zone=0|lastcpupid=0x1ffff) page_type: f5(slab) raw: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 raw: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 head: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 head: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 head: 03fffe0000000002 fffffdffc03b0a01 00000000ffffffff 00000000ffffffff head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff00000ec28b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff00000ec28c00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff00000ec28c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff00000ec28d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff00000ec28d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Add disable_delayed_work_sync() in gaokun_ucsi_remove() to ensure that uec->work is properly canceled and prevented from executing after the ucsi and gaokun_ucsi structure have been deallocated. Fixes: 00327d7f2c8c ("usb: typec: ucsi: add Huawei Matebook E Go ucsi driver") Cc: stable Signed-off-by: Duoming Zhou Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/cc31e12ef9ffbf86676585b02233165fd33f0d8e.1764065838.git.duoming@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index 8401ab414bd9..c5965656baba 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -503,6 +503,7 @@ static void gaokun_ucsi_remove(struct auxiliary_device *adev) { struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); + disable_delayed_work_sync(&uec->work); gaokun_ec_unregister_notify(uec->ec, &uec->nb); ucsi_unregister(uec->ucsi); ucsi_destroy(uec->ucsi); From c908039a29aa70870871f4848125b3d743f929bf Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Wed, 26 Nov 2025 15:26:39 +0100 Subject: [PATCH 159/161] USB: serial: option: add Telit Cinterion FE910C04 new compositions Add the following Telit Cinterion new compositions: 0x10c1: RNDIS + tty (AT/NMEA) + tty (AT) + tty (diag) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c1 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 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=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10c2: MBIM + tty (AT/NMEA) + tty (AT) + tty (diag) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 8 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c2 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10c3: ECM + tty (AT/NMEA) + tty (AT) + tty (diag) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c3 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10c5: RNDIS + tty (AT) + tty (AT) + tty (diag) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 10 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c5 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 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=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10c6: MBIM + tty (AT) + tty (AT) + tty (diag) T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 11 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c6 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10c9: MBIM + tty (AT) + tty (diag) + DPL (Data Packet Logging) + adb T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 13 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10c9 Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 6 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x10cb: RNDIS + tty (AT) + tty (diag) + DPL (Data Packet Logging) + adb T: Bus=01 Lev=01 Prnt=01 Port=09 Cnt=01 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=10cb Rev=05.15 S: Manufacturer=Telit Cinterion S: Product=FE910 S: SerialNumber=f71b8b32 C: #Ifs= 6 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 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=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org Signed-off-by: Fabio Porcedda Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 8fcac5c91bcb..c89c82ddf063 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1433,10 +1433,24 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c0, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c1, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c2, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c3, 0xff), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c4, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c5, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c6, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c9, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10cb, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x30), /* Telit FN990B (rmnet) */ .driver_info = NCTRL(5) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) }, From 072f2c49572547f4b0776fe2da6b8f61e4b34699 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Wed, 26 Nov 2025 15:26:40 +0100 Subject: [PATCH 160/161] USB: serial: option: move Telit 0x10c7 composition in the right place Move Telit 0x10c7 composition right after 0x10c6 composition and before 0x10c8 composition. Signed-off-by: Fabio Porcedda Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c89c82ddf063..51372c4fecc3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1445,6 +1445,9 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c6, 0xff), /* Telit FE910C04 (MBIM) */ .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c9, 0xff), /* Telit FE910C04 (MBIM) */ @@ -1455,9 +1458,6 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(5) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x60) }, - { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */ - .driver_info = NCTRL(4) }, - { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x30), /* Telit FN990B (MBIM) */ .driver_info = NCTRL(6) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x40) }, From 2585973c7f9ee31d21e5848c996fab2521fd383d Mon Sep 17 00:00:00 2001 From: Haotien Hsu Date: Thu, 27 Nov 2025 11:35:40 +0800 Subject: [PATCH 161/161] usb: gadget: tegra-xudc: Always reinitialize data toggle when clear halt The driver previously skipped handling ClearFeature(ENDPOINT_HALT) when the endpoint was already not halted. This prevented the controller from resetting the data sequence number and reinitializing the endpoint state. According to USB 3.2 specification Rev. 1.1, section 9.4.5, ClearFeature(ENDPOINT_HALT) must always reset the data sequence and set the stream state machine to Disabled, regardless of whether the endpoint was halted. Remove the early return so that ClearFeature(ENDPOINT_HALT) always resets the endpoint sequence state as required by the specification. Fixes: 49db427232fe ("usb: gadget: Add UDC driver for tegra XUSB device mode controller") Cc: stable Signed-off-by: Haotien Hsu Signed-off-by: Wayne Chang Link: https://patch.msgid.link/20251127033540.2287517-1-waynec@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/tegra-xudc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 0c38fc37b6e6..9d2007f448c0 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -1558,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) return -ENOTSUPP; } - if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { - dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, - halt ? "halted" : "not halted"); - return 0; - } - if (halt) { ep_halt(xudc, ep->index); } else {