| /* ========================================================================== |
| * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $ |
| * $Revision: #99 $ |
| * $Date: 2011/10/24 $ |
| * $Change: 1871160 $ |
| * |
| * 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_HOST_ONLY |
| |
| /** @file |
| * This file implements PCD Core. All code in this file is portable and doesn't |
| * use any OS specific functions. |
| * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code> |
| * header file, which can be used to implement OS specific PCD interface. |
| * |
| * An important function of the PCD is managing interrupts generated |
| * by the DWC_otg controller. The implementation of the DWC_otg device |
| * mode interrupt service routines is in dwc_otg_pcd_intr.c. |
| * |
| * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). |
| * @todo Does it work when the request size is greater than DEPTSIZ |
| * transfer size |
| * |
| */ |
| |
| #include "dwc_otg_pcd.h" |
| |
| #ifdef DWC_UTE_CFI |
| #include "dwc_otg_cfi.h" |
| |
| extern int init_cfi(cfiobject_t * cfiobj); |
| #endif |
| |
| /** |
| * Choose endpoint from ep arrays using usb_ep structure. |
| */ |
| static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle) |
| { |
| int i; |
| if (pcd->ep0.priv == handle) { |
| return &pcd->ep0; |
| } |
| for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) { |
| if (pcd->in_ep[i].priv == handle) |
| return &pcd->in_ep[i]; |
| if (pcd->out_ep[i].priv == handle) |
| return &pcd->out_ep[i]; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * This function completes a request. It call's the request call back. |
| */ |
| void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req, |
| int32_t status) |
| { |
| unsigned stopped = ep->stopped; |
| |
| DWC_DEBUGPL(DBG_PCDV, "%s(ep %p req %p)\n", __func__, ep, req); |
| DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry); |
| |
| /* don't modify queue heads during completion callback */ |
| ep->stopped = 1; |
| /* spin_unlock/spin_lock now done in fops->complete() */ |
| ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status, |
| req->actual); |
| |
| if (ep->pcd->request_pending > 0) { |
| --ep->pcd->request_pending; |
| } |
| |
| ep->stopped = stopped; |
| DWC_FREE(req); |
| } |
| |
| /** |
| * This function terminates all the requsts in the EP request queue. |
| */ |
| void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep) |
| { |
| dwc_otg_pcd_request_t *req; |
| |
| ep->stopped = 1; |
| |
| /* called with irqs blocked?? */ |
| while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN); |
| } |
| } |
| |
| void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd, |
| const struct dwc_otg_pcd_function_ops *fops) |
| { |
| pcd->fops = fops; |
| } |
| |
| /** |
| * PCD Callback function for initializing the PCD when switching to |
| * device mode. |
| * |
| * @param p void pointer to the <code>dwc_otg_pcd_t</code> |
| */ |
| static int32_t dwc_otg_pcd_start_cb(void *p) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| |
| /* |
| * Initialized the Core for Device mode. |
| */ |
| if (dwc_otg_is_device_mode(core_if)) { |
| dwc_otg_core_dev_init(core_if); |
| /* Set core_if's lock pointer to the pcd->lock */ |
| core_if->lock = pcd->lock; |
| } |
| return 1; |
| } |
| |
| /** CFI-specific buffer allocation function for EP */ |
| #ifdef DWC_UTE_CFI |
| uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, |
| size_t buflen, int flags) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| ep = get_ep_from_handle(pcd, pep); |
| if (!ep) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen, |
| flags); |
| } |
| #else |
| uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, |
| size_t buflen, int flags); |
| #endif |
| |
| /** |
| * PCD Callback function for notifying the PCD when resuming from |
| * suspend. |
| * |
| * @param p void pointer to the <code>dwc_otg_pcd_t</code> |
| */ |
| static int32_t dwc_otg_pcd_resume_cb(void *p) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; |
| |
| if (pcd->fops->resume) { |
| pcd->fops->resume(pcd); |
| } |
| |
| /* Stop the SRP timeout timer. */ |
| if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) |
| || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) { |
| if (GET_CORE_IF(pcd)->srp_timer_started) { |
| GET_CORE_IF(pcd)->srp_timer_started = 0; |
| DWC_TIMER_CANCEL(GET_CORE_IF(pcd)->srp_timer); |
| } |
| } |
| return 1; |
| } |
| |
| /** |
| * PCD Callback function for notifying the PCD device is suspended. |
| * |
| * @param p void pointer to the <code>dwc_otg_pcd_t</code> |
| */ |
| static int32_t dwc_otg_pcd_suspend_cb(void *p) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; |
| |
| if (pcd->fops->suspend) { |
| DWC_SPINUNLOCK(pcd->lock); |
| pcd->fops->suspend(pcd); |
| DWC_SPINLOCK(pcd->lock); |
| } |
| |
| return 1; |
| } |
| |
| /** |
| * PCD Callback function for stopping the PCD when switching to Host |
| * mode. |
| * |
| * @param p void pointer to the <code>dwc_otg_pcd_t</code> |
| */ |
| static int32_t dwc_otg_pcd_stop_cb(void *p) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; |
| extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd); |
| |
| dwc_otg_pcd_stop(pcd); |
| return 1; |
| } |
| |
| /** |
| * PCD Callback structure for handling mode switching. |
| */ |
| static dwc_otg_cil_callbacks_t pcd_callbacks = { |
| .start = dwc_otg_pcd_start_cb, |
| .stop = dwc_otg_pcd_stop_cb, |
| .suspend = dwc_otg_pcd_suspend_cb, |
| .resume_wakeup = dwc_otg_pcd_resume_cb, |
| .p = 0, /* Set at registration */ |
| }; |
| |
| /** |
| * This function allocates a DMA Descriptor chain for the Endpoint |
| * buffer to be used for a transfer to/from the specified endpoint. |
| */ |
| dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(dwc_dma_t * dma_desc_addr, |
| uint32_t count) |
| { |
| return DWC_DMA_ALLOC_ATOMIC(count * sizeof(dwc_otg_dev_dma_desc_t), |
| dma_desc_addr); |
| } |
| |
| /** |
| * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. |
| */ |
| void dwc_otg_ep_free_desc_chain(dwc_otg_dev_dma_desc_t * desc_addr, |
| uint32_t dma_desc_addr, uint32_t count) |
| { |
| DWC_DMA_FREE(count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr, |
| dma_desc_addr); |
| } |
| |
| #ifdef DWC_EN_ISOC |
| |
| /** |
| * This function initializes a descriptor chain for Isochronous transfer |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dwc_ep The EP to start the transfer on. |
| * |
| */ |
| void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if, |
| dwc_ep_t * dwc_ep) |
| { |
| |
| dsts_data_t dsts = {.d32 = 0 }; |
| depctl_data_t depctl = {.d32 = 0 }; |
| volatile uint32_t *addr; |
| int i, j; |
| uint32_t len; |
| |
| if (dwc_ep->is_in) |
| dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval; |
| else |
| dwc_ep->desc_cnt = |
| dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / |
| dwc_ep->bInterval; |
| |
| /** Allocate descriptors for double buffering */ |
| dwc_ep->iso_desc_addr = |
| dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr, |
| dwc_ep->desc_cnt * 2); |
| if (dwc_ep->desc_addr) { |
| DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__); |
| return; |
| } |
| |
| dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); |
| |
| /** ISO OUT EP */ |
| if (dwc_ep->is_in == 0) { |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; |
| dma_addr_t dma_ad; |
| uint32_t data_per_desc; |
| dwc_otg_dev_out_ep_regs_t *out_regs = |
| core_if->dev_if->out_ep_regs[dwc_ep->num]; |
| int offset; |
| |
| addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; |
| dma_ad = (dma_addr_t) DWC_READ_REG32(&(out_regs->doepdma)); |
| |
| /** Buffer 0 descriptors setup */ |
| dma_ad = dwc_ep->dma_addr0; |
| |
| sts.b_iso_out.bs = BS_HOST_READY; |
| sts.b_iso_out.rxsts = 0; |
| sts.b_iso_out.l = 0; |
| sts.b_iso_out.sp = 0; |
| sts.b_iso_out.ioc = 0; |
| sts.b_iso_out.pid = 0; |
| sts.b_iso_out.framenum = 0; |
| |
| offset = 0; |
| for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; |
| i += dwc_ep->pkt_per_frm) { |
| |
| for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { |
| uint32_t len = (j + 1) * dwc_ep->maxpacket; |
| if (len > dwc_ep->data_per_frame) |
| data_per_desc = |
| dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket; |
| else |
| data_per_desc = dwc_ep->maxpacket; |
| len = data_per_desc % 4; |
| if (len) |
| data_per_desc += 4 - len; |
| |
| sts.b_iso_out.rxbytes = data_per_desc; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| offset += data_per_desc; |
| dma_desc++; |
| dma_ad += data_per_desc; |
| } |
| } |
| |
| for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { |
| uint32_t len = (j + 1) * dwc_ep->maxpacket; |
| if (len > dwc_ep->data_per_frame) |
| data_per_desc = |
| dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket; |
| else |
| data_per_desc = dwc_ep->maxpacket; |
| len = data_per_desc % 4; |
| if (len) |
| data_per_desc += 4 - len; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| offset += data_per_desc; |
| dma_desc++; |
| dma_ad += data_per_desc; |
| } |
| |
| sts.b_iso_out.ioc = 1; |
| len = (j + 1) * dwc_ep->maxpacket; |
| if (len > dwc_ep->data_per_frame) |
| data_per_desc = |
| dwc_ep->data_per_frame - j * dwc_ep->maxpacket; |
| else |
| data_per_desc = dwc_ep->maxpacket; |
| len = data_per_desc % 4; |
| if (len) |
| data_per_desc += 4 - len; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| dma_desc++; |
| |
| /** Buffer 1 descriptors setup */ |
| sts.b_iso_out.ioc = 0; |
| dma_ad = dwc_ep->dma_addr1; |
| |
| offset = 0; |
| for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; |
| i += dwc_ep->pkt_per_frm) { |
| for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { |
| uint32_t len = (j + 1) * dwc_ep->maxpacket; |
| if (len > dwc_ep->data_per_frame) |
| data_per_desc = |
| dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket; |
| else |
| data_per_desc = dwc_ep->maxpacket; |
| len = data_per_desc % 4; |
| if (len) |
| data_per_desc += 4 - len; |
| |
| data_per_desc = |
| sts.b_iso_out.rxbytes = data_per_desc; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| offset += data_per_desc; |
| dma_desc++; |
| dma_ad += data_per_desc; |
| } |
| } |
| for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| offset += data_per_desc; |
| dma_desc++; |
| dma_ad += data_per_desc; |
| } |
| |
| sts.b_iso_out.ioc = 1; |
| sts.b_iso_out.l = 1; |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| dwc_ep->next_frame = 0; |
| |
| /** Write dma_ad into DOEPDMA register */ |
| DWC_WRITE_REG32(&(out_regs->doepdma), |
| (uint32_t) dwc_ep->iso_dma_desc_addr); |
| |
| } |
| /** ISO IN EP */ |
| else { |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; |
| dma_addr_t dma_ad; |
| dwc_otg_dev_in_ep_regs_t *in_regs = |
| core_if->dev_if->in_ep_regs[dwc_ep->num]; |
| unsigned int frmnumber; |
| fifosize_data_t txfifosize, rxfifosize; |
| |
| txfifosize.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| in_ep_regs[dwc_ep->num]->dtxfsts); |
| rxfifosize.d32 = |
| DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); |
| |
| addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; |
| |
| dma_ad = dwc_ep->dma_addr0; |
| |
| dsts.d32 = |
| DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); |
| |
| sts.b_iso_in.bs = BS_HOST_READY; |
| sts.b_iso_in.txsts = 0; |
| sts.b_iso_in.sp = |
| (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0; |
| sts.b_iso_in.ioc = 0; |
| sts.b_iso_in.pid = dwc_ep->pkt_per_frm; |
| |
| frmnumber = dwc_ep->next_frame; |
| |
| sts.b_iso_in.framenum = frmnumber; |
| sts.b_iso_in.txbytes = dwc_ep->data_per_frame; |
| sts.b_iso_in.l = 0; |
| |
| /** Buffer 0 descriptors setup */ |
| for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| dma_desc++; |
| |
| dma_ad += dwc_ep->data_per_frame; |
| sts.b_iso_in.framenum += dwc_ep->bInterval; |
| } |
| |
| sts.b_iso_in.ioc = 1; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| ++dma_desc; |
| |
| /** Buffer 1 descriptors setup */ |
| sts.b_iso_in.ioc = 0; |
| dma_ad = dwc_ep->dma_addr1; |
| |
| for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; |
| i += dwc_ep->pkt_per_frm) { |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| dma_desc++; |
| |
| dma_ad += dwc_ep->data_per_frame; |
| sts.b_iso_in.framenum += dwc_ep->bInterval; |
| |
| sts.b_iso_in.ioc = 0; |
| } |
| sts.b_iso_in.ioc = 1; |
| sts.b_iso_in.l = 1; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval; |
| |
| /** Write dma_ad into diepdma register */ |
| DWC_WRITE_REG32(&(in_regs->diepdma), |
| (uint32_t) dwc_ep->iso_dma_desc_addr); |
| } |
| /** Enable endpoint, clear nak */ |
| depctl.d32 = 0; |
| depctl.b.epena = 1; |
| depctl.b.usbactep = 1; |
| depctl.b.cnak = 1; |
| |
| DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); |
| depctl.d32 = DWC_READ_REG32(addr); |
| } |
| |
| /** |
| * This function initializes a descriptor chain for Isochronous transfer |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| * |
| */ |
| void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if, |
| dwc_ep_t * ep) |
| { |
| depctl_data_t depctl = {.d32 = 0 }; |
| volatile uint32_t *addr; |
| |
| if (ep->is_in) { |
| addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; |
| } else { |
| addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; |
| } |
| |
| if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) { |
| return; |
| } else { |
| deptsiz_data_t deptsiz = {.d32 = 0 }; |
| |
| ep->xfer_len = |
| ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval; |
| ep->pkt_cnt = |
| (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; |
| ep->xfer_count = 0; |
| ep->xfer_buff = |
| (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; |
| ep->dma_addr = |
| (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; |
| |
| if (ep->is_in) { |
| /* Program the transfer size and packet count |
| * as follows: xfersize = N * maxpacket + |
| * short_packet pktcnt = N + (short_packet |
| * exist ? 1 : 0) |
| */ |
| deptsiz.b.mc = ep->pkt_per_frm; |
| deptsiz.b.xfersize = ep->xfer_len; |
| deptsiz.b.pktcnt = |
| (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; |
| DWC_WRITE_REG32(&core_if->dev_if-> |
| in_ep_regs[ep->num]->dieptsiz, |
| deptsiz.d32); |
| |
| /* Write the DMA register */ |
| DWC_WRITE_REG32(& |
| (core_if->dev_if-> |
| in_ep_regs[ep->num]->diepdma), |
| (uint32_t) ep->dma_addr); |
| |
| } else { |
| deptsiz.b.pktcnt = |
| (ep->xfer_len + (ep->maxpacket - 1)) / |
| ep->maxpacket; |
| deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; |
| |
| DWC_WRITE_REG32(&core_if->dev_if-> |
| out_ep_regs[ep->num]->doeptsiz, |
| deptsiz.d32); |
| |
| /* Write the DMA register */ |
| DWC_WRITE_REG32(& |
| (core_if->dev_if-> |
| out_ep_regs[ep->num]->doepdma), |
| (uint32_t) ep->dma_addr); |
| |
| } |
| /** Enable endpoint, clear nak */ |
| depctl.d32 = 0; |
| depctl.b.epena = 1; |
| depctl.b.cnak = 1; |
| |
| DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); |
| } |
| } |
| |
| /** |
| * This function does the setup for a data transfer for an EP and |
| * starts the transfer. For an IN transfer, the packets will be |
| * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, |
| * the packets are unloaded from the Rx FIFO in the ISR. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| */ |
| |
| static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if, |
| dwc_ep_t * ep) |
| { |
| if (core_if->dma_enable) { |
| if (core_if->dma_desc_enable) { |
| if (ep->is_in) { |
| ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm; |
| } else { |
| ep->desc_cnt = ep->pkt_cnt; |
| } |
| dwc_otg_iso_ep_start_ddma_transfer(core_if, ep); |
| } else { |
| if (core_if->pti_enh_enable) { |
| dwc_otg_iso_ep_start_buf_transfer(core_if, ep); |
| } else { |
| ep->cur_pkt_addr = |
| (ep->proc_buf_num) ? ep-> |
| xfer_buff1 : ep->xfer_buff0; |
| ep->cur_pkt_dma_addr = |
| (ep->proc_buf_num) ? ep-> |
| dma_addr1 : ep->dma_addr0; |
| dwc_otg_iso_ep_start_frm_transfer(core_if, ep); |
| } |
| } |
| } else { |
| ep->cur_pkt_addr = |
| (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; |
| ep->cur_pkt_dma_addr = |
| (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; |
| dwc_otg_iso_ep_start_frm_transfer(core_if, ep); |
| } |
| } |
| |
| /** |
| * This function stops transfer for an EP and |
| * resets the ep's variables. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| */ |
| |
| void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl = {.d32 = 0 }; |
| volatile uint32_t *addr; |
| |
| if (ep->is_in == 1) { |
| addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; |
| } else { |
| addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; |
| } |
| |
| /* disable the ep */ |
| depctl.d32 = DWC_READ_REG32(addr); |
| |
| depctl.b.epdis = 1; |
| depctl.b.snak = 1; |
| |
| DWC_WRITE_REG32(addr, depctl.d32); |
| |
| if (core_if->dma_desc_enable && |
| ep->iso_desc_addr && ep->iso_dma_desc_addr) { |
| dwc_otg_ep_free_desc_chain(ep->iso_desc_addr, |
| ep->iso_dma_desc_addr, |
| ep->desc_cnt * 2); |
| } |
| |
| /* reset varibales */ |
| ep->dma_addr0 = 0; |
| ep->dma_addr1 = 0; |
| ep->xfer_buff0 = 0; |
| ep->xfer_buff1 = 0; |
| ep->data_per_frame = 0; |
| ep->data_pattern_frame = 0; |
| ep->sync_frame = 0; |
| ep->buf_proc_intrvl = 0; |
| ep->bInterval = 0; |
| ep->proc_buf_num = 0; |
| ep->pkt_per_frm = 0; |
| ep->pkt_per_frm = 0; |
| ep->desc_cnt = 0; |
| ep->iso_desc_addr = 0; |
| ep->iso_dma_desc_addr = 0; |
| } |
| |
| int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle, |
| uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0, |
| dwc_dma_t dma1, int sync_frame, int dp_frame, |
| int data_per_frame, int start_frame, |
| int buf_proc_intrvl, void *req_handle, |
| int atomic_alloc) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_irqflags_t flags = 0; |
| dwc_ep_t *dwc_ep; |
| int32_t frm_data; |
| dsts_data_t dsts; |
| dwc_otg_core_if_t *core_if; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| |
| if (!ep || !ep->desc || ep->dwc_ep.num == 0) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| core_if = GET_CORE_IF(pcd); |
| dwc_ep = &ep->dwc_ep; |
| |
| if (ep->iso_req_handle) { |
| DWC_WARN("ISO request in progress\n"); |
| } |
| |
| dwc_ep->dma_addr0 = dma0; |
| dwc_ep->dma_addr1 = dma1; |
| |
| dwc_ep->xfer_buff0 = buf0; |
| dwc_ep->xfer_buff1 = buf1; |
| |
| dwc_ep->data_per_frame = data_per_frame; |
| |
| /** @todo - pattern data support is to be implemented in the future */ |
| dwc_ep->data_pattern_frame = dp_frame; |
| dwc_ep->sync_frame = sync_frame; |
| |
| dwc_ep->buf_proc_intrvl = buf_proc_intrvl; |
| |
| dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1); |
| |
| dwc_ep->proc_buf_num = 0; |
| |
| dwc_ep->pkt_per_frm = 0; |
| frm_data = ep->dwc_ep.data_per_frame; |
| while (frm_data > 0) { |
| dwc_ep->pkt_per_frm++; |
| frm_data -= ep->dwc_ep.maxpacket; |
| } |
| |
| dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); |
| |
| if (start_frame == -1) { |
| dwc_ep->next_frame = dsts.b.soffn + 1; |
| if (dwc_ep->bInterval != 1) { |
| dwc_ep->next_frame = |
| dwc_ep->next_frame + (dwc_ep->bInterval - 1 - |
| dwc_ep->next_frame % |
| dwc_ep->bInterval); |
| } |
| } else { |
| dwc_ep->next_frame = start_frame; |
| } |
| |
| if (!core_if->pti_enh_enable) { |
| dwc_ep->pkt_cnt = |
| dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / |
| dwc_ep->bInterval; |
| } else { |
| dwc_ep->pkt_cnt = |
| (dwc_ep->data_per_frame * |
| (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval) |
| - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket; |
| } |
| |
| if (core_if->dma_desc_enable) { |
| dwc_ep->desc_cnt = |
| dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / |
| dwc_ep->bInterval; |
| } |
| |
| if (atomic_alloc) { |
| dwc_ep->pkt_info = |
| DWC_ALLOC_ATOMIC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); |
| } else { |
| dwc_ep->pkt_info = |
| DWC_ALLOC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); |
| } |
| if (!dwc_ep->pkt_info) { |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| return -DWC_E_NO_MEMORY; |
| } |
| if (core_if->pti_enh_enable) { |
| dwc_memset(dwc_ep->pkt_info, 0, |
| sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); |
| } |
| |
| dwc_ep->cur_pkt = 0; |
| ep->iso_req_handle = req_handle; |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| dwc_otg_iso_ep_start_transfer(core_if, dwc_ep); |
| return 0; |
| } |
| |
| int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle, |
| void *req_handle) |
| { |
| dwc_irqflags_t flags = 0; |
| dwc_otg_pcd_ep_t *ep; |
| dwc_ep_t *dwc_ep; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep || !ep->desc || ep->dwc_ep.num == 0) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| dwc_ep = &ep->dwc_ep; |
| |
| dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep); |
| |
| DWC_FREE(dwc_ep->pkt_info); |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| if (ep->iso_req_handle != req_handle) { |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| return -DWC_E_INVALID; |
| } |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| ep->iso_req_handle = 0; |
| return 0; |
| } |
| |
| /** |
| * This function is used for perodical data exchnage between PCD and gadget drivers. |
| * for Isochronous EPs |
| * |
| * - Every time a sync period completes this function is called to |
| * perform data exchange between PCD and gadget |
| */ |
| void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep, |
| void *req_handle) |
| { |
| int i; |
| dwc_ep_t *dwc_ep; |
| |
| dwc_ep = &ep->dwc_ep; |
| |
| DWC_SPINUNLOCK(ep->pcd->lock); |
| pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle, |
| dwc_ep->proc_buf_num ^ 0x1); |
| DWC_SPINLOCK(ep->pcd->lock); |
| |
| for (i = 0; i < dwc_ep->pkt_cnt; ++i) { |
| dwc_ep->pkt_info[i].status = 0; |
| dwc_ep->pkt_info[i].offset = 0; |
| dwc_ep->pkt_info[i].length = 0; |
| } |
| } |
| |
| int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle, |
| void *iso_req_handle) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_ep_t *dwc_ep; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep->desc || ep->dwc_ep.num == 0) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| dwc_ep = &ep->dwc_ep; |
| |
| return dwc_ep->pkt_cnt; |
| } |
| |
| void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle, |
| void *iso_req_handle, int packet, |
| int *status, int *actual, int *offset) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_ep_t *dwc_ep; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep) |
| DWC_WARN("bad ep\n"); |
| |
| dwc_ep = &ep->dwc_ep; |
| |
| *status = dwc_ep->pkt_info[packet].status; |
| *actual = dwc_ep->pkt_info[packet].length; |
| *offset = dwc_ep->pkt_info[packet].offset; |
| } |
| |
| #endif /* DWC_EN_ISOC */ |
| |
| static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep, |
| uint32_t is_in, uint32_t ep_num) |
| { |
| /* Init EP structure */ |
| pcd_ep->desc = 0; |
| pcd_ep->pcd = pcd; |
| pcd_ep->stopped = 1; |
| pcd_ep->queue_sof = 0; |
| |
| /* Init DWC ep structure */ |
| pcd_ep->dwc_ep.is_in = is_in; |
| pcd_ep->dwc_ep.num = ep_num; |
| pcd_ep->dwc_ep.active = 0; |
| pcd_ep->dwc_ep.tx_fifo_num = 0; |
| /* Control until ep is actvated */ |
| pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; |
| pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; |
| pcd_ep->dwc_ep.dma_addr = 0; |
| pcd_ep->dwc_ep.start_xfer_buff = 0; |
| pcd_ep->dwc_ep.xfer_buff = 0; |
| pcd_ep->dwc_ep.xfer_len = 0; |
| pcd_ep->dwc_ep.xfer_count = 0; |
| pcd_ep->dwc_ep.sent_zlp = 0; |
| pcd_ep->dwc_ep.total_len = 0; |
| pcd_ep->dwc_ep.desc_addr = 0; |
| pcd_ep->dwc_ep.dma_desc_addr = 0; |
| DWC_CIRCLEQ_INIT(&pcd_ep->queue); |
| } |
| |
| /** |
| * Initialize ep's |
| */ |
| static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd) |
| { |
| int i; |
| uint32_t hwcfg1; |
| dwc_otg_pcd_ep_t *ep; |
| int in_ep_cntr, out_ep_cntr; |
| uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps; |
| uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps; |
| |
| /** |
| * Initialize the EP0 structure. |
| */ |
| ep = &pcd->ep0; |
| dwc_otg_pcd_init_ep(pcd, ep, 0, 0); |
| |
| in_ep_cntr = 0; |
| hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3; |
| for (i = 1; in_ep_cntr < num_in_eps; i++) { |
| if ((hwcfg1 & 0x1) == 0) { |
| dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr]; |
| in_ep_cntr++; |
| /** |
| * @todo NGS: Add direction to EP, based on contents |
| * of HWCFG1. Need a copy of HWCFG1 in pcd structure? |
| * sprintf(";r |
| */ |
| dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i); |
| |
| DWC_CIRCLEQ_INIT(&ep->queue); |
| } |
| hwcfg1 >>= 2; |
| } |
| |
| out_ep_cntr = 0; |
| hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2; |
| for (i = 1; out_ep_cntr < num_out_eps; i++) { |
| if ((hwcfg1 & 0x1) == 0) { |
| dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr]; |
| out_ep_cntr++; |
| /** |
| * @todo NGS: Add direction to EP, based on contents |
| * of HWCFG1. Need a copy of HWCFG1 in pcd structure? |
| * sprintf(";r |
| */ |
| dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i); |
| DWC_CIRCLEQ_INIT(&ep->queue); |
| } |
| hwcfg1 >>= 2; |
| } |
| |
| pcd->ep0state = EP0_DISCONNECT; |
| pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE; |
| pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; |
| } |
| |
| /** |
| * This function is called when the SRP timer expires. The SRP should |
| * complete within 6 seconds. |
| */ |
| static void srp_timeout(void *ptr) |
| { |
| gotgctl_data_t gotgctl; |
| dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; |
| volatile uint32_t *addr = &core_if->core_global_regs->gotgctl; |
| |
| gotgctl.d32 = DWC_READ_REG32(addr); |
| |
| core_if->srp_timer_started = 0; |
| |
| if (core_if->adp_enable) { |
| if (gotgctl.b.bsesvld == 0) { |
| gpwrdn_data_t gpwrdn = {.d32 = 0 }; |
| DWC_PRINTF("SRP Timeout BSESSVLD = 0\n"); |
| /* Power off the core */ |
| if (core_if->power_down == 2) { |
| gpwrdn.b.pwrdnswtch = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs-> |
| gpwrdn, gpwrdn.d32, 0); |
| } |
| |
| gpwrdn.d32 = 0; |
| gpwrdn.b.pmuintsel = 1; |
| gpwrdn.b.pmuactv = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); |
| dwc_otg_adp_probe_start(core_if); |
| } else { |
| DWC_PRINTF("SRP Timeout BSESSVLD = 1\n"); |
| core_if->op_state = B_PERIPHERAL; |
| dwc_otg_core_init(core_if); |
| dwc_otg_enable_global_interrupts(core_if); |
| cil_pcd_start(core_if); |
| } |
| } |
| |
| if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && |
| (core_if->core_params->i2c_enable)) { |
| DWC_PRINTF("SRP Timeout\n"); |
| |
| if ((core_if->srp_success) && (gotgctl.b.bsesvld)) { |
| if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { |
| core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); |
| } |
| |
| /* Clear Session Request */ |
| gotgctl.d32 = 0; |
| gotgctl.b.sesreq = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, |
| gotgctl.d32, 0); |
| |
| core_if->srp_success = 0; |
| } else { |
| __DWC_ERROR("Device not connected/responding\n"); |
| gotgctl.b.sesreq = 0; |
| DWC_WRITE_REG32(addr, gotgctl.d32); |
| } |
| } else if (gotgctl.b.sesreq) { |
| DWC_PRINTF("SRP Timeout\n"); |
| |
| __DWC_ERROR("Device not connected/responding\n"); |
| gotgctl.b.sesreq = 0; |
| DWC_WRITE_REG32(addr, gotgctl.d32); |
| } else { |
| DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32); |
| } |
| } |
| |
| /** |
| * Tasklet |
| * |
| */ |
| extern void start_next_request(dwc_otg_pcd_ep_t * ep); |
| |
| static void start_xfer_tasklet_func(void *data) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| |
| int i; |
| depctl_data_t diepctl; |
| |
| DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n"); |
| |
| diepctl.d32 = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl); |
| |
| if (pcd->ep0.queue_sof) { |
| pcd->ep0.queue_sof = 0; |
| start_next_request(&pcd->ep0); |
| // break; |
| } |
| |
| for (i = 0; i < core_if->dev_if->num_in_eps; i++) { |
| depctl_data_t diepctl; |
| diepctl.d32 = |
| DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl); |
| |
| if (pcd->in_ep[i].queue_sof) { |
| pcd->in_ep[i].queue_sof = 0; |
| start_next_request(&pcd->in_ep[i]); |
| // break; |
| } |
| } |
| |
| return; |
| } |
| |
| /** |
| * This function initialized the PCD portion of the driver. |
| * |
| */ |
| dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if) |
| { |
| dwc_otg_pcd_t *pcd = NULL; |
| dwc_otg_dev_if_t *dev_if; |
| int i; |
| |
| /* |
| * Allocate PCD structure |
| */ |
| pcd = DWC_ALLOC(sizeof(dwc_otg_pcd_t)); |
| |
| if (pcd == NULL) { |
| return NULL; |
| } |
| |
| pcd->lock = DWC_SPINLOCK_ALLOC(); |
| if (!pcd->lock) { |
| DWC_ERROR("Could not allocate lock for pcd"); |
| DWC_FREE(pcd); |
| return NULL; |
| } |
| /* Set core_if's lock pointer to hcd->lock */ |
| core_if->lock = pcd->lock; |
| pcd->core_if = core_if; |
| |
| dev_if = core_if->dev_if; |
| dev_if->isoc_ep = NULL; |
| |
| if (core_if->hwcfg4.b.ded_fifo_en) { |
| DWC_PRINTF("Dedicated Tx FIFOs mode\n"); |
| } else { |
| DWC_PRINTF("Shared Tx FIFO mode\n"); |
| } |
| |
| /* |
| * Initialized the Core for Device mode here if there is nod ADP support. |
| * Otherwise it will be done later in dwc_otg_adp_start routine. |
| */ |
| if (dwc_otg_is_device_mode(core_if) /*&& !core_if->adp_enable*/) { |
| dwc_otg_core_dev_init(core_if); |
| } |
| |
| /* |
| * Register the PCD Callbacks. |
| */ |
| dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd); |
| |
| /* |
| * Initialize the DMA buffer for SETUP packets |
| */ |
| if (GET_CORE_IF(pcd)->dma_enable) { |
| pcd->setup_pkt = |
| DWC_DMA_ALLOC(sizeof(*pcd->setup_pkt) * 5, |
| &pcd->setup_pkt_dma_handle); |
| if (pcd->setup_pkt == NULL) { |
| DWC_FREE(pcd); |
| return NULL; |
| } |
| |
| pcd->status_buf = |
| DWC_DMA_ALLOC(sizeof(uint16_t), |
| &pcd->status_buf_dma_handle); |
| if (pcd->status_buf == NULL) { |
| DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, |
| pcd->setup_pkt, pcd->setup_pkt_dma_handle); |
| DWC_FREE(pcd); |
| return NULL; |
| } |
| |
| if (GET_CORE_IF(pcd)->dma_desc_enable) { |
| dev_if->setup_desc_addr[0] = |
| dwc_otg_ep_alloc_desc_chain(&dev_if-> |
| dma_setup_desc_addr[0], |
| 1); |
| dev_if->setup_desc_addr[1] = |
| dwc_otg_ep_alloc_desc_chain(&dev_if-> |
| dma_setup_desc_addr[1], |
| 1); |
| dev_if->in_desc_addr = |
| dwc_otg_ep_alloc_desc_chain(&dev_if-> |
| dma_in_desc_addr, 1); |
| dev_if->out_desc_addr = |
| dwc_otg_ep_alloc_desc_chain(&dev_if-> |
| dma_out_desc_addr, 1); |
| |
| if (dev_if->setup_desc_addr[0] == 0 |
| || dev_if->setup_desc_addr[1] == 0 |
| || dev_if->in_desc_addr == 0 |
| || dev_if->out_desc_addr == 0) { |
| |
| if (dev_if->out_desc_addr) |
| dwc_otg_ep_free_desc_chain(dev_if-> |
| out_desc_addr, |
| dev_if-> |
| dma_out_desc_addr, |
| 1); |
| if (dev_if->in_desc_addr) |
| dwc_otg_ep_free_desc_chain(dev_if-> |
| in_desc_addr, |
| dev_if-> |
| dma_in_desc_addr, |
| 1); |
| if (dev_if->setup_desc_addr[1]) |
| dwc_otg_ep_free_desc_chain(dev_if-> |
| setup_desc_addr |
| [1], |
| dev_if-> |
| dma_setup_desc_addr |
| [1], 1); |
| if (dev_if->setup_desc_addr[0]) |
| dwc_otg_ep_free_desc_chain(dev_if-> |
| setup_desc_addr |
| [0], |
| dev_if-> |
| dma_setup_desc_addr |
| [0], 1); |
| |
| DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, |
| pcd->setup_pkt, |
| pcd->setup_pkt_dma_handle); |
| DWC_DMA_FREE(sizeof(*pcd->status_buf), |
| pcd->status_buf, |
| pcd->status_buf_dma_handle); |
| |
| DWC_FREE(pcd); |
| |
| return NULL; |
| } |
| } |
| } else { |
| pcd->setup_pkt = DWC_ALLOC(sizeof(*pcd->setup_pkt) * 5); |
| if (pcd->setup_pkt == NULL) { |
| DWC_FREE(pcd); |
| return NULL; |
| } |
| |
| pcd->status_buf = DWC_ALLOC(sizeof(uint16_t)); |
| if (pcd->status_buf == NULL) { |
| DWC_FREE(pcd->setup_pkt); |
| DWC_FREE(pcd); |
| return NULL; |
| } |
| } |
| |
| dwc_otg_pcd_reinit(pcd); |
| |
| /* Allocate the cfi object for the PCD */ |
| #ifdef DWC_UTE_CFI |
| pcd->cfi = DWC_ALLOC(sizeof(cfiobject_t)); |
| if (NULL == pcd->cfi) |
| goto fail; |
| if (init_cfi(pcd->cfi)) { |
| CFI_INFO("%s: Failed to init the CFI object\n", __func__); |
| goto fail; |
| } |
| #endif |
| |
| /* Initialize tasklets */ |
| pcd->start_xfer_tasklet = DWC_TASK_ALLOC("xfer_tasklet", |
| start_xfer_tasklet_func, pcd); |
| pcd->test_mode_tasklet = DWC_TASK_ALLOC("test_mode_tasklet", |
| do_test_mode, pcd); |
| |
| /* Initialize SRP timer */ |
| core_if->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if); |
| |
| if (core_if->core_params->dev_out_nak) { |
| /** |
| * Initialize xfer timeout timer. Implemented for |
| * 2.93a feature "Device DDMA OUT NAK Enhancement" |
| */ |
| for(i = 0; i < MAX_EPS_CHANNELS; i++) { |
| pcd->core_if->ep_xfer_timer[i] = |
| DWC_TIMER_ALLOC("ep timer", ep_xfer_timeout, |
| &pcd->core_if->ep_xfer_info[i]); |
| } |
| } |
| |
| return pcd; |
| #ifdef DWC_UTE_CFI |
| fail: |
| #endif |
| if (pcd->setup_pkt) |
| DWC_FREE(pcd->setup_pkt); |
| if (pcd->status_buf) |
| DWC_FREE(pcd->status_buf); |
| #ifdef DWC_UTE_CFI |
| if (pcd->cfi) |
| DWC_FREE(pcd->cfi); |
| #endif |
| if (pcd) |
| DWC_FREE(pcd); |
| return NULL; |
| |
| } |
| |
| /** |
| * Remove PCD specific data |
| */ |
| void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; |
| int i; |
| if (pcd->core_if->core_params->dev_out_nak) { |
| for (i = 0; i < MAX_EPS_CHANNELS; i++) { |
| DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[i]); |
| pcd->core_if->ep_xfer_info[i].state = 0; |
| } |
| } |
| |
| if (GET_CORE_IF(pcd)->dma_enable) { |
| DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt, |
| pcd->setup_pkt_dma_handle); |
| DWC_DMA_FREE(sizeof(uint16_t), pcd->status_buf, |
| pcd->status_buf_dma_handle); |
| if (GET_CORE_IF(pcd)->dma_desc_enable) { |
| dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], |
| dev_if->dma_setup_desc_addr |
| [0], 1); |
| dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], |
| dev_if->dma_setup_desc_addr |
| [1], 1); |
| dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, |
| dev_if->dma_in_desc_addr, 1); |
| dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, |
| dev_if->dma_out_desc_addr, |
| 1); |
| } |
| } else { |
| DWC_FREE(pcd->setup_pkt); |
| DWC_FREE(pcd->status_buf); |
| } |
| DWC_SPINLOCK_FREE(pcd->lock); |
| /* Set core_if's lock pointer to NULL */ |
| pcd->core_if->lock = NULL; |
| |
| DWC_TASK_FREE(pcd->start_xfer_tasklet); |
| DWC_TASK_FREE(pcd->test_mode_tasklet); |
| if (pcd->core_if->core_params->dev_out_nak) { |
| for (i = 0; i < MAX_EPS_CHANNELS; i++) { |
| if (pcd->core_if->ep_xfer_timer[i]) { |
| DWC_TIMER_FREE(pcd->core_if->ep_xfer_timer[i]); |
| } |
| } |
| } |
| |
| /* Release the CFI object's dynamic memory */ |
| #ifdef DWC_UTE_CFI |
| if (pcd->cfi->ops.release) { |
| pcd->cfi->ops.release(pcd->cfi); |
| } |
| #endif |
| |
| DWC_FREE(pcd); |
| } |
| |
| /** |
| * Returns whether registered pcd is dual speed or not |
| */ |
| uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| |
| if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) || |
| ((core_if->hwcfg2.b.hs_phy_type == 2) && |
| (core_if->hwcfg2.b.fs_phy_type == 1) && |
| (core_if->core_params->ulpi_fs_ls))) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /** |
| * Returns whether registered pcd is OTG capable or not |
| */ |
| uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| gusbcfg_data_t usbcfg = {.d32 = 0 }; |
| |
| usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); |
| if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /** |
| * This function assigns periodic Tx FIFO to an periodic EP |
| * in shared Tx FIFO mode |
| */ |
| static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if) |
| { |
| uint32_t TxMsk = 1; |
| int i; |
| |
| for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) { |
| if ((TxMsk & core_if->tx_msk) == 0) { |
| core_if->tx_msk |= TxMsk; |
| return i + 1; |
| } |
| TxMsk <<= 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * This function assigns periodic Tx FIFO to an periodic EP |
| * in shared Tx FIFO mode |
| */ |
| static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if) |
| { |
| uint32_t PerTxMsk = 1; |
| int i; |
| for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) { |
| if ((PerTxMsk & core_if->p_tx_msk) == 0) { |
| core_if->p_tx_msk |= PerTxMsk; |
| return i + 1; |
| } |
| PerTxMsk <<= 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * This function releases periodic Tx FIFO |
| * in shared Tx FIFO mode |
| */ |
| static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if, |
| uint32_t fifo_num) |
| { |
| core_if->p_tx_msk = |
| (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk; |
| } |
| |
| /** |
| * This function releases periodic Tx FIFO |
| * in shared Tx FIFO mode |
| */ |
| static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num) |
| { |
| core_if->tx_msk = |
| (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk; |
| } |
| |
| /** |
| * This function is being called from gadget |
| * to enable PCD endpoint. |
| */ |
| int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd, |
| const uint8_t * ep_desc, void *usb_ep) |
| { |
| int num, dir; |
| dwc_otg_pcd_ep_t *ep = NULL; |
| const usb_endpoint_descriptor_t *desc; |
| dwc_irqflags_t flags; |
| fifosize_data_t dptxfsiz = {.d32 = 0 }; |
| gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; |
| gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 }; |
| int retval = 0; |
| int i, epcount; |
| |
| desc = (const usb_endpoint_descriptor_t *)ep_desc; |
| |
| if (!desc) { |
| pcd->ep0.priv = usb_ep; |
| ep = &pcd->ep0; |
| retval = -DWC_E_INVALID; |
| goto out; |
| } |
| |
| num = UE_GET_ADDR(desc->bEndpointAddress); |
| dir = UE_GET_DIR(desc->bEndpointAddress); |
| |
| if (!desc->wMaxPacketSize) { |
| DWC_WARN("bad maxpacketsize\n"); |
| retval = -DWC_E_INVALID; |
| goto out; |
| } |
| |
| if (dir == UE_DIR_IN) { |
| epcount = pcd->core_if->dev_if->num_in_eps; |
| for (i = 0; i < epcount; i++) { |
| if (num == pcd->in_ep[i].dwc_ep.num) { |
| ep = &pcd->in_ep[i]; |
| break; |
| } |
| } |
| } else { |
| epcount = pcd->core_if->dev_if->num_out_eps; |
| for (i = 0; i < epcount; i++) { |
| if (num == pcd->out_ep[i].dwc_ep.num) { |
| ep = &pcd->out_ep[i]; |
| break; |
| } |
| } |
| } |
| |
| if (!ep) { |
| DWC_WARN("bad address\n"); |
| retval = -DWC_E_INVALID; |
| goto out; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| |
| ep->desc = desc; |
| ep->priv = usb_ep; |
| |
| /* |
| * Activate the EP |
| */ |
| ep->stopped = 0; |
| |
| ep->dwc_ep.is_in = (dir == UE_DIR_IN); |
| ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize); |
| |
| ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE; |
| |
| if (ep->dwc_ep.is_in) { |
| if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) { |
| ep->dwc_ep.tx_fifo_num = 0; |
| |
| if (ep->dwc_ep.type == UE_ISOCHRONOUS) { |
| /* |
| * if ISOC EP then assign a Periodic Tx FIFO. |
| */ |
| ep->dwc_ep.tx_fifo_num = |
| assign_perio_tx_fifo(GET_CORE_IF(pcd)); |
| } |
| } else { |
| /* |
| * if Dedicated FIFOs mode is on then assign a Tx FIFO. |
| */ |
| ep->dwc_ep.tx_fifo_num = |
| assign_tx_fifo(GET_CORE_IF(pcd)); |
| } |
| |
| /* Calculating EP info controller base address */ |
| if (ep->dwc_ep.tx_fifo_num && GET_CORE_IF(pcd)->en_multiple_tx_fifo) { |
| gdfifocfg.d32 = |
| DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs-> |
| gdfifocfg); |
| gdfifocfgbase.d32 = gdfifocfg.d32 >> 16; |
| dptxfsiz.d32 = |
| (DWC_READ_REG32 |
| (&GET_CORE_IF(pcd)-> |
| core_global_regs->dtxfsiz[ep->dwc_ep. |
| tx_fifo_num-1]) >> 16); |
| gdfifocfg.b.epinfobase = |
| gdfifocfgbase.d32 + dptxfsiz.d32; |
| DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs-> |
| gdfifocfg, gdfifocfg.d32); |
| } |
| } |
| /* Set initial data PID. */ |
| if (ep->dwc_ep.type == UE_BULK) { |
| ep->dwc_ep.data_pid_start = 0; |
| } |
| |
| /* Alloc DMA Descriptors */ |
| if (GET_CORE_IF(pcd)->dma_desc_enable) { |
| #ifndef DWC_UTE_PER_IO |
| if (ep->dwc_ep.type != UE_ISOCHRONOUS) { |
| #endif |
| ep->dwc_ep.desc_addr = |
| dwc_otg_ep_alloc_desc_chain(&ep-> |
| dwc_ep.dma_desc_addr, |
| MAX_DMA_DESC_CNT); |
| if (!ep->dwc_ep.desc_addr) { |
| DWC_WARN("%s, can't allocate DMA descriptor\n", |
| __func__); |
| retval = -DWC_E_SHUTDOWN; |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| goto out; |
| } |
| #ifndef DWC_UTE_PER_IO |
| } |
| #endif |
| } |
| |
| DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n", |
| (ep->dwc_ep.is_in ? "IN" : "OUT"), |
| ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc); |
| #ifdef DWC_UTE_PER_IO |
| ep->dwc_ep.xiso_bInterval = 1 << (ep->desc->bInterval - 1); |
| #endif |
| if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { |
| ep->dwc_ep.bInterval = 1 << (ep->desc->bInterval - 1); |
| ep->dwc_ep.frame_num = 0xFFFFFFFF; |
| } |
| |
| dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); |
| |
| #ifdef DWC_UTE_CFI |
| if (pcd->cfi->ops.ep_enable) { |
| pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep); |
| } |
| #endif |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| out: |
| return retval; |
| } |
| |
| /** |
| * This function is being called from gadget |
| * to disable PCD endpoint. |
| */ |
| int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_irqflags_t flags; |
| dwc_otg_dev_dma_desc_t *desc_addr; |
| dwc_dma_t dma_desc_addr; |
| gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 }; |
| gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; |
| fifosize_data_t dptxfsiz = {.d32 = 0 }; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| |
| if (!ep || !ep->desc) { |
| DWC_DEBUGPL(DBG_PCD, "bad ep address\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| |
| dwc_otg_request_nuke(ep); |
| |
| dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep); |
| if (pcd->core_if->core_params->dev_out_nak) |
| { |
| DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[ep->dwc_ep.num]); |
| pcd->core_if->ep_xfer_info[ep->dwc_ep.num].state = 0; |
| } |
| ep->desc = NULL; |
| ep->stopped = 1; |
| |
| gdfifocfg.d32 = |
| DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg); |
| gdfifocfgbase.d32 = gdfifocfg.d32 >> 16; |
| |
| if (ep->dwc_ep.is_in) { |
| if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) { |
| /* Flush the Tx FIFO */ |
| dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); |
| } |
| release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); |
| release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); |
| if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) { |
| /* Decreasing EPinfo Base Addr */ |
| dptxfsiz.d32 = |
| (DWC_READ_REG32 |
| (&GET_CORE_IF(pcd)-> |
| core_global_regs->dtxfsiz[ep->dwc_ep.tx_fifo_num-1]) >> 16); |
| gdfifocfg.b.epinfobase = gdfifocfgbase.d32 - dptxfsiz.d32; |
| DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg, |
| gdfifocfg.d32); |
| } |
| } |
| |
| /* Free DMA Descriptors */ |
| if (GET_CORE_IF(pcd)->dma_desc_enable) { |
| if (ep->dwc_ep.type != UE_ISOCHRONOUS) { |
| desc_addr = ep->dwc_ep.desc_addr; |
| dma_desc_addr = ep->dwc_ep.dma_desc_addr; |
| |
| /* Cannot call dma_free_coherent() with IRQs disabled */ |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr, |
| MAX_DMA_DESC_CNT); |
| |
| goto out_unlocked; |
| } |
| } |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| out_unlocked: |
| DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num, |
| ep->dwc_ep.is_in ? "IN" : "OUT"); |
| return 0; |
| |
| } |
| |
| /******************************************************************************/ |
| #ifdef DWC_UTE_PER_IO |
| |
| /** |
| * Free the request and its extended parts |
| * |
| */ |
| void dwc_pcd_xiso_ereq_free(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req) |
| { |
| DWC_FREE(req->ext_req.per_io_frame_descs); |
| DWC_FREE(req); |
| } |
| |
| /** |
| * Start the next request in the endpoint's queue. |
| * |
| */ |
| int dwc_otg_pcd_xiso_start_next_request(dwc_otg_pcd_t * pcd, |
| dwc_otg_pcd_ep_t * ep) |
| { |
| int i; |
| dwc_otg_pcd_request_t *req = NULL; |
| dwc_ep_t *dwcep = NULL; |
| struct dwc_iso_xreq_port *ereq = NULL; |
| struct dwc_iso_pkt_desc_port *ddesc_iso; |
| uint16_t nat; |
| depctl_data_t diepctl; |
| |
| dwcep = &ep->dwc_ep; |
| |
| if (dwcep->xiso_active_xfers > 0) { |
| #if 0 //Disable this to decrease s/w overhead that is crucial for Isoc transfers |
| DWC_WARN("There are currently active transfers for EP%d \ |
| (active=%d; queued=%d)", dwcep->num, dwcep->xiso_active_xfers, |
| dwcep->xiso_queued_xfers); |
| #endif |
| return 0; |
| } |
| |
| nat = UGETW(ep->desc->wMaxPacketSize); |
| nat = (nat >> 11) & 0x03; |
| |
| if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| ereq = &req->ext_req; |
| ep->stopped = 0; |
| |
| /* Get the frame number */ |
| dwcep->xiso_frame_num = |
| dwc_otg_get_frame_number(GET_CORE_IF(pcd)); |
| DWC_DEBUG("FRM_NUM=%d", dwcep->xiso_frame_num); |
| |
| ddesc_iso = ereq->per_io_frame_descs; |
| |
| if (dwcep->is_in) { |
| /* Setup DMA Descriptor chain for IN Isoc request */ |
| for (i = 0; i < ereq->pio_pkt_count; i++) { |
| //if ((i % (nat + 1)) == 0) |
| if ( i > 0 ) |
| dwcep->xiso_frame_num = (dwcep->xiso_bInterval + |
| dwcep->xiso_frame_num) & 0x3FFF; |
| dwcep->desc_addr[i].buf = |
| req->dma + ddesc_iso[i].offset; |
| dwcep->desc_addr[i].status.b_iso_in.txbytes = |
| ddesc_iso[i].length; |
| dwcep->desc_addr[i].status.b_iso_in.framenum = |
| dwcep->xiso_frame_num; |
| dwcep->desc_addr[i].status.b_iso_in.bs = |
| BS_HOST_READY; |
| dwcep->desc_addr[i].status.b_iso_in.txsts = 0; |
| dwcep->desc_addr[i].status.b_iso_in.sp = |
| (ddesc_iso[i].length % |
| dwcep->maxpacket) ? 1 : 0; |
| dwcep->desc_addr[i].status.b_iso_in.ioc = 0; |
| dwcep->desc_addr[i].status.b_iso_in.pid = nat + 1; |
| dwcep->desc_addr[i].status.b_iso_in.l = 0; |
| |
| /* Process the last descriptor */ |
| if (i == ereq->pio_pkt_count - 1) { |
| dwcep->desc_addr[i].status.b_iso_in.ioc = 1; |
| dwcep->desc_addr[i].status.b_iso_in.l = 1; |
| } |
| } |
| |
| /* Setup and start the transfer for this endpoint */ |
| dwcep->xiso_active_xfers++; |
| DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| in_ep_regs[dwcep->num]->diepdma, |
| dwcep->dma_desc_addr); |
| diepctl.d32 = 0; |
| diepctl.b.epena = 1; |
| diepctl.b.cnak = 1; |
| DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| in_ep_regs[dwcep->num]->diepctl, 0, |
| diepctl.d32); |
| } else { |
| /* Setup DMA Descriptor chain for OUT Isoc request */ |
| for (i = 0; i < ereq->pio_pkt_count; i++) { |
| //if ((i % (nat + 1)) == 0) |
| dwcep->xiso_frame_num = (dwcep->xiso_bInterval + |
| dwcep->xiso_frame_num) & 0x3FFF; |
| dwcep->desc_addr[i].buf = |
| req->dma + ddesc_iso[i].offset; |
| dwcep->desc_addr[i].status.b_iso_out.rxbytes = |
| ddesc_iso[i].length; |
| dwcep->desc_addr[i].status.b_iso_out.framenum = |
| dwcep->xiso_frame_num; |
| dwcep->desc_addr[i].status.b_iso_out.bs = |
| BS_HOST_READY; |
| dwcep->desc_addr[i].status.b_iso_out.rxsts = 0; |
| dwcep->desc_addr[i].status.b_iso_out.sp = |
| (ddesc_iso[i].length % |
| dwcep->maxpacket) ? 1 : 0; |
| dwcep->desc_addr[i].status.b_iso_out.ioc = 0; |
| dwcep->desc_addr[i].status.b_iso_out.pid = nat + 1; |
| dwcep->desc_addr[i].status.b_iso_out.l = 0; |
| |
| /* Process the last descriptor */ |
| if (i == ereq->pio_pkt_count - 1) { |
| dwcep->desc_addr[i].status.b_iso_out.ioc = 1; |
| dwcep->desc_addr[i].status.b_iso_out.l = 1; |
| } |
| } |
| |
| /* Setup and start the transfer for this endpoint */ |
| dwcep->xiso_active_xfers++; |
| DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| out_ep_regs[dwcep->num]->doepdma, |
| dwcep->dma_desc_addr); |
| diepctl.d32 = 0; |
| diepctl.b.epena = 1; |
| diepctl.b.cnak = 1; |
| DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| out_ep_regs[dwcep->num]->doepctl, 0, |
| diepctl.d32); |
| } |
| |
| } else { |
| ep->stopped = 1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * - Remove the request from the queue |
| */ |
| void complete_xiso_ep(dwc_otg_pcd_ep_t * ep) |
| { |
| dwc_otg_pcd_request_t *req = NULL; |
| struct dwc_iso_xreq_port *ereq = NULL; |
| struct dwc_iso_pkt_desc_port *ddesc_iso = NULL; |
| dwc_ep_t *dwcep = NULL; |
| int i; |
| |
| //DWC_DEBUG(); |
| dwcep = &ep->dwc_ep; |
| |
| /* Get the first pending request from the queue */ |
| if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| if (!req) { |
| DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); |
| return; |
| } |
| dwcep->xiso_active_xfers--; |
| dwcep->xiso_queued_xfers--; |
| /* Remove this request from the queue */ |
| DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry); |
| } else { |
| DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); |
| return; |
| } |
| |
| ep->stopped = 1; |
| ereq = &req->ext_req; |
| ddesc_iso = ereq->per_io_frame_descs; |
| |
| if (dwcep->xiso_active_xfers < 0) { |
| DWC_WARN("EP#%d (xiso_active_xfers=%d)", dwcep->num, |
| dwcep->xiso_active_xfers); |
| } |
| |
| /* Fill the Isoc descs of portable extended req from dma descriptors */ |
| for (i = 0; i < ereq->pio_pkt_count; i++) { |
| if (dwcep->is_in) { /* IN endpoints */ |
| ddesc_iso[i].actual_length = ddesc_iso[i].length - |
| dwcep->desc_addr[i].status.b_iso_in.txbytes; |
| ddesc_iso[i].status = |
| dwcep->desc_addr[i].status.b_iso_in.txsts; |
| } else { /* OUT endpoints */ |
| ddesc_iso[i].actual_length = ddesc_iso[i].length - |
| dwcep->desc_addr[i].status.b_iso_out.rxbytes; |
| ddesc_iso[i].status = |
| dwcep->desc_addr[i].status.b_iso_out.rxsts; |
| } |
| } |
| |
| DWC_SPINUNLOCK(ep->pcd->lock); |
| |
| /* Call the completion function in the non-portable logic */ |
| ep->pcd->fops->xisoc_complete(ep->pcd, ep->priv, req->priv, 0, |
| &req->ext_req); |
| |
| DWC_SPINLOCK(ep->pcd->lock); |
| |
| /* Free the request - specific freeing needed for extended request object */ |
| dwc_pcd_xiso_ereq_free(ep, req); |
| |
| /* Start the next request */ |
| dwc_otg_pcd_xiso_start_next_request(ep->pcd, ep); |
| |
| return; |
| } |
| |
| /** |
| * Create and initialize the Isoc pkt descriptors of the extended request. |
| * |
| */ |
| static int dwc_otg_pcd_xiso_create_pkt_descs(dwc_otg_pcd_request_t * req, |
| void *ereq_nonport, |
| int atomic_alloc) |
| { |
| struct dwc_iso_xreq_port *ereq = NULL; |
| struct dwc_iso_xreq_port *req_mapped = NULL; |
| struct dwc_iso_pkt_desc_port *ipds = NULL; /* To be created in this function */ |
| uint32_t pkt_count; |
| int i; |
| |
| ereq = &req->ext_req; |
| req_mapped = (struct dwc_iso_xreq_port *)ereq_nonport; |
| pkt_count = req_mapped->pio_pkt_count; |
| |
| /* Create the isoc descs */ |
| if (atomic_alloc) { |
| ipds = DWC_ALLOC_ATOMIC(sizeof(*ipds) * pkt_count); |
| } else { |
| ipds = DWC_ALLOC(sizeof(*ipds) * pkt_count); |
| } |
| |
| if (!ipds) { |
| DWC_ERROR("Failed to allocate isoc descriptors"); |
| return -DWC_E_NO_MEMORY; |
| } |
| |
| /* Initialize the extended request fields */ |
| ereq->per_io_frame_descs = ipds; |
| ereq->error_count = 0; |
| ereq->pio_alloc_pkt_count = pkt_count; |
| ereq->pio_pkt_count = pkt_count; |
| ereq->tr_sub_flags = req_mapped->tr_sub_flags; |
| |
| /* Init the Isoc descriptors */ |
| for (i = 0; i < pkt_count; i++) { |
| ipds[i].length = req_mapped->per_io_frame_descs[i].length; |
| ipds[i].offset = req_mapped->per_io_frame_descs[i].offset; |
| ipds[i].status = req_mapped->per_io_frame_descs[i].status; /* 0 */ |
| ipds[i].actual_length = |
| req_mapped->per_io_frame_descs[i].actual_length; |
| } |
| |
| return 0; |
| } |
| |
| static void prn_ext_request(struct dwc_iso_xreq_port *ereq) |
| { |
| struct dwc_iso_pkt_desc_port *xfd = NULL; |
| int i; |
| |
| DWC_DEBUG("per_io_frame_descs=%p", ereq->per_io_frame_descs); |
| DWC_DEBUG("tr_sub_flags=%d", ereq->tr_sub_flags); |
| DWC_DEBUG("error_count=%d", ereq->error_count); |
| DWC_DEBUG("pio_alloc_pkt_count=%d", ereq->pio_alloc_pkt_count); |
| DWC_DEBUG("pio_pkt_count=%d", ereq->pio_pkt_count); |
| DWC_DEBUG("res=%d", ereq->res); |
| |
| for (i = 0; i < ereq->pio_pkt_count; i++) { |
| xfd = &ereq->per_io_frame_descs[0]; |
| DWC_DEBUG("FD #%d", i); |
| |
| DWC_DEBUG("xfd->actual_length=%d", xfd->actual_length); |
| DWC_DEBUG("xfd->length=%d", xfd->length); |
| DWC_DEBUG("xfd->offset=%d", xfd->offset); |
| DWC_DEBUG("xfd->status=%d", xfd->status); |
| } |
| } |
| |
| /** |
| * |
| */ |
| int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, |
| uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen, |
| int zero, void *req_handle, int atomic_alloc, |
| void *ereq_nonport) |
| { |
| dwc_otg_pcd_request_t *req = NULL; |
| dwc_otg_pcd_ep_t *ep; |
| dwc_irqflags_t flags; |
| int res; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| /* We support this extension only for DDMA mode */ |
| if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) |
| if (!GET_CORE_IF(pcd)->dma_desc_enable) |
| return -DWC_E_INVALID; |
| |
| /* Create a dwc_otg_pcd_request_t object */ |
| if (atomic_alloc) { |
| req = DWC_ALLOC_ATOMIC(sizeof(*req)); |
| } else { |
| req = DWC_ALLOC(sizeof(*req)); |
| } |
| |
| if (!req) { |
| return -DWC_E_NO_MEMORY; |
| } |
| |
| /* Create the Isoc descs for this request which shall be the exact match |
| * of the structure sent to us from the non-portable logic */ |
| res = |
| dwc_otg_pcd_xiso_create_pkt_descs(req, ereq_nonport, atomic_alloc); |
| if (res) { |
| DWC_WARN("Failed to init the Isoc descriptors"); |
| DWC_FREE(req); |
| return res; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| |
| DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry); |
| req->buf = buf; |
| req->dma = dma_buf; |
| req->length = buflen; |
| req->sent_zlp = zero; |
| req->priv = req_handle; |
| |
| //DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| ep->dwc_ep.dma_addr = dma_buf; |
| ep->dwc_ep.start_xfer_buff = buf; |
| ep->dwc_ep.xfer_buff = buf; |
| ep->dwc_ep.xfer_len = 0; |
| ep->dwc_ep.xfer_count = 0; |
| ep->dwc_ep.sent_zlp = 0; |
| ep->dwc_ep.total_len = buflen; |
| |
| /* Add this request to the tail */ |
| DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); |
| ep->dwc_ep.xiso_queued_xfers++; |
| |
| //DWC_DEBUG("CP_0"); |
| //DWC_DEBUG("req->ext_req.tr_sub_flags=%d", req->ext_req.tr_sub_flags); |
| //prn_ext_request((struct dwc_iso_xreq_port *) ereq_nonport); |
| //prn_ext_request(&req->ext_req); |
| |
| //DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| /* If the req->status == ASAP then check if there is any active transfer |
| * for this endpoint. If no active transfers, then get the first entry |
| * from the queue and start that transfer |
| */ |
| if (req->ext_req.tr_sub_flags == DWC_EREQ_TF_ASAP) { |
| res = dwc_otg_pcd_xiso_start_next_request(pcd, ep); |
| if (res) { |
| DWC_WARN("Failed to start the next Isoc transfer"); |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| DWC_FREE(req); |
| return res; |
| } |
| } |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| return 0; |
| } |
| |
| #endif |
| /* END ifdef DWC_UTE_PER_IO ***************************************************/ |
| int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, |
| uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen, |
| int zero, void *req_handle, int atomic_alloc) |
| { |
| dwc_irqflags_t flags; |
| dwc_otg_pcd_request_t *req; |
| dwc_otg_pcd_ep_t *ep; |
| uint32_t max_transfer; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) { |
| DWC_WARN("bad ep\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| if (atomic_alloc) { |
| req = DWC_ALLOC_ATOMIC(sizeof(*req)); |
| } else { |
| req = DWC_ALLOC(sizeof(*req)); |
| } |
| |
| if (!req) { |
| return -DWC_E_NO_MEMORY; |
| } |
| DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry); |
| if (!GET_CORE_IF(pcd)->core_params->opt) { |
| if (ep->dwc_ep.num != 0) { |
| DWC_ERROR("queue req %p, len %d buf %p\n", |
| req_handle, buflen, buf); |
| } |
| } |
| |
| req->buf = buf; |
| req->dma = dma_buf; |
| req->length = buflen; |
| req->sent_zlp = zero; |
| req->priv = req_handle; |
| req->dw_align_buf = NULL; |
| if ((dma_buf & 0x3) && GET_CORE_IF(pcd)->dma_enable |
| && !GET_CORE_IF(pcd)->dma_desc_enable) |
| req->dw_align_buf = DWC_DMA_ALLOC(buflen, |
| &req->dw_align_buf_dma); |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| |
| /* |
| * After adding request to the queue for IN ISOC wait for In Token Received |
| * when TX FIFO is empty interrupt and for OUT ISOC wait for OUT Token |
| * Received when EP is disabled interrupt to obtain starting microframe |
| * (odd/even) start transfer |
| */ |
| if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) |
| { |
| if (req != 0) { |
| depctl_data_t depctl = {.d32 = DWC_READ_REG32(&pcd->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->diepctl)}; |
| ++pcd->request_pending; |
| |
| DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); |
| if (ep->dwc_ep.is_in) |
| { |
| depctl.b.cnak = 1; |
| DWC_WRITE_REG32(&pcd->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->diepctl, depctl.d32); |
| } |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| } |
| return 0; |
| } |
| |
| /* |
| * For EP0 IN without premature status, zlp is required? |
| */ |
| if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { |
| DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num); |
| //_req->zero = 1; |
| } |
| |
| /* Start the transfer */ |
| if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) { |
| /* EP0 Transfer? */ |
| if (ep->dwc_ep.num == 0) { |
| switch (pcd->ep0state) { |
| case EP0_IN_DATA_PHASE: |
| DWC_DEBUGPL(DBG_PCD, |
| "%s ep0: EP0_IN_DATA_PHASE\n", |
| __func__); |
| break; |
| |
| case EP0_OUT_DATA_PHASE: |
| DWC_DEBUGPL(DBG_PCD, |
| "%s ep0: EP0_OUT_DATA_PHASE\n", |
| __func__); |
| if (pcd->request_config) { |
| /* Complete STATUS PHASE */ |
| ep->dwc_ep.is_in = 1; |
| pcd->ep0state = EP0_IN_STATUS_PHASE; |
| } |
| break; |
| |
| case EP0_IN_STATUS_PHASE: |
| DWC_DEBUGPL(DBG_PCD, |
| "%s ep0: EP0_IN_STATUS_PHASE\n", |
| __func__); |
| break; |
| |
| default: |
| DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", |
| pcd->ep0state); |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| return -DWC_E_SHUTDOWN; |
| } |
| |
| ep->dwc_ep.dma_addr = dma_buf; |
| ep->dwc_ep.start_xfer_buff = buf; |
| ep->dwc_ep.xfer_buff = buf; |
| ep->dwc_ep.xfer_len = buflen; |
| ep->dwc_ep.xfer_count = 0; |
| ep->dwc_ep.sent_zlp = 0; |
| ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
| |
| if (zero) { |
| if ((ep->dwc_ep.xfer_len % |
| ep->dwc_ep.maxpacket == 0) |
| && (ep->dwc_ep.xfer_len != 0)) { |
| ep->dwc_ep.sent_zlp = 1; |
| } |
| |
| } |
| |
| dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), |
| &ep->dwc_ep); |
| } // non-ep0 endpoints |
| else { |
| #ifdef DWC_UTE_CFI |
| if (ep->dwc_ep.buff_mode != BM_STANDARD) { |
| /* store the request length */ |
| ep->dwc_ep.cfi_req_len = buflen; |
| pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, |
| ep, req); |
| } else { |
| #endif |
| max_transfer = |
| GET_CORE_IF(ep->pcd)-> |
| core_params->max_transfer_size; |
| |
| /* Setup and start the Transfer */ |
| if (req->dw_align_buf){ |
| if (ep->dwc_ep.is_in) |
| dwc_memcpy(req->dw_align_buf, buf, buflen); |
| ep->dwc_ep.dma_addr = req->dw_align_buf_dma; |
| ep->dwc_ep.start_xfer_buff = req->dw_align_buf; |
| ep->dwc_ep.xfer_buff = req->dw_align_buf; |
| } else { |
| ep->dwc_ep.dma_addr = dma_buf; |
| ep->dwc_ep.start_xfer_buff = buf; |
| ep->dwc_ep.xfer_buff = buf; |
| } |
| ep->dwc_ep.xfer_len = 0; |
| ep->dwc_ep.xfer_count = 0; |
| ep->dwc_ep.sent_zlp = 0; |
| ep->dwc_ep.total_len = buflen; |
| |
| ep->dwc_ep.maxxfer = max_transfer; |
| if (GET_CORE_IF(pcd)->dma_desc_enable) { |
| uint32_t out_max_xfer = |
| DDMA_MAX_TRANSFER_SIZE - |
| (DDMA_MAX_TRANSFER_SIZE % 4); |
| if (ep->dwc_ep.is_in) { |
| if (ep->dwc_ep.maxxfer > |
| DDMA_MAX_TRANSFER_SIZE) { |
| ep->dwc_ep.maxxfer = |
| DDMA_MAX_TRANSFER_SIZE; |
| } |
| } else { |
| if (ep->dwc_ep.maxxfer > |
| out_max_xfer) { |
| ep->dwc_ep.maxxfer = |
| out_max_xfer; |
| } |
| } |
| } |
| if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { |
| ep->dwc_ep.maxxfer -= |
| (ep->dwc_ep.maxxfer % |
| ep->dwc_ep.maxpacket); |
| } |
| |
| if (zero) { |
| if ((ep->dwc_ep.total_len % |
| ep->dwc_ep.maxpacket == 0) |
| && (ep->dwc_ep.total_len != 0)) { |
| ep->dwc_ep.sent_zlp = 1; |
| } |
| } |
| #ifdef DWC_UTE_CFI |
| } |
| #endif |
| dwc_otg_ep_start_transfer(GET_CORE_IF(pcd), |
| &ep->dwc_ep); |
| } |
| } |
| |
| if (req != 0) { |
| ++pcd->request_pending; |
| DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); |
| if (ep->dwc_ep.is_in && ep->stopped |
| && !(GET_CORE_IF(pcd)->dma_enable)) { |
| /** @todo NGS Create a function for this. */ |
| diepmsk_data_t diepmsk = {.d32 = 0 }; |
| diepmsk.b.intktxfemp = 1; |
| if (GET_CORE_IF(pcd)->multiproc_int_enable) { |
| DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| dev_global_regs-> |
| diepeachintmsk[ep->dwc_ep.num], |
| 0, diepmsk.d32); |
| } else { |
| DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| dev_global_regs->diepmsk, 0, |
| diepmsk.d32); |
| } |
| |
| } |
| } |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| return 0; |
| } |
| |
| int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle, |
| void *req_handle) |
| { |
| dwc_irqflags_t flags; |
| dwc_otg_pcd_request_t *req; |
| dwc_otg_pcd_ep_t *ep; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) { |
| DWC_WARN("bad argument\n"); |
| return -DWC_E_INVALID; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| |
| /* make sure it's actually queued on this endpoint */ |
| DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) { |
| if (req->priv == (void *)req_handle) { |
| break; |
| } |
| } |
| |
| if (req->priv != (void *)req_handle) { |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| return -DWC_E_INVALID; |
| } |
| |
| if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) { |
| dwc_otg_request_done(ep, req, -DWC_E_RESTART); |
| } else { |
| req = NULL; |
| } |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| return req ? 0 : -DWC_E_SHUTDOWN; |
| |
| } |
| |
| int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_irqflags_t flags; |
| int retval = 0; |
| |
| ep = get_ep_from_handle(pcd, ep_handle); |
| |
| if (!ep || (!ep->desc && ep != &pcd->ep0) || |
| (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) { |
| DWC_WARN("%s, bad ep\n", __func__); |
| return -DWC_E_INVALID; |
| } |
| |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num, |
| ep->dwc_ep.is_in ? "IN" : "OUT"); |
| retval = -DWC_E_AGAIN; |
| } else if (value == 0) { |
| dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); |
| } else if (value == 1) { |
| if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) { |
| dtxfsts_data_t txstatus; |
| fifosize_data_t txfifosize; |
| |
| txfifosize.d32 = |
| DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs-> |
| dtxfsiz[ep->dwc_ep.tx_fifo_num]); |
| txstatus.d32 = |
| DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if-> |
| in_ep_regs[ep->dwc_ep.num]->dtxfsts); |
| |
| if (txstatus.b.txfspcavail < txfifosize.b.depth) { |
| DWC_WARN("%s() Data In Tx Fifo\n", __func__); |
| retval = -DWC_E_AGAIN; |
| } else { |
| if (ep->dwc_ep.num == 0) { |
| pcd->ep0state = EP0_STALL; |
| } |
| |
| ep->stopped = 1; |
| dwc_otg_ep_set_stall(GET_CORE_IF(pcd), |
| &ep->dwc_ep); |
| } |
| } else { |
| if (ep->dwc_ep.num == 0) { |
| pcd->ep0state = EP0_STALL; |
| } |
| |
| ep->stopped = 1; |
| dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep); |
| } |
| } else if (value == 2) { |
| ep->dwc_ep.stall_clear_flag = 0; |
| } else if (value == 3) { |
| ep->dwc_ep.stall_clear_flag = 1; |
| } |
| |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| |
| return retval; |
| } |
| |
| /** |
| * This function initiates remote wakeup of the host from suspend state. |
| */ |
| void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set) |
| { |
| dctl_data_t dctl = { 0 }; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| dsts_data_t dsts; |
| |
| dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); |
| if (!dsts.b.suspsts) { |
| DWC_WARN("Remote wakeup while is not in suspend state\n"); |
| } |
| /* Check if DEVICE_REMOTE_WAKEUP feature enabled */ |
| if (pcd->remote_wakeup_enable) { |
| if (set) { |
| |
| if (core_if->adp_enable) { |
| gpwrdn_data_t gpwrdn; |
| |
| dwc_otg_adp_probe_stop(core_if); |
| |
| /* Mask SRP detected interrupt from Power Down Logic */ |
| gpwrdn.d32 = 0; |
| gpwrdn.b.srp_det_msk = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs-> |
| gpwrdn, gpwrdn.d32, 0); |
| |
| /* Disable Power Down Logic */ |
| gpwrdn.d32 = 0; |
| gpwrdn.b.pmuactv = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs-> |
| gpwrdn, gpwrdn.d32, 0); |
| |
| /* |
| * Initialize the Core for Device mode. |
| */ |
| core_if->op_state = B_PERIPHERAL; |
| dwc_otg_core_init(core_if); |
| dwc_otg_enable_global_interrupts(core_if); |
| cil_pcd_start(core_if); |
| |
| dwc_otg_initiate_srp(core_if); |
| } |
| |
| dctl.b.rmtwkupsig = 1; |
| DWC_MODIFY_REG32(&core_if->dev_if-> |
| dev_global_regs->dctl, 0, dctl.d32); |
| DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); |
| |
| dwc_mdelay(2); |
| DWC_MODIFY_REG32(&core_if->dev_if-> |
| dev_global_regs->dctl, dctl.d32, 0); |
| DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n"); |
| } |
| } else { |
| DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n"); |
| } |
| } |
| |
| #ifdef CONFIG_USB_DWC_OTG_LPM |
| /** |
| * This function initiates remote wakeup of the host from L1 sleep state. |
| */ |
| void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set) |
| { |
| glpmcfg_data_t lpmcfg; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| |
| lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); |
| |
| /* Check if we are in L1 state */ |
| if (!lpmcfg.b.prt_sleep_sts) { |
| DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n"); |
| return; |
| } |
| |
| /* Check if host allows remote wakeup */ |
| if (!lpmcfg.b.rem_wkup_en) { |
| DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n"); |
| return; |
| } |
| |
| /* Check if Resume OK */ |
| if (!lpmcfg.b.sleep_state_resumeok) { |
| DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n"); |
| return; |
| } |
| |
| lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); |
| lpmcfg.b.en_utmi_sleep = 0; |
| lpmcfg.b.hird_thres &= (~(1 << 4)); |
| DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); |
| |
| if (set) { |
| dctl_data_t dctl = {.d32 = 0 }; |
| dctl.b.rmtwkupsig = 1; |
| /* Set RmtWkUpSig bit to start remote wakup signaling. |
| * Hardware will automatically clear this bit. |
| */ |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, |
| 0, dctl.d32); |
| DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); |
| } |
| |
| } |
| #endif |
| |
| /** |
| * Performs remote wakeup. |
| */ |
| void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set) |
| { |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| dwc_irqflags_t flags; |
| if (dwc_otg_is_device_mode(core_if)) { |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| #ifdef CONFIG_USB_DWC_OTG_LPM |
| if (core_if->lx_state == DWC_OTG_L1) { |
| dwc_otg_pcd_rem_wkup_from_sleep(pcd, set); |
| } else { |
| #endif |
| dwc_otg_pcd_rem_wkup_from_suspend(pcd, set); |
| #ifdef CONFIG_USB_DWC_OTG_LPM |
| } |
| #endif |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| } |
| return; |
| } |
| |
| void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs) |
| { |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| dctl_data_t dctl = { 0 }; |
| |
| if (dwc_otg_is_device_mode(core_if)) { |
| dctl.b.sftdiscon = 1; |
| DWC_PRINTF("Soft disconnect for %d useconds\n",no_of_usecs); |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); |
| dwc_udelay(no_of_usecs); |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32,0); |
| |
| } else{ |
| DWC_PRINTF("NOT SUPPORTED IN HOST MODE\n"); |
| } |
| return; |
| |
| } |
| |
| int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd) |
| { |
| dsts_data_t dsts; |
| gotgctl_data_t gotgctl; |
| |
| /* |
| * This function starts the Protocol if no session is in progress. If |
| * a session is already in progress, but the device is suspended, |
| * remote wakeup signaling is started. |
| */ |
| |
| /* Check if valid session */ |
| gotgctl.d32 = |
| DWC_READ_REG32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl)); |
| if (gotgctl.b.bsesvld) { |
| /* Check if suspend state */ |
| dsts.d32 = |
| DWC_READ_REG32(& |
| (GET_CORE_IF(pcd)->dev_if-> |
| dev_global_regs->dsts)); |
| if (dsts.b.suspsts) { |
| dwc_otg_pcd_remote_wakeup(pcd, 1); |
| } |
| } else { |
| dwc_otg_pcd_initiate_srp(pcd); |
| } |
| |
| return 0; |
| |
| } |
| |
| /** |
| * Start the SRP timer to detect when the SRP does not complete within |
| * 6 seconds. |
| * |
| * @param pcd the pcd structure. |
| */ |
| void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd) |
| { |
| dwc_irqflags_t flags; |
| DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); |
| dwc_otg_initiate_srp(GET_CORE_IF(pcd)); |
| DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); |
| } |
| |
| int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd) |
| { |
| return dwc_otg_get_frame_number(GET_CORE_IF(pcd)); |
| } |
| |
| int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd) |
| { |
| return GET_CORE_IF(pcd)->core_params->lpm_enable; |
| } |
| |
| uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd) |
| { |
| return pcd->b_hnp_enable; |
| } |
| |
| uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd) |
| { |
| return pcd->a_hnp_support; |
| } |
| |
| uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd) |
| { |
| return pcd->a_alt_hnp_support; |
| } |
| |
| int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd) |
| { |
| return pcd->remote_wakeup_enable; |
| } |
| |
| #endif /* DWC_HOST_ONLY */ |