xref: /openbmc/linux/drivers/usb/dwc2/hcd_queue.c (revision b58e6cee)
1197ba5f4SPaul Zimmerman /*
2197ba5f4SPaul Zimmerman  * hcd_queue.c - DesignWare HS OTG Controller host queuing routines
3197ba5f4SPaul Zimmerman  *
4197ba5f4SPaul Zimmerman  * Copyright (C) 2004-2013 Synopsys, Inc.
5197ba5f4SPaul Zimmerman  *
6197ba5f4SPaul Zimmerman  * Redistribution and use in source and binary forms, with or without
7197ba5f4SPaul Zimmerman  * modification, are permitted provided that the following conditions
8197ba5f4SPaul Zimmerman  * are met:
9197ba5f4SPaul Zimmerman  * 1. Redistributions of source code must retain the above copyright
10197ba5f4SPaul Zimmerman  *    notice, this list of conditions, and the following disclaimer,
11197ba5f4SPaul Zimmerman  *    without modification.
12197ba5f4SPaul Zimmerman  * 2. Redistributions in binary form must reproduce the above copyright
13197ba5f4SPaul Zimmerman  *    notice, this list of conditions and the following disclaimer in the
14197ba5f4SPaul Zimmerman  *    documentation and/or other materials provided with the distribution.
15197ba5f4SPaul Zimmerman  * 3. The names of the above-listed copyright holders may not be used
16197ba5f4SPaul Zimmerman  *    to endorse or promote products derived from this software without
17197ba5f4SPaul Zimmerman  *    specific prior written permission.
18197ba5f4SPaul Zimmerman  *
19197ba5f4SPaul Zimmerman  * ALTERNATIVELY, this software may be distributed under the terms of the
20197ba5f4SPaul Zimmerman  * GNU General Public License ("GPL") as published by the Free Software
21197ba5f4SPaul Zimmerman  * Foundation; either version 2 of the License, or (at your option) any
22197ba5f4SPaul Zimmerman  * later version.
23197ba5f4SPaul Zimmerman  *
24197ba5f4SPaul Zimmerman  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25197ba5f4SPaul Zimmerman  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26197ba5f4SPaul Zimmerman  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27197ba5f4SPaul Zimmerman  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28197ba5f4SPaul Zimmerman  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29197ba5f4SPaul Zimmerman  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30197ba5f4SPaul Zimmerman  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31197ba5f4SPaul Zimmerman  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32197ba5f4SPaul Zimmerman  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33197ba5f4SPaul Zimmerman  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34197ba5f4SPaul Zimmerman  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35197ba5f4SPaul Zimmerman  */
36197ba5f4SPaul Zimmerman 
37197ba5f4SPaul Zimmerman /*
38197ba5f4SPaul Zimmerman  * This file contains the functions to manage Queue Heads and Queue
39197ba5f4SPaul Zimmerman  * Transfer Descriptors for Host mode
40197ba5f4SPaul Zimmerman  */
41197ba5f4SPaul Zimmerman #include <linux/kernel.h>
42197ba5f4SPaul Zimmerman #include <linux/module.h>
43197ba5f4SPaul Zimmerman #include <linux/spinlock.h>
44197ba5f4SPaul Zimmerman #include <linux/interrupt.h>
45197ba5f4SPaul Zimmerman #include <linux/dma-mapping.h>
46197ba5f4SPaul Zimmerman #include <linux/io.h>
47197ba5f4SPaul Zimmerman #include <linux/slab.h>
48197ba5f4SPaul Zimmerman #include <linux/usb.h>
49197ba5f4SPaul Zimmerman 
50197ba5f4SPaul Zimmerman #include <linux/usb/hcd.h>
51197ba5f4SPaul Zimmerman #include <linux/usb/ch11.h>
52197ba5f4SPaul Zimmerman 
53197ba5f4SPaul Zimmerman #include "core.h"
54197ba5f4SPaul Zimmerman #include "hcd.h"
55197ba5f4SPaul Zimmerman 
56197ba5f4SPaul Zimmerman /**
57197ba5f4SPaul Zimmerman  * dwc2_qh_init() - Initializes a QH structure
58197ba5f4SPaul Zimmerman  *
59197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
60197ba5f4SPaul Zimmerman  * @qh:    The QH to init
61197ba5f4SPaul Zimmerman  * @urb:   Holds the information about the device/endpoint needed to initialize
62197ba5f4SPaul Zimmerman  *         the QH
63197ba5f4SPaul Zimmerman  */
64197ba5f4SPaul Zimmerman #define SCHEDULE_SLOP 10
65197ba5f4SPaul Zimmerman static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
66197ba5f4SPaul Zimmerman 			 struct dwc2_hcd_urb *urb)
67197ba5f4SPaul Zimmerman {
68197ba5f4SPaul Zimmerman 	int dev_speed, hub_addr, hub_port;
69197ba5f4SPaul Zimmerman 	char *speed, *type;
70197ba5f4SPaul Zimmerman 
71197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "%s()\n", __func__);
72197ba5f4SPaul Zimmerman 
73197ba5f4SPaul Zimmerman 	/* Initialize QH */
74197ba5f4SPaul Zimmerman 	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
75197ba5f4SPaul Zimmerman 	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
76197ba5f4SPaul Zimmerman 
77197ba5f4SPaul Zimmerman 	qh->data_toggle = DWC2_HC_PID_DATA0;
78197ba5f4SPaul Zimmerman 	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
79197ba5f4SPaul Zimmerman 	INIT_LIST_HEAD(&qh->qtd_list);
80197ba5f4SPaul Zimmerman 	INIT_LIST_HEAD(&qh->qh_list_entry);
81197ba5f4SPaul Zimmerman 
82197ba5f4SPaul Zimmerman 	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
83197ba5f4SPaul Zimmerman 	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
84197ba5f4SPaul Zimmerman 
85197ba5f4SPaul Zimmerman 	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
86197ba5f4SPaul Zimmerman 
87197ba5f4SPaul Zimmerman 	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
88197ba5f4SPaul Zimmerman 	    hub_addr != 0 && hub_addr != 1) {
89197ba5f4SPaul Zimmerman 		dev_vdbg(hsotg->dev,
90197ba5f4SPaul Zimmerman 			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
91197ba5f4SPaul Zimmerman 			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
92197ba5f4SPaul Zimmerman 			 hub_port);
93197ba5f4SPaul Zimmerman 		qh->do_split = 1;
94197ba5f4SPaul Zimmerman 	}
95197ba5f4SPaul Zimmerman 
96197ba5f4SPaul Zimmerman 	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
97197ba5f4SPaul Zimmerman 	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
98197ba5f4SPaul Zimmerman 		/* Compute scheduling parameters once and save them */
99197ba5f4SPaul Zimmerman 		u32 hprt, prtspd;
100197ba5f4SPaul Zimmerman 
101197ba5f4SPaul Zimmerman 		/* Todo: Account for split transfers in the bus time */
102197ba5f4SPaul Zimmerman 		int bytecount =
103197ba5f4SPaul Zimmerman 			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
104197ba5f4SPaul Zimmerman 
105197ba5f4SPaul Zimmerman 		qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ?
106197ba5f4SPaul Zimmerman 				USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
107197ba5f4SPaul Zimmerman 				qh->ep_type == USB_ENDPOINT_XFER_ISOC,
108197ba5f4SPaul Zimmerman 				bytecount));
109197ba5f4SPaul Zimmerman 		/* Start in a slightly future (micro)frame */
110197ba5f4SPaul Zimmerman 		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
111197ba5f4SPaul Zimmerman 						     SCHEDULE_SLOP);
112197ba5f4SPaul Zimmerman 		qh->interval = urb->interval;
113197ba5f4SPaul Zimmerman #if 0
114197ba5f4SPaul Zimmerman 		/* Increase interrupt polling rate for debugging */
115197ba5f4SPaul Zimmerman 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
116197ba5f4SPaul Zimmerman 			qh->interval = 8;
117197ba5f4SPaul Zimmerman #endif
118197ba5f4SPaul Zimmerman 		hprt = readl(hsotg->regs + HPRT0);
119197ba5f4SPaul Zimmerman 		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
120197ba5f4SPaul Zimmerman 		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
121197ba5f4SPaul Zimmerman 		    (dev_speed == USB_SPEED_LOW ||
122197ba5f4SPaul Zimmerman 		     dev_speed == USB_SPEED_FULL)) {
123197ba5f4SPaul Zimmerman 			qh->interval *= 8;
124197ba5f4SPaul Zimmerman 			qh->sched_frame |= 0x7;
125197ba5f4SPaul Zimmerman 			qh->start_split_frame = qh->sched_frame;
126197ba5f4SPaul Zimmerman 		}
127197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
128197ba5f4SPaul Zimmerman 	}
129197ba5f4SPaul Zimmerman 
130197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
131197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
132197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
133197ba5f4SPaul Zimmerman 		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
134197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
135197ba5f4SPaul Zimmerman 		 dwc2_hcd_get_ep_num(&urb->pipe_info),
136197ba5f4SPaul Zimmerman 		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
137197ba5f4SPaul Zimmerman 
138197ba5f4SPaul Zimmerman 	qh->dev_speed = dev_speed;
139197ba5f4SPaul Zimmerman 
140197ba5f4SPaul Zimmerman 	switch (dev_speed) {
141197ba5f4SPaul Zimmerman 	case USB_SPEED_LOW:
142197ba5f4SPaul Zimmerman 		speed = "low";
143197ba5f4SPaul Zimmerman 		break;
144197ba5f4SPaul Zimmerman 	case USB_SPEED_FULL:
145197ba5f4SPaul Zimmerman 		speed = "full";
146197ba5f4SPaul Zimmerman 		break;
147197ba5f4SPaul Zimmerman 	case USB_SPEED_HIGH:
148197ba5f4SPaul Zimmerman 		speed = "high";
149197ba5f4SPaul Zimmerman 		break;
150197ba5f4SPaul Zimmerman 	default:
151197ba5f4SPaul Zimmerman 		speed = "?";
152197ba5f4SPaul Zimmerman 		break;
153197ba5f4SPaul Zimmerman 	}
154197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
155197ba5f4SPaul Zimmerman 
156197ba5f4SPaul Zimmerman 	switch (qh->ep_type) {
157197ba5f4SPaul Zimmerman 	case USB_ENDPOINT_XFER_ISOC:
158197ba5f4SPaul Zimmerman 		type = "isochronous";
159197ba5f4SPaul Zimmerman 		break;
160197ba5f4SPaul Zimmerman 	case USB_ENDPOINT_XFER_INT:
161197ba5f4SPaul Zimmerman 		type = "interrupt";
162197ba5f4SPaul Zimmerman 		break;
163197ba5f4SPaul Zimmerman 	case USB_ENDPOINT_XFER_CONTROL:
164197ba5f4SPaul Zimmerman 		type = "control";
165197ba5f4SPaul Zimmerman 		break;
166197ba5f4SPaul Zimmerman 	case USB_ENDPOINT_XFER_BULK:
167197ba5f4SPaul Zimmerman 		type = "bulk";
168197ba5f4SPaul Zimmerman 		break;
169197ba5f4SPaul Zimmerman 	default:
170197ba5f4SPaul Zimmerman 		type = "?";
171197ba5f4SPaul Zimmerman 		break;
172197ba5f4SPaul Zimmerman 	}
173197ba5f4SPaul Zimmerman 
174197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
175197ba5f4SPaul Zimmerman 
176197ba5f4SPaul Zimmerman 	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
177197ba5f4SPaul Zimmerman 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
178197ba5f4SPaul Zimmerman 			 qh->usecs);
179197ba5f4SPaul Zimmerman 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
180197ba5f4SPaul Zimmerman 			 qh->interval);
181197ba5f4SPaul Zimmerman 	}
182197ba5f4SPaul Zimmerman }
183197ba5f4SPaul Zimmerman 
184197ba5f4SPaul Zimmerman /**
185197ba5f4SPaul Zimmerman  * dwc2_hcd_qh_create() - Allocates and initializes a QH
186197ba5f4SPaul Zimmerman  *
187197ba5f4SPaul Zimmerman  * @hsotg:        The HCD state structure for the DWC OTG controller
188197ba5f4SPaul Zimmerman  * @urb:          Holds the information about the device/endpoint needed
189197ba5f4SPaul Zimmerman  *                to initialize the QH
190197ba5f4SPaul Zimmerman  * @atomic_alloc: Flag to do atomic allocation if needed
191197ba5f4SPaul Zimmerman  *
192197ba5f4SPaul Zimmerman  * Return: Pointer to the newly allocated QH, or NULL on error
193197ba5f4SPaul Zimmerman  */
194b58e6ceeSMian Yousaf Kaukab struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
195197ba5f4SPaul Zimmerman 					  struct dwc2_hcd_urb *urb,
196197ba5f4SPaul Zimmerman 					  gfp_t mem_flags)
197197ba5f4SPaul Zimmerman {
198197ba5f4SPaul Zimmerman 	struct dwc2_qh *qh;
199197ba5f4SPaul Zimmerman 
200197ba5f4SPaul Zimmerman 	if (!urb->priv)
201197ba5f4SPaul Zimmerman 		return NULL;
202197ba5f4SPaul Zimmerman 
203197ba5f4SPaul Zimmerman 	/* Allocate memory */
204197ba5f4SPaul Zimmerman 	qh = kzalloc(sizeof(*qh), mem_flags);
205197ba5f4SPaul Zimmerman 	if (!qh)
206197ba5f4SPaul Zimmerman 		return NULL;
207197ba5f4SPaul Zimmerman 
208197ba5f4SPaul Zimmerman 	dwc2_qh_init(hsotg, qh, urb);
209197ba5f4SPaul Zimmerman 
210197ba5f4SPaul Zimmerman 	if (hsotg->core_params->dma_desc_enable > 0 &&
211197ba5f4SPaul Zimmerman 	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
212197ba5f4SPaul Zimmerman 		dwc2_hcd_qh_free(hsotg, qh);
213197ba5f4SPaul Zimmerman 		return NULL;
214197ba5f4SPaul Zimmerman 	}
215197ba5f4SPaul Zimmerman 
216197ba5f4SPaul Zimmerman 	return qh;
217197ba5f4SPaul Zimmerman }
218197ba5f4SPaul Zimmerman 
219197ba5f4SPaul Zimmerman /**
220197ba5f4SPaul Zimmerman  * dwc2_hcd_qh_free() - Frees the QH
221197ba5f4SPaul Zimmerman  *
222197ba5f4SPaul Zimmerman  * @hsotg: HCD instance
223197ba5f4SPaul Zimmerman  * @qh:    The QH to free
224197ba5f4SPaul Zimmerman  *
225197ba5f4SPaul Zimmerman  * QH should already be removed from the list. QTD list should already be empty
226197ba5f4SPaul Zimmerman  * if called from URB Dequeue.
227197ba5f4SPaul Zimmerman  *
228197ba5f4SPaul Zimmerman  * Must NOT be called with interrupt disabled or spinlock held
229197ba5f4SPaul Zimmerman  */
230197ba5f4SPaul Zimmerman void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
231197ba5f4SPaul Zimmerman {
232db62b9a8SGregory Herrero 	if (hsotg->core_params->dma_desc_enable > 0) {
233197ba5f4SPaul Zimmerman 		dwc2_hcd_qh_free_ddma(hsotg, qh);
234db62b9a8SGregory Herrero 	} else {
235db62b9a8SGregory Herrero 		/* kfree(NULL) is safe */
236db62b9a8SGregory Herrero 		kfree(qh->dw_align_buf);
237db62b9a8SGregory Herrero 		qh->dw_align_buf_dma = (dma_addr_t)0;
238db62b9a8SGregory Herrero 	}
239197ba5f4SPaul Zimmerman 	kfree(qh);
240197ba5f4SPaul Zimmerman }
241197ba5f4SPaul Zimmerman 
242197ba5f4SPaul Zimmerman /**
243197ba5f4SPaul Zimmerman  * dwc2_periodic_channel_available() - Checks that a channel is available for a
244197ba5f4SPaul Zimmerman  * periodic transfer
245197ba5f4SPaul Zimmerman  *
246197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
247197ba5f4SPaul Zimmerman  *
248197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
249197ba5f4SPaul Zimmerman  */
250197ba5f4SPaul Zimmerman static int dwc2_periodic_channel_available(struct dwc2_hsotg *hsotg)
251197ba5f4SPaul Zimmerman {
252197ba5f4SPaul Zimmerman 	/*
253197ba5f4SPaul Zimmerman 	 * Currently assuming that there is a dedicated host channel for
254197ba5f4SPaul Zimmerman 	 * each periodic transaction plus at least one host channel for
255197ba5f4SPaul Zimmerman 	 * non-periodic transactions
256197ba5f4SPaul Zimmerman 	 */
257197ba5f4SPaul Zimmerman 	int status;
258197ba5f4SPaul Zimmerman 	int num_channels;
259197ba5f4SPaul Zimmerman 
260197ba5f4SPaul Zimmerman 	num_channels = hsotg->core_params->host_channels;
261197ba5f4SPaul Zimmerman 	if (hsotg->periodic_channels + hsotg->non_periodic_channels <
262197ba5f4SPaul Zimmerman 								num_channels
263197ba5f4SPaul Zimmerman 	    && hsotg->periodic_channels < num_channels - 1) {
264197ba5f4SPaul Zimmerman 		status = 0;
265197ba5f4SPaul Zimmerman 	} else {
266197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
267197ba5f4SPaul Zimmerman 			"%s: Total channels: %d, Periodic: %d, "
268197ba5f4SPaul Zimmerman 			"Non-periodic: %d\n", __func__, num_channels,
269197ba5f4SPaul Zimmerman 			hsotg->periodic_channels, hsotg->non_periodic_channels);
270197ba5f4SPaul Zimmerman 		status = -ENOSPC;
271197ba5f4SPaul Zimmerman 	}
272197ba5f4SPaul Zimmerman 
273197ba5f4SPaul Zimmerman 	return status;
274197ba5f4SPaul Zimmerman }
275197ba5f4SPaul Zimmerman 
276197ba5f4SPaul Zimmerman /**
277197ba5f4SPaul Zimmerman  * dwc2_check_periodic_bandwidth() - Checks that there is sufficient bandwidth
278197ba5f4SPaul Zimmerman  * for the specified QH in the periodic schedule
279197ba5f4SPaul Zimmerman  *
280197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
281197ba5f4SPaul Zimmerman  * @qh:    QH containing periodic bandwidth required
282197ba5f4SPaul Zimmerman  *
283197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
284197ba5f4SPaul Zimmerman  *
285197ba5f4SPaul Zimmerman  * For simplicity, this calculation assumes that all the transfers in the
286197ba5f4SPaul Zimmerman  * periodic schedule may occur in the same (micro)frame
287197ba5f4SPaul Zimmerman  */
288197ba5f4SPaul Zimmerman static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
289197ba5f4SPaul Zimmerman 					 struct dwc2_qh *qh)
290197ba5f4SPaul Zimmerman {
291197ba5f4SPaul Zimmerman 	int status;
292197ba5f4SPaul Zimmerman 	s16 max_claimed_usecs;
293197ba5f4SPaul Zimmerman 
294197ba5f4SPaul Zimmerman 	status = 0;
295197ba5f4SPaul Zimmerman 
296197ba5f4SPaul Zimmerman 	if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) {
297197ba5f4SPaul Zimmerman 		/*
298197ba5f4SPaul Zimmerman 		 * High speed mode
299197ba5f4SPaul Zimmerman 		 * Max periodic usecs is 80% x 125 usec = 100 usec
300197ba5f4SPaul Zimmerman 		 */
301197ba5f4SPaul Zimmerman 		max_claimed_usecs = 100 - qh->usecs;
302197ba5f4SPaul Zimmerman 	} else {
303197ba5f4SPaul Zimmerman 		/*
304197ba5f4SPaul Zimmerman 		 * Full speed mode
305197ba5f4SPaul Zimmerman 		 * Max periodic usecs is 90% x 1000 usec = 900 usec
306197ba5f4SPaul Zimmerman 		 */
307197ba5f4SPaul Zimmerman 		max_claimed_usecs = 900 - qh->usecs;
308197ba5f4SPaul Zimmerman 	}
309197ba5f4SPaul Zimmerman 
310197ba5f4SPaul Zimmerman 	if (hsotg->periodic_usecs > max_claimed_usecs) {
311197ba5f4SPaul Zimmerman 		dev_err(hsotg->dev,
312197ba5f4SPaul Zimmerman 			"%s: already claimed usecs %d, required usecs %d\n",
313197ba5f4SPaul Zimmerman 			__func__, hsotg->periodic_usecs, qh->usecs);
314197ba5f4SPaul Zimmerman 		status = -ENOSPC;
315197ba5f4SPaul Zimmerman 	}
316197ba5f4SPaul Zimmerman 
317197ba5f4SPaul Zimmerman 	return status;
318197ba5f4SPaul Zimmerman }
319197ba5f4SPaul Zimmerman 
320197ba5f4SPaul Zimmerman /**
321197ba5f4SPaul Zimmerman  * Microframe scheduler
322197ba5f4SPaul Zimmerman  * track the total use in hsotg->frame_usecs
323197ba5f4SPaul Zimmerman  * keep each qh use in qh->frame_usecs
324197ba5f4SPaul Zimmerman  * when surrendering the qh then donate the time back
325197ba5f4SPaul Zimmerman  */
326197ba5f4SPaul Zimmerman static const unsigned short max_uframe_usecs[] = {
327197ba5f4SPaul Zimmerman 	100, 100, 100, 100, 100, 100, 30, 0
328197ba5f4SPaul Zimmerman };
329197ba5f4SPaul Zimmerman 
330197ba5f4SPaul Zimmerman void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
331197ba5f4SPaul Zimmerman {
332197ba5f4SPaul Zimmerman 	int i;
333197ba5f4SPaul Zimmerman 
334197ba5f4SPaul Zimmerman 	for (i = 0; i < 8; i++)
335197ba5f4SPaul Zimmerman 		hsotg->frame_usecs[i] = max_uframe_usecs[i];
336197ba5f4SPaul Zimmerman }
337197ba5f4SPaul Zimmerman 
338197ba5f4SPaul Zimmerman static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
339197ba5f4SPaul Zimmerman {
340197ba5f4SPaul Zimmerman 	unsigned short utime = qh->usecs;
341197ba5f4SPaul Zimmerman 	int i;
342197ba5f4SPaul Zimmerman 
343197ba5f4SPaul Zimmerman 	for (i = 0; i < 8; i++) {
344197ba5f4SPaul Zimmerman 		/* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
345197ba5f4SPaul Zimmerman 		if (utime <= hsotg->frame_usecs[i]) {
346197ba5f4SPaul Zimmerman 			hsotg->frame_usecs[i] -= utime;
347197ba5f4SPaul Zimmerman 			qh->frame_usecs[i] += utime;
348197ba5f4SPaul Zimmerman 			return i;
349197ba5f4SPaul Zimmerman 		}
350197ba5f4SPaul Zimmerman 	}
351197ba5f4SPaul Zimmerman 	return -ENOSPC;
352197ba5f4SPaul Zimmerman }
353197ba5f4SPaul Zimmerman 
354197ba5f4SPaul Zimmerman /*
355197ba5f4SPaul Zimmerman  * use this for FS apps that can span multiple uframes
356197ba5f4SPaul Zimmerman  */
357197ba5f4SPaul Zimmerman static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
358197ba5f4SPaul Zimmerman {
359197ba5f4SPaul Zimmerman 	unsigned short utime = qh->usecs;
360197ba5f4SPaul Zimmerman 	unsigned short xtime;
361197ba5f4SPaul Zimmerman 	int t_left;
362197ba5f4SPaul Zimmerman 	int i;
363197ba5f4SPaul Zimmerman 	int j;
364197ba5f4SPaul Zimmerman 	int k;
365197ba5f4SPaul Zimmerman 
366197ba5f4SPaul Zimmerman 	for (i = 0; i < 8; i++) {
367197ba5f4SPaul Zimmerman 		if (hsotg->frame_usecs[i] <= 0)
368197ba5f4SPaul Zimmerman 			continue;
369197ba5f4SPaul Zimmerman 
370197ba5f4SPaul Zimmerman 		/*
371197ba5f4SPaul Zimmerman 		 * we need n consecutive slots so use j as a start slot
372197ba5f4SPaul Zimmerman 		 * j plus j+1 must be enough time (for now)
373197ba5f4SPaul Zimmerman 		 */
374197ba5f4SPaul Zimmerman 		xtime = hsotg->frame_usecs[i];
375197ba5f4SPaul Zimmerman 		for (j = i + 1; j < 8; j++) {
376197ba5f4SPaul Zimmerman 			/*
377197ba5f4SPaul Zimmerman 			 * if we add this frame remaining time to xtime we may
378197ba5f4SPaul Zimmerman 			 * be OK, if not we need to test j for a complete frame
379197ba5f4SPaul Zimmerman 			 */
380197ba5f4SPaul Zimmerman 			if (xtime + hsotg->frame_usecs[j] < utime) {
381197ba5f4SPaul Zimmerman 				if (hsotg->frame_usecs[j] <
382197ba5f4SPaul Zimmerman 							max_uframe_usecs[j])
383197ba5f4SPaul Zimmerman 					continue;
384197ba5f4SPaul Zimmerman 			}
385197ba5f4SPaul Zimmerman 			if (xtime >= utime) {
386197ba5f4SPaul Zimmerman 				t_left = utime;
387197ba5f4SPaul Zimmerman 				for (k = i; k < 8; k++) {
388197ba5f4SPaul Zimmerman 					t_left -= hsotg->frame_usecs[k];
389197ba5f4SPaul Zimmerman 					if (t_left <= 0) {
390197ba5f4SPaul Zimmerman 						qh->frame_usecs[k] +=
391197ba5f4SPaul Zimmerman 							hsotg->frame_usecs[k]
392197ba5f4SPaul Zimmerman 								+ t_left;
393197ba5f4SPaul Zimmerman 						hsotg->frame_usecs[k] = -t_left;
394197ba5f4SPaul Zimmerman 						return i;
395197ba5f4SPaul Zimmerman 					} else {
396197ba5f4SPaul Zimmerman 						qh->frame_usecs[k] +=
397197ba5f4SPaul Zimmerman 							hsotg->frame_usecs[k];
398197ba5f4SPaul Zimmerman 						hsotg->frame_usecs[k] = 0;
399197ba5f4SPaul Zimmerman 					}
400197ba5f4SPaul Zimmerman 				}
401197ba5f4SPaul Zimmerman 			}
402197ba5f4SPaul Zimmerman 			/* add the frame time to x time */
403197ba5f4SPaul Zimmerman 			xtime += hsotg->frame_usecs[j];
404197ba5f4SPaul Zimmerman 			/* we must have a fully available next frame or break */
405197ba5f4SPaul Zimmerman 			if (xtime < utime &&
406197ba5f4SPaul Zimmerman 			   hsotg->frame_usecs[j] == max_uframe_usecs[j])
407197ba5f4SPaul Zimmerman 				continue;
408197ba5f4SPaul Zimmerman 		}
409197ba5f4SPaul Zimmerman 	}
410197ba5f4SPaul Zimmerman 	return -ENOSPC;
411197ba5f4SPaul Zimmerman }
412197ba5f4SPaul Zimmerman 
413197ba5f4SPaul Zimmerman static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
414197ba5f4SPaul Zimmerman {
415197ba5f4SPaul Zimmerman 	int ret;
416197ba5f4SPaul Zimmerman 
417197ba5f4SPaul Zimmerman 	if (qh->dev_speed == USB_SPEED_HIGH) {
418197ba5f4SPaul Zimmerman 		/* if this is a hs transaction we need a full frame */
419197ba5f4SPaul Zimmerman 		ret = dwc2_find_single_uframe(hsotg, qh);
420197ba5f4SPaul Zimmerman 	} else {
421197ba5f4SPaul Zimmerman 		/*
422197ba5f4SPaul Zimmerman 		 * if this is a fs transaction we may need a sequence
423197ba5f4SPaul Zimmerman 		 * of frames
424197ba5f4SPaul Zimmerman 		 */
425197ba5f4SPaul Zimmerman 		ret = dwc2_find_multi_uframe(hsotg, qh);
426197ba5f4SPaul Zimmerman 	}
427197ba5f4SPaul Zimmerman 	return ret;
428197ba5f4SPaul Zimmerman }
429197ba5f4SPaul Zimmerman 
430197ba5f4SPaul Zimmerman /**
431197ba5f4SPaul Zimmerman  * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
432197ba5f4SPaul Zimmerman  * host channel is large enough to handle the maximum data transfer in a single
433197ba5f4SPaul Zimmerman  * (micro)frame for a periodic transfer
434197ba5f4SPaul Zimmerman  *
435197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
436197ba5f4SPaul Zimmerman  * @qh:    QH for a periodic endpoint
437197ba5f4SPaul Zimmerman  *
438197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
439197ba5f4SPaul Zimmerman  */
440197ba5f4SPaul Zimmerman static int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg,
441197ba5f4SPaul Zimmerman 				    struct dwc2_qh *qh)
442197ba5f4SPaul Zimmerman {
443197ba5f4SPaul Zimmerman 	u32 max_xfer_size;
444197ba5f4SPaul Zimmerman 	u32 max_channel_xfer_size;
445197ba5f4SPaul Zimmerman 	int status = 0;
446197ba5f4SPaul Zimmerman 
447197ba5f4SPaul Zimmerman 	max_xfer_size = dwc2_max_packet(qh->maxp) * dwc2_hb_mult(qh->maxp);
448197ba5f4SPaul Zimmerman 	max_channel_xfer_size = hsotg->core_params->max_transfer_size;
449197ba5f4SPaul Zimmerman 
450197ba5f4SPaul Zimmerman 	if (max_xfer_size > max_channel_xfer_size) {
451197ba5f4SPaul Zimmerman 		dev_err(hsotg->dev,
452197ba5f4SPaul Zimmerman 			"%s: Periodic xfer length %d > max xfer length for channel %d\n",
453197ba5f4SPaul Zimmerman 			__func__, max_xfer_size, max_channel_xfer_size);
454197ba5f4SPaul Zimmerman 		status = -ENOSPC;
455197ba5f4SPaul Zimmerman 	}
456197ba5f4SPaul Zimmerman 
457197ba5f4SPaul Zimmerman 	return status;
458197ba5f4SPaul Zimmerman }
459197ba5f4SPaul Zimmerman 
460197ba5f4SPaul Zimmerman /**
461197ba5f4SPaul Zimmerman  * dwc2_schedule_periodic() - Schedules an interrupt or isochronous transfer in
462197ba5f4SPaul Zimmerman  * the periodic schedule
463197ba5f4SPaul Zimmerman  *
464197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
465197ba5f4SPaul Zimmerman  * @qh:    QH for the periodic transfer. The QH should already contain the
466197ba5f4SPaul Zimmerman  *         scheduling information.
467197ba5f4SPaul Zimmerman  *
468197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
469197ba5f4SPaul Zimmerman  */
470197ba5f4SPaul Zimmerman static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
471197ba5f4SPaul Zimmerman {
472197ba5f4SPaul Zimmerman 	int status;
473197ba5f4SPaul Zimmerman 
474197ba5f4SPaul Zimmerman 	if (hsotg->core_params->uframe_sched > 0) {
475197ba5f4SPaul Zimmerman 		int frame = -1;
476197ba5f4SPaul Zimmerman 
477197ba5f4SPaul Zimmerman 		status = dwc2_find_uframe(hsotg, qh);
478197ba5f4SPaul Zimmerman 		if (status == 0)
479197ba5f4SPaul Zimmerman 			frame = 7;
480197ba5f4SPaul Zimmerman 		else if (status > 0)
481197ba5f4SPaul Zimmerman 			frame = status - 1;
482197ba5f4SPaul Zimmerman 
483197ba5f4SPaul Zimmerman 		/* Set the new frame up */
484197ba5f4SPaul Zimmerman 		if (frame >= 0) {
485197ba5f4SPaul Zimmerman 			qh->sched_frame &= ~0x7;
486197ba5f4SPaul Zimmerman 			qh->sched_frame |= (frame & 7);
487197ba5f4SPaul Zimmerman 		}
488197ba5f4SPaul Zimmerman 
489197ba5f4SPaul Zimmerman 		if (status > 0)
490197ba5f4SPaul Zimmerman 			status = 0;
491197ba5f4SPaul Zimmerman 	} else {
492197ba5f4SPaul Zimmerman 		status = dwc2_periodic_channel_available(hsotg);
493197ba5f4SPaul Zimmerman 		if (status) {
494197ba5f4SPaul Zimmerman 			dev_info(hsotg->dev,
495197ba5f4SPaul Zimmerman 				 "%s: No host channel available for periodic transfer\n",
496197ba5f4SPaul Zimmerman 				 __func__);
497197ba5f4SPaul Zimmerman 			return status;
498197ba5f4SPaul Zimmerman 		}
499197ba5f4SPaul Zimmerman 
500197ba5f4SPaul Zimmerman 		status = dwc2_check_periodic_bandwidth(hsotg, qh);
501197ba5f4SPaul Zimmerman 	}
502197ba5f4SPaul Zimmerman 
503197ba5f4SPaul Zimmerman 	if (status) {
504197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
505197ba5f4SPaul Zimmerman 			"%s: Insufficient periodic bandwidth for periodic transfer\n",
506197ba5f4SPaul Zimmerman 			__func__);
507197ba5f4SPaul Zimmerman 		return status;
508197ba5f4SPaul Zimmerman 	}
509197ba5f4SPaul Zimmerman 
510197ba5f4SPaul Zimmerman 	status = dwc2_check_max_xfer_size(hsotg, qh);
511197ba5f4SPaul Zimmerman 	if (status) {
512197ba5f4SPaul Zimmerman 		dev_dbg(hsotg->dev,
513197ba5f4SPaul Zimmerman 			"%s: Channel max transfer size too small for periodic transfer\n",
514197ba5f4SPaul Zimmerman 			__func__);
515197ba5f4SPaul Zimmerman 		return status;
516197ba5f4SPaul Zimmerman 	}
517197ba5f4SPaul Zimmerman 
518197ba5f4SPaul Zimmerman 	if (hsotg->core_params->dma_desc_enable > 0)
519197ba5f4SPaul Zimmerman 		/* Don't rely on SOF and start in ready schedule */
520197ba5f4SPaul Zimmerman 		list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
521197ba5f4SPaul Zimmerman 	else
522197ba5f4SPaul Zimmerman 		/* Always start in inactive schedule */
523197ba5f4SPaul Zimmerman 		list_add_tail(&qh->qh_list_entry,
524197ba5f4SPaul Zimmerman 			      &hsotg->periodic_sched_inactive);
525197ba5f4SPaul Zimmerman 
526197ba5f4SPaul Zimmerman 	if (hsotg->core_params->uframe_sched <= 0)
527197ba5f4SPaul Zimmerman 		/* Reserve periodic channel */
528197ba5f4SPaul Zimmerman 		hsotg->periodic_channels++;
529197ba5f4SPaul Zimmerman 
530197ba5f4SPaul Zimmerman 	/* Update claimed usecs per (micro)frame */
531197ba5f4SPaul Zimmerman 	hsotg->periodic_usecs += qh->usecs;
532197ba5f4SPaul Zimmerman 
533197ba5f4SPaul Zimmerman 	return status;
534197ba5f4SPaul Zimmerman }
535197ba5f4SPaul Zimmerman 
536197ba5f4SPaul Zimmerman /**
537197ba5f4SPaul Zimmerman  * dwc2_deschedule_periodic() - Removes an interrupt or isochronous transfer
538197ba5f4SPaul Zimmerman  * from the periodic schedule
539197ba5f4SPaul Zimmerman  *
540197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
541197ba5f4SPaul Zimmerman  * @qh:	   QH for the periodic transfer
542197ba5f4SPaul Zimmerman  */
543197ba5f4SPaul Zimmerman static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
544197ba5f4SPaul Zimmerman 				     struct dwc2_qh *qh)
545197ba5f4SPaul Zimmerman {
546197ba5f4SPaul Zimmerman 	int i;
547197ba5f4SPaul Zimmerman 
548197ba5f4SPaul Zimmerman 	list_del_init(&qh->qh_list_entry);
549197ba5f4SPaul Zimmerman 
550197ba5f4SPaul Zimmerman 	/* Update claimed usecs per (micro)frame */
551197ba5f4SPaul Zimmerman 	hsotg->periodic_usecs -= qh->usecs;
552197ba5f4SPaul Zimmerman 
553197ba5f4SPaul Zimmerman 	if (hsotg->core_params->uframe_sched > 0) {
554197ba5f4SPaul Zimmerman 		for (i = 0; i < 8; i++) {
555197ba5f4SPaul Zimmerman 			hsotg->frame_usecs[i] += qh->frame_usecs[i];
556197ba5f4SPaul Zimmerman 			qh->frame_usecs[i] = 0;
557197ba5f4SPaul Zimmerman 		}
558197ba5f4SPaul Zimmerman 	} else {
559197ba5f4SPaul Zimmerman 		/* Release periodic channel reservation */
560197ba5f4SPaul Zimmerman 		hsotg->periodic_channels--;
561197ba5f4SPaul Zimmerman 	}
562197ba5f4SPaul Zimmerman }
563197ba5f4SPaul Zimmerman 
564197ba5f4SPaul Zimmerman /**
565197ba5f4SPaul Zimmerman  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
566197ba5f4SPaul Zimmerman  * schedule if it is not already in the schedule. If the QH is already in
567197ba5f4SPaul Zimmerman  * the schedule, no action is taken.
568197ba5f4SPaul Zimmerman  *
569197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure for the DWC OTG controller
570197ba5f4SPaul Zimmerman  * @qh:    The QH to add
571197ba5f4SPaul Zimmerman  *
572197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
573197ba5f4SPaul Zimmerman  */
574197ba5f4SPaul Zimmerman int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
575197ba5f4SPaul Zimmerman {
576197ba5f4SPaul Zimmerman 	int status;
577197ba5f4SPaul Zimmerman 	u32 intr_mask;
578197ba5f4SPaul Zimmerman 
579197ba5f4SPaul Zimmerman 	if (dbg_qh(qh))
580197ba5f4SPaul Zimmerman 		dev_vdbg(hsotg->dev, "%s()\n", __func__);
581197ba5f4SPaul Zimmerman 
582197ba5f4SPaul Zimmerman 	if (!list_empty(&qh->qh_list_entry))
583197ba5f4SPaul Zimmerman 		/* QH already in a schedule */
584197ba5f4SPaul Zimmerman 		return 0;
585197ba5f4SPaul Zimmerman 
586197ba5f4SPaul Zimmerman 	/* Add the new QH to the appropriate schedule */
587197ba5f4SPaul Zimmerman 	if (dwc2_qh_is_non_per(qh)) {
588197ba5f4SPaul Zimmerman 		/* Always start in inactive schedule */
589197ba5f4SPaul Zimmerman 		list_add_tail(&qh->qh_list_entry,
590197ba5f4SPaul Zimmerman 			      &hsotg->non_periodic_sched_inactive);
591197ba5f4SPaul Zimmerman 		return 0;
592197ba5f4SPaul Zimmerman 	}
593197ba5f4SPaul Zimmerman 
594197ba5f4SPaul Zimmerman 	status = dwc2_schedule_periodic(hsotg, qh);
595197ba5f4SPaul Zimmerman 	if (status)
596197ba5f4SPaul Zimmerman 		return status;
597197ba5f4SPaul Zimmerman 	if (!hsotg->periodic_qh_count) {
598197ba5f4SPaul Zimmerman 		intr_mask = readl(hsotg->regs + GINTMSK);
599197ba5f4SPaul Zimmerman 		intr_mask |= GINTSTS_SOF;
600197ba5f4SPaul Zimmerman 		writel(intr_mask, hsotg->regs + GINTMSK);
601197ba5f4SPaul Zimmerman 	}
602197ba5f4SPaul Zimmerman 	hsotg->periodic_qh_count++;
603197ba5f4SPaul Zimmerman 
604197ba5f4SPaul Zimmerman 	return 0;
605197ba5f4SPaul Zimmerman }
606197ba5f4SPaul Zimmerman 
607197ba5f4SPaul Zimmerman /**
608197ba5f4SPaul Zimmerman  * dwc2_hcd_qh_unlink() - Removes a QH from either the non-periodic or periodic
609197ba5f4SPaul Zimmerman  * schedule. Memory is not freed.
610197ba5f4SPaul Zimmerman  *
611197ba5f4SPaul Zimmerman  * @hsotg: The HCD state structure
612197ba5f4SPaul Zimmerman  * @qh:    QH to remove from schedule
613197ba5f4SPaul Zimmerman  */
614197ba5f4SPaul Zimmerman void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
615197ba5f4SPaul Zimmerman {
616197ba5f4SPaul Zimmerman 	u32 intr_mask;
617197ba5f4SPaul Zimmerman 
618197ba5f4SPaul Zimmerman 	dev_vdbg(hsotg->dev, "%s()\n", __func__);
619197ba5f4SPaul Zimmerman 
620197ba5f4SPaul Zimmerman 	if (list_empty(&qh->qh_list_entry))
621197ba5f4SPaul Zimmerman 		/* QH is not in a schedule */
622197ba5f4SPaul Zimmerman 		return;
623197ba5f4SPaul Zimmerman 
624197ba5f4SPaul Zimmerman 	if (dwc2_qh_is_non_per(qh)) {
625197ba5f4SPaul Zimmerman 		if (hsotg->non_periodic_qh_ptr == &qh->qh_list_entry)
626197ba5f4SPaul Zimmerman 			hsotg->non_periodic_qh_ptr =
627197ba5f4SPaul Zimmerman 					hsotg->non_periodic_qh_ptr->next;
628197ba5f4SPaul Zimmerman 		list_del_init(&qh->qh_list_entry);
629197ba5f4SPaul Zimmerman 		return;
630197ba5f4SPaul Zimmerman 	}
631197ba5f4SPaul Zimmerman 
632197ba5f4SPaul Zimmerman 	dwc2_deschedule_periodic(hsotg, qh);
633197ba5f4SPaul Zimmerman 	hsotg->periodic_qh_count--;
634197ba5f4SPaul Zimmerman 	if (!hsotg->periodic_qh_count) {
635197ba5f4SPaul Zimmerman 		intr_mask = readl(hsotg->regs + GINTMSK);
636197ba5f4SPaul Zimmerman 		intr_mask &= ~GINTSTS_SOF;
637197ba5f4SPaul Zimmerman 		writel(intr_mask, hsotg->regs + GINTMSK);
638197ba5f4SPaul Zimmerman 	}
639197ba5f4SPaul Zimmerman }
640197ba5f4SPaul Zimmerman 
641197ba5f4SPaul Zimmerman /*
642197ba5f4SPaul Zimmerman  * Schedule the next continuing periodic split transfer
643197ba5f4SPaul Zimmerman  */
644197ba5f4SPaul Zimmerman static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
645197ba5f4SPaul Zimmerman 				      struct dwc2_qh *qh, u16 frame_number,
646197ba5f4SPaul Zimmerman 				      int sched_next_periodic_split)
647197ba5f4SPaul Zimmerman {
648197ba5f4SPaul Zimmerman 	u16 incr;
649197ba5f4SPaul Zimmerman 
650197ba5f4SPaul Zimmerman 	if (sched_next_periodic_split) {
651197ba5f4SPaul Zimmerman 		qh->sched_frame = frame_number;
652197ba5f4SPaul Zimmerman 		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
653197ba5f4SPaul Zimmerman 		if (dwc2_frame_num_le(frame_number, incr)) {
654197ba5f4SPaul Zimmerman 			/*
655197ba5f4SPaul Zimmerman 			 * Allow one frame to elapse after start split
656197ba5f4SPaul Zimmerman 			 * microframe before scheduling complete split, but
657197ba5f4SPaul Zimmerman 			 * DON'T if we are doing the next start split in the
658197ba5f4SPaul Zimmerman 			 * same frame for an ISOC out
659197ba5f4SPaul Zimmerman 			 */
660197ba5f4SPaul Zimmerman 			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
661197ba5f4SPaul Zimmerman 			    qh->ep_is_in != 0) {
662197ba5f4SPaul Zimmerman 				qh->sched_frame =
663197ba5f4SPaul Zimmerman 					dwc2_frame_num_inc(qh->sched_frame, 1);
664197ba5f4SPaul Zimmerman 			}
665197ba5f4SPaul Zimmerman 		}
666197ba5f4SPaul Zimmerman 	} else {
667197ba5f4SPaul Zimmerman 		qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
668197ba5f4SPaul Zimmerman 						     qh->interval);
669197ba5f4SPaul Zimmerman 		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
670197ba5f4SPaul Zimmerman 			qh->sched_frame = frame_number;
671197ba5f4SPaul Zimmerman 		qh->sched_frame |= 0x7;
672197ba5f4SPaul Zimmerman 		qh->start_split_frame = qh->sched_frame;
673197ba5f4SPaul Zimmerman 	}
674197ba5f4SPaul Zimmerman }
675197ba5f4SPaul Zimmerman 
676197ba5f4SPaul Zimmerman /*
677197ba5f4SPaul Zimmerman  * Deactivates a QH. For non-periodic QHs, removes the QH from the active
678197ba5f4SPaul Zimmerman  * non-periodic schedule. The QH is added to the inactive non-periodic
679197ba5f4SPaul Zimmerman  * schedule if any QTDs are still attached to the QH.
680197ba5f4SPaul Zimmerman  *
681197ba5f4SPaul Zimmerman  * For periodic QHs, the QH is removed from the periodic queued schedule. If
682197ba5f4SPaul Zimmerman  * there are any QTDs still attached to the QH, the QH is added to either the
683197ba5f4SPaul Zimmerman  * periodic inactive schedule or the periodic ready schedule and its next
684197ba5f4SPaul Zimmerman  * scheduled frame is calculated. The QH is placed in the ready schedule if
685197ba5f4SPaul Zimmerman  * the scheduled frame has been reached already. Otherwise it's placed in the
686197ba5f4SPaul Zimmerman  * inactive schedule. If there are no QTDs attached to the QH, the QH is
687197ba5f4SPaul Zimmerman  * completely removed from the periodic schedule.
688197ba5f4SPaul Zimmerman  */
689197ba5f4SPaul Zimmerman void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
690197ba5f4SPaul Zimmerman 			    int sched_next_periodic_split)
691197ba5f4SPaul Zimmerman {
692197ba5f4SPaul Zimmerman 	u16 frame_number;
693197ba5f4SPaul Zimmerman 
694197ba5f4SPaul Zimmerman 	if (dbg_qh(qh))
695197ba5f4SPaul Zimmerman 		dev_vdbg(hsotg->dev, "%s()\n", __func__);
696197ba5f4SPaul Zimmerman 
697197ba5f4SPaul Zimmerman 	if (dwc2_qh_is_non_per(qh)) {
698197ba5f4SPaul Zimmerman 		dwc2_hcd_qh_unlink(hsotg, qh);
699197ba5f4SPaul Zimmerman 		if (!list_empty(&qh->qtd_list))
700197ba5f4SPaul Zimmerman 			/* Add back to inactive non-periodic schedule */
701197ba5f4SPaul Zimmerman 			dwc2_hcd_qh_add(hsotg, qh);
702197ba5f4SPaul Zimmerman 		return;
703197ba5f4SPaul Zimmerman 	}
704197ba5f4SPaul Zimmerman 
705197ba5f4SPaul Zimmerman 	frame_number = dwc2_hcd_get_frame_number(hsotg);
706197ba5f4SPaul Zimmerman 
707197ba5f4SPaul Zimmerman 	if (qh->do_split) {
708197ba5f4SPaul Zimmerman 		dwc2_sched_periodic_split(hsotg, qh, frame_number,
709197ba5f4SPaul Zimmerman 					  sched_next_periodic_split);
710197ba5f4SPaul Zimmerman 	} else {
711197ba5f4SPaul Zimmerman 		qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
712197ba5f4SPaul Zimmerman 						     qh->interval);
713197ba5f4SPaul Zimmerman 		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
714197ba5f4SPaul Zimmerman 			qh->sched_frame = frame_number;
715197ba5f4SPaul Zimmerman 	}
716197ba5f4SPaul Zimmerman 
717197ba5f4SPaul Zimmerman 	if (list_empty(&qh->qtd_list)) {
718197ba5f4SPaul Zimmerman 		dwc2_hcd_qh_unlink(hsotg, qh);
719197ba5f4SPaul Zimmerman 		return;
720197ba5f4SPaul Zimmerman 	}
721197ba5f4SPaul Zimmerman 	/*
722197ba5f4SPaul Zimmerman 	 * Remove from periodic_sched_queued and move to
723197ba5f4SPaul Zimmerman 	 * appropriate queue
724197ba5f4SPaul Zimmerman 	 */
725197ba5f4SPaul Zimmerman 	if ((hsotg->core_params->uframe_sched > 0 &&
726197ba5f4SPaul Zimmerman 	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
727197ba5f4SPaul Zimmerman 	    (hsotg->core_params->uframe_sched <= 0 &&
728197ba5f4SPaul Zimmerman 	     qh->sched_frame == frame_number))
729197ba5f4SPaul Zimmerman 		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
730197ba5f4SPaul Zimmerman 	else
731197ba5f4SPaul Zimmerman 		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
732197ba5f4SPaul Zimmerman }
733197ba5f4SPaul Zimmerman 
734197ba5f4SPaul Zimmerman /**
735197ba5f4SPaul Zimmerman  * dwc2_hcd_qtd_init() - Initializes a QTD structure
736197ba5f4SPaul Zimmerman  *
737197ba5f4SPaul Zimmerman  * @qtd: The QTD to initialize
738197ba5f4SPaul Zimmerman  * @urb: The associated URB
739197ba5f4SPaul Zimmerman  */
740197ba5f4SPaul Zimmerman void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
741197ba5f4SPaul Zimmerman {
742197ba5f4SPaul Zimmerman 	qtd->urb = urb;
743197ba5f4SPaul Zimmerman 	if (dwc2_hcd_get_pipe_type(&urb->pipe_info) ==
744197ba5f4SPaul Zimmerman 			USB_ENDPOINT_XFER_CONTROL) {
745197ba5f4SPaul Zimmerman 		/*
746197ba5f4SPaul Zimmerman 		 * The only time the QTD data toggle is used is on the data
747197ba5f4SPaul Zimmerman 		 * phase of control transfers. This phase always starts with
748197ba5f4SPaul Zimmerman 		 * DATA1.
749197ba5f4SPaul Zimmerman 		 */
750197ba5f4SPaul Zimmerman 		qtd->data_toggle = DWC2_HC_PID_DATA1;
751197ba5f4SPaul Zimmerman 		qtd->control_phase = DWC2_CONTROL_SETUP;
752197ba5f4SPaul Zimmerman 	}
753197ba5f4SPaul Zimmerman 
754197ba5f4SPaul Zimmerman 	/* Start split */
755197ba5f4SPaul Zimmerman 	qtd->complete_split = 0;
756197ba5f4SPaul Zimmerman 	qtd->isoc_split_pos = DWC2_HCSPLT_XACTPOS_ALL;
757197ba5f4SPaul Zimmerman 	qtd->isoc_split_offset = 0;
758197ba5f4SPaul Zimmerman 	qtd->in_process = 0;
759197ba5f4SPaul Zimmerman 
760197ba5f4SPaul Zimmerman 	/* Store the qtd ptr in the urb to reference the QTD */
761197ba5f4SPaul Zimmerman 	urb->qtd = qtd;
762197ba5f4SPaul Zimmerman }
763197ba5f4SPaul Zimmerman 
764197ba5f4SPaul Zimmerman /**
765197ba5f4SPaul Zimmerman  * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
76633ad261aSGregory Herrero  *			Caller must hold driver lock.
767197ba5f4SPaul Zimmerman  *
768197ba5f4SPaul Zimmerman  * @hsotg:        The DWC HCD structure
769197ba5f4SPaul Zimmerman  * @qtd:          The QTD to add
770b58e6ceeSMian Yousaf Kaukab  * @qh:           Queue head to add qtd to
771197ba5f4SPaul Zimmerman  *
772197ba5f4SPaul Zimmerman  * Return: 0 if successful, negative error code otherwise
773197ba5f4SPaul Zimmerman  *
774b58e6ceeSMian Yousaf Kaukab  * If the QH to which the QTD is added is not currently scheduled, it is placed
775b58e6ceeSMian Yousaf Kaukab  * into the proper schedule based on its EP type.
776197ba5f4SPaul Zimmerman  */
777197ba5f4SPaul Zimmerman int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
778b58e6ceeSMian Yousaf Kaukab 		     struct dwc2_qh *qh)
779197ba5f4SPaul Zimmerman {
780197ba5f4SPaul Zimmerman 	int retval;
781197ba5f4SPaul Zimmerman 
782b58e6ceeSMian Yousaf Kaukab 	if (unlikely(!qh)) {
783b58e6ceeSMian Yousaf Kaukab 		dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
784b58e6ceeSMian Yousaf Kaukab 		retval = -EINVAL;
785b58e6ceeSMian Yousaf Kaukab 		goto fail;
786197ba5f4SPaul Zimmerman 	}
787197ba5f4SPaul Zimmerman 
788b58e6ceeSMian Yousaf Kaukab 	retval = dwc2_hcd_qh_add(hsotg, qh);
789197ba5f4SPaul Zimmerman 	if (retval)
790197ba5f4SPaul Zimmerman 		goto fail;
791197ba5f4SPaul Zimmerman 
792b58e6ceeSMian Yousaf Kaukab 	qtd->qh = qh;
793b58e6ceeSMian Yousaf Kaukab 	list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
794197ba5f4SPaul Zimmerman 
795197ba5f4SPaul Zimmerman 	return 0;
796197ba5f4SPaul Zimmerman fail:
797197ba5f4SPaul Zimmerman 	return retval;
798197ba5f4SPaul Zimmerman }
799