text: /*
   * Copyright (C) 2018 Marcus Comstedt
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of
   * this software and associated documentation files (the "Software"), to deal in
   * the Software without restriction, including without limitation the rights to
   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
   * the Software, and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include <bsp/usb.h>
  #include <bsp/regaccess.h>
  #include <bsp/dma.h>
  #include <bsp/irq.h>
  #include <bsp/util.h>
  #include <bsp/uart.h>
  #include <rdb/gctl.h>
  #include <rdb/uib.h>
  #include <rdb/uibin.h>
  #include <rdb/vic.h>
  
  static const struct Fx3UsbCallbacks *Fx3UsbUserCallbacks = NULL;
  
  static void Fx3UsbGctlCoreIsr(void) __attribute__ ((isr ("IRQ")));
  static void Fx3UsbUsbCoreIsr(void) __attribute__ ((isr ("IRQ")));
  static void Fx3UsbGctlPowerIsr(void) __attribute__ ((isr ("IRQ")));
  
  static void Fx3UsbWritePhyReg(uint16_t phy_addr, uint16_t phy_val)
  {
    if (!(Fx3ReadReg32(FX3_OTG_CTRL) & FX3_OTG_CTRL_SSDEV_ENABLE))
      return;
  
    Fx3WriteReg32(0xe0033024, phy_addr);
    Fx3WriteReg32(0xe0033024, phy_addr | (1UL << 16));
    while(!(Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
    Fx3WriteReg32(0xe0033024, phy_addr);
    while((Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
    Fx3WriteReg32(0xe0033024, phy_val);
    Fx3WriteReg32(0xe0033024, phy_val | (1UL << 17));
    while(!(Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
    Fx3WriteReg32(0xe0033024, phy_val);
    while((Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
    Fx3WriteReg32(0xe0033024, phy_val | (1UL << 19));
    while(!(Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
    Fx3WriteReg32(0xe0033024, phy_val);
    while((Fx3ReadReg32(0xe0033028) & (1UL << 16)))
      ;
  }
  
  
  static void Fx3UsbConnectHighSpeed(void)
  {
  	Fx3UsbWritePhyReg(0x1005, 0x0000);
  	/* Force the link state machine into SS.Disabled. */
  	Fx3WriteReg32(FX3_LNK_LTSSM_STATE,(0UL << (6)) | (1UL << 12));
  
  	/* Change EPM config to full speed */
  	Fx3ClearReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
  	Fx3UtilDelayUs(5);
  	Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  			(2UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  			(2UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
  	Fx3SetReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
  	Fx3UtilDelayUs(5);
  
  	/* Switch the EPM to USB 2.0 mode, turn off USB 3.0 PHY and remove Rx Termination. */
  	Fx3ClearReg32(FX3_OTG_CTRL, FX3_OTG_CTRL_SSDEV_ENABLE);
  	Fx3UtilDelayUs(5);
  	Fx3ClearReg32(FX3_OTG_CTRL, FX3_OTG_CTRL_SSEPM_ENABLE);
  
          Fx3ClearReg32(FX3_UIB_INTR_MASK, FX3_UIB_INTR_DEV_CTL_INT |
  					FX3_UIB_INTR_DEV_EP_INT |
  					FX3_UIB_INTR_LNK_INT |
  					FX3_UIB_INTR_PROT_INT |
  					FX3_UIB_INTR_PROT_EP_INT |
  					FX3_UIB_INTR_EPM_URUN);
  
          *(volatile uint32_t *)(void*)(FX3_LNK_PHY_CONF) &= 0x1FFFFFFFUL;
          Fx3WriteReg32(FX3_LNK_PHY_MPLL_STATUS,0x910410);
  
  	/* Power cycle the PHY blocks. */
          Fx3ClearReg32(FX3_GCTL_CONTROL, FX3_GCTL_CONTROL_USB_POWER_EN);
  	Fx3UtilDelayUs(5);
  	Fx3SetReg32(FX3_GCTL_CONTROL, FX3_GCTL_CONTROL_USB_POWER_EN);
  	Fx3UtilDelayUs(10);
  
  	/* Clear USB 2.0 interrupts. */
  	Fx3WriteReg32(FX3_DEV_CTRL_INTR, ~0UL);
  	Fx3WriteReg32(FX3_DEV_EP_INTR, ~0UL);
  	Fx3WriteReg32(FX3_OTG_INTR, ~0UL);
  
  	/* Clear and disable USB 3.0 interrupts. */
  	Fx3WriteReg32(FX3_LNK_INTR_MASK, 0UL);
  	Fx3WriteReg32(FX3_LNK_INTR, ~0UL);
  	Fx3WriteReg32(FX3_PROT_INTR_MASK, 0UL);
  	Fx3WriteReg32(FX3_PROT_INTR, ~0UL);
  
          Fx3SetReg32(FX3_UIB_INTR_MASK, FX3_UIB_INTR_DEV_CTL_INT |
  					FX3_UIB_INTR_DEV_EP_INT |
  					FX3_UIB_INTR_LNK_INT |
  					FX3_UIB_INTR_PROT_INT |
  					FX3_UIB_INTR_PROT_EP_INT |
  					FX3_UIB_INTR_EPM_URUN);
  
  	/* Disable EP0-IN and EP0-OUT (USB-2). */
          Fx3ClearReg32(FX3_DEV_EPI_CS+0, FX3_DEV_EPI_CS_VALID);
          Fx3ClearReg32(FX3_DEV_EPO_CS+0, FX3_DEV_EPO_CS_VALID);
  	
  	Fx3WriteReg32(FX3_EHCI_PORTSC, (1UL << 22));
  
          Fx3WriteReg32(FX3_DEV_PWR_CS, (1UL << 2) | (1UL << 3));
  
  	/* Enable USB 2.0 PHY. */
  	Fx3UtilDelayUs(2);
  	Fx3SetReg32(FX3_OTG_CTRL, (1UL << 13));
  
  	Fx3UtilDelayUs(100);
          Fx3WriteReg32(FX3_PHY_CONF, 0xd4a480UL);
          Fx3WriteReg32(FX3_PHY_CLK_AND_TEST, 0xa0000011UL);
  	Fx3UtilDelayUs(80);
  
  	Fx3ClearReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
  	Fx3UtilDelayUs(5);
  	Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  			(2UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  			(0UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
  	Fx3SetReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
  	Fx3UtilDelayUs(5);
  
  	/* For USB 2.0 connections, enable pull-up on D+ pin. */
          Fx3ClearReg32(FX3_DEV_PWR_CS, (1UL << 3));
  
  
  }
  
  static void Fx3UsbConnectSuperSpeed(void)
  {
    Fx3WriteReg32(FX3_LNK_PHY_TX_TRIM, 0x0b569011UL);
    Fx3UsbWritePhyReg(0x1006, 0x180);
    Fx3UsbWritePhyReg(0x1024, 0x0080);
  
    /* Switch EPM clock to USB 3.0 mode */
    Fx3ClearReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
    Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  		(1UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  		(1UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
    Fx3SetReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
  
    Fx3WriteReg32(FX3_LNK_PHY_CONF,
  		FX3_LNK_PHY_CONF_RX_TERMINATION_ENABLE |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR_VAL |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR |
  		(1UL << FX3_LNK_PHY_CONF_PHY_MODE_SHIFT));
    Fx3SetReg32(FX3_OTG_CTRL, FX3_OTG_CTRL_SSEPM_ENABLE);
    
    /* Reset EPM mux */
    Fx3SetReg32(FX3_IEPM_CS,
  	      FX3_IEPM_CS_EPM_MUX_RESET |
  	      FX3_IEPM_CS_EPM_FLUSH);
    Fx3UtilDelayUs(1);
    Fx3ClearReg32(FX3_IEPM_CS,
  		FX3_IEPM_CS_EPM_MUX_RESET |
  		FX3_IEPM_CS_EPM_FLUSH);
    Fx3UtilDelayUs(1);
  
    Fx3UsbWritePhyReg(0x0030, 0x00c0);
    Fx3UsbWritePhyReg(0x1010, 0x0080);
    
    Fx3WriteReg32(FX3_EEPM_ENDPOINT+0,
  		(512UL << FX3_EEPM_ENDPOINT_PACKET_SIZE_SHIFT));
    Fx3WriteReg32(FX3_IEPM_ENDPOINT+0,
  		(512UL << FX3_IEPM_ENDPOINT_PACKET_SIZE_SHIFT));
  }
  
  static void Fx3UsbEnablePhy(void)
  {
    Fx3WriteReg32(FX3_OTG_INTR, ~0UL);
    Fx3WriteReg32(FX3_LNK_INTR, ~0UL);
    Fx3WriteReg32(FX3_LNK_INTR_MASK,
  		FX3_LNK_INTR_MASK_LTSSM_RESET |
  		FX3_LNK_INTR_MASK_LTSSM_DISCONNECT |
  		FX3_LNK_INTR_MASK_LTSSM_CONNECT |
  		FX3_LNK_INTR_MASK_LGO_U3 |
  		FX3_LNK_INTR_MASK_LTSSM_STATE_CHG);
    Fx3WriteReg32(FX3_PROT_EP_INTR_MASK, 0);
    Fx3WriteReg32(FX3_PROT_INTR, ~0UL);
    Fx3WriteReg32(FX3_PROT_INTR_MASK,
  		FX3_PROT_INTR_MASK_STATUS_STAGE |
  		FX3_PROT_INTR_MASK_SUTOK_EN |
  		FX3_PROT_INTR_MASK_TIMEOUT_PORT_CFG_EN |
  		FX3_PROT_INTR_MASK_TIMEOUT_PORT_CAP_EN |
  		FX3_PROT_INTR_MASK_LMP_PORT_CFG_EN |
  		FX3_PROT_INTR_MASK_LMP_PORT_CAP_EN |
  		FX3_PROT_INTR_MASK_LMP_RCV_EN);
    Fx3WriteReg32(FX3_DEV_CTRL_INTR, ~0UL);
    Fx3WriteReg32(FX3_DEV_CTRL_INTR_MASK,
  		FX3_DEV_CTRL_INTR_MASK_URESUME |
  		FX3_DEV_CTRL_INTR_MASK_SUDAV |
  		FX3_DEV_CTRL_INTR_MASK_HSGRANT |
  		FX3_DEV_CTRL_INTR_MASK_URESET |
  		FX3_DEV_CTRL_INTR_MASK_SUSP);
    Fx3WriteReg32(FX3_DEV_EP_INTR, ~0UL);
    Fx3WriteReg32(FX3_DEV_EP_INTR_MASK, 0);
    Fx3WriteReg32(FX3_UIB_INTR_MASK,
  		FX3_UIB_INTR_MASK_PROT_INT |
  		FX3_UIB_INTR_MASK_LNK_INT |
  		FX3_UIB_INTR_MASK_DEV_CTL_INT);
    Fx3WriteReg32(FX3_VIC_INT_ENABLE, (1UL << FX3_IRQ_USB_CORE));
    Fx3SetReg32(FX3_GCTL_CONTROL, FX3_GCTL_CONTROL_USB_POWER_EN);
  
    /* Setup LNK for superspeed */
    Fx3WriteReg32(FX3_LNK_DEVICE_POWER_CONTROL,
  		FX3_LNK_DEVICE_POWER_CONTROL_AUTO_U2 |
  		FX3_LNK_DEVICE_POWER_CONTROL_AUTO_U1);
    Fx3WriteReg32(0xe003309c, 10000);
    Fx3WriteReg32(0xe0033080, 10000);
    Fx3WriteReg32(0xe0033084, 0x00fa004b);
    Fx3WriteReg32(0xe00330c4, 0x00000177);
    Fx3WriteReg32(0xe0033078, 0x58);
    Fx3WriteReg32(FX3_PROT_LMP_PORT_CAPABILITY_TIMER, 2312);
    Fx3WriteReg32(FX3_PROT_LMP_PORT_CONFIGURATION_TIMER, 2312);
    Fx3SetReg32(FX3_LNK_COMPLIANCE_PATTERN_8,
  	      FX3_LNK_COMPLIANCE_PATTERN_8_LFPS);
    Fx3WriteReg32(FX3_LNK_PHY_CONF,
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR |
  		(1UL << FX3_LNK_PHY_CONF_PHY_MODE_SHIFT));
    uint32_t old_int_enable = Fx3ReadReg32(FX3_VIC_INT_ENABLE);
    Fx3WriteReg32(FX3_VIC_INT_CLEAR, ~0UL);
    Fx3ClearReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
    Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  		(2UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  		(2UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
    Fx3SetReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
    Fx3WriteReg32(FX3_LNK_LTSSM_STATE,
  		(0UL << FX3_LNK_LTSSM_STATE_LTSSM_OVERRIDE_VALUE_SHIFT) |
  		FX3_LNK_LTSSM_STATE_LTSSM_OVERRIDE_EN);
    Fx3SetReg32(FX3_OTG_CTRL, FX3_OTG_CTRL_SSDEV_ENABLE);
    Fx3UtilDelayUs(100);
    Fx3SetField32(FX3_LNK_CONF, EPM_FIRST_DELAY, 15UL);
    Fx3SetReg32(FX3_LNK_CONF, FX3_LNK_CONF_LDN_DETECTION);
    Fx3WriteReg32(FX3_LNK_PHY_MPLL_STATUS,
  		FX3_LNK_PHY_MPLL_STATUS_REF_SSP_EN |
  		(136UL << FX3_LNK_PHY_MPLL_STATUS_SSC_REF_CLK_SEL_SHIFT) |
  		(0UL << FX3_LNK_PHY_MPLL_STATUS_SSC_RANGE_SHIFT) |
  		FX3_LNK_PHY_MPLL_STATUS_SSC_EN |
  		(3UL << FX3_LNK_PHY_MPLL_STATUS_MPLL_MULTIPLIER_SHIFT));
    Fx3UsbWritePhyReg(0x30, 0xc0);
    Fx3ClearReg32(FX3_LNK_LTSSM_STATE, FX3_LNK_LTSSM_STATE_LTSSM_OVERRIDE_EN);
    Fx3WriteReg32(FX3_LNK_PHY_CONF,
  		FX3_LNK_PHY_CONF_RX_TERMINATION_ENABLE |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR_VAL |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR |
  		(1UL << FX3_LNK_PHY_CONF_PHY_MODE_SHIFT));
    Fx3UtilDelayUs(100);
    Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  		FX3_GCTL_UIB_CORE_CLK_CLK_EN |
  		(1UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  		(1UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
    Fx3WriteReg32(FX3_VIC_INT_ENABLE, old_int_enable);
  }
  
  void Fx3UsbConnect(void)
  {
    Fx3WriteReg32(FX3_GCTL_IOPOWER_INTR, ~0UL);
    Fx3WriteReg32(FX3_GCTL_IOPOWER_INTR_MASK, FX3_GCTL_IOPOWER_INTR_MASK_VBUS);
    Fx3WriteReg32(FX3_VIC_INT_ENABLE, (1UL << FX3_IRQ_GCTL_POWER));
    if (Fx3ReadReg32(FX3_GCTL_IOPOWER) & FX3_GCTL_IOPOWER_VBUS) {
      Fx3UartTxString("VBUS POWER!\n");
      Fx3UsbEnablePhy();
    }
  }
  
  void Fx3UsbStallEp0(Fx3UsbSpeed_t s)
  {
    Fx3UartTxString("STALL EP0!\n");
    if (s == FX3_USB_SUPER_SPEED){
      Fx3SetReg32(FX3_PROT_EPI_CS1+0, FX3_PROT_EPI_CS1_STALL);
      Fx3SetReg32(FX3_PROT_EPO_CS1+0, FX3_PROT_EPO_CS1_STALL);
      Fx3UtilDelayUs(1);
      Fx3SetReg32(FX3_PROT_CS, FX3_PROT_CS_SETUP_CLR_BUSY);
    } else {
      Fx3SetReg32(FX3_DEV_EPI_CS+0, FX3_DEV_EPI_CS_STALL);
      Fx3SetReg32(FX3_DEV_EPO_CS+0, FX3_DEV_EPO_CS_STALL);
      Fx3UtilDelayUs(1);
      Fx3SetReg32(FX3_DEV_CS, FX3_DEV_CS_SETUP_CLR_BUSY);
    }
  
  }
  
  
  
  void Fx3UsbUnstallEp0(Fx3UsbSpeed_t s)
  {
    if (s == FX3_USB_SUPER_SPEED){
      Fx3ClearReg32(FX3_PROT_EPI_CS1+0, FX3_PROT_EPI_CS1_STALL);
      Fx3ClearReg32(FX3_PROT_EPO_CS1+0, FX3_PROT_EPO_CS1_STALL);
      Fx3UtilDelayUs(1);
      Fx3SetReg32(FX3_PROT_CS, FX3_PROT_CS_SETUP_CLR_BUSY);
    } else {
      Fx3ClearReg32(FX3_DEV_EPI_CS+0,FX3_DEV_EPI_CS_STALL);
      Fx3ClearReg32(FX3_DEV_EPO_CS+0,FX3_DEV_EPO_CS_STALL);
      Fx3UtilDelayUs(1);
      Fx3SetReg32(FX3_DEV_CS, FX3_DEV_CS_SETUP_CLR_BUSY);
    }
    
  }
  
  
  
  void Fx3UsbDmaDataOut(uint8_t ep, volatile void *buffer, uint16_t length)
  {
    uint16_t d = Fx3DmaAllocateDescriptor();
    Fx3DmaSimpleTransferWrite(FX3_UIBIN_DMA_SCK(ep), d, buffer, length);
    Fx3DmaFreeDescriptor(d);
  }
  
  void Fx3UsbDmaDataIn(uint8_t ep, const volatile void *buffer, uint16_t length)
  {
    uint16_t d = Fx3DmaAllocateDescriptor();
    Fx3DmaSimpleTransferRead(FX3_UIB_DMA_SCK(ep), d, buffer, length);
    Fx3DmaFreeDescriptor(d);
  }
  
  static void Fx3UsbUsbCoreIsr(void)
  {
    uint32_t req = Fx3ReadReg32(FX3_UIB_INTR) & Fx3ReadReg32(FX3_UIB_INTR_MASK);
    Fx3WriteReg32(FX3_UIB_INTR, req);
  
    Fx3UartTxString("Fx3UsbUsbCoreIsr\n");
    if (req & FX3_UIB_INTR_PROT_INT) {
      Fx3UartTxString("  PROT\n");
      uint32_t prot_req =
        Fx3ReadReg32(FX3_PROT_INTR) & Fx3ReadReg32(FX3_PROT_INTR_MASK);
      Fx3WriteReg32(FX3_PROT_INTR, prot_req);
  
      if (prot_req & FX3_PROT_INTR_STATUS_STAGE) {
        Fx3UartTxString("    STATUS_STAGE\n");
      }
      if (prot_req & FX3_PROT_INTR_SUTOK_EV) {
        Fx3UartTxString("    SUTOK_EV\n");
        Fx3WriteReg32(FX3_PROT_EPO_CS1 + 0, FX3_PROT_EPO_CS1_VALID);
        Fx3WriteReg32(FX3_PROT_EPI_CS1 + 0, FX3_PROT_EPI_CS1_VALID);
  
        Fx3DmaAbortSocket(FX3_UIB_DMA_SCK(0));
        Fx3DmaAbortSocket(FX3_UIBIN_DMA_SCK(0));
        Fx3SetReg32(FX3_EEPM_ENDPOINT, FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
        Fx3UtilDelayUs(10);
        Fx3ClearReg32(FX3_EEPM_ENDPOINT, FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
   
        uint32_t sudat0 = Fx3ReadReg32(FX3_PROT_SETUP_DAT+0);
        uint32_t sudat1 = Fx3ReadReg32(FX3_PROT_SETUP_DAT+4);
        uint8_t req_type = sudat0 >> FX3_PROT_SETUP_DAT_SETUP_REQUEST_TYPE_SHIFT;
        uint16_t length = sudat1 >> (FX3_PROT_SETUP_DAT_SETUP_LENGTH_SHIFT - 32);
        if ((req_type & FX3_USB_REQTYPE_DIR_MASK) == FX3_USB_REQTYPE_IN)
  	/* IN transfer */
  	Fx3WriteReg32(FX3_DEV_EPI_XFER_CNT, length);
        else
  	/* OUT transfer */
  	Fx3WriteReg32(FX3_DEV_EPO_XFER_CNT, length);
        (*Fx3UsbUserCallbacks->sutok)
  	(req_type, sudat0 >> FX3_PROT_SETUP_DAT_SETUP_REQUEST_SHIFT,
  	 sudat0 >> FX3_PROT_SETUP_DAT_SETUP_VALUE_SHIFT,
  	 sudat1 >> (FX3_PROT_SETUP_DAT_SETUP_INDEX_SHIFT - 32),
  	 length, FX3_USB_SUPER_SPEED);
      }
      if (prot_req & FX3_PROT_INTR_TIMEOUT_PORT_CFG_EV) {
        Fx3UartTxString("    TIMEOUT_PORT_CFG_EV\n");
      }
      if (prot_req & FX3_PROT_INTR_TIMEOUT_PORT_CAP_EV) {
        Fx3UartTxString("    TIMEOUT_PORT_CAP_EV\n");
      }
      if (prot_req & FX3_PROT_INTR_LMP_PORT_CFG_EV) {
        Fx3UartTxString("    LMP_PORT_CFG_EV\n");
        /* Disable timeout */
        Fx3WriteReg32(FX3_PROT_LMP_PORT_CONFIGURATION_TIMER, 0x80008000UL);
        Fx3WriteReg32(FX3_PROT_INTR, FX3_PROT_INTR_TIMEOUT_PORT_CFG_EV);
      }
      if (prot_req & FX3_PROT_INTR_LMP_PORT_CAP_EV) {
        Fx3UartTxString("    LMP_PORT_CAP_EV\n");
        /* Disable timeout */
        Fx3WriteReg32(FX3_PROT_LMP_PORT_CAPABILITY_TIMER, 0x80008000UL);
        Fx3WriteReg32(FX3_PROT_INTR, FX3_PROT_INTR_TIMEOUT_PORT_CAP_EV);
      }
      if (prot_req & FX3_PROT_INTR_LMP_RCV_EV) {
        Fx3UartTxString("    LMP_RCV_EV\n");
      }
    }
    if (req & FX3_UIB_INTR_LNK_INT) {
      Fx3UartTxString("  LNK\n");
      uint32_t lnk_req =
        Fx3ReadReg32(FX3_LNK_INTR) & Fx3ReadReg32(FX3_LNK_INTR_MASK);
      Fx3WriteReg32(FX3_LNK_INTR, lnk_req);
  
      if (lnk_req & FX3_LNK_INTR_LTSSM_RESET) {
        Fx3UartTxString("    LTSSM_RESET\n");
      }
      if (lnk_req & FX3_LNK_INTR_LTSSM_DISCONNECT) {
        Fx3UartTxString("    LTSSM_DISCONNECT\n");
        Fx3UsbConnectHighSpeed();
      }
      if (lnk_req & FX3_LNK_INTR_LTSSM_CONNECT) {
        Fx3UartTxString("    LTSSM_CONNECT\n");
        Fx3UsbConnectSuperSpeed();
      }
      if (lnk_req & FX3_LNK_INTR_LGO_U3) {
        Fx3UartTxString("    LGO_U3\n");
      }
      if (lnk_req & FX3_LNK_INTR_LTSSM_STATE_CHG) {
        Fx3UartTxString("    LTSSM_STATE_CHG %u\n");
      }
    }
    if (req & FX3_UIB_INTR_DEV_CTL_INT) {
  	Fx3UartTxString("  DEV_CTL\n");
  	uint32_t dev_ctrl_req;
  	dev_ctrl_req = Fx3ReadReg32(FX3_DEV_CTRL_INTR) & Fx3ReadReg32(FX3_DEV_CTRL_INTR_MASK);
          Fx3WriteReg32(FX3_DEV_CTRL_INTR, dev_ctrl_req);
          if (dev_ctrl_req & (1UL << 8))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 8));
  		Fx3UartTxString("    resume\n");
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 8));
  	}
  	if (dev_ctrl_req & (1UL << 2))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 2));
  		Fx3UartTxString("    suspend\n");
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 2));
  	}
  
  	if (dev_ctrl_req & (1UL << 3))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 3));
  		Fx3UartTxString("    reset\n");
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 3));
  	}
  	if (dev_ctrl_req & (1UL << 4))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 4));
  		Fx3UartTxString("    hsgrant\n");
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 4));
  	}
  	if (dev_ctrl_req & (1UL << 11))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 11));
  		Fx3UartTxString("    status\n");
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 11));
  	}
  	if (dev_ctrl_req & (1UL << 6))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 6));
  		Fx3UartTxString("    sudav\n");
  		Fx3WriteReg32(FX3_DEV_EPO_CS + 0, FX3_DEV_EPO_CS_VALID);
  	        Fx3WriteReg32(FX3_DEV_EPI_CS + 0, FX3_DEV_EPI_CS_VALID);
  
  		Fx3DmaAbortSocket(FX3_UIB_DMA_SCK(0));
  	        Fx3DmaAbortSocket(FX3_UIBIN_DMA_SCK(0));
  		Fx3SetReg32(FX3_EEPM_ENDPOINT, FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
  	        Fx3UtilDelayUs(10);
  		Fx3ClearReg32(FX3_EEPM_ENDPOINT, FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
  
  		uint32_t setupdat0;
  		uint32_t setupdat1;
  		setupdat0 = Fx3ReadReg32(FX3_DEV_SETUPDAT+0);
  		setupdat1 = Fx3ReadReg32(FX3_DEV_SETUPDAT+4);
  
  		uint8_t req_type = setupdat0 >> 0;
  		uint16_t length = setupdat1 >> 	(48 - 32);
  		if ((req_type & FX3_USB_REQTYPE_DIR_MASK) == FX3_USB_REQTYPE_IN)
  			/* IN transfer */
  			Fx3WriteReg32(FX3_DEV_EPI_XFER_CNT, length);
  		else
  			/* OUT transfer */
  			Fx3WriteReg32(FX3_DEV_EPO_XFER_CNT, length);
  			(*Fx3UsbUserCallbacks->sutok)
  			(req_type, setupdat0 >> FX3_PROT_SETUP_DAT_SETUP_REQUEST_SHIFT,
  			 setupdat0 >> FX3_PROT_SETUP_DAT_SETUP_VALUE_SHIFT,
  			 setupdat1 >> (FX3_PROT_SETUP_DAT_SETUP_INDEX_SHIFT - 32),
  			 length, FX3_USB_HIGH_SPEED); 
  		
  	}
  	if (dev_ctrl_req & (1UL << 7))
  	{
  		Fx3WriteReg32(FX3_DEV_CTRL_INTR, (1UL << 7));
  		Fx3SetReg32(FX3_DEV_CTRL_INTR_MASK, (1UL << 7));
  	}
    }
  
    Fx3WriteReg32(FX3_VIC_ADDRESS, 0);
  }  
  
  static void Fx3UsbGctlCoreIsr(void)
  {
    uint32_t req = Fx3ReadReg32(FX3_GCTL_WAKEUP_EVENT) & Fx3ReadReg32(FX3_GCTL_WAKEUP_EN);
    Fx3WriteReg32(FX3_GCTL_WAKEUP_EVENT, req);
  
    Fx3UartTxString("Fx3UsbGctlCoreIsr\n");
  
    Fx3WriteReg32(FX3_VIC_ADDRESS, 0);
  }
  
  static void Fx3UsbGctlPowerIsr(void)
  {
    uint32_t req = Fx3ReadReg32(FX3_GCTL_IOPOWER_INTR) & Fx3ReadReg32(FX3_GCTL_IOPOWER_INTR_MASK);
    Fx3WriteReg32(FX3_GCTL_IOPOWER_INTR, req);
  
    Fx3UartTxString("Fx3UsbGctlPowerIsr\n");
  
    if (req & FX3_GCTL_IOPOWER_INTR_VBUS) {
      Fx3UartTxString("  VBUS\n");
    }
  
    Fx3WriteReg32(FX3_VIC_ADDRESS, 0);
  }
  
  void Fx3UsbInit(const struct Fx3UsbCallbacks *callbacks)
  {
    Fx3UsbUserCallbacks = callbacks;
  
    /* Select bus clock */
    Fx3ClearReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
    Fx3WriteReg32(FX3_GCTL_UIB_CORE_CLK,
  		(2UL << FX3_GCTL_UIB_CORE_CLK_PCLK_SRC_SHIFT) |
  		(2UL << FX3_GCTL_UIB_CORE_CLK_EPMCLK_SRC_SHIFT));
    Fx3SetReg32(FX3_GCTL_UIB_CORE_CLK, FX3_GCTL_UIB_CORE_CLK_CLK_EN);
    Fx3UtilDelayUs(5);
  
    Fx3WriteReg32(FX3_GCTL_CONTROL,
  		FX3_GCTL_CONTROL_HARD_RESET_N |
  		FX3_GCTL_CONTROL_CPU_RESET_N |
  		FX3_GCTL_CONTROL_BOOTROM_EN |
  		(1UL<<27) |
  		FX3_GCTL_CONTROL_MAIN_POWER_EN |
  		FX3_GCTL_CONTROL_MAIN_CLOCK_EN |
  		FX3_GCTL_CONTROL_WAKEUP_CPU_INT |
  		FX3_GCTL_CONTROL_WAKEUP_CLK |
  		FX3_GCTL_CONTROL_POR);
  
    /* Reset USB */
    Fx3ClearReg32(FX3_UIB_POWER, FX3_UIB_POWER_RESETN);
    Fx3UtilDelayUs(50);
    Fx3SetReg32(FX3_UIB_POWER, FX3_UIB_POWER_RESETN);
    Fx3UtilDelayUs(100);
    while((Fx3ReadReg32(FX3_UIB_POWER) & FX3_UIB_POWER_ACTIVE) == 0)
      ;
  
    Fx3WriteReg32(FX3_OTG_CTRL, 0);
  
    Fx3WriteReg32(FX3_PHY_CLK_AND_TEST,
  		(1UL<<31) |
  		FX3_PHY_CLK_AND_TEST_SUSPEND_N |
  		FX3_PHY_CLK_AND_TEST_VLOAD |
  		FX3_PHY_CLK_AND_TEST_DATABUS16_8);
    Fx3SetReg32(FX3_DEV_PWR_CS, FX3_DEV_PWR_CS_DISCON);
    Fx3WriteReg32(FX3_GCTL_WAKEUP_EN, 0);
    Fx3WriteReg32(FX3_GCTL_WAKEUP_POLARITY, 0);
    Fx3WriteReg32(FX3_LNK_PHY_CONF,
  		FX3_LNK_PHY_CONF_RX_TERMINATION_ENABLE |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR_VAL |
  		FX3_LNK_PHY_CONF_RX_TERMINATION_OVR |
  		(1UL << FX3_LNK_PHY_CONF_PHY_MODE_SHIFT));
    Fx3WriteReg32(FX3_LNK_ERROR_CONF, ~0UL);
    Fx3WriteReg32(FX3_LNK_INTR, ~0UL);
    Fx3WriteReg32(FX3_LNK_INTR_MASK,
  		FX3_LNK_INTR_MASK_LTSSM_RESET |
  		FX3_LNK_INTR_MASK_LTSSM_DISCONNECT |
  		FX3_LNK_INTR_MASK_LTSSM_CONNECT |
  		FX3_LNK_INTR_MASK_LGO_U3 |
  		FX3_LNK_INTR_MASK_LTSSM_STATE_CHG);
    Fx3WriteReg32(FX3_PROT_EP_INTR_MASK, 0);
    Fx3WriteReg32(FX3_PROT_INTR, ~0UL);
    Fx3WriteReg32(FX3_PROT_INTR_MASK,
  		FX3_PROT_INTR_MASK_STATUS_STAGE |
  		FX3_PROT_INTR_MASK_SUTOK_EN |
  		FX3_PROT_INTR_MASK_TIMEOUT_PORT_CFG_EN |
  		FX3_PROT_INTR_MASK_TIMEOUT_PORT_CAP_EN |
  		FX3_PROT_INTR_MASK_LMP_PORT_CFG_EN |
  		FX3_PROT_INTR_MASK_LMP_PORT_CAP_EN |
  		FX3_PROT_INTR_MASK_LMP_RCV_EN);
  
    Fx3WriteReg32(FX3_PHY_CONF,
  		FX3_PHY_CONF_PREEMDEPTH |
  		FX3_PHY_CONF_ENPRE |
  		(1UL << FX3_PHY_CONF_FSRFTSEL_SHIFT) |
  		(1UL << FX3_PHY_CONF_LSRFTSEL_SHIFT) |
  		(2UL << FX3_PHY_CONF_HSTEDVSEL_SHIFT) |
  		(4UL << FX3_PHY_CONF_FSTUNEVSEL_SHIFT) |
  		(2UL << FX3_PHY_CONF_HSDEDVSEL_SHIFT) |
  		(4UL << FX3_PHY_CONF_HSDRVSLOPE_SHIFT));
  
    Fx3WriteReg32(FX3_DEV_CTRL_INTR, ~0UL);
    Fx3WriteReg32(FX3_DEV_CTRL_INTR_MASK,
  		FX3_DEV_CTRL_INTR_MASK_URESUME |
  		FX3_DEV_CTRL_INTR_MASK_SUDAV |
  		FX3_DEV_CTRL_INTR_MASK_HSGRANT |
  		FX3_DEV_CTRL_INTR_MASK_URESET |
  		FX3_DEV_CTRL_INTR_MASK_SUSP);
    Fx3WriteReg32(FX3_DEV_EP_INTR, ~0UL);
    Fx3WriteReg32(FX3_DEV_EP_INTR_MASK, 0);
  
    /* USB3 EP0 valid as control */
    Fx3WriteReg32(FX3_PROT_EPO_CS1+0, FX3_PROT_EPO_CS1_VALID);
    Fx3WriteReg32(FX3_PROT_EPI_CS1+0, FX3_PROT_EPI_CS1_VALID);
    Fx3WriteReg32(FX3_PROT_EPO_CS2+0,
  		(16UL << FX3_PROT_EPO_CS2_ISOINPKS_SHIFT) |
  		(3UL << FX3_PROT_EPO_CS2_TYPE_SHIFT));
    Fx3WriteReg32(FX3_PROT_EPI_CS2+0,
  		(16UL << FX3_PROT_EPI_CS2_ISOINPKS_SHIFT) |
  		(3UL << FX3_PROT_EPI_CS2_TYPE_SHIFT));
  
    /* USB2 EP0 valid as control */
    Fx3WriteReg32(FX3_DEV_EPI_CS+0,
  		FX3_DEV_EPI_CS_VALID |
  		(0UL << FX3_DEV_EPI_CS_TYPE_SHIFT) |
  		(64UL << FX3_DEV_EPI_CS_PAYLOAD_SHIFT));
    Fx3WriteReg32(FX3_DEV_EPO_CS+0,
  		FX3_DEV_EPO_CS_VALID |
  		(0UL << FX3_DEV_EPO_CS_TYPE_SHIFT) |
  		(64UL << FX3_DEV_EPO_CS_PAYLOAD_SHIFT));
  
    /* Endpoint manager settings for EP0 */
    Fx3WriteReg32(FX3_EEPM_ENDPOINT+0,
  		(64UL << FX3_EEPM_ENDPOINT_PACKET_SIZE_SHIFT));
    Fx3WriteReg32(FX3_IEPM_ENDPOINT+0,
  		(64UL << FX3_IEPM_ENDPOINT_PACKET_SIZE_SHIFT));
  
    /* Invalidate all other EPs */
    unsigned i;
    for (i=1; i<16; i++) {
      Fx3ClearReg32(FX3_DEV_EPO_CS + (i<<2), FX3_DEV_EPO_CS_VALID);
      Fx3WriteReg32(FX3_PROT_EPO_CS1 + (i<<2), 0);
      Fx3ClearReg32(FX3_DEV_EPI_CS + (i<<2), FX3_DEV_EPI_CS_VALID);
      Fx3WriteReg32(FX3_PROT_EPI_CS1 + (i<<2), 0);
    }
  
    Fx3WriteReg32(FX3_UIB_INTR_MASK, 0);
  
    /* Configure vectored interrupt controller */
    Fx3WriteReg32(FX3_VIC_VEC_ADDRESS + (FX3_IRQ_GCTL_CORE<<2), Fx3UsbGctlCoreIsr);
    Fx3WriteReg32(FX3_VIC_VEC_ADDRESS + (FX3_IRQ_USB_CORE<<2), Fx3UsbUsbCoreIsr);
    Fx3WriteReg32(FX3_VIC_VEC_ADDRESS + (FX3_IRQ_GCTL_POWER<<2), Fx3UsbGctlPowerIsr);
    Fx3WriteReg32(FX3_VIC_INT_CLEAR,
  		(1UL << FX3_IRQ_GCTL_POWER) | (1UL << FX3_IRQ_USB_CORE) | (1UL << FX3_IRQ_GCTL_CORE));
  }
  
  void Fx3UsbEnableInEndpoint(uint8_t ep, Fx3UsbEndpointType_t type, uint16_t pktsize)
  {
    
    static const uint8_t usb2_type_map[] = {
      [FX3_USB_EP_ISOCHRONOUS] = 1,
      [FX3_USB_EP_INTERRUPT] = 3,
      [FX3_USB_EP_BULK] = 2,
      [FX3_USB_EP_CONTROL] = 0,
    };
  
    /* USB3 EP valid */
    Fx3WriteReg32(FX3_PROT_EPI_CS1+(ep<<2), FX3_PROT_EPI_CS1_VALID);
    Fx3WriteReg32(FX3_PROT_EPI_CS2+(ep<<2),
  		(16UL << FX3_PROT_EPI_CS2_ISOINPKS_SHIFT) |
  		((type << FX3_PROT_EPI_CS2_TYPE_SHIFT) & FX3_PROT_EPI_CS2_TYPE_MASK));
  
    /* USB2 EP valid */
    Fx3WriteReg32(FX3_DEV_EPI_CS+(ep<<2),
  		FX3_DEV_EPI_CS_VALID |
  		(usb2_type_map[type&3] << FX3_DEV_EPI_CS_TYPE_SHIFT) |
  		((pktsize << FX3_DEV_EPI_CS_PAYLOAD_SHIFT) & FX3_DEV_EPI_CS_PAYLOAD_MASK));
  
    /* Endpoint manager settings for EP */
    Fx3WriteReg32(FX3_EEPM_ENDPOINT+(ep<<2),
  		(pktsize << FX3_EEPM_ENDPOINT_PACKET_SIZE_SHIFT)
  		& FX3_EEPM_ENDPOINT_PACKET_SIZE_MASK);
  }
  
  void Fx3UsbFlushInEndpoint(uint8_t ep)
  {
    Fx3SetReg32(FX3_EEPM_ENDPOINT+(ep<<2), FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
    Fx3UtilDelayUs(5);
    Fx3ClearReg32(FX3_EEPM_ENDPOINT+(ep<<2), FX3_EEPM_ENDPOINT_SOCKET_FLUSH);
  }
  