1de66754eSMathias Nyman // SPDX-License-Identifier: GPL-2.0
2de66754eSMathias Nyman
3de66754eSMathias Nyman /*
4de66754eSMathias Nyman * xHCI host controller sideband support
5de66754eSMathias Nyman *
6de66754eSMathias Nyman * Copyright (c) 2023-2025, Intel Corporation.
7de66754eSMathias Nyman *
8de66754eSMathias Nyman * Author: Mathias Nyman
9de66754eSMathias Nyman */
10de66754eSMathias Nyman
11de66754eSMathias Nyman #include <linux/usb/xhci-sideband.h>
12de66754eSMathias Nyman #include <linux/dma-direct.h>
13de66754eSMathias Nyman
14de66754eSMathias Nyman #include "xhci.h"
15de66754eSMathias Nyman
16de66754eSMathias Nyman /* sideband internal helpers */
17de66754eSMathias Nyman static struct sg_table *
xhci_ring_to_sgtable(struct xhci_sideband * sb,struct xhci_ring * ring)18de66754eSMathias Nyman xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
19de66754eSMathias Nyman {
20de66754eSMathias Nyman struct xhci_segment *seg;
21de66754eSMathias Nyman struct sg_table *sgt;
22de66754eSMathias Nyman unsigned int n_pages;
23de66754eSMathias Nyman struct page **pages;
24de66754eSMathias Nyman struct device *dev;
25de66754eSMathias Nyman size_t sz;
26de66754eSMathias Nyman int i;
27de66754eSMathias Nyman
28de66754eSMathias Nyman dev = xhci_to_hcd(sb->xhci)->self.sysdev;
29de66754eSMathias Nyman sz = ring->num_segs * TRB_SEGMENT_SIZE;
30de66754eSMathias Nyman n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
31de66754eSMathias Nyman pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
32de66754eSMathias Nyman if (!pages)
33de66754eSMathias Nyman return NULL;
34de66754eSMathias Nyman
35de66754eSMathias Nyman sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
36de66754eSMathias Nyman if (!sgt) {
37de66754eSMathias Nyman kvfree(pages);
38de66754eSMathias Nyman return NULL;
39de66754eSMathias Nyman }
40de66754eSMathias Nyman
41de66754eSMathias Nyman seg = ring->first_seg;
42de66754eSMathias Nyman if (!seg)
43de66754eSMathias Nyman goto err;
44de66754eSMathias Nyman /*
45de66754eSMathias Nyman * Rings can potentially have multiple segments, create an array that
46de66754eSMathias Nyman * carries page references to allocated segments. Utilize the
47de66754eSMathias Nyman * sg_alloc_table_from_pages() to create the sg table, and to ensure
48de66754eSMathias Nyman * that page links are created.
49de66754eSMathias Nyman */
50de66754eSMathias Nyman for (i = 0; i < ring->num_segs; i++) {
51de66754eSMathias Nyman dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
52de66754eSMathias Nyman TRB_SEGMENT_SIZE);
53de66754eSMathias Nyman pages[i] = sg_page(sgt->sgl);
54de66754eSMathias Nyman sg_free_table(sgt);
55de66754eSMathias Nyman seg = seg->next;
56de66754eSMathias Nyman }
57de66754eSMathias Nyman
58de66754eSMathias Nyman if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
59de66754eSMathias Nyman goto err;
60de66754eSMathias Nyman
61de66754eSMathias Nyman /*
62de66754eSMathias Nyman * Save first segment dma address to sg dma_address field for the sideband
63de66754eSMathias Nyman * client to have access to the IOVA of the ring.
64de66754eSMathias Nyman */
65de66754eSMathias Nyman sg_dma_address(sgt->sgl) = ring->first_seg->dma;
66de66754eSMathias Nyman
67de66754eSMathias Nyman return sgt;
68de66754eSMathias Nyman
69de66754eSMathias Nyman err:
70de66754eSMathias Nyman kvfree(pages);
71de66754eSMathias Nyman kfree(sgt);
72de66754eSMathias Nyman
73de66754eSMathias Nyman return NULL;
74de66754eSMathias Nyman }
75de66754eSMathias Nyman
76de66754eSMathias Nyman static void
__xhci_sideband_remove_endpoint(struct xhci_sideband * sb,struct xhci_virt_ep * ep)77de66754eSMathias Nyman __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
78de66754eSMathias Nyman {
79de66754eSMathias Nyman /*
80de66754eSMathias Nyman * Issue a stop endpoint command when an endpoint is removed.
81de66754eSMathias Nyman * The stop ep cmd handler will handle the ring cleanup.
82de66754eSMathias Nyman */
83de66754eSMathias Nyman xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
84de66754eSMathias Nyman
85de66754eSMathias Nyman ep->sideband = NULL;
86de66754eSMathias Nyman sb->eps[ep->ep_index] = NULL;
87de66754eSMathias Nyman }
88de66754eSMathias Nyman
89de66754eSMathias Nyman /* sideband api functions */
90de66754eSMathias Nyman
91de66754eSMathias Nyman /**
92*b85a2ebdSWesley Cheng * xhci_sideband_notify_ep_ring_free - notify client of xfer ring free
93*b85a2ebdSWesley Cheng * @sb: sideband instance for this usb device
94*b85a2ebdSWesley Cheng * @ep_index: usb endpoint index
95*b85a2ebdSWesley Cheng *
96*b85a2ebdSWesley Cheng * Notifies the xHCI sideband client driver of a xHCI transfer ring free
97*b85a2ebdSWesley Cheng * routine. This will allow for the client to ensure that all transfers
98*b85a2ebdSWesley Cheng * are completed.
99*b85a2ebdSWesley Cheng *
100*b85a2ebdSWesley Cheng * The callback should be synchronous, as the ring free happens after.
101*b85a2ebdSWesley Cheng */
xhci_sideband_notify_ep_ring_free(struct xhci_sideband * sb,unsigned int ep_index)102*b85a2ebdSWesley Cheng void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
103*b85a2ebdSWesley Cheng unsigned int ep_index)
104*b85a2ebdSWesley Cheng {
105*b85a2ebdSWesley Cheng struct xhci_sideband_event evt;
106*b85a2ebdSWesley Cheng
107*b85a2ebdSWesley Cheng evt.type = XHCI_SIDEBAND_XFER_RING_FREE;
108*b85a2ebdSWesley Cheng evt.evt_data = &ep_index;
109*b85a2ebdSWesley Cheng
110*b85a2ebdSWesley Cheng if (sb->notify_client)
111*b85a2ebdSWesley Cheng sb->notify_client(sb->intf, &evt);
112*b85a2ebdSWesley Cheng }
113*b85a2ebdSWesley Cheng EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
114*b85a2ebdSWesley Cheng
115*b85a2ebdSWesley Cheng /**
116de66754eSMathias Nyman * xhci_sideband_add_endpoint - add endpoint to sideband access list
117de66754eSMathias Nyman * @sb: sideband instance for this usb device
118de66754eSMathias Nyman * @host_ep: usb host endpoint
119de66754eSMathias Nyman *
120de66754eSMathias Nyman * Adds an endpoint to the list of sideband accessed endpoints for this usb
121de66754eSMathias Nyman * device.
122de66754eSMathias Nyman * After an endpoint is added the sideband client can get the endpoint transfer
123de66754eSMathias Nyman * ring buffer by calling xhci_sideband_endpoint_buffer()
124de66754eSMathias Nyman *
125de66754eSMathias Nyman * Return: 0 on success, negative error otherwise.
126de66754eSMathias Nyman */
127de66754eSMathias Nyman int
xhci_sideband_add_endpoint(struct xhci_sideband * sb,struct usb_host_endpoint * host_ep)128de66754eSMathias Nyman xhci_sideband_add_endpoint(struct xhci_sideband *sb,
129de66754eSMathias Nyman struct usb_host_endpoint *host_ep)
130de66754eSMathias Nyman {
131de66754eSMathias Nyman struct xhci_virt_ep *ep;
132de66754eSMathias Nyman unsigned int ep_index;
133de66754eSMathias Nyman
134de66754eSMathias Nyman mutex_lock(&sb->mutex);
135de66754eSMathias Nyman ep_index = xhci_get_endpoint_index(&host_ep->desc);
136de66754eSMathias Nyman ep = &sb->vdev->eps[ep_index];
137de66754eSMathias Nyman
138de66754eSMathias Nyman if (ep->ep_state & EP_HAS_STREAMS) {
139de66754eSMathias Nyman mutex_unlock(&sb->mutex);
140de66754eSMathias Nyman return -EINVAL;
141de66754eSMathias Nyman }
142de66754eSMathias Nyman
143de66754eSMathias Nyman /*
144de66754eSMathias Nyman * Note, we don't know the DMA mask of the audio DSP device, if its
145de66754eSMathias Nyman * smaller than for xhci it won't be able to access the endpoint ring
146de66754eSMathias Nyman * buffer. This could be solved by not allowing the audio class driver
147de66754eSMathias Nyman * to add the endpoint the normal way, but instead offload it immediately,
148de66754eSMathias Nyman * and let this function add the endpoint and allocate the ring buffer
149de66754eSMathias Nyman * with the smallest common DMA mask
150de66754eSMathias Nyman */
151de66754eSMathias Nyman if (sb->eps[ep_index] || ep->sideband) {
152de66754eSMathias Nyman mutex_unlock(&sb->mutex);
153de66754eSMathias Nyman return -EBUSY;
154de66754eSMathias Nyman }
155de66754eSMathias Nyman
156de66754eSMathias Nyman ep->sideband = sb;
157de66754eSMathias Nyman sb->eps[ep_index] = ep;
158de66754eSMathias Nyman mutex_unlock(&sb->mutex);
159de66754eSMathias Nyman
160de66754eSMathias Nyman return 0;
161de66754eSMathias Nyman }
162de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
163de66754eSMathias Nyman
164de66754eSMathias Nyman /**
165de66754eSMathias Nyman * xhci_sideband_remove_endpoint - remove endpoint from sideband access list
166de66754eSMathias Nyman * @sb: sideband instance for this usb device
167de66754eSMathias Nyman * @host_ep: usb host endpoint
168de66754eSMathias Nyman *
169de66754eSMathias Nyman * Removes an endpoint from the list of sideband accessed endpoints for this usb
170de66754eSMathias Nyman * device.
171de66754eSMathias Nyman * sideband client should no longer touch the endpoint transfer buffer after
172de66754eSMathias Nyman * calling this.
173de66754eSMathias Nyman *
174de66754eSMathias Nyman * Return: 0 on success, negative error otherwise.
175de66754eSMathias Nyman */
176de66754eSMathias Nyman int
xhci_sideband_remove_endpoint(struct xhci_sideband * sb,struct usb_host_endpoint * host_ep)177de66754eSMathias Nyman xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
178de66754eSMathias Nyman struct usb_host_endpoint *host_ep)
179de66754eSMathias Nyman {
180de66754eSMathias Nyman struct xhci_virt_ep *ep;
181de66754eSMathias Nyman unsigned int ep_index;
182de66754eSMathias Nyman
183de66754eSMathias Nyman mutex_lock(&sb->mutex);
184de66754eSMathias Nyman ep_index = xhci_get_endpoint_index(&host_ep->desc);
185de66754eSMathias Nyman ep = sb->eps[ep_index];
186de66754eSMathias Nyman
187de66754eSMathias Nyman if (!ep || !ep->sideband || ep->sideband != sb) {
188de66754eSMathias Nyman mutex_unlock(&sb->mutex);
189de66754eSMathias Nyman return -ENODEV;
190de66754eSMathias Nyman }
191de66754eSMathias Nyman
192de66754eSMathias Nyman __xhci_sideband_remove_endpoint(sb, ep);
193de66754eSMathias Nyman xhci_initialize_ring_info(ep->ring);
194de66754eSMathias Nyman mutex_unlock(&sb->mutex);
195de66754eSMathias Nyman
196de66754eSMathias Nyman return 0;
197de66754eSMathias Nyman }
198de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
199de66754eSMathias Nyman
200de66754eSMathias Nyman int
xhci_sideband_stop_endpoint(struct xhci_sideband * sb,struct usb_host_endpoint * host_ep)201de66754eSMathias Nyman xhci_sideband_stop_endpoint(struct xhci_sideband *sb,
202de66754eSMathias Nyman struct usb_host_endpoint *host_ep)
203de66754eSMathias Nyman {
204de66754eSMathias Nyman struct xhci_virt_ep *ep;
205de66754eSMathias Nyman unsigned int ep_index;
206de66754eSMathias Nyman
207de66754eSMathias Nyman ep_index = xhci_get_endpoint_index(&host_ep->desc);
208de66754eSMathias Nyman ep = sb->eps[ep_index];
209de66754eSMathias Nyman
210de66754eSMathias Nyman if (!ep || !ep->sideband || ep->sideband != sb)
211de66754eSMathias Nyman return -EINVAL;
212de66754eSMathias Nyman
213de66754eSMathias Nyman return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
214de66754eSMathias Nyman }
215de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
216de66754eSMathias Nyman
217de66754eSMathias Nyman /**
218de66754eSMathias Nyman * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
219de66754eSMathias Nyman * @sb: sideband instance for this usb device
220de66754eSMathias Nyman * @host_ep: usb host endpoint
221de66754eSMathias Nyman *
222de66754eSMathias Nyman * Returns the address of the endpoint buffer where xHC controller reads queued
223de66754eSMathias Nyman * transfer TRBs from. This is the starting address of the ringbuffer where the
224de66754eSMathias Nyman * sideband client should write TRBs to.
225de66754eSMathias Nyman *
226de66754eSMathias Nyman * Caller needs to free the returned sg_table
227de66754eSMathias Nyman *
228de66754eSMathias Nyman * Return: struct sg_table * if successful. NULL otherwise.
229de66754eSMathias Nyman */
230de66754eSMathias Nyman struct sg_table *
xhci_sideband_get_endpoint_buffer(struct xhci_sideband * sb,struct usb_host_endpoint * host_ep)231de66754eSMathias Nyman xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
232de66754eSMathias Nyman struct usb_host_endpoint *host_ep)
233de66754eSMathias Nyman {
234de66754eSMathias Nyman struct xhci_virt_ep *ep;
235de66754eSMathias Nyman unsigned int ep_index;
236de66754eSMathias Nyman
237de66754eSMathias Nyman ep_index = xhci_get_endpoint_index(&host_ep->desc);
238de66754eSMathias Nyman ep = sb->eps[ep_index];
239de66754eSMathias Nyman
240de66754eSMathias Nyman if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb)
241de66754eSMathias Nyman return NULL;
242de66754eSMathias Nyman
243de66754eSMathias Nyman return xhci_ring_to_sgtable(sb, ep->ring);
244de66754eSMathias Nyman }
245de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
246de66754eSMathias Nyman
247de66754eSMathias Nyman /**
248de66754eSMathias Nyman * xhci_sideband_get_event_buffer - return the event buffer for this device
249de66754eSMathias Nyman * @sb: sideband instance for this usb device
250de66754eSMathias Nyman *
251de66754eSMathias Nyman * If a secondary xhci interupter is set up for this usb device then this
252de66754eSMathias Nyman * function returns the address of the event buffer where xHC writes
253de66754eSMathias Nyman * the transfer completion events.
254de66754eSMathias Nyman *
255de66754eSMathias Nyman * Caller needs to free the returned sg_table
256de66754eSMathias Nyman *
257de66754eSMathias Nyman * Return: struct sg_table * if successful. NULL otherwise.
258de66754eSMathias Nyman */
259de66754eSMathias Nyman struct sg_table *
xhci_sideband_get_event_buffer(struct xhci_sideband * sb)260de66754eSMathias Nyman xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
261de66754eSMathias Nyman {
262de66754eSMathias Nyman if (!sb || !sb->ir)
263de66754eSMathias Nyman return NULL;
264de66754eSMathias Nyman
265de66754eSMathias Nyman return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
266de66754eSMathias Nyman }
267de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
268de66754eSMathias Nyman
269de66754eSMathias Nyman /**
270de66754eSMathias Nyman * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
271de66754eSMathias Nyman * @sb: sideband instance for this usb device
272de66754eSMathias Nyman * @num_seg: number of event ring segments to allocate
273de66754eSMathias Nyman * @ip_autoclear: IP autoclearing support such as MSI implemented
274de66754eSMathias Nyman *
275de66754eSMathias Nyman * Sets up a xhci interrupter that can be used for this sideband accessed usb
276de66754eSMathias Nyman * device. Transfer events for this device can be routed to this interrupters
277de66754eSMathias Nyman * event ring by setting the 'Interrupter Target' field correctly when queueing
278de66754eSMathias Nyman * the transfer TRBs.
279de66754eSMathias Nyman * Once this interrupter is created the interrupter target ID can be obtained
280de66754eSMathias Nyman * by calling xhci_sideband_interrupter_id()
281de66754eSMathias Nyman *
282de66754eSMathias Nyman * Returns 0 on success, negative error otherwise
283de66754eSMathias Nyman */
284de66754eSMathias Nyman int
xhci_sideband_create_interrupter(struct xhci_sideband * sb,int num_seg,bool ip_autoclear,u32 imod_interval,int intr_num)285de66754eSMathias Nyman xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
286fce57295SWesley Cheng bool ip_autoclear, u32 imod_interval, int intr_num)
287de66754eSMathias Nyman {
288de66754eSMathias Nyman int ret = 0;
289de66754eSMathias Nyman
290de66754eSMathias Nyman if (!sb || !sb->xhci)
291de66754eSMathias Nyman return -ENODEV;
292de66754eSMathias Nyman
293de66754eSMathias Nyman mutex_lock(&sb->mutex);
294de66754eSMathias Nyman if (sb->ir) {
295de66754eSMathias Nyman ret = -EBUSY;
296de66754eSMathias Nyman goto out;
297de66754eSMathias Nyman }
298de66754eSMathias Nyman
299de66754eSMathias Nyman sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
300fce57295SWesley Cheng num_seg, imod_interval,
301fce57295SWesley Cheng intr_num);
302de66754eSMathias Nyman if (!sb->ir) {
303de66754eSMathias Nyman ret = -ENOMEM;
304de66754eSMathias Nyman goto out;
305de66754eSMathias Nyman }
306de66754eSMathias Nyman
307de66754eSMathias Nyman sb->ir->ip_autoclear = ip_autoclear;
308de66754eSMathias Nyman
309de66754eSMathias Nyman out:
310de66754eSMathias Nyman mutex_unlock(&sb->mutex);
311de66754eSMathias Nyman
312de66754eSMathias Nyman return ret;
313de66754eSMathias Nyman }
314de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
315de66754eSMathias Nyman
316de66754eSMathias Nyman /**
317de66754eSMathias Nyman * xhci_sideband_remove_interrupter - remove the interrupter from a sideband
318de66754eSMathias Nyman * @sb: sideband instance for this usb device
319de66754eSMathias Nyman *
320de66754eSMathias Nyman * Removes a registered interrupt for a sideband. This would allow for other
321de66754eSMathias Nyman * sideband users to utilize this interrupter.
322de66754eSMathias Nyman */
323de66754eSMathias Nyman void
xhci_sideband_remove_interrupter(struct xhci_sideband * sb)324de66754eSMathias Nyman xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
325de66754eSMathias Nyman {
326de66754eSMathias Nyman if (!sb || !sb->ir)
327de66754eSMathias Nyman return;
328de66754eSMathias Nyman
329de66754eSMathias Nyman mutex_lock(&sb->mutex);
330de66754eSMathias Nyman xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
331de66754eSMathias Nyman
332de66754eSMathias Nyman sb->ir = NULL;
333de66754eSMathias Nyman mutex_unlock(&sb->mutex);
334de66754eSMathias Nyman }
335de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
336de66754eSMathias Nyman
337de66754eSMathias Nyman /**
338de66754eSMathias Nyman * xhci_sideband_interrupter_id - return the interrupter target id
339de66754eSMathias Nyman * @sb: sideband instance for this usb device
340de66754eSMathias Nyman *
341de66754eSMathias Nyman * If a secondary xhci interrupter is set up for this usb device then this
342de66754eSMathias Nyman * function returns the ID used by the interrupter. The sideband client
343de66754eSMathias Nyman * needs to write this ID to the 'Interrupter Target' field of the transfer TRBs
344de66754eSMathias Nyman * it queues on the endpoints transfer ring to ensure transfer completion event
345de66754eSMathias Nyman * are written by xHC to the correct interrupter event ring.
346de66754eSMathias Nyman *
347de66754eSMathias Nyman * Returns interrupter id on success, negative error othgerwise
348de66754eSMathias Nyman */
349de66754eSMathias Nyman int
xhci_sideband_interrupter_id(struct xhci_sideband * sb)350de66754eSMathias Nyman xhci_sideband_interrupter_id(struct xhci_sideband *sb)
351de66754eSMathias Nyman {
352de66754eSMathias Nyman if (!sb || !sb->ir)
353de66754eSMathias Nyman return -ENODEV;
354de66754eSMathias Nyman
355de66754eSMathias Nyman return sb->ir->intr_num;
356de66754eSMathias Nyman }
357de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
358de66754eSMathias Nyman
359de66754eSMathias Nyman /**
360de66754eSMathias Nyman * xhci_sideband_register - register a sideband for a usb device
361de66754eSMathias Nyman * @intf: usb interface associated with the sideband device
362de66754eSMathias Nyman *
363de66754eSMathias Nyman * Allows for clients to utilize XHCI interrupters and fetch transfer and event
364de66754eSMathias Nyman * ring parameters for executing data transfers.
365de66754eSMathias Nyman *
366de66754eSMathias Nyman * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
367de66754eSMathias Nyman */
368de66754eSMathias Nyman struct xhci_sideband *
xhci_sideband_register(struct usb_interface * intf,enum xhci_sideband_type type,int (* notify_client)(struct usb_interface * intf,struct xhci_sideband_event * evt))369*b85a2ebdSWesley Cheng xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
370*b85a2ebdSWesley Cheng int (*notify_client)(struct usb_interface *intf,
371*b85a2ebdSWesley Cheng struct xhci_sideband_event *evt))
372de66754eSMathias Nyman {
373de66754eSMathias Nyman struct usb_device *udev = interface_to_usbdev(intf);
374de66754eSMathias Nyman struct usb_hcd *hcd = bus_to_hcd(udev->bus);
375de66754eSMathias Nyman struct xhci_hcd *xhci = hcd_to_xhci(hcd);
376de66754eSMathias Nyman struct xhci_virt_device *vdev;
377de66754eSMathias Nyman struct xhci_sideband *sb;
378de66754eSMathias Nyman
379de66754eSMathias Nyman /*
380de66754eSMathias Nyman * Make sure the usb device is connected to a xhci controller. Fail
381de66754eSMathias Nyman * registration if the type is anything other than XHCI_SIDEBAND_VENDOR,
382de66754eSMathias Nyman * as this is the only type that is currently supported by xhci-sideband.
383de66754eSMathias Nyman */
384de66754eSMathias Nyman if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR)
385de66754eSMathias Nyman return NULL;
386de66754eSMathias Nyman
387de66754eSMathias Nyman sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
388de66754eSMathias Nyman if (!sb)
389de66754eSMathias Nyman return NULL;
390de66754eSMathias Nyman
391de66754eSMathias Nyman mutex_init(&sb->mutex);
392de66754eSMathias Nyman
393de66754eSMathias Nyman /* check this device isn't already controlled via sideband */
394de66754eSMathias Nyman spin_lock_irq(&xhci->lock);
395de66754eSMathias Nyman
396de66754eSMathias Nyman vdev = xhci->devs[udev->slot_id];
397de66754eSMathias Nyman
398de66754eSMathias Nyman if (!vdev || vdev->sideband) {
399de66754eSMathias Nyman xhci_warn(xhci, "XHCI sideband for slot %d already in use\n",
400de66754eSMathias Nyman udev->slot_id);
401de66754eSMathias Nyman spin_unlock_irq(&xhci->lock);
402de66754eSMathias Nyman kfree(sb);
403de66754eSMathias Nyman return NULL;
404de66754eSMathias Nyman }
405de66754eSMathias Nyman
406de66754eSMathias Nyman sb->xhci = xhci;
407de66754eSMathias Nyman sb->vdev = vdev;
408de66754eSMathias Nyman sb->intf = intf;
409de66754eSMathias Nyman sb->type = type;
410*b85a2ebdSWesley Cheng sb->notify_client = notify_client;
411de66754eSMathias Nyman vdev->sideband = sb;
412de66754eSMathias Nyman
413de66754eSMathias Nyman spin_unlock_irq(&xhci->lock);
414de66754eSMathias Nyman
415de66754eSMathias Nyman return sb;
416de66754eSMathias Nyman }
417de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_register);
418de66754eSMathias Nyman
419de66754eSMathias Nyman /**
420de66754eSMathias Nyman * xhci_sideband_unregister - unregister sideband access to a usb device
421de66754eSMathias Nyman * @sb: sideband instance to be unregistered
422de66754eSMathias Nyman *
423de66754eSMathias Nyman * Unregisters sideband access to a usb device and frees the sideband
424de66754eSMathias Nyman * instance.
425de66754eSMathias Nyman * After this the endpoint and interrupter event buffers should no longer
426de66754eSMathias Nyman * be accessed via sideband. The xhci driver can now take over handling
427de66754eSMathias Nyman * the buffers.
428de66754eSMathias Nyman */
429de66754eSMathias Nyman void
xhci_sideband_unregister(struct xhci_sideband * sb)430de66754eSMathias Nyman xhci_sideband_unregister(struct xhci_sideband *sb)
431de66754eSMathias Nyman {
432de66754eSMathias Nyman struct xhci_hcd *xhci;
433de66754eSMathias Nyman int i;
434de66754eSMathias Nyman
435de66754eSMathias Nyman if (!sb)
436de66754eSMathias Nyman return;
437de66754eSMathias Nyman
438de66754eSMathias Nyman xhci = sb->xhci;
439de66754eSMathias Nyman
440de66754eSMathias Nyman mutex_lock(&sb->mutex);
441de66754eSMathias Nyman for (i = 0; i < EP_CTX_PER_DEV; i++)
442de66754eSMathias Nyman if (sb->eps[i])
443de66754eSMathias Nyman __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
444de66754eSMathias Nyman mutex_unlock(&sb->mutex);
445de66754eSMathias Nyman
446de66754eSMathias Nyman xhci_sideband_remove_interrupter(sb);
447de66754eSMathias Nyman
448de66754eSMathias Nyman spin_lock_irq(&xhci->lock);
449de66754eSMathias Nyman sb->xhci = NULL;
450de66754eSMathias Nyman sb->vdev->sideband = NULL;
451de66754eSMathias Nyman spin_unlock_irq(&xhci->lock);
452de66754eSMathias Nyman
453de66754eSMathias Nyman kfree(sb);
454de66754eSMathias Nyman }
455de66754eSMathias Nyman EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
456de66754eSMathias Nyman MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management");
457de66754eSMathias Nyman MODULE_LICENSE("GPL");
458