On 18.8.2025 12.50, Michał Pecio wrote:
On Tue, 12 Aug 2025 16:24:42 +0300, Sakari Ailus wrote:
From: "Rai, Amardeep" <amardeep.rai@xxxxxxxxx>
Detect eUSB2 double isoc bw capable hosts and devices, and set the proper
xhci endpoint context values such as 'Mult', 'Max Burst Size', and 'Max
ESIT Payload' to enable the double isochronous bandwidth endpoints.
Intel xHC uses the endpoint context 'Mult' field for eUSB2 isoc
endpoints even if hosts supporting Large ESIT Payload Capability should
normally ignore the mult field.
Signed-off-by: Rai, Amardeep <amardeep.rai@xxxxxxxxx>
Co-developed-by: Kannappan R <r.kannappan@xxxxxxxxx>
Signed-off-by: Kannappan R <r.kannappan@xxxxxxxxx>
Reviewed-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
Co-developed-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>
Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>
Co-developed-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
---
drivers/usb/host/xhci-caps.h | 2 ++
drivers/usb/host/xhci-mem.c | 60 ++++++++++++++++++++++++++++--------
drivers/usb/host/xhci-ring.c | 6 ++--
drivers/usb/host/xhci.c | 16 +++++++++-
drivers/usb/host/xhci.h | 19 ++++++++++++
5 files changed, 87 insertions(+), 16 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 4b8ff4815644..89bc83e4f1eb 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -89,3 +89,5 @@
#define HCC2_GSC(p) ((p) & (1 << 8))
/* true: HC support Virtualization Based Trusted I/O Capability */
#define HCC2_VTC(p) ((p) & (1 << 9))
+/* true: HC support Double BW on a eUSB2 HS ISOC EP */
+#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11))
I guess this bit is not defined in the original xHCI 1.2 spec which
predates BW doubling, any reference to where it is specified and what
it means exactly?
USB 2.0 Double Isochronous IN Bandwidth Capability (DIC) – RO.
This bit when set to 1, indicates that the xHC supports the USB 2.0 Double Isochronous IN
Bandwidth Capability on a eUSB2 HS Isochronous Endpoint.
This feature is only applicable to a directly connected inbox native eUSB2 device.
The xHCI specification with this addition is on its way, we got permission from
author(s) to start upstreaming the code already.
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 6680afa4f596..ea51434c80fa 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1328,18 +1328,36 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
return interval;
}
-/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps.
+/*
+ * xHCs without LEC use the "Mult" field in the endpoint context for SuperSpeed
+ * isoc eps, and High speed isoc eps that support bandwidth doubling. Standard
* High speed endpoint descriptors can define "the number of additional
* transaction opportunities per microframe", but that goes in the Max Burst
* endpoint context field.
*/
-static u32 xhci_get_endpoint_mult(struct usb_device *udev,
- struct usb_host_endpoint *ep)
+static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_host_endpoint *ep)
{
- if (udev->speed < USB_SPEED_SUPER ||
- !usb_endpoint_xfer_isoc(&ep->desc))
+ 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);
+
+ /* eUSB2 double isoc bw devices are the only USB2 devices using mult */
+ if (xhci_eusb2_is_isoc_bw_double(udev, ep)) {
+ if (!lec || xhci->quirks & XHCI_INTEL_HOST)
+ return 1;
+ }
+
+ /* Oherwise only isoc transfers on hosts without LEC uses mult field */
+ if (!usb_endpoint_xfer_isoc(&ep->desc) || lec)
return 0;
- return ep->ss_ep_comp.bmAttributes;
+
+ if (udev->speed >= USB_SPEED_SUPER)
+ return ep->ss_ep_comp.bmAttributes;
+
+ return 0;
}
That's a complicated control flow. I think it could just be:
+ /* SuperSpeed isoc transfers on hosts without LEC uses mult field */
+ if (udev->speed >= USB_SPEED_SUPER && usb_endpoint_xfer_isoc(&ep->desc) && !lec)
+ return ep->ss_ep_comp.bmAttributes;
+ return 0;
Possibly, not sure there's much difference, especially if you break that line at
80 columns
+/*
+ * USB 2.0 specification, chapter 5.6.4 Isochronous Transfer Bus Access
+ * Constraint. A high speed endpoint can move up to 3072 bytes per microframe
+ * (or 192Mb/s).
+ */
+#define MAX_ISOC_XFER_SIZE_HS 3072
+
+static inline bool xhci_eusb2_is_isoc_bw_double(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ return udev->speed == USB_SPEED_HIGH &&
+ usb_endpoint_is_isoc_in(&ep->desc) &&
+ le16_to_cpu(ep->desc.wMaxPacketSize) == 0 &&
+ le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval) >
+ MAX_ISOC_XFER_SIZE_HS;
+}
It looks like eUSB2v2 is another spec which uses this descriptor for
larger isoc endpoints and this code might trigger on such devices once
USB core learns to parse them.
Could make sense to check that bcdUSB is 0x220 here to avoid that.
We could also check if ep->eusb2_isoc_ep_comp.bDescriptorType exists
like in PATCH 2/4, and could then remove the wMaxPacketSize == 0 check as
usb core descriptor parsing will already do that check for us, before copying
the descriptor.
The USB_SPEED_HIGH check could also be moved to descriptor parsing in usb core.
This way we could remove the speed check hare and from PATCH 2/3
Would there be no issues with that? Or conversely, any chance that your
code could be trivially modified to support full 2v2, and "bw doubling"
removed from names and comments?
Proably not, eUSB2v2 may have some odd burst and mult fields while double
bandwidth has fixed values.
Thanks
Mathias