Repostiory containing DAPLink source code with Reset Pin workaround for HANI_IOT board.

Upstream: https://github.com/ARMmbed/DAPLink

source/hic_hal/maxim/max32620/usbd_max32620.c

Committer:
Pawel Zarembski
Date:
2020-04-07
Revision:
0:01f31e923fe2

File content as of revision 0:01f31e923fe2:

/* CMSIS-DAP Interface Firmware
 * Copyright (c) 2009-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string.h>
#include "rl_usb.h"
#include "util.h"

#include "max32620.h"
#include "usb_regs.h"
#include "clkman_regs.h"
#include "pwrman_regs.h"
#include "tmr_regs.h"

#define __NO_USB_LIB_C
#include "usb_config.c"

#define EPNUM_MASK  (~USB_ENDPOINT_DIRECTION_MASK)

#define INIT_INTS     (MXC_F_USB_DEV_INTEN_BRST | MXC_F_USB_DEV_INTFL_BRST_DN | MXC_F_USB_DEV_INTEN_VBUS | MXC_F_USB_DEV_INTFL_NO_VBUS)
#define CONNECT_INTS  (MXC_F_USB_DEV_INTEN_SETUP | MXC_F_USB_DEV_INTEN_EP_IN | MXC_F_USB_DEV_INTEN_EP_OUT | MXC_F_USB_DEV_INTEN_DMA_ERR)

typedef struct {
	volatile uint32_t buf0_desc;
	volatile uint32_t buf0_address;
	volatile uint32_t buf1_desc;
	volatile uint32_t buf1_address;
} ep_buffer_t;

typedef struct {
	ep_buffer_t out_buffer;
	ep_buffer_t in_buffer;
} ep0_buffer_t;

typedef struct {
	ep0_buffer_t ep0;
	ep_buffer_t ep[MXC_USB_NUM_EP - 1];
} ep_buffer_descriptor_t;

typedef struct {
  U8 type;
  U16 len;
} ep_info_t;

/* static storage for endpoint buffer descriptor table, must be 512 byte aligned for DMA */
__attribute__ ((aligned (512)))
ep_buffer_descriptor_t ep_buffer_descriptor;

static uint32_t ep_buffer[MXC_USB_NUM_EP][MXC_USB_MAX_PACKET / sizeof(uint32_t)];
static ep_info_t ep_info[MXC_USB_NUM_EP];
static volatile int suspended;
static volatile int setup_waiting;
static volatile int ep0_expect_zlp;

#if CDC_ENDPOINT
/* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
 * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
 * timer interrupt is used instead.
 */
/******************************************************************************/
void TMR0_IRQHandler(void)
{
    MXC_TMR0->intfl = MXC_TMR0->intfl;

    if (usbd_configured()) {
        USBD_CDC_ACM_SOF_Event();
    }
}
#endif

/******************************************************************************/
static ep_buffer_t *get_desc(U32 EPNum)
{
  ep_buffer_t *desc;

  if (EPNum == 0x80) {
    desc = &ep_buffer_descriptor.ep0.in_buffer;
  } else if (EPNum == 0x00) {
    desc = &ep_buffer_descriptor.ep0.out_buffer;
  } else {
    desc = &ep_buffer_descriptor.ep[(EPNum & EPNUM_MASK) - 1];
  }

  return desc;
}

/*
 *  USB Device Interrupt enable
 *   Called by USBD_Init to enable the USB Interrupt
 *    Return Value:    None
 */

void          USBD_IntrEna(void)
{
    NVIC_EnableIRQ(USB_IRQn);            /* Enable OTG interrupt */
}

/******************************************************************************/
/*
 *  Usb interrupt enable/disable
 *    Parameters:      ena: enable/disable
 *                       0: disable interrupt
 *                       1: enable interrupt
 */
#ifdef __RTX
void __svc(1) USBD_Intr (int ena);
void __SVC_1 (int ena)
{
  if (ena) {
    NVIC_EnableIRQ(USB_IRQn);           /* Enable USB interrupt               */
  } else {
    NVIC_DisableIRQ(USB_IRQn);          /* Disable USB interrupt              */
  }
}
#endif

/******************************************************************************/
static void reset_state(void)
{
  unsigned int ep;

  suspended = 0;
  setup_waiting = 0;
  ep0_expect_zlp = 0;
  memset(ep_info, 0, sizeof(ep_info));

  MXC_USB->ep[0] |= (MXC_S_USB_EP_DIR_CONTROL | MXC_F_USB_EP_INT_EN | MXC_F_USB_EP_DT);
  for (ep = 1; ep < MXC_USB_NUM_EP; ep++) {
    MXC_USB->ep[ep] = MXC_F_USB_EP_DT;
  }
}

/*
 *  USB Device Initialize Function
 *   Called by the User to initialize USB Device
 *    Return Value:    None
 */
void USBD_Init (void)
{
	uint32_t reg;

	/* Enable USB power domain */
	MXC_PWRMAN->pwr_rst_ctrl |= MXC_F_PWRMAN_PWR_RST_CTRL_USB_POWERED;
  /* Setup the USB clocking, select  */
  MXC_CLKMAN->clk_ctrl |= MXC_F_CLKMAN_CLK_CTRL_USB_CLOCK_ENABLE;
	/* Force USB clock gater */
	reg = MXC_CLKMAN->clk_gate_ctrl0;
	reg &= ~MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER;
	reg |= (0x2 << MXC_F_CLKMAN_CLK_GATE_CTRL0_USB_CLK_GATER_POS);
	MXC_CLKMAN->clk_gate_ctrl0 = reg;

  MXC_USB->cn = 0;
  MXC_USB->cn = MXC_F_USB_CN_USB_EN;
  MXC_USB->dev_inten = 0;
  MXC_USB->dev_intfl = 0xFFFF;  // clear interrupts
  MXC_USB->dev_cn = 0;
  MXC_USB->dev_cn |= MXC_F_USB_DEV_CN_URST;
  MXC_USB->dev_cn = 0;

  reset_state();

  /* set the descriptor location */
  MXC_USB->ep_base = (uint32_t)&ep_buffer_descriptor;

  /* enable some interrupts */
  MXC_USB->dev_inten = INIT_INTS;
  NVIC_EnableIRQ(USB_IRQn);
}

/*
 *  USB Device Connect Function
 *   Called by the User to Connect/Disconnect USB Device
 *    Parameters:      con:   Connect/Disconnect
 *    Return Value:    None
 */
void USBD_Connect (BOOL con)
{
  if (con) {
    MXC_USB->dev_intfl = 0xFFFF;  // clear interrupts
    MXC_USB->dev_inten |= CONNECT_INTS;
    MXC_USB->ep[0] |= MXC_F_USB_EP_INT_EN;
    MXC_USB->dev_cn |= (MXC_F_USB_DEV_CN_CONNECT | MXC_F_USB_DEV_CN_FIFO_MODE);
  } else {
    MXC_USB->dev_inten &= ~CONNECT_INTS;
    MXC_USB->ep[0] &= ~MXC_F_USB_EP_INT_EN;
    MXC_USB->dev_cn &= ~MXC_F_USB_DEV_CN_CONNECT;
  }
}

/*
 *  USB Device Remote Wakeup Configuration Function
 *    Parameters:      cfg:   Device Enable/Disable
 *    Return Value:    None
 */
void USBD_WakeUpCfg (BOOL cfg)
{
}

/*
 *  USB Device Set Address Function
 *    Parameters:      adr:   USB Device Address
 *    Return Value:    None
 */
void USBD_SetAddress (U32 adr, U32 setup)
{
  /* Performed by Hardware */
}

/*
 *  USB Device Configure Function
 *    Parameters:      cfg:   Device Configure/Deconfigure
 *    Return Value:    None
 */
void USBD_Configure (BOOL cfg)
{
#if CDC_ENDPOINT
    /* CDC-ACM class processes FIFOs in the SOF interrupt. The USB Device interface
     * of Maxim's microcontrollers does not provide and SOF interrupt. A periodic
     * timer interrupt is used instead.
     */

    #define SOF_INT_US  1000

    if (cfg) {
        // Setup timer interrupt for SOF
        MXC_TMR0->ctrl = MXC_S_TMR_CTRL_MODE_CONTINUOUS;
        MXC_TMR0->count32 = 0;
        MXC_TMR0->term_cnt32 = (SystemCoreClock / 1000000) * SOF_INT_US;

        // Enable the interrupt
        MXC_TMR0->intfl = MXC_TMR0->intfl;
        NVIC_EnableIRQ(TMR0_0_IRQn);
        MXC_TMR0->inten = MXC_F_TMR_INTEN_TIMER0;

        // Start the timer
        MXC_TMR0->ctrl |= MXC_F_TMR_CTRL_ENABLE0;

    } else {
        // Disable tmr
        MXC_TMR0->ctrl &= ~(MXC_F_TMR_CTRL_ENABLE0);
    }
#endif
}

/*
 *  Configure USB Device Endpoint according to Descriptor
 *    Parameters:      pEPD:  Pointer to Device Endpoint Descriptor
 *    Return Value:    None
 */
void USBD_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD)
{
  U32 EPNum;

  EPNum = pEPD->bEndpointAddress & EPNUM_MASK;

  if (EPNum < MXC_USB_NUM_EP) {

    // Clear existing configurations
    MXC_USB->ep[EPNum] = MXC_F_USB_EP_DT;

    if (pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) {
      ep_info[EPNum].type = MXC_S_USB_EP_DIR_IN;
    } else {
      ep_info[EPNum].type = MXC_S_USB_EP_DIR_OUT;
    }

    ep_info[EPNum].len = pEPD->wMaxPacketSize;
  }
}

/*
 *  Set Direction for USB Device Control Endpoint
 *    Parameters:      dir:   Out (dir == 0), In (dir <> 0)
 *    Return Value:    None
 */
void USBD_DirCtrlEP (U32 dir)
{
  /* Not needed */
}

/*
 *  Enable USB Device Endpoint
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *    Return Value:    None
 */
void USBD_EnableEP (U32 EPNum)
{
  ep_buffer_t *desc = get_desc(EPNum);

  EPNum &= EPNUM_MASK;
  MXC_USB->ep[EPNum] |= (MXC_F_USB_EP_INT_EN | ep_info[EPNum].type | MXC_F_USB_EP_DT);

  if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
    // This is an OUT endpoint. Go ahead and register a request.
    desc = get_desc(EPNum);
    desc->buf0_address = (uint32_t)ep_buffer[EPNum];
    desc->buf0_desc = sizeof(ep_buffer[EPNum]);
    MXC_USB->out_owner = (1 << EPNum);
  }
}

/*
 *  Disable USB Device Endpoint
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *    Return Value:    None
 */
void USBD_DisableEP (U32 EPNum)
{
  EPNum &= EPNUM_MASK;
  MXC_USB->ep[EPNum] = 0;
}

/*
 *  Reset USB Device Endpoint
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *    Return Value:    None
 */
void USBD_ResetEP (U32 EPNum)
{
  ep_buffer_t *desc = get_desc(EPNum);

  EPNum &= EPNUM_MASK;
  MXC_USB->ep[EPNum] |= MXC_F_USB_EP_DT;

  if (ep_info[EPNum].type == MXC_S_USB_EP_DIR_OUT) {
    // This is an OUT endpoint. Go ahead and register a request.
    desc = get_desc(EPNum);
    desc->buf0_address = (uint32_t)ep_buffer[EPNum];
    desc->buf0_desc = sizeof(ep_buffer[EPNum]);
    MXC_USB->out_owner = (1 << EPNum);
  }
}

/*
 *  Set Stall for USB Device Endpoint
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *    Return Value:    None
 */
void USBD_SetStallEP (U32 EPNum)
{
  EPNum &= EPNUM_MASK;

  if (EPNum == 0) {
    MXC_USB->ep[0] |= (MXC_F_USB_EP_ST_STALL | MXC_F_USB_EP_STALL);
  } else {
    MXC_USB->ep[EPNum] |= MXC_F_USB_EP_STALL;
  }
}

/*
 *  Clear Stall for USB Device Endpoint
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *    Return Value:    None
 */
void USBD_ClrStallEP (U32 EPNum)
{
  USBD_ResetEP(EPNum);
  MXC_USB->ep[EPNum & EPNUM_MASK] &= ~MXC_F_USB_EP_STALL;
}

/*
 *  Read USB Device Endpoint Data
 *    Parameters:      EPNum: Device Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *                     pData: Pointer to Data Buffer
 *    Return Value:    Number of bytes read
 */
U32 USBD_ReadEP (U32 EPNum, U8 *pData, U32 size)
{
    U32 cnt;
  ep_buffer_t *desc = get_desc(EPNum);
  USB_SETUP_PACKET *sup;

  EPNum &= EPNUM_MASK;

  if ((EPNum == 0) && setup_waiting) {
        cnt = USBD_MAX_PACKET0;

        if (size < cnt) {
            cnt = size;
        }
    setup_waiting = 0;
    memcpy(pData, (void*)&MXC_USB->setup0, cnt);
    sup = (USB_SETUP_PACKET*)pData;

    if ( (sup->bmRequestType.Dir == REQUEST_HOST_TO_DEVICE) && (sup->wLength > 0) ) {
      // There is an OUT stage for this setup packet. Register a request.
      if (!(MXC_USB->out_owner & 1)) {
        desc = &ep_buffer_descriptor.ep0.out_buffer;
        desc->buf0_address = (uint32_t)ep_buffer[0];
        desc->buf0_desc = sup->wLength;
        MXC_USB->out_owner = 1;
      }
    }
  } else {
    cnt = desc->buf0_desc;

        if (size < cnt) {
            cnt = size;
        }
    memcpy(pData, ep_buffer[EPNum], cnt);

    // Register the next request.
    desc->buf0_address = (uint32_t)ep_buffer[EPNum];
    desc->buf0_desc = sizeof(ep_buffer[EPNum]);
    MXC_USB->out_owner = (1 << EPNum);
  }

  return cnt;
}

/*
 *  Write USB Device Endpoint Data
 *    Parameters:      EPNum: Endpoint Number
 *                       EPNum.0..3: Address
 *                       EPNum.7:    Dir
 *                     pData: Pointer to Data Buffer
 *                     cnt:   Number of bytes to write
 *    Return Value:    Number of bytes written
 */
U32 USBD_WriteEP (U32 EPNum, U8 *pData, U32 cnt)
{
  ep_buffer_t *desc = get_desc(EPNum);
  uint32_t mask;

  EPNum &= EPNUM_MASK;
  mask = (1 << EPNum);

  if (MXC_USB->in_owner & mask) {
    return 0;
  }

  if (EPNum == 0) {
    // Prepare to ACK the status stage.
    MXC_USB->ep[0] |= MXC_F_USB_EP_ST_ACK;

    if ((cnt == 0) && !ep0_expect_zlp) {
      // This is a status stage ACK. Handled in hardware.
      return 0;
    } else if (cnt == USBD_MAX_PACKET0) {
      ep0_expect_zlp = 1;
    } else {
      ep0_expect_zlp = 0;
    }
  }

  if (cnt > MXC_USB_MAX_PACKET) {
    cnt = MXC_USB_MAX_PACKET;
  }

  /* prepare data to be sent */
  memcpy(ep_buffer[EPNum], pData, cnt);
  desc->buf0_address = (uint32_t)ep_buffer[EPNum];
  desc->buf0_desc = cnt;

  /* start the transaction */
  MXC_USB->in_owner = mask;

  return cnt;
}

/*
 *  USB Device Interrupt Service Routine
 */
void USB_IRQHandler (void)
{
    NVIC_DisableIRQ(USB_IRQn);
    USBD_SignalHandler();
}

void USBD_Handler(void)
{

#ifdef __RTX
  while(1) {}
#else

#endif

  uint32_t irq_flags;
  unsigned int ep;
  uint32_t ep_int, mask;

  // Read and clear interrupts
  irq_flags = MXC_USB->dev_intfl;
  MXC_USB->dev_intfl = irq_flags;

  /* reset interrupt */
  if (irq_flags & MXC_F_USB_DEV_INTFL_BRST) {
    if (suspended) {
      suspended = 0;
#ifdef __RTX
      if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_RESUME, USBD_RTX_DevTask); }
#else
      if (USBD_P_Resume_Event) { USBD_P_Resume_Event(); }
#endif
    }

    reset_state();
    usbd_reset_core();

#ifdef __RTX
    if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_RESET, USBD_RTX_DevTask); }
#else
    if (USBD_P_Reset_Event) { USBD_P_Reset_Event(); }
#endif

  }

  /* reset done interrupt */
  if (irq_flags & MXC_F_USB_DEV_INTFL_BRST_DN) {
    reset_state();
  }

  /* suspend interrupt */
  if (irq_flags & MXC_F_USB_DEV_INTFL_SUSP) {
    suspended = 1;
#ifdef __RTX
    if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_SUSPEND, USBD_RTX_DevTask); }
#else
    if (USBD_P_Suspend_Event) { USBD_P_Suspend_Event(); }
#endif
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_VBUS) {
#ifdef __RTX
    if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_POWER_ON,  USBD_RTX_DevTask); }
#else
    if (USBD_P_Power_Event) { USBD_P_Power_Event(1); }
#endif
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_NO_VBUS) {
#ifdef __RTX
    if (USBD_RTX_DevTask) { isr_evt_set(USBD_EVT_POWER_OFF, USBD_RTX_DevTask); }
#else
    if (USBD_P_Power_Event) { USBD_P_Power_Event(0); }
#endif
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_SETUP) {
    setup_waiting = 1;
#ifdef __RTX
    if (USBD_RTX_EPTask[0]) { isr_evt_set(USBD_EVT_SETUP, USBD_RTX_EPTask[0]); }
#else
    if (USBD_P_EP[0]) { USBD_P_EP[0](USBD_EVT_SETUP); }
#endif
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_EP_IN) {

    // Read and clear endpoint interrupts
    ep_int = MXC_USB->in_int;
    MXC_USB->in_int = ep_int;

    mask = 1;
    for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
      if (ep_int & mask) {
#ifdef __RTX
        if (USBD_RTX_EPTask[ep]) { isr_evt_set(USBD_EVT_IN,  USBD_RTX_EPTask[ep]); }
#else
        if (USBD_P_EP[ep]) { USBD_P_EP[ep](USBD_EVT_IN); }
#endif
      }

      mask <<= 1;
    }
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_EP_OUT) {

    // Read and clear endpoint interrupts
    ep_int = MXC_USB->out_int;
    MXC_USB->out_int = ep_int;

    mask = 1;
    for (ep = 0; ep < MXC_USB_NUM_EP; ep++) {
      if (ep_int & mask) {
#ifdef __RTX
        if (USBD_RTX_EPTask[ep]) { isr_evt_set(USBD_EVT_OUT, USBD_RTX_EPTask[ep]); }
#else
        if (USBD_P_EP[ep]) { USBD_P_EP[ep](USBD_EVT_OUT); }
#endif
      }

      mask <<= 1;
    }
  }

  if (irq_flags & MXC_F_USB_DEV_INTFL_DMA_ERR) {
    // Read and clear endpoint interrupts
    ep_int = MXC_USB->dma_err_int;
    MXC_USB->dma_err_int = ep_int;
    while(1); // not recoverable
  }

  NVIC_EnableIRQ(USB_IRQn);

}