udelay.c

Go to the documentation of this file.
00001 /**************************************************************************/
00017 #include "em_device.h"
00018 #include "em_cmu.h"
00019 #include "em_int.h"
00020 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00021 #include "em_rtcc.h"
00022 #else
00023 #include "em_rtc.h"
00024 #endif
00025 
00026 #include "udelay.h"
00027 
00028 /**************************************************************************/
00052 /* this should be approx 2 Bo*oMips to start (note initial shift), and will
00053  *    still work even if initially too large, it will just take slightly longer */
00054 volatile unsigned long loops_per_jiffy = (1<<12);
00055 
00056 /* This is the number of bits of precision for the loops_per_jiffy.  Each
00057  *    bit takes on average 1.5/HZ seconds.  This (like the original) is a little
00058  *       better than 1% */
00059 #define LPS_PREC 8
00060 
00061 static void calibrate_delay(void);
00062 __STATIC_INLINE uint32_t clock(void);
00063 static void _delay( uint32_t delay);
00064 
00067 /***************************************************************************/
00071 void UDELAY_Calibrate(void)
00072 {
00073   CMU_Select_TypeDef lfaClkSel;
00074   CMU_ClkDiv_TypeDef rtcClkDiv;
00075   bool rtcRestore       = false;
00076   bool leClkTurnoff     = false;
00077   bool rtcClkTurnoff    = false;
00078   bool lfaClkSrcRestore = false;
00079   bool lfaClkTurnoff    = false;
00080 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00081   RTCC_Init_TypeDef init = RTCC_INIT_DEFAULT;
00082   uint32_t rtcCtrl=0, rtcIen=0;
00083 #else
00084   RTC_Init_TypeDef init = RTC_INIT_DEFAULT;
00085   uint32_t rtcCtrl=0, rtcComp0=0, rtcComp1=0, rtcIen=0;
00086 #endif
00087 
00088   /* Ensure LE modules are accessible */
00089 #if defined (_CMU_HFBUSCLKEN0_MASK)
00090   if ( !( CMU->HFBUSCLKEN0 & CMU_HFBUSCLKEN0_LE) )
00091 #else
00092   if ( !( CMU->HFCORECLKEN0 & CMU_HFCORECLKEN0_LE) )
00093 #endif
00094   {
00095     CMU_ClockEnable(cmuClock_CORELE, true);
00096     leClkTurnoff = true;
00097   }
00098 
00099 #if defined (CMU_LFECLKEN0_RTCC)
00100   lfaClkSel = CMU_ClockSelectGet(cmuClock_LFE);
00101 #else
00102   lfaClkSel = CMU_ClockSelectGet(cmuClock_LFA);
00103 #endif
00104 
00105   #if defined( UDELAY_LFXO )
00106     if ( !(CMU->STATUS & CMU_STATUS_LFXOENS) )
00107     {
00108       lfaClkTurnoff = true;
00109       CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
00110     }
00111 
00112     if ( lfaClkSel != cmuSelect_LFXO )
00113     {
00114       lfaClkSrcRestore = true;
00115 #if defined (CMU_LFECLKEN0_RTCC)
00116       CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFXO);
00117 #else
00118       CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
00119 #endif
00120     }
00121 
00122   #else
00123     if ( lfaClkSel != cmuSelect_LFRCO )
00124     {
00125       lfaClkSrcRestore = true;
00126     }
00127     if ( !(CMU->STATUS & CMU_STATUS_LFRCOENS) )
00128     {
00129       lfaClkTurnoff = true;
00130     }
00131     /* Enable LFACLK in CMU (will also enable oscillator if not enabled) */
00132 #if defined (CMU_LFECLKEN0_RTCC)
00133     CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFRCO);
00134 #else
00135     CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
00136 #endif
00137   #endif
00138 
00139   /* Set up a reasonable prescaler. */
00140 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00141   rtcClkDiv = CMU_ClockDivGet(cmuClock_RTCC);
00142   CMU_ClockDivSet(cmuClock_RTCC, cmuClkDiv_256);
00143   if ( !(CMU->LFECLKEN0 & CMU_LFECLKEN0_RTCC) )
00144   {
00145     /* Enable clock to RTCC module */
00146     CMU_ClockEnable(cmuClock_RTCC, true);
00147     rtcClkTurnoff = true;
00148   }
00149 #else
00150   rtcClkDiv = CMU_ClockDivGet(cmuClock_RTC);
00151   CMU_ClockDivSet(cmuClock_RTC, cmuClkDiv_256);
00152   if ( !(CMU->LFACLKEN0 & CMU_LFACLKEN0_RTC) )
00153   {
00154     /* Enable clock to RTC module */
00155     CMU_ClockEnable(cmuClock_RTC, true);
00156     rtcClkTurnoff = true;
00157   }
00158 #endif
00159 
00160 
00161   INT_Disable();
00162 
00163 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00164   if ( RTCC->CTRL & RTCC_CTRL_ENABLE )
00165   {
00166     /* Stash away current RTC settings. */
00167     rtcCtrl   = RTCC->CTRL;
00168 #if 0
00169     rtcComp0  = RTCC->COMP0;
00170     rtcComp1  = RTCC->COMP1;
00171 #endif
00172     rtcIen    = RTCC->IEN;
00173 
00174     RTCC->CTRL = _RTCC_CTRL_RESETVALUE;
00175     RTCC->IEN  = 0;
00176     RTCC->IFC  = _RTCC_IEN_MASK;
00177 
00178     NVIC_ClearPendingIRQ( RTCC_IRQn );
00179 
00180     rtcRestore = true;
00181   }
00182   init.precntWrapOnCCV0 = false;  /* Count to max before wrapping */
00183   init.cntWrapOnCCV1 = false;  /* Count to max before wrapping */
00184 
00185   RTCC_Init(&init);        /* Start RTC counter. */
00186 
00187 #else
00188   if ( RTC->CTRL & RTC_CTRL_EN )
00189   {
00190     /* Stash away current RTC settings. */
00191     rtcCtrl   = RTC->CTRL;
00192     rtcComp0  = RTC->COMP0;
00193     rtcComp1  = RTC->COMP1;
00194     rtcIen    = RTC->IEN;
00195 
00196     RTC->CTRL = _RTC_CTRL_RESETVALUE;
00197     RTC->IEN  = 0;
00198     RTC->IFC  = _RTC_IEN_MASK;
00199 
00200     NVIC_ClearPendingIRQ( RTC_IRQn );
00201 
00202     rtcRestore = true;
00203   }
00204   init.comp0Top = false;  /* Count to max before wrapping */
00205 
00206   RTC_Init(&init);        /* Start RTC counter. */
00207 
00208 #endif
00209 
00210   calibrate_delay();      /* Calibrate the micro second delay loop. */
00211 
00212   INT_Enable();
00213 
00214   /* Restore all RTC related settings to how they were previously set. */
00215   if ( rtcRestore )
00216   {
00217 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00218     CMU_ClockDivSet(cmuClock_RTCC, rtcClkDiv);
00219     RTCC->CTRL  = rtcCtrl;
00220     RTCC->IEN   = rtcIen;
00221 #else
00222     CMU_ClockDivSet(cmuClock_RTC, rtcClkDiv);
00223     RTC_FreezeEnable(true);
00224     #if defined(_EFM32_GECKO_FAMILY)
00225       RTC_Sync(RTC_SYNCBUSY_COMP0 | RTC_SYNCBUSY_COMP1 | RTC_SYNCBUSY_CTRL);
00226     #endif
00227     RTC->COMP0 = rtcComp0;
00228     RTC->COMP1 = rtcComp1;
00229     RTC->CTRL  = rtcCtrl;
00230     RTC->IEN   = rtcIen;
00231     RTC_FreezeEnable(false);
00232 #endif
00233   }
00234   else
00235   {
00236 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00237     RTCC_Enable(false);
00238 #else
00239     RTC_Enable(false);
00240 #endif
00241   }
00242 
00243   if ( rtcClkTurnoff )
00244   {
00245 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00246     CMU_ClockEnable(cmuClock_RTCC, false);
00247 #else
00248     CMU_ClockEnable(cmuClock_RTC, false);
00249 #endif
00250   }
00251 
00252   if ( lfaClkSrcRestore )
00253   {
00254 #if defined (CMU_LFECLKEN0_RTCC)
00255     CMU_ClockSelectSet(cmuClock_LFE, lfaClkSel);
00256 #else
00257     CMU_ClockSelectSet(cmuClock_LFA, lfaClkSel);
00258 #endif
00259   }
00260 
00261   if ( lfaClkTurnoff )
00262   {
00263     #if defined( UDELAY_LFXO )
00264       CMU_OscillatorEnable(cmuOsc_LFXO, false, false);
00265     #else
00266       CMU_OscillatorEnable(cmuOsc_LFRCO, false, false);
00267     #endif
00268   }
00269 
00270   if ( leClkTurnoff )
00271   {
00272     CMU_ClockEnable(cmuClock_CORELE, false);
00273   }
00274 }
00275 
00276 #if defined(__GNUC__) /* GCC */
00277 /***************************************************************************/
00288 void UDELAY_Delay( uint32_t usecs )
00289 {
00290   __ASM volatile (
00291 #if ( __CORTEX_M == 0x00 )
00292 "        .syntax unified           \n"
00293 "        .arch armv6-m             \n"
00294 #endif
00295 "        cmp     %0, #0            \n"    /* Return if 0 delay. */
00296 "        beq.n   2f                \n"
00297 "        subs    %0, #1            \n"    /* Correct for off by one error. */
00298 "        movs    r2, #0x88         \n"
00299 "        lsls    r2, r2, #8        \n"
00300 "        adds    r2, #0x00         \n"
00301 "        muls    %0, r2            \n"
00302 "                                  \n"
00303 "        ldr     r2, [%1]          \n"
00304 "        movs    r0, %0, lsr #11   \n"
00305 "        movs    r2, r2, lsr #11   \n"
00306 "                                  \n"
00307 "        muls    r0, r2            \n"
00308 "        movs    r0, r0, lsr #6    \n"
00309 "                                  \n"
00310 "        beq.n   2f                \n"
00311 "                                  \n"
00312 "1:      subs    r0, #1            \n"
00313 "        bhi     1b                \n"
00314 #if ( __CORTEX_M == 0x00 )
00315 "2:                                \n"
00316 "        .syntax divided           \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc" );
00317 #else
00318 "2:                                \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc" );
00319 #endif
00320 }
00321 #endif /* defined(__GNUC__) */
00322 
00325 static void calibrate_delay(void)
00326 {
00327   /* From linux 2.4 source. */
00328   unsigned long loopbit;
00329   unsigned long ticks;
00330   int lps_precision = LPS_PREC;
00331 
00332   loops_per_jiffy = (1<<12);
00333 
00334   while (loops_per_jiffy <<= 1) {
00335     /* wait for "start of" clock tick */
00336     ticks = clock();
00337     while (ticks == clock())
00338       /* nothing */;
00339     /* Go .. */
00340     ticks = clock();
00341     _delay(loops_per_jiffy);
00342     ticks = clock() - ticks;
00343     if (ticks)
00344       break;
00345   }
00346 
00347   /* Do a binary approximation to get loops_per_jiffy set to equal one clock
00348      (up to lps_precision bits) */
00349 
00350   loops_per_jiffy >>= 1;
00351   loopbit = loops_per_jiffy;
00352   while ( lps_precision-- && (loopbit >>= 1) ) {
00353     loops_per_jiffy |= loopbit;
00354     ticks = clock();
00355     while (ticks == clock());
00356     ticks = clock();
00357     _delay(loops_per_jiffy);
00358     if (clock() != ticks)   /* longer than 1 tick */
00359       loops_per_jiffy &= ~loopbit;
00360   }
00361 }
00362 
00363 __STATIC_INLINE uint32_t clock(void)
00364 {
00365 #if defined( RTCC_PRESENT ) && ( RTCC_COUNT == 1 )
00366   return RTCC_CounterGet();
00367 #else
00368   return RTC_CounterGet();
00369 #endif
00370 }
00371 
00372 #if defined(__ICCARM__) /* IAR */
00373 static void _delay( uint32_t delay)
00374 {
00375   __ASM volatile (
00376 "_delay_1:                         \n"
00377 "        subs    r0, #1            \n"
00378 "        bhi.n   _delay_1          \n" );
00379 }
00380 
00381 void UDELAY_Delay( uint32_t usecs )
00382 {
00383   __ASM volatile (
00384 "        cmp     %0, #0            \n"    /* Return if 0 delay. */
00385 "        beq.n   udelay_2          \n"
00386 "        subs    %0, #1            \n"    /* Correct for off by one error. */
00387 "        movs    r2, #0x88         \n"
00388 "        lsls    r2, r2, #8        \n"
00389 "        adds    r2, #0x00         \n"
00390 "        muls    %0, r2            \n"
00391 "                                  \n"
00392 "        ldr     r2, [%1]          \n"
00393 "        movs    r0, %0, lsr #11   \n"
00394 "        movs    r2, r2, lsr #11   \n"
00395 "                                  \n"
00396 "        muls    r0, r2            \n"
00397 "        movs    r0, r0, lsr #6    \n"
00398 "                                  \n"
00399 "        beq.n   udelay_2          \n"
00400 "                                  \n"
00401 "udelay_1:                         \n"
00402 "        subs    r0, #1            \n"
00403 "        bhi.n   udelay_1          \n"
00404 "udelay_2:                         \n" : : "r" (usecs), "r" (&loops_per_jiffy) : "r0", "r2", "cc");
00405 }
00406 #endif /* defined(__ICCARM__) */
00407 
00408 #if defined(__GNUC__) /* GCC */
00409 static void _delay( uint32_t delay )
00410 {
00411   __ASM volatile (
00412 #if ( __CORTEX_M == 0x00 )
00413 "        .syntax unified           \n"
00414 "        .arch armv6-m             \n"
00415 #endif
00416 "1:      subs    %0, #1            \n"
00417 #if ( __CORTEX_M == 0x00 )
00418 "        bhi.n   1b                \n"
00419 "        .syntax divided           \n" : : "r" (delay) );
00420 #else
00421 "        bhi.n   1b                \n" : : "r" (delay) );
00422 #endif
00423 }
00424 #endif /* defined(__GNUC__) */
00425 
00426 #if defined(__CC_ARM) /* Keil */
00427 static __ASM void _delay( uint32_t delay)
00428 {
00429 _delay_1
00430         subs    r0, #1
00431         bhi     _delay_1
00432         bx      lr
00433 }
00434 
00435 __ASM void UDELAY_Delay( uint32_t usecs __attribute__ ((unused)) )
00436 {
00437         IMPORT  loops_per_jiffy
00438 
00439         cmp     r0, #0                  /* Return if 0 delay. */
00440         beq.n   udelay_2
00441         subs    r0, #1                  /* Correct for off by one error. */
00442         movs    r2, #0x88
00443         lsls    r2, r2, #8
00444         adds    r2, #0x00
00445         muls    r0, r2, r0
00446 
00447         ldr     r2, =loops_per_jiffy
00448         ldr     r2, [r2]
00449         movs    r0, r0, lsr #11
00450         movs    r2, r2, lsr #11
00451 
00452         muls    r0, r2, r0
00453         movs    r0, r0, lsr #6
00454         beq     udelay_2
00455 udelay_1
00456         subs    r0, #1
00457         bhi     udelay_1
00458 udelay_2
00459         bx      lr
00460 }
00461 #endif /* defined(__CC_ARM) */
00462