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