1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123 |
- /*==========================================================================
- * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $
- * $Revision: #10 $
- * $Date: 2011/10/20 $
- * $Change: 1869464 $
- *
- * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- * otherwise expressly agreed to in writing between Synopsys and you.
- *
- * The Software IS NOT an item of Licensed Software or Licensed Product under
- * any End User Software License Agreement or Agreement for Licensed Product
- * with Synopsys or any supplement thereto. You are permitted to use and
- * redistribute this Software in source and binary forms, with or without
- * modification, provided that redistributions of source code must retain this
- * notice. You may not view, use, disclose, copy or distribute this file or
- * any information contained herein except pursuant to this license grant from
- * Synopsys. If you do not agree with this notice, including the disclaimer
- * below, then you are not authorized to use the Software.
- *
- * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- * ========================================================================== */
- #ifndef DWC_DEVICE_ONLY
- /** @file
- * This file contains Descriptor DMA support implementation for host mode.
- */
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- static inline uint8_t frame_list_idx(uint16_t frame)
- {
- return (frame & (MAX_FRLIST_EN_NUM - 1));
- }
- static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed)
- {
- return (idx + inc) &
- (((speed ==
- DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
- MAX_DMA_DESC_NUM_GENERIC) - 1);
- }
- static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed)
- {
- return (idx - inc) &
- (((speed ==
- DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
- MAX_DMA_DESC_NUM_GENERIC) - 1);
- }
- static inline uint16_t max_desc_num(dwc_otg_qh_t * qh)
- {
- return (((qh->ep_type == UE_ISOCHRONOUS)
- && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH))
- ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC);
- }
- static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh)
- {
- return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)
- ? ((qh->interval + 8 - 1) / 8)
- : qh->interval);
- }
- static int desc_list_alloc(dwc_otg_qh_t * qh)
- {
- int retval = 0;
- qh->desc_list = (dwc_otg_host_dma_desc_t *)
- DWC_DMA_ALLOC(sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh),
- &qh->desc_list_dma);
- if (!qh->desc_list) {
- retval = -DWC_E_NO_MEMORY;
- DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__);
-
- }
- dwc_memset(qh->desc_list, 0x00,
- sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
- qh->n_bytes =
- (uint32_t *) DWC_ALLOC(sizeof(uint32_t) * max_desc_num(qh));
- if (!qh->n_bytes) {
- retval = -DWC_E_NO_MEMORY;
- DWC_ERROR
- ("%s: Failed to allocate array for descriptors' size actual values\n",
- __func__);
- }
- return retval;
- }
- static void desc_list_free(dwc_otg_qh_t * qh)
- {
- if (qh->desc_list) {
- DWC_DMA_FREE(max_desc_num(qh), qh->desc_list,
- qh->desc_list_dma);
- qh->desc_list = NULL;
- }
- if (qh->n_bytes) {
- DWC_FREE(qh->n_bytes);
- qh->n_bytes = NULL;
- }
- }
- static int frame_list_alloc(dwc_otg_hcd_t * hcd)
- {
- int retval = 0;
- if (hcd->frame_list)
- return 0;
- hcd->frame_list = DWC_DMA_ALLOC(4 * MAX_FRLIST_EN_NUM,
- &hcd->frame_list_dma);
- if (!hcd->frame_list) {
- retval = -DWC_E_NO_MEMORY;
- DWC_ERROR("%s: Frame List allocation failed\n", __func__);
- }
- dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM);
- return retval;
- }
- static void frame_list_free(dwc_otg_hcd_t * hcd)
- {
- if (!hcd->frame_list)
- return;
-
- DWC_DMA_FREE(4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma);
- hcd->frame_list = NULL;
- }
- static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en)
- {
- hcfg_data_t hcfg;
- hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
- if (hcfg.b.perschedena) {
- /* already enabled */
- return;
- }
- DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hflbaddr,
- hcd->frame_list_dma);
- switch (fr_list_en) {
- case 64:
- hcfg.b.frlisten = 3;
- break;
- case 32:
- hcfg.b.frlisten = 2;
- break;
- case 16:
- hcfg.b.frlisten = 1;
- break;
- case 8:
- hcfg.b.frlisten = 0;
- break;
- default:
- break;
- }
- hcfg.b.perschedena = 1;
- DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n");
- DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- }
- static void per_sched_disable(dwc_otg_hcd_t * hcd)
- {
- hcfg_data_t hcfg;
- hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
-
- if (!hcfg.b.perschedena) {
- /* already disabled */
- return;
- }
- hcfg.b.perschedena = 0;
- DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n");
- DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- }
- /*
- * Activates/Deactivates FrameList entries for the channel
- * based on endpoint servicing period.
- */
- void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable)
- {
- uint16_t i, j, inc;
- dwc_hc_t *hc = NULL;
- if (!qh->channel) {
- DWC_ERROR("qh->channel = %p", qh->channel);
- return;
- }
- if (!hcd) {
- DWC_ERROR("------hcd = %p", hcd);
- return;
- }
- if (!hcd->frame_list) {
- DWC_ERROR("-------hcd->frame_list = %p", hcd->frame_list);
- return;
- }
- hc = qh->channel;
- inc = frame_incr_val(qh);
- if (qh->ep_type == UE_ISOCHRONOUS)
- i = frame_list_idx(qh->sched_frame);
- else
- i = 0;
- j = i;
- do {
- if (enable)
- hcd->frame_list[j] |= (1 << hc->hc_num);
- else
- hcd->frame_list[j] &= ~(1 << hc->hc_num);
- j = (j + inc) & (MAX_FRLIST_EN_NUM - 1);
- }
- while (j != i);
- if (!enable)
- return;
- hc->schinfo = 0;
- if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) {
- j = 1;
- /* TODO - check this */
- inc = (8 + qh->interval - 1) / qh->interval;
- for (i = 0; i < inc; i++) {
- hc->schinfo |= j;
- j = j << qh->interval;
- }
- } else {
- hc->schinfo = 0xff;
- }
- }
- #if 1
- void dump_frame_list(dwc_otg_hcd_t * hcd)
- {
- int i = 0;
- DWC_PRINTF("--FRAME LIST (hex) --\n");
- for (i = 0; i < MAX_FRLIST_EN_NUM; i++) {
- DWC_PRINTF("%x\t", hcd->frame_list[i]);
- if (!(i % 8) && i)
- DWC_PRINTF("\n");
- }
- DWC_PRINTF("\n----\n");
- }
- #endif
- static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- dwc_hc_t *hc = qh->channel;
- if (dwc_qh_is_non_per(qh))
- hcd->non_periodic_channels--;
- else
- update_frame_list(hcd, qh, 0);
- /*
- * The condition is added to prevent double cleanup try in case of device
- * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb().
- */
- if (hc->qh) {
- dwc_otg_hc_cleanup(hcd->core_if, hc);
- DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
- hc->qh = NULL;
- }
- qh->channel = NULL;
- qh->ntd = 0;
- if (qh->desc_list) {
- dwc_memset(qh->desc_list, 0x00,
- sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
- }
- }
- /**
- * Initializes a QH structure's Descriptor DMA related members.
- * Allocates memory for descriptor list.
- * On first periodic QH, allocates memory for FrameList
- * and enables periodic scheduling.
- *
- * @param hcd The HCD state structure for the DWC OTG controller.
- * @param qh The QH to init.
- *
- * @return 0 if successful, negative error code otherwise.
- */
- int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- int retval = 0;
- if (qh->do_split) {
- DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n");
- return -1;
- }
- retval = desc_list_alloc(qh);
- if ((retval == 0)
- && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) {
- if (!hcd->frame_list) {
- retval = frame_list_alloc(hcd);
- /* Enable periodic schedule on first periodic QH */
- if (retval == 0)
- per_sched_enable(hcd, MAX_FRLIST_EN_NUM);
- }
- }
- qh->ntd = 0;
- return retval;
- }
- /**
- * Frees descriptor list memory associated with the QH.
- * If QH is periodic and the last, frees FrameList memory
- * and disables periodic scheduling.
- *
- * @param hcd The HCD state structure for the DWC OTG controller.
- * @param qh The QH to init.
- */
- void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- desc_list_free(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.
- */
- if (qh->channel)
- release_channel_ddma(hcd, qh);
- if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)
- && !hcd->periodic_channels && hcd->frame_list) {
- per_sched_disable(hcd);
- frame_list_free(hcd);
- }
- }
- static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx)
- {
- if (qh->dev_speed == DWC_OTG_EP_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 uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- uint8_t * skip_frames)
- {
- uint16_t frame = 0;
- hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd);
-
- /* 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 == DWC_OTG_EP_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 (dwc_micro_frame_num(hcd->frame_number) >= 5) {
- *skip_frames = 2 * 8;
- frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
- } else {
- *skip_frames = 1 * 8;
- frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
- }
- frame = dwc_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 = dwc_frame_num_inc(hcd->frame_number, 2);
- }
- return frame;
- }
- /*
- * Calculate initial descriptor index for isochronous transfer
- * based on scheduled frame.
- */
- static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- uint16_t frame = 0, fr_idx, fr_idx_tmp;
- uint8_t skip_frames = 0;
- /*
- * 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 = calc_starting_frame(hcd, qh, &skip_frames);
- /*
- * Calculate initial descriptor index based on FrameList current bitmap
- * and servicing period.
- */
- fr_idx_tmp = frame_list_idx(frame);
- fr_idx =
- (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) -
- fr_idx_tmp)
- % frame_incr_val(qh);
- fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM;
- } else {
- qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames);
- fr_idx = frame_list_idx(qh->sched_frame);
- }
- qh->td_first = qh->td_last = 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 init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- uint8_t skip_frames)
- {
- struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- dwc_otg_qtd_t *qtd;
- dwc_otg_host_dma_desc_t *dma_desc;
- uint16_t idx, inc, n_desc, ntd_max, max_xfer_size;
- idx = qh->td_last;
- inc = qh->interval;
- n_desc = 0;
- ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval;
- if (skip_frames && !qh->channel)
- ntd_max = ntd_max - skip_frames / qh->interval;
- max_xfer_size =
- (qh->dev_speed ==
- DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS :
- MAX_ISOC_XFER_SIZE_FS;
- DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
- while ((qh->ntd < ntd_max)
- && (qtd->isoc_frame_index_last <
- qtd->urb->packet_count)) {
- dma_desc = &qh->desc_list[idx];
- dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t));
- 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->status.b_isoc.n_bytes = qh->n_bytes[idx];
- dma_desc->status.b_isoc.a = 1;
- dma_desc->status.b_isoc.sts = 0;
- dma_desc->buf = qtd->urb->dma + frame_desc->offset;
- qh->ntd++;
- qtd->isoc_frame_index_last++;
- #ifdef ISOC_URB_GIVEBACK_ASAP
- /*
- * Set IOC for each descriptor corresponding to the
- * last frame of the URB.
- */
- if (qtd->isoc_frame_index_last ==
- qtd->urb->packet_count)
- dma_desc->status.b_isoc.ioc = 1;
- #endif
- idx = desclist_idx_inc(idx, inc, qh->dev_speed);
- n_desc++;
- }
- qtd->in_process = 1;
- }
- qh->td_last = idx;
- #ifdef ISOC_URB_GIVEBACK_ASAP
- /* Set IOC for the last descriptor if descriptor list is full */
- if (qh->ntd == ntd_max) {
- idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
- qh->desc_list[idx].status.b_isoc.ioc = 1;
- }
- #else
- /*
- * Set IOC bit only for one descriptor.
- * Always try to be ahead of HW processing,
- * i.e. on IOC generation driver activates next descriptors but
- * core continues to process descriptors followed the one with IOC set.
- */
- if (n_desc > DESCNUM_THRESHOLD) {
- /*
- * Move IOC "up". Required even if there is only one QTD
- * in the list, cause QTDs migth 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 = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed);
- } else {
- /*
- * Set the IOC for the latest descriptor
- * if either number of descriptor is not greather than threshold
- * or no more new descriptors activated.
- */
- idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
- }
- qh->desc_list[idx].status.b_isoc.ioc = 1;
- #endif
- }
- static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- dwc_hc_t *hc;
- dwc_otg_host_dma_desc_t *dma_desc;
- dwc_otg_qtd_t *qtd;
- int num_packets, len, n_desc = 0;
- hc = qh->channel;
- /*
- * Start with hc->xfer_buff initialized in
- * assign_and_init_hc(), then if SG transfer consists of multiple URBs,
- * this pointer re-assigned to the buffer of the currently processed QTD.
- * For non-SG request there is always one QTD active.
- */
- DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
- if (n_desc) {
- /* SG request - more than 1 QTDs */
- hc->xfer_buff = (uint8_t *)qtd->urb->dma + qtd->urb->actual_length;
- hc->xfer_len = qtd->urb->length - qtd->urb->actual_length;
- }
- qtd->n_desc = 0;
- do {
- dma_desc = &qh->desc_list[n_desc];
- len = hc->xfer_len;
- if (len > MAX_DMA_DESC_SIZE)
- len = MAX_DMA_DESC_SIZE - hc->max_packet + 1;
- if (hc->ep_is_in) {
- if (len > 0) {
- num_packets = (len + hc->max_packet - 1) / hc->max_packet;
- } else {
- /* Need 1 packet for transfer length of 0. */
- num_packets = 1;
- }
- /* Always program an integral # of max packets for IN transfers. */
- len = num_packets * hc->max_packet;
- }
- dma_desc->status.b.n_bytes = len;
- qh->n_bytes[n_desc] = len;
- if ((qh->ep_type == UE_CONTROL)
- && (qtd->control_phase == DWC_OTG_CONTROL_SETUP))
- dma_desc->status.b.sup = 1; /* Setup Packet */
- dma_desc->status.b.a = 1; /* Active descriptor */
- dma_desc->status.b.sts = 0;
- dma_desc->buf =
- ((unsigned long)hc->xfer_buff & 0xffffffff);
- /*
- * Last descriptor(or single) of IN transfer
- * with actual size less than MaxPacket.
- */
- if (len > hc->xfer_len) {
- hc->xfer_len = 0;
- } else {
- hc->xfer_buff += len;
- hc->xfer_len -= len;
- }
- qtd->n_desc++;
- n_desc++;
- }
- while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC));
-
- qtd->in_process = 1;
- if (qh->ep_type == UE_CONTROL)
- break;
- if (n_desc == MAX_DMA_DESC_NUM_GENERIC)
- break;
- }
- if (n_desc) {
- /* Request Transfer Complete interrupt for the last descriptor */
- qh->desc_list[n_desc - 1].status.b.ioc = 1;
- /* End of List indicator */
- qh->desc_list[n_desc - 1].status.b.eol = 1;
- hc->ntd = n_desc;
- }
- }
- /**
- * 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.
- * In case of Isochronous, the starting descriptor index is calculated based
- * on the scheduled frame, but only on the first transfer descriptor within a session.
- * Then starts the transfer via enabling the channel.
- * For Isochronous endpoint the channel is not halted on XferComplete
- * interrupt so remains assigned to the endpoint(QH) until session is done.
- *
- * @param hcd The HCD state structure for the DWC OTG controller.
- * @param qh The QH to init.
- *
- * @return 0 if successful, negative error code otherwise.
- */
- void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- {
- /* Channel is already assigned */
- dwc_hc_t *hc = qh->channel;
- uint8_t skip_frames = 0;
- switch (hc->ep_type) {
- case DWC_OTG_EP_TYPE_CONTROL:
- case DWC_OTG_EP_TYPE_BULK:
- init_non_isoc_dma_desc(hcd, qh);
- dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- break;
- case DWC_OTG_EP_TYPE_INTR:
- init_non_isoc_dma_desc(hcd, qh);
- update_frame_list(hcd, qh, 1);
- dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- break;
- case DWC_OTG_EP_TYPE_ISOC:
- if (!qh->ntd)
- skip_frames = recalc_initial_desc_idx(hcd, qh);
- init_isoc_dma_desc(hcd, qh, skip_frames);
- if (!hc->xfer_started) {
- update_frame_list(hcd, qh, 1);
- /*
- * Always set to max, instead of actual size.
- * Otherwise ntd will be changed with
- * channel being enabled. Not recommended.
- *
- */
- hc->ntd = max_desc_num(qh);
- /* Enable channel only once for ISOC */
- dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- }
- break;
- default:
- break;
- }
- }
- static void complete_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- dwc_otg_halt_status_e halt_status)
- {
- struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- dwc_otg_qtd_t *qtd, *qtd_tmp;
- dwc_otg_qh_t *qh;
- dwc_otg_host_dma_desc_t *dma_desc;
- uint16_t idx, remain;
- uint8_t urb_compl;
- qh = hc->qh;
- idx = qh->td_first;
- if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry)
- qtd->in_process = 0;
- return;
- } else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) ||
- (halt_status == DWC_OTG_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 no.
- * 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 == DWC_OTG_HC_XFER_AHB_ERR)
- ? (-DWC_E_IO)
- : (-DWC_E_OVERFLOW);
-
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- for (idx = 0; idx < qtd->urb->packet_count; idx++) {
- frame_desc = &qtd->urb->iso_descs[idx];
- frame_desc->status = err;
- }
- hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err);
- dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- }
- return;
- }
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- if (!qtd->in_process)
- break;
- urb_compl = 0;
- do {
- dma_desc = &qh->desc_list[idx];
-
- frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0;
- if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) {
- /*
- * XactError or, unable to complete all the transactions
- * in the scheduled micro-frame/frame,
- * both indicated by DMA_DESC_STS_PKTERR.
- */
- qtd->urb->error_count++;
- frame_desc->actual_length = qh->n_bytes[idx] - remain;
- frame_desc->status = -DWC_E_PROTOCOL;
- } 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.
- */
- hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- /*
- * This check is necessary because urb_dequeue can be called
- * from urb complete callback(sound driver example).
- * All pending URBs are dequeued there, so no need for
- * further processing.
- */
- if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- return;
- }
- urb_compl = 1;
- }
- qh->ntd--;
- /* Stop if IOC requested descriptor reached */
- if (dma_desc->status.b_isoc.ioc) {
- idx = desclist_idx_inc(idx, qh->interval, hc->speed);
- goto stop_scan;
- }
- idx = desclist_idx_inc(idx, qh->interval, hc->speed);
- if (urb_compl)
- break;
- }
- while (idx != qh->td_first);
- }
- stop_scan:
- qh->td_first = idx;
- }
- uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_qtd_t * qtd,
- dwc_otg_host_dma_desc_t * dma_desc,
- dwc_otg_halt_status_e halt_status,
- uint32_t n_bytes, uint8_t * xfer_done)
- {
- uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0;
- dwc_otg_hcd_urb_t *urb = qtd->urb;
- if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
- urb->status = -DWC_E_IO;
- return 1;
- }
- if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) {
- switch (halt_status) {
- case DWC_OTG_HC_XFER_STALL:
- urb->status = -DWC_E_PIPE;
- break;
- case DWC_OTG_HC_XFER_BABBLE_ERR:
- urb->status = -DWC_E_OVERFLOW;
- break;
- case DWC_OTG_HC_XFER_XACT_ERR:
- urb->status = -DWC_E_PROTOCOL;
- break;
- default:
- DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__,
- halt_status);
- break;
- }
- return 1;
- }
- if (dma_desc->status.b.a == 1) {
- DWC_DEBUGPL(DBG_HCDV,
- "Active descriptor encountered on channel %d\n",
- hc->hc_num);
- return 0;
- }
- if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) {
- if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
- urb->actual_length += n_bytes - remain;
- if (remain || urb->actual_length == urb->length) {
- /*
- * For Control Data stage do not set urb->status=0 to prevent
- * URB callback. Set it when Status phase done. See below.
- */
- *xfer_done = 1;
- }
- } else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) {
- urb->status = 0;
- *xfer_done = 1;
- }
- /* No handling for SETUP stage */
- } else {
- /* BULK and INTR */
- urb->actual_length += n_bytes - remain;
- if (remain || urb->actual_length == urb->length) {
- urb->status = 0;
- *xfer_done = 1;
- }
- }
- return 0;
- }
- static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- dwc_otg_halt_status_e halt_status)
- {
- dwc_otg_hcd_urb_t *urb = NULL;
- dwc_otg_qtd_t *qtd, *qtd_tmp;
- dwc_otg_qh_t *qh;
- dwc_otg_host_dma_desc_t *dma_desc;
- uint32_t n_bytes, n_desc, i;
- uint8_t failed = 0, xfer_done;
- n_desc = 0;
- qh = hc->qh;
- if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- qtd->in_process = 0;
- }
- return;
- }
- DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
- urb = qtd->urb;
- n_bytes = 0;
- xfer_done = 0;
- for (i = 0; i < qtd->n_desc; i++) {
- dma_desc = &qh->desc_list[n_desc];
- n_bytes = qh->n_bytes[n_desc];
- failed =
- update_non_isoc_urb_state_ddma(hcd, hc, qtd,
- dma_desc,
- halt_status, n_bytes,
- &xfer_done);
- if (failed
- || (xfer_done
- && (urb->status != -DWC_E_IN_PROGRESS))) {
- hcd->fops->complete(hcd, urb->priv, urb,
- urb->status);
- dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- if (failed)
- goto stop_scan;
- } else if (qh->ep_type == UE_CONTROL) {
- if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) {
- if (urb->length > 0) {
- qtd->control_phase = DWC_OTG_CONTROL_DATA;
- } else {
- qtd->control_phase = DWC_OTG_CONTROL_STATUS;
- }
- DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n");
- } else if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
- if (xfer_done) {
- qtd->control_phase = DWC_OTG_CONTROL_STATUS;
- DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n");
- } else if (i + 1 == qtd->n_desc) {
- /*
- * Last descriptor for Control data stage which is
- * not completed yet.
- */
- dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- }
- }
- }
- n_desc++;
- }
- }
- stop_scan:
- if (qh->ep_type != UE_CONTROL) {
- /*
- * Resetting the data toggle for bulk
- * and interrupt endpoints in case of stall. See handle_hc_stall_intr()
- */
- if (halt_status == DWC_OTG_HC_XFER_STALL)
- qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- else
- dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- }
- if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
- hcint_data_t hcint;
- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- if (hcint.b.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;
- clear_hc_int(hc_regs, nyet);
- }
- }
- }
- /**
- * This function is called from interrupt handlers.
- * Scans the descriptor list, updates URB's status and
- * calls completion routine for the URB if it's done.
- * 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.
- *
- * @param hcd The HCD state structure for the DWC OTG controller.
- * @param hc Host channel, the transfer is completed on.
- * @param hc_regs Host channel registers.
- * @param halt_status Reason the channel is being halted,
- * or just XferComplete for isochronous transfer
- */
- void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- dwc_otg_halt_status_e halt_status)
- {
- uint8_t continue_isoc_xfer = 0;
- dwc_otg_transaction_type_e tr_type;
- dwc_otg_qh_t *qh = hc->qh;
- if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
- /* Release the channel if halted or session completed */
- if (halt_status != DWC_OTG_HC_XFER_COMPLETE ||
- DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- /* Halt the channel if session completed */
- if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
- dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
- }
- release_channel_ddma(hcd, qh);
- dwc_otg_hcd_qh_remove(hcd, qh);
- } else {
- /* Keep in assigned schedule to continue transfer */
- DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
- &qh->qh_list_entry);
- 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 */
- complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
- release_channel_ddma(hcd, qh);
- dwc_otg_hcd_qh_remove(hcd, qh);
- if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- /* Add back to inactive non-periodic schedule on normal completion */
- dwc_otg_hcd_qh_add(hcd, qh);
- }
- }
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) {
- if (continue_isoc_xfer) {
- if (tr_type == DWC_OTG_TRANSACTION_NONE) {
- tr_type = DWC_OTG_TRANSACTION_PERIODIC;
- } else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) {
- tr_type = DWC_OTG_TRANSACTION_ALL;
- }
- }
- dwc_otg_hcd_queue_transactions(hcd, tr_type);
- }
- }
- #endif /* DWC_DEVICE_ONLY */
|