blob: 22fd410b7efac484b2c65d557293a24edaac01a4 [file] [log] [blame]
/* ==========================================================================
* $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;
}