| /* |
| * hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines |
| * |
| * Copyright (C) 2004-2013 Synopsys, Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions, and the following disclaimer, |
| * without modification. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The names of the above-listed copyright holders may not be used |
| * to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * ALTERNATIVELY, this software may be distributed under the terms of the |
| * GNU General Public License ("GPL") as published by the Free Software |
| * Foundation; either version 2 of the License, or (at your option) any |
| * later version. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * This file contains the Descriptor DMA implementation for Host mode |
| */ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/spinlock.h> |
| #include <linux/interrupt.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| #include <linux/usb.h> |
| |
| #include <linux/usb/hcd.h> |
| #include <linux/usb/ch11.h> |
| |
| #include "core.h" |
| #include "hcd.h" |
| |
| static u16 dwc2_frame_list_idx(u16 frame) |
| { |
| return frame & (FRLISTEN_64_SIZE - 1); |
| } |
| |
| static u16 dwc2_desclist_idx_inc(u16 idx, u16 inc, u8 speed) |
| { |
| return (idx + inc) & |
| ((speed == USB_SPEED_HIGH ? MAX_DMA_DESC_NUM_HS_ISOC : |
| MAX_DMA_DESC_NUM_GENERIC) - 1); |
| } |
| |
| static u16 dwc2_desclist_idx_dec(u16 idx, u16 inc, u8 speed) |
| { |
| return (idx - inc) & |
| ((speed == USB_SPEED_HIGH ? MAX_DMA_DESC_NUM_HS_ISOC : |
| MAX_DMA_DESC_NUM_GENERIC) - 1); |
| } |
| |
| static u16 dwc2_max_desc_num(struct dwc2_qh *qh) |
| { |
| return (qh->ep_type == USB_ENDPOINT_XFER_ISOC && |
| qh->dev_speed == USB_SPEED_HIGH) ? |
| MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC; |
| } |
| |
| static u16 dwc2_frame_incr_val(struct dwc2_qh *qh) |
| { |
| return qh->dev_speed == USB_SPEED_HIGH ? |
| (qh->interval + 8 - 1) / 8 : qh->interval; |
| } |
| |
| static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, |
| gfp_t flags) |
| { |
| struct kmem_cache *desc_cache; |
| |
| if (qh->ep_type == USB_ENDPOINT_XFER_ISOC |
| && qh->dev_speed == USB_SPEED_HIGH) |
| desc_cache = hsotg->desc_hsisoc_cache; |
| else |
| desc_cache = hsotg->desc_gen_cache; |
| |
| qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) * |
| dwc2_max_desc_num(qh); |
| |
| qh->desc_list = kmem_cache_zalloc(desc_cache, flags | GFP_DMA); |
| if (!qh->desc_list) |
| return -ENOMEM; |
| |
| qh->desc_list_dma = dma_map_single(hsotg->dev, qh->desc_list, |
| qh->desc_list_sz, |
| DMA_TO_DEVICE); |
| |
| qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags); |
| if (!qh->n_bytes) { |
| dma_unmap_single(hsotg->dev, qh->desc_list_dma, |
| qh->desc_list_sz, |
| DMA_FROM_DEVICE); |
| kfree(qh->desc_list); |
| qh->desc_list = NULL; |
| return -ENOMEM; |
| } |
| |
| return 0; |
| } |
| |
| static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) |
| { |
| struct kmem_cache *desc_cache; |
| |
| if (qh->ep_type == USB_ENDPOINT_XFER_ISOC |
| && qh->dev_speed == USB_SPEED_HIGH) |
| desc_cache = hsotg->desc_hsisoc_cache; |
| else |
| desc_cache = hsotg->desc_gen_cache; |
| |
| if (qh->desc_list) { |
| dma_unmap_single(hsotg->dev, qh->desc_list_dma, |
| qh->desc_list_sz, DMA_FROM_DEVICE); |
| kmem_cache_free(desc_cache, qh->desc_list); |
| qh->desc_list = NULL; |
| } |
| |
| kfree(qh->n_bytes); |
| qh->n_bytes = NULL; |
| } |
| |
| static int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags) |
| { |
| if (hsotg->frame_list) |
| return 0; |
| |
| hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE; |
| hsotg->frame_list = kzalloc(hsotg->frame_list_sz, GFP_ATOMIC | GFP_DMA); |
| if (!hsotg->frame_list) |
| return -ENOMEM; |
| |
| hsotg->frame_list_dma = dma_map_single(hsotg->dev, hsotg->frame_list, |
| hsotg->frame_list_sz, |
| DMA_TO_DEVICE); |
| |
| return 0; |
| } |
| |
| static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| |
| if (!hsotg->frame_list) { |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| return; |
| } |
| |
| dma_unmap_single(hsotg->dev, hsotg->frame_list_dma, |
| hsotg->frame_list_sz, DMA_FROM_DEVICE); |
| |
| kfree(hsotg->frame_list); |
| hsotg->frame_list = NULL; |
| |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| |
| } |
| |
| static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en) |
| { |
| u32 hcfg; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| |
| hcfg = dwc2_readl(hsotg->regs + HCFG); |
| if (hcfg & HCFG_PERSCHEDENA) { |
| /* already enabled */ |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| return; |
| } |
| |
| dwc2_writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); |
| |
| hcfg &= ~HCFG_FRLISTEN_MASK; |
| hcfg |= fr_list_en | HCFG_PERSCHEDENA; |
| dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n"); |
| dwc2_writel(hcfg, hsotg->regs + HCFG); |
| |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| } |
| |
| static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) |
| { |
| u32 hcfg; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| |
| hcfg = dwc2_readl(hsotg->regs + HCFG); |
| if (!(hcfg & HCFG_PERSCHEDENA)) { |
| /* already disabled */ |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| return; |
| } |
| |
| hcfg &= ~HCFG_PERSCHEDENA; |
| dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n"); |
| dwc2_writel(hcfg, hsotg->regs + HCFG); |
| |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| } |
| |
| /* |
| * Activates/Deactivates FrameList entries for the channel based on endpoint |
| * servicing period |
| */ |
| static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, |
| int enable) |
| { |
| struct dwc2_host_chan *chan; |
| u16 i, j, inc; |
| |
| if (!hsotg) { |
| pr_err("hsotg = %p\n", hsotg); |
| return; |
| } |
| |
| if (!qh->channel) { |
| dev_err(hsotg->dev, "qh->channel = %p\n", qh->channel); |
| return; |
| } |
| |
| if (!hsotg->frame_list) { |
| dev_err(hsotg->dev, "hsotg->frame_list = %p\n", |
| hsotg->frame_list); |
| return; |
| } |
| |
| chan = qh->channel; |
| inc = dwc2_frame_incr_val(qh); |
| if (qh->ep_type == USB_ENDPOINT_XFER_ISOC) |
| i = dwc2_frame_list_idx(qh->sched_frame); |
| else |
| i = 0; |
| |
| j = i; |
| do { |
| if (enable) |
| hsotg->frame_list[j] |= 1 << chan->hc_num; |
| else |
| hsotg->frame_list[j] &= ~(1 << chan->hc_num); |
| j = (j + inc) & (FRLISTEN_64_SIZE - 1); |
| } while (j != i); |
| |
| /* |
| * Sync frame list since controller will access it if periodic |
| * channel is currently enabled. |
| */ |
| dma_sync_single_for_device(hsotg->dev, |
| hsotg->frame_list_dma, |
| hsotg->frame_list_sz, |
| DMA_TO_DEVICE); |
| |
| if (!enable) |
| return; |
| |
| chan->schinfo = 0; |
| if (chan->speed == USB_SPEED_HIGH && qh->interval) { |
| j = 1; |
| /* TODO - check this */ |
| inc = (8 + qh->interval - 1) / qh->interval; |
| for (i = 0; i < inc; i++) { |
| chan->schinfo |= j; |
| j = j << qh->interval; |
| } |
| } else { |
| chan->schinfo = 0xff; |
| } |
| } |
| |
| static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg, |
| struct dwc2_qh *qh) |
| { |
| struct dwc2_host_chan *chan = qh->channel; |
| |
| if (dwc2_qh_is_non_per(qh)) { |
| if (hsotg->core_params->uframe_sched > 0) |
| hsotg->available_host_channels++; |
| else |
| hsotg->non_periodic_channels--; |
| } else { |
| dwc2_update_frame_list(hsotg, qh, 0); |
| hsotg->available_host_channels++; |
| } |
| |
| /* |
| * The condition is added to prevent double cleanup try in case of |
| * device disconnect. See channel cleanup in dwc2_hcd_disconnect(). |
| */ |
| if (chan->qh) { |
| if (!list_empty(&chan->hc_list_entry)) |
| list_del(&chan->hc_list_entry); |
| dwc2_hc_cleanup(hsotg, chan); |
| list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list); |
| chan->qh = NULL; |
| } |
| |
| qh->channel = NULL; |
| qh->ntd = 0; |
| |
| if (qh->desc_list) |
| memset(qh->desc_list, 0, sizeof(struct dwc2_hcd_dma_desc) * |
| dwc2_max_desc_num(qh)); |
| } |
| |
| /** |
| * dwc2_hcd_qh_init_ddma() - Initializes a QH structure's Descriptor DMA |
| * related members |
| * |
| * @hsotg: The HCD state structure for the DWC OTG controller |
| * @qh: The QH to init |
| * |
| * Return: 0 if successful, negative error code otherwise |
| * |
| * Allocates memory for the descriptor list. For the first periodic QH, |
| * allocates memory for the FrameList and enables periodic scheduling. |
| */ |
| int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, |
| gfp_t mem_flags) |
| { |
| int retval; |
| |
| if (qh->do_split) { |
| dev_err(hsotg->dev, |
| "SPLIT Transfers are not supported in Descriptor DMA mode.\n"); |
| retval = -EINVAL; |
| goto err0; |
| } |
| |
| retval = dwc2_desc_list_alloc(hsotg, qh, mem_flags); |
| if (retval) |
| goto err0; |
| |
| if (qh->ep_type == USB_ENDPOINT_XFER_ISOC || |
| qh->ep_type == USB_ENDPOINT_XFER_INT) { |
| if (!hsotg->frame_list) { |
| retval = dwc2_frame_list_alloc(hsotg, mem_flags); |
| if (retval) |
| goto err1; |
| /* Enable periodic schedule on first periodic QH */ |
| dwc2_per_sched_enable(hsotg, HCFG_FRLISTEN_64); |
| } |
| } |
| |
| qh->ntd = 0; |
| return 0; |
| |
| err1: |
| dwc2_desc_list_free(hsotg, qh); |
| err0: |
| return retval; |
| } |
| |
| /** |
| * dwc2_hcd_qh_free_ddma() - Frees a QH structure's Descriptor DMA related |
| * members |
| * |
| * @hsotg: The HCD state structure for the DWC OTG controller |
| * @qh: The QH to free |
| * |
| * Frees descriptor list memory associated with the QH. If QH is periodic and |
| * the last, frees FrameList memory and disables periodic scheduling. |
| */ |
| void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) |
| { |
| unsigned long flags; |
| |
| dwc2_desc_list_free(hsotg, qh); |
| |
| /* |
| * Channel still assigned due to some reasons. |
| * Seen on Isoc URB dequeue. Channel halted but no subsequent |
| * ChHalted interrupt to release the channel. Afterwards |
| * when it comes here from endpoint disable routine |
| * channel remains assigned. |
| */ |
| spin_lock_irqsave(&hsotg->lock, flags); |
| if (qh->channel) |
| dwc2_release_channel_ddma(hsotg, qh); |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| |
| if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC || |
| qh->ep_type == USB_ENDPOINT_XFER_INT) && |
| (hsotg->core_params->uframe_sched > 0 || |
| !hsotg->periodic_channels) && hsotg->frame_list) { |
| dwc2_per_sched_disable(hsotg); |
| dwc2_frame_list_free(hsotg); |
| } |
| } |
| |
| static u8 dwc2_frame_to_desc_idx(struct dwc2_qh *qh, u16 frame_idx) |
| { |
| if (qh->dev_speed == USB_SPEED_HIGH) |
| /* Descriptor set (8 descriptors) index which is 8-aligned */ |
| return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8; |
| else |
| return frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1); |
| } |
| |
| /* |
| * Determine starting frame for Isochronous transfer. |
| * Few frames skipped to prevent race condition with HC. |
| */ |
| static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg, |
| struct dwc2_qh *qh, u16 *skip_frames) |
| { |
| u16 frame; |
| |
| hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); |
| |
| /* sched_frame is always frame number (not uFrame) both in FS and HS! */ |
| |
| /* |
| * skip_frames is used to limit activated descriptors number |
| * to avoid the situation when HC services the last activated |
| * descriptor firstly. |
| * Example for FS: |
| * Current frame is 1, scheduled frame is 3. Since HC always fetches |
| * the descriptor corresponding to curr_frame+1, the descriptor |
| * corresponding to frame 2 will be fetched. If the number of |
| * descriptors is max=64 (or greather) the list will be fully programmed |
| * with Active descriptors and it is possible case (rare) that the |
| * latest descriptor(considering rollback) corresponding to frame 2 will |
| * be serviced first. HS case is more probable because, in fact, up to |
| * 11 uframes (16 in the code) may be skipped. |
| */ |
| if (qh->dev_speed == USB_SPEED_HIGH) { |
| /* |
| * Consider uframe counter also, to start xfer asap. If half of |
| * the frame elapsed skip 2 frames otherwise just 1 frame. |
| * Starting descriptor index must be 8-aligned, so if the |
| * current frame is near to complete the next one is skipped as |
| * well. |
| */ |
| if (dwc2_micro_frame_num(hsotg->frame_number) >= 5) { |
| *skip_frames = 2 * 8; |
| frame = dwc2_frame_num_inc(hsotg->frame_number, |
| *skip_frames); |
| } else { |
| *skip_frames = 1 * 8; |
| frame = dwc2_frame_num_inc(hsotg->frame_number, |
| *skip_frames); |
| } |
| |
| frame = dwc2_full_frame_num(frame); |
| } else { |
| /* |
| * Two frames are skipped for FS - the current and the next. |
| * But for descriptor programming, 1 frame (descriptor) is |
| * enough, see example above. |
| */ |
| *skip_frames = 1; |
| frame = dwc2_frame_num_inc(hsotg->frame_number, 2); |
| } |
| |
| return frame; |
| } |
| |
| /* |
| * Calculate initial descriptor index for isochronous transfer based on |
| * scheduled frame |
| */ |
| static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg, |
| struct dwc2_qh *qh) |
| { |
| u16 frame, fr_idx, fr_idx_tmp, skip_frames; |
| |
| /* |
| * With current ISOC processing algorithm the channel is being released |
| * when no more QTDs in the list (qh->ntd == 0). Thus this function is |
| * called only when qh->ntd == 0 and qh->channel == 0. |
| * |
| * So qh->channel != NULL branch is not used and just not removed from |
| * the source file. It is required for another possible approach which |
| * is, do not disable and release the channel when ISOC session |
| * completed, just move QH to inactive schedule until new QTD arrives. |
| * On new QTD, the QH moved back to 'ready' schedule, starting frame and |
| * therefore starting desc_index are recalculated. In this case channel |
| * is released only on ep_disable. |
| */ |
| |
| /* |
| * Calculate starting descriptor index. For INTERRUPT endpoint it is |
| * always 0. |
| */ |
| if (qh->channel) { |
| frame = dwc2_calc_starting_frame(hsotg, qh, &skip_frames); |
| /* |
| * Calculate initial descriptor index based on FrameList current |
| * bitmap and servicing period |
| */ |
| fr_idx_tmp = dwc2_frame_list_idx(frame); |
| fr_idx = (FRLISTEN_64_SIZE + |
| dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp) |
| % dwc2_frame_incr_val(qh); |
| fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE; |
| } else { |
| qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh, |
| &skip_frames); |
| fr_idx = dwc2_frame_list_idx(qh->sched_frame); |
| } |
| |
| qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx); |
| |
| return skip_frames; |
| } |
| |
| #define ISOC_URB_GIVEBACK_ASAP |
| |
| #define MAX_ISOC_XFER_SIZE_FS 1023 |
| #define MAX_ISOC_XFER_SIZE_HS 3072 |
| #define DESCNUM_THRESHOLD 4 |
| |
| static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_qtd *qtd, |
| struct dwc2_qh *qh, u32 max_xfer_size, |
| u16 idx) |
| { |
| struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx]; |
| struct dwc2_hcd_iso_packet_desc *frame_desc; |
| |
| memset(dma_desc, 0, sizeof(*dma_desc)); |
| frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; |
| |
| if (frame_desc->length > max_xfer_size) |
| qh->n_bytes[idx] = max_xfer_size; |
| else |
| qh->n_bytes[idx] = frame_desc->length; |
| |
| dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset); |
| dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT & |
| HOST_DMA_ISOC_NBYTES_MASK; |
| |
| /* Set active bit */ |
| dma_desc->status |= HOST_DMA_A; |
| |
| qh->ntd++; |
| qtd->isoc_frame_index_last++; |
| |
| #ifdef ISOC_URB_GIVEBACK_ASAP |
| /* Set IOC for each descriptor corresponding to last frame of URB */ |
| if (qtd->isoc_frame_index_last == qtd->urb->packet_count) |
| dma_desc->status |= HOST_DMA_IOC; |
| #endif |
| |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + |
| (idx * sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| } |
| |
| static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_qh *qh, u16 skip_frames) |
| { |
| struct dwc2_qtd *qtd; |
| u32 max_xfer_size; |
| u16 idx, inc, n_desc = 0, ntd_max = 0; |
| u16 cur_idx; |
| u16 next_idx; |
| |
| idx = qh->td_last; |
| inc = qh->interval; |
| hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); |
| cur_idx = dwc2_frame_list_idx(hsotg->frame_number); |
| next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed); |
| |
| /* |
| * Ensure current frame number didn't overstep last scheduled |
| * descriptor. If it happens, the only way to recover is to move |
| * qh->td_last to current frame number + 1. |
| * So that next isoc descriptor will be scheduled on frame number + 1 |
| * and not on a past frame. |
| */ |
| if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) { |
| if (inc < 32) { |
| dev_vdbg(hsotg->dev, |
| "current frame number overstep last descriptor\n"); |
| qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc, |
| qh->dev_speed); |
| idx = qh->td_last; |
| } |
| } |
| |
| if (qh->interval) { |
| ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) / |
| qh->interval; |
| if (skip_frames && !qh->channel) |
| ntd_max -= skip_frames / qh->interval; |
| } |
| |
| max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ? |
| MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS; |
| |
| list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) { |
| if (qtd->in_process && |
| qtd->isoc_frame_index_last == |
| qtd->urb->packet_count) |
| continue; |
| |
| qtd->isoc_td_first = idx; |
| while (qh->ntd < ntd_max && qtd->isoc_frame_index_last < |
| qtd->urb->packet_count) { |
| dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh, |
| max_xfer_size, idx); |
| idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed); |
| n_desc++; |
| } |
| qtd->isoc_td_last = idx; |
| qtd->in_process = 1; |
| } |
| |
| qh->td_last = idx; |
| |
| #ifdef ISOC_URB_GIVEBACK_ASAP |
| /* Set IOC for last descriptor if descriptor list is full */ |
| if (qh->ntd == ntd_max) { |
| idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); |
| qh->desc_list[idx].status |= HOST_DMA_IOC; |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + (idx * |
| sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| } |
| #else |
| /* |
| * Set IOC bit only for one descriptor. Always try to be ahead of HW |
| * processing, i.e. on IOC generation driver activates next descriptor |
| * but core continues to process descriptors following the one with IOC |
| * set. |
| */ |
| |
| if (n_desc > DESCNUM_THRESHOLD) |
| /* |
| * Move IOC "up". Required even if there is only one QTD |
| * in the list, because QTDs might continue to be queued, |
| * but during the activation it was only one queued. |
| * Actually more than one QTD might be in the list if this |
| * function called from XferCompletion - QTDs was queued during |
| * HW processing of the previous descriptor chunk. |
| */ |
| idx = dwc2_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), |
| qh->dev_speed); |
| else |
| /* |
| * Set the IOC for the latest descriptor if either number of |
| * descriptors is not greater than threshold or no more new |
| * descriptors activated |
| */ |
| idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); |
| |
| qh->desc_list[idx].status |= HOST_DMA_IOC; |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + |
| (idx * sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| #endif |
| } |
| |
| static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| struct dwc2_qtd *qtd, struct dwc2_qh *qh, |
| int n_desc) |
| { |
| struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[n_desc]; |
| int len = chan->xfer_len; |
| |
| if (len > MAX_DMA_DESC_SIZE - (chan->max_packet - 1)) |
| len = MAX_DMA_DESC_SIZE - (chan->max_packet - 1); |
| |
| if (chan->ep_is_in) { |
| int num_packets; |
| |
| if (len > 0 && chan->max_packet) |
| num_packets = (len + chan->max_packet - 1) |
| / chan->max_packet; |
| else |
| /* Need 1 packet for transfer length of 0 */ |
| num_packets = 1; |
| |
| /* Always program an integral # of packets for IN transfers */ |
| len = num_packets * chan->max_packet; |
| } |
| |
| dma_desc->status = len << HOST_DMA_NBYTES_SHIFT & HOST_DMA_NBYTES_MASK; |
| qh->n_bytes[n_desc] = len; |
| |
| if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL && |
| qtd->control_phase == DWC2_CONTROL_SETUP) |
| dma_desc->status |= HOST_DMA_SUP; |
| |
| dma_desc->buf = (u32)chan->xfer_dma; |
| |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + |
| (n_desc * sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| |
| /* |
| * Last (or only) descriptor of IN transfer with actual size less |
| * than MaxPacket |
| */ |
| if (len > chan->xfer_len) { |
| chan->xfer_len = 0; |
| } else { |
| chan->xfer_dma += len; |
| chan->xfer_len -= len; |
| } |
| } |
| |
| static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_qh *qh) |
| { |
| struct dwc2_qtd *qtd; |
| struct dwc2_host_chan *chan = qh->channel; |
| int n_desc = 0; |
| |
| dev_vdbg(hsotg->dev, "%s(): qh=%p dma=%08lx len=%d\n", __func__, qh, |
| (unsigned long)chan->xfer_dma, chan->xfer_len); |
| |
| /* |
| * Start with chan->xfer_dma initialized in assign_and_init_hc(), then |
| * if SG transfer consists of multiple URBs, this pointer is re-assigned |
| * to the buffer of the currently processed QTD. For non-SG request |
| * there is always one QTD active. |
| */ |
| |
| list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) { |
| dev_vdbg(hsotg->dev, "qtd=%p\n", qtd); |
| |
| if (n_desc) { |
| /* SG request - more than 1 QTD */ |
| chan->xfer_dma = qtd->urb->dma + |
| qtd->urb->actual_length; |
| chan->xfer_len = qtd->urb->length - |
| qtd->urb->actual_length; |
| dev_vdbg(hsotg->dev, "buf=%08lx len=%d\n", |
| (unsigned long)chan->xfer_dma, chan->xfer_len); |
| } |
| |
| qtd->n_desc = 0; |
| do { |
| if (n_desc > 1) { |
| qh->desc_list[n_desc - 1].status |= HOST_DMA_A; |
| dev_vdbg(hsotg->dev, |
| "set A bit in desc %d (%p)\n", |
| n_desc - 1, |
| &qh->desc_list[n_desc - 1]); |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + |
| ((n_desc - 1) * |
| sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| } |
| dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc); |
| dev_vdbg(hsotg->dev, |
| "desc %d (%p) buf=%08x status=%08x\n", |
| n_desc, &qh->desc_list[n_desc], |
| qh->desc_list[n_desc].buf, |
| qh->desc_list[n_desc].status); |
| qtd->n_desc++; |
| n_desc++; |
| } while (chan->xfer_len > 0 && |
| n_desc != MAX_DMA_DESC_NUM_GENERIC); |
| |
| dev_vdbg(hsotg->dev, "n_desc=%d\n", n_desc); |
| qtd->in_process = 1; |
| if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL) |
| break; |
| if (n_desc == MAX_DMA_DESC_NUM_GENERIC) |
| break; |
| } |
| |
| if (n_desc) { |
| qh->desc_list[n_desc - 1].status |= |
| HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A; |
| dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n", |
| n_desc - 1, &qh->desc_list[n_desc - 1]); |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma + (n_desc - 1) * |
| sizeof(struct dwc2_hcd_dma_desc), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| if (n_desc > 1) { |
| qh->desc_list[0].status |= HOST_DMA_A; |
| dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n", |
| &qh->desc_list[0]); |
| dma_sync_single_for_device(hsotg->dev, |
| qh->desc_list_dma, |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_TO_DEVICE); |
| } |
| chan->ntd = n_desc; |
| } |
| } |
| |
| /** |
| * dwc2_hcd_start_xfer_ddma() - Starts a transfer in Descriptor DMA mode |
| * |
| * @hsotg: The HCD state structure for the DWC OTG controller |
| * @qh: The QH to init |
| * |
| * Return: 0 if successful, negative error code otherwise |
| * |
| * For Control and Bulk endpoints, initializes descriptor list and starts the |
| * transfer. For Interrupt and Isochronous endpoints, initializes descriptor |
| * list then updates FrameList, marking appropriate entries as active. |
| * |
| * For Isochronous endpoints the starting descriptor index is calculated based |
| * on the scheduled frame, but only on the first transfer descriptor within a |
| * session. Then the transfer is started via enabling the channel. |
| * |
| * For Isochronous endpoints the channel is not halted on XferComplete |
| * interrupt so remains assigned to the endpoint(QH) until session is done. |
| */ |
| void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) |
| { |
| /* Channel is already assigned */ |
| struct dwc2_host_chan *chan = qh->channel; |
| u16 skip_frames = 0; |
| |
| switch (chan->ep_type) { |
| case USB_ENDPOINT_XFER_CONTROL: |
| case USB_ENDPOINT_XFER_BULK: |
| dwc2_init_non_isoc_dma_desc(hsotg, qh); |
| dwc2_hc_start_transfer_ddma(hsotg, chan); |
| break; |
| case USB_ENDPOINT_XFER_INT: |
| dwc2_init_non_isoc_dma_desc(hsotg, qh); |
| dwc2_update_frame_list(hsotg, qh, 1); |
| dwc2_hc_start_transfer_ddma(hsotg, chan); |
| break; |
| case USB_ENDPOINT_XFER_ISOC: |
| if (!qh->ntd) |
| skip_frames = dwc2_recalc_initial_desc_idx(hsotg, qh); |
| dwc2_init_isoc_dma_desc(hsotg, qh, skip_frames); |
| |
| if (!chan->xfer_started) { |
| dwc2_update_frame_list(hsotg, qh, 1); |
| |
| /* |
| * Always set to max, instead of actual size. Otherwise |
| * ntd will be changed with channel being enabled. Not |
| * recommended. |
| */ |
| chan->ntd = dwc2_max_desc_num(qh); |
| |
| /* Enable channel only once for ISOC */ |
| dwc2_hc_start_transfer_ddma(hsotg, chan); |
| } |
| |
| break; |
| default: |
| break; |
| } |
| } |
| |
| #define DWC2_CMPL_DONE 1 |
| #define DWC2_CMPL_STOP 2 |
| |
| static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| struct dwc2_qtd *qtd, |
| struct dwc2_qh *qh, u16 idx) |
| { |
| struct dwc2_hcd_dma_desc *dma_desc; |
| struct dwc2_hcd_iso_packet_desc *frame_desc; |
| u16 remain = 0; |
| int rc = 0; |
| |
| if (!qtd->urb) |
| return -EINVAL; |
| |
| dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx * |
| sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_FROM_DEVICE); |
| |
| dma_desc = &qh->desc_list[idx]; |
| |
| frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; |
| dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset); |
| if (chan->ep_is_in) |
| remain = (dma_desc->status & HOST_DMA_ISOC_NBYTES_MASK) >> |
| HOST_DMA_ISOC_NBYTES_SHIFT; |
| |
| if ((dma_desc->status & HOST_DMA_STS_MASK) == HOST_DMA_STS_PKTERR) { |
| /* |
| * XactError, or unable to complete all the transactions |
| * in the scheduled micro-frame/frame, both indicated by |
| * HOST_DMA_STS_PKTERR |
| */ |
| qtd->urb->error_count++; |
| frame_desc->actual_length = qh->n_bytes[idx] - remain; |
| frame_desc->status = -EPROTO; |
| } else { |
| /* Success */ |
| frame_desc->actual_length = qh->n_bytes[idx] - remain; |
| frame_desc->status = 0; |
| } |
| |
| if (++qtd->isoc_frame_index == qtd->urb->packet_count) { |
| /* |
| * urb->status is not used for isoc transfers here. The |
| * individual frame_desc status are used instead. |
| */ |
| dwc2_host_complete(hsotg, qtd, 0); |
| dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); |
| |
| /* |
| * This check is necessary because urb_dequeue can be called |
| * from urb complete callback (sound driver for example). All |
| * pending URBs are dequeued there, so no need for further |
| * processing. |
| */ |
| if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) |
| return -1; |
| rc = DWC2_CMPL_DONE; |
| } |
| |
| qh->ntd--; |
| |
| /* Stop if IOC requested descriptor reached */ |
| if (dma_desc->status & HOST_DMA_IOC) |
| rc = DWC2_CMPL_STOP; |
| |
| return rc; |
| } |
| |
| static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| enum dwc2_halt_status halt_status) |
| { |
| struct dwc2_hcd_iso_packet_desc *frame_desc; |
| struct dwc2_qtd *qtd, *qtd_tmp; |
| struct dwc2_qh *qh; |
| u16 idx; |
| int rc; |
| |
| qh = chan->qh; |
| idx = qh->td_first; |
| |
| if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { |
| list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) |
| qtd->in_process = 0; |
| return; |
| } |
| |
| if (halt_status == DWC2_HC_XFER_AHB_ERR || |
| halt_status == DWC2_HC_XFER_BABBLE_ERR) { |
| /* |
| * Channel is halted in these error cases, considered as serious |
| * issues. |
| * Complete all URBs marking all frames as failed, irrespective |
| * whether some of the descriptors (frames) succeeded or not. |
| * Pass error code to completion routine as well, to update |
| * urb->status, some of class drivers might use it to stop |
| * queing transfer requests. |
| */ |
| int err = halt_status == DWC2_HC_XFER_AHB_ERR ? |
| -EIO : -EOVERFLOW; |
| |
| list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, |
| qtd_list_entry) { |
| if (qtd->urb) { |
| for (idx = 0; idx < qtd->urb->packet_count; |
| idx++) { |
| frame_desc = &qtd->urb->iso_descs[idx]; |
| frame_desc->status = err; |
| } |
| |
| dwc2_host_complete(hsotg, qtd, err); |
| } |
| |
| dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); |
| } |
| |
| return; |
| } |
| |
| list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { |
| if (!qtd->in_process) |
| break; |
| |
| /* |
| * Ensure idx corresponds to descriptor where first urb of this |
| * qtd was added. In fact, during isoc desc init, dwc2 may skip |
| * an index if current frame number is already over this index. |
| */ |
| if (idx != qtd->isoc_td_first) { |
| dev_vdbg(hsotg->dev, |
| "try to complete %d instead of %d\n", |
| idx, qtd->isoc_td_first); |
| idx = qtd->isoc_td_first; |
| } |
| |
| do { |
| struct dwc2_qtd *qtd_next; |
| u16 cur_idx; |
| |
| rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh, |
| idx); |
| if (rc < 0) |
| return; |
| idx = dwc2_desclist_idx_inc(idx, qh->interval, |
| chan->speed); |
| if (!rc) |
| continue; |
| |
| if (rc == DWC2_CMPL_DONE) |
| break; |
| |
| /* rc == DWC2_CMPL_STOP */ |
| |
| if (qh->interval >= 32) |
| goto stop_scan; |
| |
| qh->td_first = idx; |
| cur_idx = dwc2_frame_list_idx(hsotg->frame_number); |
| qtd_next = list_first_entry(&qh->qtd_list, |
| struct dwc2_qtd, |
| qtd_list_entry); |
| if (dwc2_frame_idx_num_gt(cur_idx, |
| qtd_next->isoc_td_last)) |
| break; |
| |
| goto stop_scan; |
| |
| } while (idx != qh->td_first); |
| } |
| |
| stop_scan: |
| qh->td_first = idx; |
| } |
| |
| static int dwc2_update_non_isoc_urb_state_ddma(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| struct dwc2_qtd *qtd, |
| struct dwc2_hcd_dma_desc *dma_desc, |
| enum dwc2_halt_status halt_status, |
| u32 n_bytes, int *xfer_done) |
| { |
| struct dwc2_hcd_urb *urb = qtd->urb; |
| u16 remain = 0; |
| |
| if (chan->ep_is_in) |
| remain = (dma_desc->status & HOST_DMA_NBYTES_MASK) >> |
| HOST_DMA_NBYTES_SHIFT; |
| |
| dev_vdbg(hsotg->dev, "remain=%d dwc2_urb=%p\n", remain, urb); |
| |
| if (halt_status == DWC2_HC_XFER_AHB_ERR) { |
| dev_err(hsotg->dev, "EIO\n"); |
| urb->status = -EIO; |
| return 1; |
| } |
| |
| if ((dma_desc->status & HOST_DMA_STS_MASK) == HOST_DMA_STS_PKTERR) { |
| switch (halt_status) { |
| case DWC2_HC_XFER_STALL: |
| dev_vdbg(hsotg->dev, "Stall\n"); |
| urb->status = -EPIPE; |
| break; |
| case DWC2_HC_XFER_BABBLE_ERR: |
| dev_err(hsotg->dev, "Babble\n"); |
| urb->status = -EOVERFLOW; |
| break; |
| case DWC2_HC_XFER_XACT_ERR: |
| dev_err(hsotg->dev, "XactErr\n"); |
| urb->status = -EPROTO; |
| break; |
| default: |
| dev_err(hsotg->dev, |
| "%s: Unhandled descriptor error status (%d)\n", |
| __func__, halt_status); |
| break; |
| } |
| return 1; |
| } |
| |
| if (dma_desc->status & HOST_DMA_A) { |
| dev_vdbg(hsotg->dev, |
| "Active descriptor encountered on channel %d\n", |
| chan->hc_num); |
| return 0; |
| } |
| |
| if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL) { |
| if (qtd->control_phase == DWC2_CONTROL_DATA) { |
| urb->actual_length += n_bytes - remain; |
| if (remain || urb->actual_length >= urb->length) { |
| /* |
| * For Control Data stage do not set urb->status |
| * to 0, to prevent URB callback. Set it when |
| * Status phase is done. See below. |
| */ |
| *xfer_done = 1; |
| } |
| } else if (qtd->control_phase == DWC2_CONTROL_STATUS) { |
| urb->status = 0; |
| *xfer_done = 1; |
| } |
| /* No handling for SETUP stage */ |
| } else { |
| /* BULK and INTR */ |
| urb->actual_length += n_bytes - remain; |
| dev_vdbg(hsotg->dev, "length=%d actual=%d\n", urb->length, |
| urb->actual_length); |
| if (remain || urb->actual_length >= urb->length) { |
| urb->status = 0; |
| *xfer_done = 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| int chnum, struct dwc2_qtd *qtd, |
| int desc_num, |
| enum dwc2_halt_status halt_status, |
| int *xfer_done) |
| { |
| struct dwc2_qh *qh = chan->qh; |
| struct dwc2_hcd_urb *urb = qtd->urb; |
| struct dwc2_hcd_dma_desc *dma_desc; |
| u32 n_bytes; |
| int failed; |
| |
| dev_vdbg(hsotg->dev, "%s()\n", __func__); |
| |
| if (!urb) |
| return -EINVAL; |
| |
| dma_sync_single_for_cpu(hsotg->dev, |
| qh->desc_list_dma + (desc_num * |
| sizeof(struct dwc2_hcd_dma_desc)), |
| sizeof(struct dwc2_hcd_dma_desc), |
| DMA_FROM_DEVICE); |
| |
| dma_desc = &qh->desc_list[desc_num]; |
| n_bytes = qh->n_bytes[desc_num]; |
| dev_vdbg(hsotg->dev, |
| "qtd=%p dwc2_urb=%p desc_num=%d desc=%p n_bytes=%d\n", |
| qtd, urb, desc_num, dma_desc, n_bytes); |
| failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc, |
| halt_status, n_bytes, |
| xfer_done); |
| if (failed || (*xfer_done && urb->status != -EINPROGRESS)) { |
| dwc2_host_complete(hsotg, qtd, urb->status); |
| dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); |
| dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x\n", |
| failed, *xfer_done); |
| return failed; |
| } |
| |
| if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL) { |
| switch (qtd->control_phase) { |
| case DWC2_CONTROL_SETUP: |
| if (urb->length > 0) |
| qtd->control_phase = DWC2_CONTROL_DATA; |
| else |
| qtd->control_phase = DWC2_CONTROL_STATUS; |
| dev_vdbg(hsotg->dev, |
| " Control setup transaction done\n"); |
| break; |
| case DWC2_CONTROL_DATA: |
| if (*xfer_done) { |
| qtd->control_phase = DWC2_CONTROL_STATUS; |
| dev_vdbg(hsotg->dev, |
| " Control data transfer done\n"); |
| } else if (desc_num + 1 == qtd->n_desc) { |
| /* |
| * Last descriptor for Control data stage which |
| * is not completed yet |
| */ |
| dwc2_hcd_save_data_toggle(hsotg, chan, chnum, |
| qtd); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, |
| int chnum, |
| enum dwc2_halt_status halt_status) |
| { |
| struct list_head *qtd_item, *qtd_tmp; |
| struct dwc2_qh *qh = chan->qh; |
| struct dwc2_qtd *qtd = NULL; |
| int xfer_done; |
| int desc_num = 0; |
| |
| if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { |
| list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) |
| qtd->in_process = 0; |
| return; |
| } |
| |
| list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { |
| int i; |
| int qtd_desc_count; |
| |
| qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); |
| xfer_done = 0; |
| qtd_desc_count = qtd->n_desc; |
| |
| for (i = 0; i < qtd_desc_count; i++) { |
| if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd, |
| desc_num, halt_status, |
| &xfer_done)) |
| goto stop_scan; |
| |
| desc_num++; |
| } |
| } |
| |
| stop_scan: |
| if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) { |
| /* |
| * Resetting the data toggle for bulk and interrupt endpoints |
| * in case of stall. See handle_hc_stall_intr(). |
| */ |
| if (halt_status == DWC2_HC_XFER_STALL) |
| qh->data_toggle = DWC2_HC_PID_DATA0; |
| else |
| dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); |
| } |
| |
| if (halt_status == DWC2_HC_XFER_COMPLETE) { |
| if (chan->hcint & HCINTMSK_NYET) { |
| /* |
| * Got a NYET on the last transaction of the transfer. |
| * It means that the endpoint should be in the PING |
| * state at the beginning of the next transfer. |
| */ |
| qh->ping_state = 1; |
| } |
| } |
| } |
| |
| /** |
| * dwc2_hcd_complete_xfer_ddma() - Scans the descriptor list, updates URB's |
| * status and calls completion routine for the URB if it's done. Called from |
| * interrupt handlers. |
| * |
| * @hsotg: The HCD state structure for the DWC OTG controller |
| * @chan: Host channel the transfer is completed on |
| * @chnum: Index of Host channel registers |
| * @halt_status: Reason the channel is being halted or just XferComplete |
| * for isochronous transfers |
| * |
| * Releases the channel to be used by other transfers. |
| * In case of Isochronous endpoint the channel is not halted until the end of |
| * the session, i.e. QTD list is empty. |
| * If periodic channel released the FrameList is updated accordingly. |
| * Calls transaction selection routines to activate pending transfers. |
| */ |
| void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg, |
| struct dwc2_host_chan *chan, int chnum, |
| enum dwc2_halt_status halt_status) |
| { |
| struct dwc2_qh *qh = chan->qh; |
| int continue_isoc_xfer = 0; |
| enum dwc2_transaction_type tr_type; |
| |
| if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) { |
| dwc2_complete_isoc_xfer_ddma(hsotg, chan, halt_status); |
| |
| /* Release the channel if halted or session completed */ |
| if (halt_status != DWC2_HC_XFER_COMPLETE || |
| list_empty(&qh->qtd_list)) { |
| struct dwc2_qtd *qtd, *qtd_tmp; |
| |
| /* |
| * Kill all remainings QTDs since channel has been |
| * halted. |
| */ |
| list_for_each_entry_safe(qtd, qtd_tmp, |
| &qh->qtd_list, |
| qtd_list_entry) { |
| dwc2_host_complete(hsotg, qtd, |
| -ECONNRESET); |
| dwc2_hcd_qtd_unlink_and_free(hsotg, |
| qtd, qh); |
| } |
| |
| /* Halt the channel if session completed */ |
| if (halt_status == DWC2_HC_XFER_COMPLETE) |
| dwc2_hc_halt(hsotg, chan, halt_status); |
| dwc2_release_channel_ddma(hsotg, qh); |
| dwc2_hcd_qh_unlink(hsotg, qh); |
| } else { |
| /* Keep in assigned schedule to continue transfer */ |
| list_move(&qh->qh_list_entry, |
| &hsotg->periodic_sched_assigned); |
| /* |
| * If channel has been halted during giveback of urb |
| * then prevent any new scheduling. |
| */ |
| if (!chan->halt_status) |
| continue_isoc_xfer = 1; |
| } |
| /* |
| * Todo: Consider the case when period exceeds FrameList size. |
| * Frame Rollover interrupt should be used. |
| */ |
| } else { |
| /* |
| * Scan descriptor list to complete the URB(s), then release |
| * the channel |
| */ |
| dwc2_complete_non_isoc_xfer_ddma(hsotg, chan, chnum, |
| halt_status); |
| dwc2_release_channel_ddma(hsotg, qh); |
| dwc2_hcd_qh_unlink(hsotg, qh); |
| |
| if (!list_empty(&qh->qtd_list)) { |
| /* |
| * Add back to inactive non-periodic schedule on normal |
| * completion |
| */ |
| dwc2_hcd_qh_add(hsotg, qh); |
| } |
| } |
| |
| tr_type = dwc2_hcd_select_transactions(hsotg); |
| if (tr_type != DWC2_TRANSACTION_NONE || continue_isoc_xfer) { |
| if (continue_isoc_xfer) { |
| if (tr_type == DWC2_TRANSACTION_NONE) |
| tr_type = DWC2_TRANSACTION_PERIODIC; |
| else if (tr_type == DWC2_TRANSACTION_NON_PERIODIC) |
| tr_type = DWC2_TRANSACTION_ALL; |
| } |
| dwc2_hcd_queue_transactions(hsotg, tr_type); |
| } |
| } |