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