| /* ========================================================================== |
| * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ |
| * $Revision: #191 $ |
| * $Date: 2012/08/10 $ |
| * $Change: 2047372 $ |
| * |
| * 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. |
| * ========================================================================== */ |
| |
| /** @file |
| * |
| * The Core Interface Layer provides basic services for accessing and |
| * managing the DWC_otg hardware. These services are used by both the |
| * Host Controller Driver and the Peripheral Controller Driver. |
| * |
| * The CIL manages the memory map for the core so that the HCD and PCD |
| * don't have to do this separately. It also handles basic tasks like |
| * reading/writing the registers and data FIFOs in the controller. |
| * Some of the data access functions provide encapsulation of several |
| * operations required to perform a task, such as writing multiple |
| * registers to start a transfer. Finally, the CIL performs basic |
| * services that are not specific to either the host or device modes |
| * of operation. These services include management of the OTG Host |
| * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A |
| * Diagnostic API is also provided to allow testing of the controller |
| * hardware. |
| * |
| * The Core Interface Layer has the following requirements: |
| * - Provides basic controller operations. |
| * - Minimal use of OS services. |
| * - The OS services used will be abstracted by using inline functions |
| * or macros. |
| * |
| */ |
| |
| #include <common.h> |
| |
| #include "global.h" |
| #include "dwc_otg_regs.h" |
| #include "dwc_otg_cil.h" |
| #include "dwc_otg_driver.h" |
| |
| |
| |
| static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if); |
| |
| void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) |
| { |
| DWC_WRITE_REG32(reg,(DWC_READ_REG32(reg) & ~clear_mask) | set_mask); |
| } |
| |
| dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr) |
| { |
| dwc_otg_core_if_t *core_if = (&global.core_if_t); |
| dwc_otg_dev_if_t *dev_if = (&global.dev_if_t); |
| uint8_t *pt_core_if = ( uint8_t *)(&global.core_if_t); |
| uint8_t *pt_dev_if = ( uint8_t *)(&global.dev_if_t); |
| uint8_t *reg_base = (uint8_t *) reg_base_addr; |
| int i = 0; |
| for(i= 0;i<sizeof(global.core_if_t);i++) |
| { |
| pt_core_if[i] = 0; |
| } |
| core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base; |
| |
| for(i= 0;i<sizeof(global.dev_if_t);i++) |
| { |
| pt_dev_if[i] = 0; |
| } |
| |
| dev_if->dev_global_regs = (dwc_otg_device_global_regs_t *) (reg_base +DWC_DEV_GLOBAL_REG_OFFSET); |
| |
| for (i = 0; i < MAX_EPS_CHANNELS; i++) |
| { |
| dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *) |
| (reg_base + DWC_DEV_IN_EP_REG_OFFSET + |
| (i * DWC_EP_REG_OFFSET)); |
| |
| dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *) |
| (reg_base + DWC_DEV_OUT_EP_REG_OFFSET + |
| (i * DWC_EP_REG_OFFSET)); |
| |
| core_if->data_fifo[i] =(uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET +(i * DWC_OTG_DATA_FIFO_SIZE)); |
| } |
| |
| dev_if->speed = 0; |
| |
| core_if->dev_if = dev_if; |
| |
| core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET); |
| |
| core_if->hwcfg1.d32 =DWC_READ_REG32(&core_if->core_global_regs->ghwcfg1); |
| core_if->hwcfg2.d32 =DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2); |
| core_if->hwcfg3.d32 =DWC_READ_REG32(&core_if->core_global_regs->ghwcfg3); |
| core_if->hwcfg4.d32 =DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4); |
| |
| core_if->dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); |
| |
| dwc_otg_setup_params(core_if); |
| |
| |
| return core_if; |
| } |
| |
| /** |
| * Initializes the DevSpd field of the DCFG register depending on the PHY type |
| * and the enumeration speed of the device. |
| */ |
| void init_devspd(dwc_otg_core_if_t * core_if,uint8_t speed) |
| { |
| uint32_t val; |
| dcfg_data_t dcfg; |
| |
| val = speed; |
| dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); |
| dcfg.b.devspd = val; |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); |
| } |
| } |
| |
| /** |
| * This function initializes the DWC_otg controller registers and |
| * prepares the core for device mode or host mode operation. |
| * |
| * @param core_if Programming view of the DWC_otg controller |
| * |
| */ |
| void dwc_otg_core_init(dwc_otg_core_if_t * core_if) |
| { |
| dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| gahbcfg_data_t ahbcfg; |
| gotgctl_data_t gotgctl; |
| |
| gusbcfg_data_t usbcfg; |
| ahbcfg.d32 = 0; |
| usbcfg.d32 = 0; |
| gotgctl.d32 = 0; |
| |
| |
| usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); |
| if(global.g_enum == NEED_ENUM) |
| { |
| |
| #if !USE_ASIC |
| usbcfg.b.ulpi_ext_vbus_drv =(core_if->core_params->phy_ulpi_ext_vbus == DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0; |
| |
| usbcfg.b.term_sel_dl_pulse = (core_if->core_params->ts_dline == 1) ? 1 : 0; |
| |
| |
| DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); |
| #endif |
| dwc_otg_core_reset(core_if); |
| } |
| |
| |
| dev_if->num_in_eps = 2;//calc_num_in_eps(core_if); |
| dev_if->num_out_eps = 2;//calc_num_out_eps(core_if); |
| |
| core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth; |
| core_if->rx_fifo_size = DWC_READ_REG32(&global_regs->grxfsiz); |
| core_if->nperio_tx_fifo_size = DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16; |
| { |
| /* High speed PHY. */ |
| if (!core_if->phy_init_done) |
| { |
| core_if->phy_init_done = 1; |
| /* HS PHY parameters. These parameters are preserved |
| * during soft reset so only program the first time. Do |
| * a soft reset immediately after setting phyif. */ |
| #if !USE_ASIC |
| if (core_if->core_params->phy_type == 2) |
| { |
| /* ULPI interface */ |
| usbcfg.b.ulpi_utmi_sel = 1; |
| usbcfg.b.phyif = 0; |
| usbcfg.b.ddrsel = core_if->core_params->phy_ulpi_ddr; |
| } |
| #else |
| if (core_if->core_params->phy_type == 1) |
| { |
| /* UTMI+ interface */ |
| usbcfg.b.ulpi_utmi_sel = 0; |
| if (core_if->core_params->phy_utmi_width == 16) |
| { |
| usbcfg.b.phyif = 1; |
| |
| } |
| else |
| { |
| usbcfg.b.phyif = 0; |
| } |
| } |
| #endif |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); |
| /* Reset after setting the PHY parameters */ |
| dwc_otg_core_reset(core_if); |
| } |
| } |
| } |
| if(global.g_enum == NEED_ENUM) |
| { |
| #if !USE_ASIC |
| if ((core_if->hwcfg2.b.hs_phy_type == 2) &&(core_if->hwcfg2.b.fs_phy_type == 1) &&(core_if->core_params->ulpi_fs_ls)) |
| { |
| usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); |
| usbcfg.b.ulpi_fsls = 1; |
| usbcfg.b.ulpi_clk_sus_m = 1; |
| DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); |
| } |
| else |
| { |
| usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); |
| usbcfg.b.ulpi_fsls = 0; |
| usbcfg.b.ulpi_clk_sus_m = 0; |
| DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); |
| } |
| #endif |
| } |
| |
| |
| ahbcfg.b.nptxfemplvl_txfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; |
| ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; |
| |
| |
| ahbcfg.b.dmaenable = 0; |
| // ÅäÖÃÈ«¿ÕFIFO²úÉúÖÐ¶Ï |
| ahbcfg.b.nptxfemplvl_txfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_EMPTY ; |
| if(global.g_enum == NEED_ENUM) |
| { |
| |
| DWC_WRITE_REG32(&global_regs->gahbcfg, ahbcfg.d32); |
| } |
| |
| |
| core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en; |
| |
| core_if->pti_enh_enable = core_if->core_params->pti_enable != 0; |
| core_if->multiproc_int_enable = dwc_param_mpi_enable_default;//core_if->core_params->mpi_enable; |
| /* |
| * Program the GUSBCFG register. |
| */ |
| usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); |
| usbcfg.b.hnpcap = 0; |
| usbcfg.b.srpcap = 0; |
| |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); |
| } |
| { |
| gotgctl.b.otgver = core_if->core_params->otg_ver; |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, 0, gotgctl.d32); |
| } |
| /* Set OTG version supported */ |
| core_if->otg_ver = core_if->core_params->otg_ver; |
| } |
| if(global.g_enum == NEED_ENUM) |
| { |
| dwc_otg_core_dev_init(core_if); |
| } |
| |
| } |
| |
| /** |
| * This function enables the Device mode interrupts. |
| * |
| * @param core_if Programming view of DWC_otg controller |
| */ |
| void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if) |
| { |
| gintmsk_data_t intr_mask ; |
| |
| dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
| intr_mask.d32 = 0; |
| /* Disable all interrupts. */ |
| DWC_WRITE_REG32(&global_regs->gintmsk, 0); |
| |
| /* Clear any pending interrupts */ |
| DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF); |
| |
| /* Enable the common interrupts */ |
| intr_mask.b.rxstsqlvl = 1; |
| intr_mask.b.usbsuspend = 1; |
| DWC_WRITE_REG32(&global_regs->gintmsk, intr_mask.d32); |
| |
| intr_mask.d32 = 0x1e3400; |
| DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); |
| |
| } |
| |
| /** |
| * This function initializes the DWC_otg controller registers for |
| * device mode. |
| * |
| * @param core_if Programming view of DWC_otg controller |
| * |
| */ |
| void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if) |
| { |
| int i; |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| dcfg_data_t dcfg; |
| grstctl_t resetctl; |
| dctl_data_t dctl; |
| diepmsk_data_t msk; |
| dcfg.d32 = 0; |
| resetctl.d32 = 0; |
| dctl.d32 = 0; |
| msk.d32 = 0; |
| /* Restart the Phy Clock */ |
| |
| DWC_WRITE_REG32(core_if->pcgcctl, 0); |
| |
| |
| /* Device configuration register */ |
| init_devspd(core_if,0);//ĬÈÏÅäÖóɸßËÙ |
| dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); |
| dcfg.b.descdma = 0; |
| dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80; |
| |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); |
| |
| |
| /* Flush the FIFOs */ |
| dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ |
| dwc_otg_flush_rx_fifo(core_if); |
| |
| /* Flush the Learning Queue. */ |
| resetctl.b.intknqflsh = 1; |
| DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32); |
| |
| /* Clear all pending Device Interrupts */ |
| /** @todo - if the condition needed to be checked |
| * or in any case all pending interrutps should be cleared? |
| */ |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, 0); |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, 0); |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF); |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, 0); |
| |
| for (i = 0; i <= dev_if->num_in_eps; i++) |
| { |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl,0); |
| |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, 0); |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, 0); |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepint, 0xFF); |
| |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl,0); |
| |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doeptsiz, 0); |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepdma, 0); |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepint, 0xFF); |
| } |
| |
| //ÓÃÓÚhsic |
| if(1==global.g_USB_MODE) |
| { |
| DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, 0x40000000); |
| } |
| dwc_otg_enable_device_interrupts(core_if); |
| msk.b.txfifoundrn = 1; |
| DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk,msk.d32, msk.d32); |
| dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl); |
| dctl.b.sftdiscon = 0; |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32); |
| |
| } |
| |
| void dwc_otg_core_dev_disconnet(dwc_otg_core_if_t * core_if) |
| { |
| dctl_data_t dctl; |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl); |
| dctl.b.sftdiscon = 1; |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32); |
| } |
| /** |
| * This function reads a setup packet from the Rx FIFO into the destination |
| * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) |
| * Interrupt routine when a SETUP packet has been received in Slave mode. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dest Destination buffer for packet data. |
| */ |
| void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest) |
| { |
| dest[0] = DWC_READ_REG32(core_if->data_fifo[0]); |
| dest[1] = DWC_READ_REG32(core_if->data_fifo[0]); |
| |
| } |
| |
| /** |
| * This function enables EP0 OUT to receive SETUP packets and configures EP0 |
| * IN for transmitting packets. It is normally called when the |
| * "Enumeration Done" interrupt occurs. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP0 data. |
| */ |
| void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| dsts_data_t dsts; |
| depctl_data_t diepctl; |
| dctl_data_t dctl; |
| ep->stp_rollover = 0; |
| dctl.d32 = 0; |
| |
| /* Read the Device Status and Endpoint 0 Control registers */ |
| dsts.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dsts); |
| diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl); |
| |
| /* Set the MPS of the IN EP based on the enumeration speed */ |
| switch (dsts.b.enumspd) |
| { |
| case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: |
| case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: |
| case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: |
| diepctl.b.mps = DWC_DEP0CTL_MPS_64; |
| break; |
| } |
| |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); |
| dctl.b.cgnpinnak = 1; |
| |
| DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); |
| |
| } |
| |
| /** |
| * This function activates an EP. The Device EP control register for |
| * the EP is configured as defined in the ep structure. Note: This |
| * function is not used for EP0. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to activate. |
| */ |
| void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| depctl_data_t depctl; |
| volatile uint32_t *addr; |
| daint_data_t daintmsk; |
| |
| daintmsk.d32 = 0; |
| /* Read DEPCTLn register */ |
| if (ep->is_in == 1) |
| { |
| addr = &dev_if->in_ep_regs[ep->num]->diepctl; |
| daintmsk.ep.in = 1 << ep->num; |
| } |
| else |
| { |
| addr = &dev_if->out_ep_regs[ep->num]->doepctl; |
| daintmsk.ep.out = 1 << ep->num; |
| } |
| |
| /* If the EP is already active don't change the EP Control |
| * register. */ |
| depctl.d32 = DWC_READ_REG32(addr); |
| if (!depctl.b.usbactep) |
| { |
| depctl.b.mps = ep->maxpacket; |
| depctl.b.eptype = ep->type; |
| depctl.b.txfnum = ep->tx_fifo_num; |
| |
| |
| depctl.b.setd0pid = 1; |
| |
| depctl.b.usbactep = 1; |
| |
| DWC_WRITE_REG32(addr, depctl.d32); |
| } |
| { |
| |
| DWC_MODIFY_REG32(&dev_if->dev_global_regs->daintmsk,0, daintmsk.d32); |
| } |
| |
| ep->stall_clear_flag = 0; |
| |
| return; |
| } |
| /** |
| * 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. the ISR. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| */ |
| |
| void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl; |
| deptsiz_data_t deptsiz; |
| |
| if (ep->is_in == 1) |
| { |
| dwc_otg_dev_in_ep_regs_t *in_regs = core_if->dev_if->in_ep_regs[ep->num]; |
| |
| depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl)); |
| deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz)); |
| |
| if (ep->maxpacket > (ep->maxxfer / MAX_PKT_CNT)) |
| ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?\ |
| ep->maxxfer : (ep->total_len - ep->xfer_len); |
| else |
| ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ?\ |
| MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len); |
| |
| |
| /* Zero Length Packet? */ |
| if ((ep->xfer_len - ep->xfer_count) == 0) |
| { |
| deptsiz.b.xfersize = 0; |
| deptsiz.b.pktcnt = 1; |
| } |
| else |
| { |
| deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; |
| deptsiz.b.pktcnt = (ep->xfer_len - ep->xfer_count - 1 + ep->maxpacket) / ep->maxpacket; |
| #if 0/*unsigned the max value is 1023*/ |
| if (deptsiz.b.pktcnt > MAX_PKT_CNT) |
| { |
| deptsiz.b.pktcnt = MAX_PKT_CNT; |
| deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; |
| } |
| #endif |
| } |
| |
| if((ep->xfer_len - ep->xfer_count)% ep->maxpacket== 0) |
| { |
| printf("sent_zlp = 1\n"); |
| ep->sent_zlp = 1; |
| } |
| |
| DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); |
| |
| if (ep->xfer_len > 0) |
| { |
| uint32_t fifoemptymsk = 0; |
| fifoemptymsk = 1 << ep->num; |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,0, fifoemptymsk); |
| |
| } |
| |
| /* EP enable, IN data in FIFO */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); |
| #if SYNC_USB_CTRL |
| REG32(ARM_PORTA)=0x87; |
| #endif |
| |
| } |
| else |
| { |
| /* OUT endpoint */ |
| dwc_otg_dev_out_ep_regs_t *out_regs = core_if->dev_if->out_ep_regs[ep->num]; |
| |
| depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl)); |
| deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz)); |
| |
| |
| if (ep->maxpacket > (ep->maxxfer / MAX_PKT_CNT)) |
| ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ep->maxxfer : (ep->total_len - ep->xfer_len); |
| else |
| ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ? MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len); |
| |
| |
| if ((ep->xfer_len - ep->xfer_count) == 0) |
| { |
| deptsiz.b.xfersize = ep->maxpacket; |
| deptsiz.b.pktcnt = 1; |
| } |
| else |
| { |
| deptsiz.b.pktcnt = (ep->xfer_len - ep->xfer_count + (ep->maxpacket - 1)) / ep->maxpacket; |
| #if 0/*unsigned the max value is 1023*/ |
| if (deptsiz.b.pktcnt > MAX_PKT_CNT) |
| { |
| deptsiz.b.pktcnt = MAX_PKT_CNT; |
| } |
| #endif |
| ep->xfer_len = deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count; |
| deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; |
| } |
| |
| { |
| |
| DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); |
| } |
| /* EP enable */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| |
| DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); |
| #if SYNC_USB_CTRL |
| REG32(ARM_PORTA)=0x76; |
| #endif |
| |
| } |
| } |
| |
| /** |
| * This function setup a zero length transfer in Buffer DMA and |
| * Slave modes for usb requests with zero field set |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| * |
| */ |
| void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| |
| depctl_data_t depctl; |
| deptsiz_data_t deptsiz; |
| |
| /* IN endpoint */ |
| if (ep->is_in == 1) |
| { |
| dwc_otg_dev_in_ep_regs_t *in_regs = core_if->dev_if->in_ep_regs[ep->num]; |
| |
| depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl)); |
| deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz)); |
| |
| deptsiz.b.xfersize = 0; |
| deptsiz.b.pktcnt = 1; |
| { |
| DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); |
| { |
| /* Enable the Tx FIFO Empty Interrupt for this EP */ |
| if (ep->xfer_len > 0) |
| { |
| uint32_t fifoemptymsk = 0; |
| fifoemptymsk = 1 << ep->num; |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,0, fifoemptymsk); |
| } |
| } |
| } |
| |
| /* EP enable, IN data in FIFO */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); |
| |
| } |
| else |
| { |
| /* OUT endpoint */ |
| dwc_otg_dev_out_ep_regs_t *out_regs = core_if->dev_if->out_ep_regs[ep->num]; |
| |
| depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl)); |
| deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz)); |
| |
| /* Zero Length Packet */ |
| deptsiz.b.xfersize = ep->maxpacket; |
| deptsiz.b.pktcnt = 1; |
| { |
| DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); |
| } |
| |
| /* EP enable */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| |
| DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); |
| |
| } |
| } |
| |
| /** |
| * This function does the setup for a data transfer for EP0 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 EP0 data. |
| */ |
| void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl; |
| deptsiz0_data_t deptsiz; |
| uint32_t fifoemptymsk; |
| |
| ep->total_len = ep->xfer_len; |
| |
| /* IN endpoint */ |
| if (ep->is_in == 1) |
| { |
| printf("ep0 tx data\n"); |
| dwc_otg_dev_in_ep_regs_t *in_regs = core_if->dev_if->in_ep_regs[0]; |
| depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); |
| if (depctl.b.epena) |
| return; |
| |
| |
| /* If dedicated FIFO every time flush fifo before enable ep*/ |
| dwc_otg_flush_tx_fifo(core_if, ep->tx_fifo_num); |
| |
| depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); |
| deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz); |
| |
| /* Zero Length Packet? */ |
| if (ep->xfer_len == 0) |
| { |
| deptsiz.b.xfersize = 0; |
| deptsiz.b.pktcnt = 1; |
| } |
| else |
| { |
| /* Program the transfer size and packet count |
| * as follows: xfersize = N * maxpacket + |
| * short_packet pktcnt = N + (short_packet |
| * exist ? 1 : 0) |
| */ |
| if (ep->xfer_len > ep->maxpacket) |
| { |
| ep->xfer_len = ep->maxpacket; |
| deptsiz.b.xfersize = ep->maxpacket; |
| } |
| else |
| { |
| deptsiz.b.xfersize = ep->xfer_len; |
| } |
| deptsiz.b.pktcnt = 1; |
| |
| } |
| |
| |
| { |
| DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); |
| } |
| |
| /* EP enable, IN data in FIFO */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); |
| |
| /** |
| * Enable the Non-Periodic Tx FIFO empty interrupt, the |
| * data will be written into the fifo by the ISR. |
| */ |
| |
| /* Enable the Tx FIFO Empty Interrupt for this EP */ |
| if (ep->xfer_len > 0) |
| { |
| fifoemptymsk = 0; |
| fifoemptymsk |= 1 << ep->num; |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,0, fifoemptymsk); |
| } |
| |
| } |
| else |
| { |
| |
| /* OUT endpoint */ |
| dwc_otg_dev_out_ep_regs_t *out_regs = core_if->dev_if->out_ep_regs[0]; |
| |
| depctl.d32 = DWC_READ_REG32(&out_regs->doepctl); |
| deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz); |
| |
| /* Program the transfer size and packet count as follows: |
| * xfersize = N * (maxpacket + 4 - (maxpacket % 4)) |
| * pktcnt = N */ |
| /* Zero Length Packet */ |
| deptsiz.b.xfersize = ep->maxpacket; |
| deptsiz.b.pktcnt = 1; |
| deptsiz.b.supcnt = 3; |
| DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); |
| |
| /* EP enable */ |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&(out_regs->doepctl), depctl.d32); |
| |
| } |
| } |
| |
| /** |
| * This function continues control IN transfers started by |
| * dwc_otg_ep0_start_transfer, when the transfer does not fit in a |
| * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one |
| * bit for the packet count. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP0 data. |
| */ |
| void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl; |
| deptsiz0_data_t deptsiz; |
| |
| if (ep->is_in == 1) |
| { |
| dwc_otg_dev_in_ep_regs_t *in_regs = core_if->dev_if->in_ep_regs[0]; |
| |
| depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); |
| deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz); |
| |
| deptsiz.b.xfersize = (ep->total_len - ep->xfer_count) > ep->maxpacket ? ep->maxpacket : (ep->total_len - ep->xfer_count); |
| deptsiz.b.pktcnt = 1; |
| |
| ep->xfer_len += deptsiz.b.xfersize; |
| |
| DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); |
| |
| |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); |
| |
| /** |
| * Enable the Non-Periodic Tx FIFO empty interrupt, the |
| * data will be written into the fifo by the ISR. |
| */ |
| |
| /* Enable the Tx FIFO Empty Interrupt for this EP */ |
| if (ep->xfer_len > 0) |
| { |
| uint32_t fifoemptymsk = 0; |
| fifoemptymsk |= 1 << ep->num; |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,0, fifoemptymsk); |
| } |
| |
| } |
| else |
| { |
| dwc_otg_dev_out_ep_regs_t *out_regs = core_if->dev_if->out_ep_regs[0]; |
| |
| depctl.d32 = DWC_READ_REG32(&out_regs->doepctl); |
| deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz); |
| |
| deptsiz.b.xfersize = ep->maxpacket; |
| deptsiz.b.pktcnt = 1; |
| DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); |
| depctl.b.cnak = 1; |
| depctl.b.epena = 1; |
| DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); |
| |
| } |
| } |
| |
| |
| /** |
| * This function writes a packet into the Tx FIFO associated with the |
| * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For |
| * periodic EPs the periodic Tx FIFO associated with the EP is written |
| * with all packets for the next micro-frame. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to write packet for. |
| * @param dma Indicates if DMA is being used. |
| */ |
| void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep, |
| int dma) |
| { |
| uint32_t i; |
| uint32_t byte_count; |
| uint32_t dword_count; |
| uint32_t *fifo; |
| uint32_t *data_buff = (uint32_t *) ep->xfer_buff; |
| |
| if (ep->xfer_count >= ep->xfer_len) |
| { |
| return; |
| } |
| |
| /* Find the byte length of the packet either short packet or MPS */ |
| if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) |
| { |
| byte_count = ep->xfer_len - ep->xfer_count; |
| } |
| else |
| { |
| byte_count = ep->maxpacket; |
| } |
| |
| /* Find the DWORD length, padded by extra bytes as neccessary if MPS |
| * is not a multiple of DWORD */ |
| dword_count = (byte_count + 3) / 4; |
| |
| /**@todo NGS Where are the Periodic Tx FIFO addresses |
| * intialized? What should this be? */ |
| |
| fifo = core_if->data_fifo[ep->num]; |
| for (i = 0; i < dword_count; i++, data_buff++) |
| { |
| DWC_WRITE_REG32(fifo, *data_buff); |
| |
| } |
| |
| |
| ep->xfer_count += byte_count; |
| ep->xfer_buff += byte_count; |
| } |
| |
| /** |
| * Set the EP STALL. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to set the stall on. |
| */ |
| void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl; |
| volatile uint32_t *depctl_addr; |
| if (ep->is_in == 1) |
| { |
| depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); |
| depctl.d32 = DWC_READ_REG32(depctl_addr); |
| |
| /* set the disable and stall bits */ |
| if (depctl.b.epena) |
| { |
| depctl.b.epdis = 1; |
| } |
| depctl.b.stall = 1; |
| DWC_WRITE_REG32(depctl_addr, depctl.d32); |
| } |
| else |
| { |
| depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); |
| depctl.d32 = DWC_READ_REG32(depctl_addr); |
| |
| depctl.b.stall = 1; |
| DWC_WRITE_REG32(depctl_addr, depctl.d32); |
| } |
| |
| return; |
| } |
| |
| /** |
| * Clear the EP STALL. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to clear stall from. |
| */ |
| void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| depctl_data_t depctl; |
| volatile uint32_t *depctl_addr; |
| |
| if (ep->is_in == 1) |
| { |
| depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); |
| } |
| else |
| { |
| depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); |
| } |
| |
| depctl.d32 = DWC_READ_REG32(depctl_addr); |
| |
| /* clear the stall bits */ |
| depctl.b.stall = 0; |
| |
| /* |
| * USB Spec 9.4.5: For endpoints using data toggle, regardless |
| * of whether an endpoint has the Halt feature set, a |
| * ClearFeature(ENDPOINT_HALT) request always results in the |
| * data toggle being reinitialized to DATA0. |
| */ |
| if (ep->type == DWC_OTG_EP_TYPE_INTR || |
| ep->type == DWC_OTG_EP_TYPE_BULK) |
| { |
| depctl.b.setd0pid = 1; /* DATA0 */ |
| } |
| |
| DWC_WRITE_REG32(depctl_addr, depctl.d32); |
| return; |
| } |
| |
| /** |
| * This function reads a packet from the Rx FIFO into the destination |
| * buffer. To read SETUP data use dwc_otg_read_setup_packet. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dest Destination buffer for the packet. |
| * @param bytes Number of bytes to copy to the destination. |
| */ |
| void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, |
| uint8_t * dest, uint16_t bytes) |
| { |
| int i; |
| int word_count = (bytes + 3) / 4; |
| |
| volatile uint32_t *fifo = core_if->data_fifo[0]; |
| uint32_t *data_buff = (uint32_t *) dest; |
| |
| |
| for (i = 0; i < word_count; i++, data_buff++) |
| { |
| *data_buff = DWC_READ_REG32(fifo); |
| } |
| |
| return; |
| } |
| |
| /** |
| * Flush a Tx FIFO. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param num Tx FIFO to flush. |
| */ |
| void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num) |
| { |
| dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
| volatile grstctl_t greset; |
| int count = 0; |
| greset.d32 = 0; |
| |
| greset.b.txfflsh = 1; |
| greset.b.txfnum = num; |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); |
| |
| |
| do |
| { |
| greset.d32 = DWC_READ_REG32(&global_regs->grstctl); |
| if (++count > 10000) |
| { |
| break; |
| } |
| usdelay(10); |
| } |
| while (greset.b.txfflsh == 1); |
| |
| /* Wait for 3 PHY Clocks */ |
| usdelay(10); |
| } |
| |
| } |
| |
| /** |
| * Flush Rx FIFO. |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| */ |
| void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if) |
| { |
| dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
| volatile grstctl_t greset; |
| int count = 0; |
| greset.d32 = 0; |
| greset.b.rxfflsh = 1; |
| if(global.g_enum == NEED_ENUM) |
| { |
| DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); |
| |
| do |
| { |
| greset.d32 = DWC_READ_REG32(&global_regs->grstctl); |
| if (++count > 10000) |
| { |
| break; |
| } |
| usdelay(10); |
| } |
| while (greset.b.rxfflsh == 1); |
| |
| /* Wait for 3 PHY Clocks */ |
| |
| usdelay(10); |
| } |
| } |
| |
| /** |
| * Do core a soft reset of the core. Be careful with this because it |
| * resets all the internal state machines of the core. |
| */ |
| void dwc_otg_core_reset(dwc_otg_core_if_t * core_if) |
| { |
| dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
| volatile grstctl_t greset; |
| int count = 0; |
| greset.d32 =0; |
| /* Wait for AHB master IDLE state. */ |
| do |
| { |
| usdelay(10); |
| greset.d32 = DWC_READ_REG32(&global_regs->grstctl); |
| if (++count > 100000) |
| { |
| return; |
| } |
| } |
| while (greset.b.ahbidle == 0); |
| |
| /* Core Soft Reset */ |
| count = 0; |
| greset.b.csftrst = 1; |
| DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); |
| do |
| { |
| greset.d32 = DWC_READ_REG32(&global_regs->grstctl); |
| if (++count > 10000) |
| { |
| break; |
| } |
| usdelay(10); |
| } |
| while (greset.b.csftrst == 1); |
| /* Wait for 3 PHY Clocks */ |
| usdelay(10); |
| } |
| |
| static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if) |
| { |
| int i; |
| core_if->core_params = &global.g_core_params; |
| core_if->core_params->otg_cap = DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE; |
| #if !USE_ASIC |
| core_if->core_params->phy_type = DWC_PHY_TYPE_PARAM_ULPI; |
| #else |
| core_if->core_params->phy_type = DWC_PHY_TYPE_PARAM_UTMI; |
| #endif |
| core_if->core_params->speed = DWC_SPEED_PARAM_HIGH;//DWC_SPEED_PARAM_FULL |
| core_if->core_params->phy_ulpi_ddr = dwc_param_phy_ulpi_ddr_default; |
| core_if->core_params->phy_ulpi_ext_vbus = dwc_param_phy_ulpi_ext_vbus_default; |
| core_if->core_params->phy_utmi_width = 8; |
| core_if->core_params->ts_dline = dwc_param_ts_dline_default; |
| core_if->core_params->ulpi_fs_ls = dwc_param_ulpi_fs_ls_default; |
| core_if->core_params->en_multiple_tx_fifo = dwc_param_en_multiple_tx_fifo_default; |
| for (i = 0; i < 15; i++) |
| { |
| core_if->core_params->dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default; |
| } |
| core_if->core_params->otg_ver = 1; |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |