From: Niklas Neronin niklas.neronin@linux.intel.com
[ Upstream commit 719de070f764e079cdcb4ddeeb5b19b3ddddf9c1 ]
Add xhci support for PCI hosts that have zero USB3 ports. Avoid creating a shared Host Controller Driver (HCD) when there is only one root hub. Additionally, all references to 'xhci->shared_hcd' are now checked before use.
Only xhci-pci.c requires modification to accommodate this change, as the xhci core already supports configurations with zero USB3 ports. This capability was introduced when xHCI Platform and MediaTek added support for zero USB3 ports.
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220181 Tested-by: Nick Nielsen nick.kainielsen@free.fr Tested-by: grm1 grm1@mailbox.org Signed-off-by: Niklas Neronin niklas.neronin@linux.intel.com Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com Link: https://lore.kernel.org/r/20250917210726.97100-4-mathias.nyman@linux.intel.c... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES — this change is a focused bugfix that lets PCI xHCI controllers with only a single root hub enumerate correctly, matching support already present in the core and other host adapters.
- `drivers/usb/host/xhci-pci.c:640` now sets `xhci->allow_single_roothub = 1`, allowing the existing `xhci_has_one_roothub()` helper to recognize hosts that genuinely provide only USB2 or only USB3 ports. For such hardware the new branch at `drivers/usb/host/xhci- pci.c:641-659` skips creating the secondary HCD and still runs `xhci_ext_cap_init()`, preventing the allocation/registration of a useless SuperSpeed root hub that currently causes probe failures on the systems reported in bug 220181. - Stream capability handling switches to `xhci_get_usb3_hcd()` at `drivers/usb/host/xhci-pci.c:662-664`, so the code safely handles both the traditional dual-root-hub case and the new single-root-hub case without dereferencing a NULL `shared_hcd`. - The xHCI core has supported “single-roothub” controllers since commit 873f323618c2 (see the helper definitions in `drivers/usb/host/xhci.h:1659-1737`), and platform drivers already rely on the same pattern (`drivers/usb/host/xhci-plat.c:207` and `drivers/usb/host/xhci-mtk.c:629-655`). This patch simply brings the PCI glue in line with that infrastructure, so it has no architectural side effects. - Scope is limited to the PCI front-end; it doesn’t alter shared data structures or other subsystems. Tested-by tags and the fact that the alternative drivers have run this logic for multiple release cycles further reduce regression risk. Backporters only need to ensure the target stable branch already contains the earlier “allow_single_roothub” support (present in 6.1+). If that prerequisite is met, the change is small, self-contained, and fixes real hardware breakage.
Natural next steps: 1) cherry-pick (plus prerequisite check) into the relevant stable trees; 2) rerun basic USB enumeration on affected hardware to confirm the controller now probes successfully.
drivers/usb/host/xhci-pci.c | 42 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 00fac8b233d2a..5c8ab519f497d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -610,7 +610,7 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) { int retval; struct xhci_hcd *xhci; - struct usb_hcd *hcd; + struct usb_hcd *hcd, *usb3_hcd; struct reset_control *reset;
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); @@ -636,26 +636,32 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) hcd = dev_get_drvdata(&dev->dev); xhci = hcd_to_xhci(hcd); xhci->reset = reset; - xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, - pci_name(dev), hcd); - if (!xhci->shared_hcd) { - retval = -ENOMEM; - goto dealloc_usb2_hcd; - }
- retval = xhci_ext_cap_init(xhci); - if (retval) - goto put_usb3_hcd; + xhci->allow_single_roothub = 1; + if (!xhci_has_one_roothub(xhci)) { + xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, + pci_name(dev), hcd); + if (!xhci->shared_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + }
- retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_SHARED); - if (retval) - goto put_usb3_hcd; - /* Roothub already marked as USB 3.0 speed */ + retval = xhci_ext_cap_init(xhci); + if (retval) + goto put_usb3_hcd; + + retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); + if (retval) + goto put_usb3_hcd; + } else { + retval = xhci_ext_cap_init(xhci); + if (retval) + goto dealloc_usb2_hcd; + }
- if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && - HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; + usb3_hcd = xhci_get_usb3_hcd(xhci); + if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1;
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ pm_runtime_put_noidle(&dev->dev);