xhci: support secondary bandwidth tracking
Add support for tracking both root port LS/FS bandwidth instances,
and external USB2 hub LS/FS secondary bandwidth domains.
include info to both traces and dynamic debug
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8850fd3..d698e09 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1929,6 +1929,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
{
int ret;
+ xhci_get_port_bandwidth(xhci, udev);
+
switch (*cmd_status) {
case COMP_COMMAND_ABORTED:
case COMP_COMMAND_RING_STOPPED:
@@ -2821,26 +2823,66 @@ static int xhci_get_port_bandwidth(struct xhci_hcd *xhci,
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_command *command;
- dma_addr_t dma;
- unsigned int i;
- char *ctx;
- unsigned int hub_slot_id = 0;
- unsigned int dev_speed =
- udev->speed == USB_SPEED_HIGH ? 0x3 :
- udev->speed == USB_SPEED_SUPER ? 0x4 :
- 0x0;
+ struct xhci_hub *rhub;
unsigned long flags;
- int ret = 0;
+ unsigned int hub_slot_id = 0;
+ unsigned int bw_ctx_size;
+ unsigned int dev_speed;
+ unsigned int port_index;
unsigned int max_ports;
+ unsigned int i;
+ dma_addr_t dma;
+ char *speed_name;
+ char *bw_ctx;
+ int ret = 0;
+
+ /*
+ * xhci has three types of bandwidth instances, SS, HS and LS/FS.
+ * A external single-TT USB2 hub has one LS/FS secondary bandwith
+ * instance, a multi-TT USB2 hub has several secondary LS/FS BIs.
+ */
+
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ dev_speed = 2;
+ speed_name = "LS/FS";
+ rhub = &xhci->usb2_rhub;
+ if (udev->parent && udev->parent->parent) {
+ hub_slot_id = udev->parent->slot_id;
+ max_ports = udev->parent->maxchild;
+ bw_ctx_size = max_ports + 1;
+ }
+ break;
+ case USB_SPEED_HIGH:
+ dev_speed = 3;
+ speed_name = "HS";
+ rhub = &xhci->usb2_rhub;
+ break;
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ dev_speed = 4;
+ speed_name = "SS";
+ rhub = &xhci->usb3_rhub;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (!hub_slot_id) {
+ max_ports = rhub->num_ports;
+ bw_ctx_size = HCS_MAX_PORTS(xhci->hcs_params1) + 1;
+ }
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
if (!command)
return -ENOMEM;
- max_ports = HCS_MAX_PORTS(xhci->hcs_params1) + 1;
- ctx = dma_alloc_coherent(dev, round_up(max_ports, 8), &dma, GFP_KERNEL);
- if (!ctx)
+ bw_ctx = dma_alloc_coherent(dev, round_up(bw_ctx_size, 8), &dma, GFP_KERNEL);
+ if (!bw_ctx) {
+ xhci_free_command(xhci, command);
return -ENOMEM;
+ }
spin_lock_irqsave(&xhci->lock, flags);
ret = xhci_queue_get_port_bandwidth(xhci, command, dma,
@@ -2856,14 +2898,31 @@ static int xhci_get_port_bandwidth(struct xhci_hcd *xhci,
wait_for_completion(command->completion);
/* Read Bandwidth utilization from Context */
- for (i = 0; i < max_ports; i++)
- dev_warn(&udev->dev,
- "port %d - available bandwidth: %hhd %%\n", i, ctx[i]);
+ for (i = 0; i < max_ports; i++) {
+ if (hub_slot_id)
+ port_index = i + 1;
+ else
+ port_index = rhub->ports[i]->hw_portnum + 1;
+ dev_dbg(&udev->dev,
+ "Available %s bandwidth on %s port%d - %hhd %%",
+ speed_name, hub_slot_id ? "external hub" : "root hub",
+ port_index, bw_ctx[port_index]);
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_context_change,
+ "Available %s bandwidth on %s port%d - %hhd %%",
+ speed_name,
+ hub_slot_id ? "external hub" : "root hub",
+ port_index,
+ bw_ctx[port_index]);
+ }
out:
- dma_free_coherent(dev, round_up(max_ports, 8), ctx, dma);
+ dma_free_coherent(dev, round_up(max_ports, 8), bw_ctx, dma);
xhci_free_command(xhci, command);
+ if (hub_slot_id)
+ xhci_get_port_bandwidth(xhci, udev->parent);
+
return 0;
}