| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 00e1a43b64abc8950b471678b7ed4415f3513f3e Mon Sep 17 00:00:00 2001 |
| 2 | From: Jonathan Bell <jonathan@raspberrypi.org> |
| 3 | Date: Tue, 11 Jun 2019 11:33:39 +0100 |
| 4 | Subject: [PATCH] xhci: implement xhci_fixup_endpoint for interval |
| 5 | adjustments |
| 6 | |
| 7 | Must be called in a non-atomic context, after the endpoint |
| 8 | has been registered with the hardware via xhci_add_endpoint |
| 9 | and before the first URB is submitted for the endpoint. |
| 10 | |
| 11 | Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org> |
| 12 | --- |
| 13 | drivers/usb/host/xhci.c | 98 +++++++++++++++++++++++++++++++++++++++++ |
| 14 | 1 file changed, 98 insertions(+) |
| 15 | |
| 16 | --- a/drivers/usb/host/xhci.c |
| 17 | +++ b/drivers/usb/host/xhci.c |
| 18 | @@ -1487,6 +1487,103 @@ command_cleanup: |
| 19 | } |
| 20 | |
| 21 | /* |
| 22 | + * RPI: Fixup endpoint intervals when requested |
| 23 | + * - Check interval versus the (cached) endpoint context |
| 24 | + * - set the endpoint interval to the new value |
| 25 | + * - force an endpoint configure command |
| 26 | + * XXX: bandwidth is not recalculated. We should probably do that. |
| 27 | + */ |
| 28 | +static void xhci_fixup_endpoint(struct usb_hcd *hcd, struct usb_device *udev, |
| 29 | + struct usb_host_endpoint *ep, int interval) |
| 30 | +{ |
| 31 | + struct xhci_hcd *xhci; |
| 32 | + struct xhci_ep_ctx *ep_ctx_out, *ep_ctx_in; |
| 33 | + struct xhci_command *command; |
| 34 | + struct xhci_input_control_ctx *ctrl_ctx; |
| 35 | + struct xhci_virt_device *vdev; |
| 36 | + int xhci_interval; |
| 37 | + int ret; |
| 38 | + int ep_index; |
| 39 | + unsigned long flags; |
| 40 | + u32 ep_info_tmp; |
| 41 | + |
| 42 | + xhci = hcd_to_xhci(hcd); |
| 43 | + ep_index = xhci_get_endpoint_index(&ep->desc); |
| 44 | + |
| 45 | + /* FS/LS interval translations */ |
| 46 | + if ((udev->speed == USB_SPEED_FULL || |
| 47 | + udev->speed == USB_SPEED_LOW)) |
| 48 | + interval *= 8; |
| 49 | + |
| 50 | + mutex_lock(&xhci->mutex); |
| 51 | + |
| 52 | + spin_lock_irqsave(&xhci->lock, flags); |
| 53 | + |
| 54 | + vdev = xhci->devs[udev->slot_id]; |
| 55 | + /* Get context-derived endpoint interval */ |
| 56 | + ep_ctx_out = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index); |
| 57 | + ep_ctx_in = xhci_get_ep_ctx(xhci, vdev->in_ctx, ep_index); |
| 58 | + xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx_out->ep_info)); |
| 59 | + |
| 60 | + if (interval == xhci_interval) { |
| 61 | + spin_unlock_irqrestore(&xhci->lock, flags); |
| 62 | + mutex_unlock(&xhci->mutex); |
| 63 | + return; |
| 64 | + } |
| 65 | + |
| 66 | + xhci_dbg(xhci, "Fixup interval=%d xhci_interval=%d\n", |
| 67 | + interval, xhci_interval); |
| 68 | + command = xhci_alloc_command_with_ctx(xhci, true, GFP_ATOMIC); |
| 69 | + if (!command) { |
| 70 | + /* Failure here is benign, poll at the original rate */ |
| 71 | + spin_unlock_irqrestore(&xhci->lock, flags); |
| 72 | + mutex_unlock(&xhci->mutex); |
| 73 | + return; |
| 74 | + } |
| 75 | + |
| 76 | + /* xHCI uses exponents for intervals... */ |
| 77 | + xhci_interval = fls(interval) - 1; |
| 78 | + xhci_interval = clamp_val(xhci_interval, 3, 10); |
| 79 | + ep_info_tmp = le32_to_cpu(ep_ctx_out->ep_info); |
| 80 | + ep_info_tmp &= ~EP_INTERVAL(255); |
| 81 | + ep_info_tmp |= EP_INTERVAL(xhci_interval); |
| 82 | + |
| 83 | + /* Keep the endpoint context up-to-date while issuing the command. */ |
| 84 | + xhci_endpoint_copy(xhci, vdev->in_ctx, |
| 85 | + vdev->out_ctx, ep_index); |
| 86 | + ep_ctx_in->ep_info = cpu_to_le32(ep_info_tmp); |
| 87 | + |
| 88 | + /* |
| 89 | + * We need to drop the lock, so take an explicit copy |
| 90 | + * of the ep context. |
| 91 | + */ |
| 92 | + xhci_endpoint_copy(xhci, command->in_ctx, vdev->in_ctx, ep_index); |
| 93 | + |
| 94 | + ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx); |
| 95 | + if (!ctrl_ctx) { |
| 96 | + xhci_warn(xhci, |
| 97 | + "%s: Could not get input context, bad type.\n", |
| 98 | + __func__); |
| 99 | + spin_unlock_irqrestore(&xhci->lock, flags); |
| 100 | + xhci_free_command(xhci, command); |
| 101 | + mutex_unlock(&xhci->mutex); |
| 102 | + return; |
| 103 | + } |
| 104 | + ctrl_ctx->add_flags = xhci_get_endpoint_flag_from_index(ep_index); |
| 105 | + ctrl_ctx->drop_flags = 0; |
| 106 | + |
| 107 | + spin_unlock_irqrestore(&xhci->lock, flags); |
| 108 | + |
| 109 | + ret = xhci_configure_endpoint(xhci, udev, command, |
| 110 | + false, false); |
| 111 | + if (ret) |
| 112 | + xhci_warn(xhci, "%s: Configure endpoint failed: %d\n", |
| 113 | + __func__, ret); |
| 114 | + xhci_free_command(xhci, command); |
| 115 | + mutex_unlock(&xhci->mutex); |
| 116 | +} |
| 117 | + |
| 118 | +/* |
| 119 | * non-error returns are a promise to giveback() the urb later |
| 120 | * we drop ownership so next owner (or urb unlink) can get it |
| 121 | */ |
| 122 | @@ -5402,6 +5499,7 @@ static const struct hc_driver xhci_hc_dr |
| 123 | .endpoint_reset = xhci_endpoint_reset, |
| 124 | .check_bandwidth = xhci_check_bandwidth, |
| 125 | .reset_bandwidth = xhci_reset_bandwidth, |
| 126 | + .fixup_endpoint = xhci_fixup_endpoint, |
| 127 | .address_device = xhci_address_device, |
| 128 | .enable_device = xhci_enable_device, |
| 129 | .update_hub_device = xhci_update_hub_device, |