USBDevice with MAX32620HSP platform support

Fork of USBDevice by mbed official

Revision:
59:2af474687369
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbtimer.c	Thu Aug 13 15:46:06 2015 +0100
@@ -0,0 +1,381 @@
+/***************************************************************************//**
+ * @file em_usbtimer.c
+ * @brief USB protocol stack library, timer API.
+ * @version 3.20.14
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ * 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 "em_device.h"
+#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
+#include "em_usb.h"
+#if defined( USB_DEVICE ) || defined( USB_HOST )
+#include "em_cmu.h"
+#include "em_timer.h"
+#include "em_usbtypes.h"
+#include "em_usbhal.h"
+
+#include "device_peripherals.h"
+
+/*
+ *  Use one HW timer to serve n software milisecond timers.
+ *  A timer is, when running, in a linked list of timers.
+ *  A given timers timeout period is the acculmulated timeout
+ *  of all timers preceeding it in the queue.
+ *  This makes timer start (linked list insertion) computing intensive,
+ *  but the checking of the queue at each tick very effective.
+ *             ______          ______          ______
+ *            |      |    --->|      |    --->|      |
+ *   head --> |      |   |    |      |   |    |      |
+ *            |______|---     |______|---     |______|---/ NULL
+ */
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#ifndef USB_TIMER
+#error HW platform must define the timer to use for USB
+#endif
+
+#if ( USB_TIMER == USB_TIMER0 ) && ( TIMER_COUNT >= 1 )
+  #define TIMER             TIMER0
+  #define TIMER_CLK         cmuClock_TIMER0
+  #define TIMER_IRQ         TIMER0_IRQn
+  #define TIMER_IRQHandler  TIMER0_IRQHandler
+
+#elif ( USB_TIMER == USB_TIMER1 ) && ( TIMER_COUNT >= 2 )
+  #define TIMER             TIMER1
+  #define TIMER_CLK         cmuClock_TIMER1
+  #define TIMER_IRQ         TIMER1_IRQn
+  #define TIMER_IRQHandler  TIMER1_IRQHandler
+
+#elif ( USB_TIMER == USB_TIMER2 ) && ( TIMER_COUNT >= 3 )
+  #define TIMER             TIMER2
+  #define TIMER_CLK         cmuClock_TIMER2
+  #define TIMER_IRQ         TIMER2_IRQn
+  #define TIMER_IRQHandler  TIMER2_IRQHandler
+
+#elif ( USB_TIMER == USB_TIMER3 ) && ( TIMER_COUNT == 4 )
+  #define TIMER             TIMER3
+  #define TIMER_CLK         cmuClock_TIMER3
+  #define TIMER_IRQ         TIMER3_IRQn
+  #define TIMER_IRQHandler  TIMER3_IRQHandler
+
+#else
+#error "Illegal USB TIMER definition"
+#endif
+
+typedef struct _timer
+{
+  uint32_t                  timeout;  /* Delta value relative to prev. timer */
+  struct _timer             *next;
+  USBTIMER_Callback_TypeDef callback;
+  bool                      running;
+} USBTIMER_Timer_TypeDef;
+
+#if ( NUM_QTIMERS > 0 )
+static USBTIMER_Timer_TypeDef timers[ NUM_QTIMERS ];
+static USBTIMER_Timer_TypeDef *head = NULL;
+#endif
+
+static uint32_t ticksPrMs, ticksPr1us, ticksPr10us, ticksPr100us;
+
+#if ( NUM_QTIMERS > 0 )
+
+static void TimerTick( void );
+
+void TIMER_IRQHandler( void )
+{
+  uint32_t flags;
+
+  flags = TIMER_IntGet( TIMER );
+
+  if ( flags & TIMER_IF_CC0 )
+  {
+    TIMER_IntClear( TIMER, TIMER_IFC_CC0 );
+    TIMER_CompareSet( TIMER, 0, TIMER_CaptureGet( TIMER, 0 ) + ticksPrMs );
+    TimerTick();
+  }
+}
+#endif /* ( NUM_QTIMERS > 0 ) */
+
+static void DelayTicks( uint16_t ticks )
+{
+  uint16_t startTime;
+  volatile uint16_t now;
+
+  if ( ticks )
+  {
+    startTime = TIMER_CounterGet( TIMER );
+    do
+    {
+      now = TIMER_CounterGet(TIMER);
+    } while ( (uint16_t)( now - startTime ) < ticks );
+  }
+}
+
+/** @endcond */
+
+/** @addtogroup USB_COMMON
+ *  @{*/
+
+/***************************************************************************//**
+ * @brief
+ *   Active wait millisecond delay function. Can also be used inside
+ *   interrupt handlers.
+ *
+ * @param[in] msec
+ *   Number of milliseconds to wait.
+ ******************************************************************************/
+void USBTIMER_DelayMs( uint32_t msec )
+{
+  uint64_t totalTicks;
+
+  totalTicks = (uint64_t)ticksPrMs * msec;
+  while ( totalTicks > 20000 )
+  {
+    DelayTicks( 20000 );
+    totalTicks -= 20000;
+  }
+  DelayTicks( (uint16_t)totalTicks );
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Active wait microsecond delay function. Can also be used inside
+ *   interrupt handlers.
+ *
+ * @param[in] usec
+ *   Number of microseconds to wait.
+ ******************************************************************************/
+void USBTIMER_DelayUs( uint32_t usec )
+{
+  uint64_t totalTicks;
+
+  totalTicks = (uint64_t)ticksPr1us * usec;
+  if ( totalTicks == 0 )
+  {
+    usec /= 10;
+    totalTicks = (uint64_t)ticksPr10us * usec;
+
+    if ( totalTicks == 0 )
+    {
+      usec /= 10;
+      totalTicks = (uint64_t)ticksPr100us * usec;
+    }
+  }
+
+  while ( totalTicks > 60000 )
+  {
+    DelayTicks( 60000 );
+    totalTicks -= 60000;
+  }
+  DelayTicks( (uint16_t)totalTicks );
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Activate the hardware timer used to pace the 1 millisecond timer system.
+ *
+ * @details
+ *   Call this function whenever the HFPERCLK frequency is changed.
+ *   This function is initially called by HOST and DEVICE stack xxxx_Init()
+ *   functions.
+ ******************************************************************************/
+void USBTIMER_Init( void )
+{
+  uint32_t freq;
+  TIMER_Init_TypeDef timerInit     = TIMER_INIT_DEFAULT;
+  TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
+
+  freq = CMU_ClockFreqGet( cmuClock_HFPER );
+  ticksPrMs = ( freq + 500 ) / 1000;
+  ticksPr1us = ( freq + 500000 ) / 1000000;
+  ticksPr10us = ( freq + 50000 ) / 100000;
+  ticksPr100us = ( freq + 5000 ) / 10000;
+
+  timerCCInit.mode = timerCCModeCompare;
+  CMU_ClockEnable( TIMER_CLK, true );
+  TIMER_TopSet( TIMER, 0xFFFF );
+  TIMER_InitCC( TIMER, 0, &timerCCInit );
+  TIMER_Init( TIMER, &timerInit );
+
+#if ( NUM_QTIMERS > 0 )
+  TIMER_IntClear( TIMER, 0xFFFFFFFF );
+  TIMER_IntEnable( TIMER, TIMER_IEN_CC0 );
+  TIMER_CompareSet( TIMER, 0, TIMER_CounterGet( TIMER ) + ticksPrMs );
+  NVIC_ClearPendingIRQ( TIMER_IRQ );
+  NVIC_EnableIRQ( TIMER_IRQ );
+#endif /* ( NUM_QTIMERS > 0 ) */
+}
+
+#if ( NUM_QTIMERS > 0 ) || defined( DOXY_DOC_ONLY )
+/***************************************************************************//**
+ * @brief
+ *   Start a timer.
+ *
+ * @details
+ *   If the timer is already running, it will be restarted with new timeout.
+ *
+ * @param[in] id
+ *   Timer id (0..).
+ *
+ * @param[in] timeout
+ *   Number of milliseconds before timer will elapse.
+ *
+ * @param[in] callback
+ *   Function to be called on timer elapse, ref. @ref USBTIMER_Callback_TypeDef.
+ ******************************************************************************/
+void USBTIMER_Start( uint32_t id, uint32_t timeout,
+                     USBTIMER_Callback_TypeDef callback )
+{
+  uint32_t accumulated;
+  USBTIMER_Timer_TypeDef *this, **last;
+
+  INT_Disable();
+
+  if ( timers[ id ].running )
+  {
+    USBTIMER_Stop( id );
+  }
+
+  if ( timeout == 0 )
+  {
+    callback();
+    INT_Enable();
+    return;
+  }
+
+  timers[ id ].running  = true;
+  timers[ id ].callback = callback;
+  timers[ id ].next     = NULL;
+
+  if ( !head )                                        /* Queue empty ? */
+  {
+    timers[ id ].timeout  = timeout;
+    head = &timers[ id ];
+  }
+  else
+  {
+    this = head;
+    last = &head;
+    accumulated = 0;
+
+    /* Do a sorted insert */
+    while ( this  )
+    {
+      if ( timeout < accumulated + this->timeout )  /* Insert before "this" ? */
+      {
+        timers[ id ].timeout  = timeout - accumulated;
+        timers[ id ].next     = this;
+        *last = &timers[ id ];
+        this->timeout -= timers[ id ].timeout;        /* Adjust timeout     */
+        break;
+      }
+      else if ( this->next == NULL )                  /* At end of queue ?  */
+      {
+        timers[ id ].timeout  = timeout - accumulated - this->timeout;
+        this->next = &timers[ id ];
+        break;
+      }
+      accumulated += this->timeout;
+      last = &this->next;
+      this = this->next;
+    }
+  }
+
+  INT_Enable();
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Stop a timer.
+ *
+ * @param[in] id
+ *   Timer id (0..).
+ ******************************************************************************/
+void USBTIMER_Stop( uint32_t id )
+{
+  USBTIMER_Timer_TypeDef *this, **last;
+
+  INT_Disable();
+
+  if ( head )                                           /* Queue empty ?    */
+  {
+    this = head;
+    last = &head;
+    timers[ id ].running = false;
+
+    while ( this  )
+    {
+      if ( this == &timers[ id ] )                      /* Correct timer ?  */
+      {
+        if ( this->next )
+        {
+          this->next->timeout += timers[ id ].timeout;  /* Adjust timeout   */
+        }
+        *last = this->next;
+        break;
+      }
+      last = &this->next;
+      this = this->next;
+    }
+  }
+
+  INT_Enable();
+}
+#endif /* ( NUM_QTIMERS > 0 ) */
+
+/** @} (end addtogroup USB_COMMON) */
+
+#if ( NUM_QTIMERS > 0 )
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+static void TimerTick( void )
+{
+  USBTIMER_Callback_TypeDef cb;
+
+  INT_Disable();
+
+  if ( head )
+  {
+    head->timeout--;
+
+    while ( head  )
+    {
+      if ( head->timeout == 0 )
+      {
+        cb = head->callback;
+        head->running = false;
+        head = head->next;
+        /* The callback may place new items in the queue !!! */
+        if ( cb )
+        {
+          (cb)();
+        }
+        continue; /* There might be more than one timeout pr. tick */
+      }
+      break;
+    }
+  }
+
+  INT_Enable();
+}
+/** @endcond */
+#endif /* ( NUM_QTIMERS > 0 ) */
+
+#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
+#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */