| /* | |
| * 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->host_interval + 8 - 1) / 8 : qh->host_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_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 = kcalloc(dwc2_max_desc_num(qh), sizeof(u32), flags); | |
| if (!qh->n_bytes) { | |
| 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; | |
| 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->next_active_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->host_interval) { | |
| j = 1; | |
| /* TODO - check this */ | |
| inc = (8 + qh->host_interval - 1) / qh->host_interval; | |
| for (i = 0; i < inc; i++) { | |
| chan->schinfo |= j; | |
| j = j << qh->host_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->params.uframe_sched) | |
| 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_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->params.uframe_sched || | |
| !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); | |
| /* | |
| * next_active_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->next_active_frame) - | |
| fr_idx_tmp) % dwc2_frame_incr_val(qh); | |
| fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE; | |
| } else { | |
| qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh, | |
| &skip_frames); | |
| fr_idx = dwc2_frame_list_idx(qh->next_active_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_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_dma_desc)), | |
| sizeof(struct dwc2_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->host_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->host_interval) { | |
| ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) / | |
| qh->host_interval; | |
| if (skip_frames && !qh->channel) | |
| ntd_max -= skip_frames / qh->host_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_dma_desc)), | |
| sizeof(struct dwc2_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_dma_desc)), | |
| sizeof(struct dwc2_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_dma_desc *dma_desc = &qh->desc_list[n_desc]; | |
| int len = chan->xfer_len; | |
| if (len > HOST_DMA_NBYTES_LIMIT - (chan->max_packet - 1)) | |
| len = HOST_DMA_NBYTES_LIMIT - (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_dma_desc)), | |
| sizeof(struct dwc2_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_dma_desc)), | |
| sizeof(struct dwc2_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_dma_desc), | |
| sizeof(struct dwc2_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_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_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_dma_desc)), | |
| sizeof(struct dwc2_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->host_interval, | |
| chan->speed); | |
| if (!rc) | |
| continue; | |
| if (rc == DWC2_CMPL_DONE) | |
| break; | |
| /* rc == DWC2_CMPL_STOP */ | |
| if (qh->host_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_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_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_dma_desc)), | |
| sizeof(struct dwc2_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)) { | |
| qtd = NULL; | |
| 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, NULL); | |
| } | |
| 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_tail(&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); | |
| } | |
| } |