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 #include "em_rtc.h"
00021 
00022 #include "udelay.h"
00023 
00024 /**************************************************************************/
00046 /* this should be approx 2 Bo*oMips to start (note initial shift), and will
00047  *    still work even if initially too large, it will just take slightly longer */
00048 volatile unsigned long loops_per_jiffy = (1<<12);
00049 
00050 /* This is the number of bits of precision for the loops_per_jiffy.  Each
00051  *    bit takes on average 1.5/HZ seconds.  This (like the original) is a little
00052  *       better than 1% */
00053 #define LPS_PREC 8
00054 
00055 static void calibrate_delay(void);
00056 __STATIC_INLINE uint32_t clock(void);
00057 static void _delay( uint32_t delay);
00058 
00061 /***************************************************************************/
00065 void UDELAY_Calibrate(void)
00066 {
00067   CMU_Select_TypeDef lfaClkSel;
00068   CMU_ClkDiv_TypeDef rtcClkDiv;
00069   bool rtcRestore       = false;
00070   bool leClkTurnoff     = false;
00071   bool rtcClkTurnoff    = false;
00072   bool lfaClkSrcRestore = false;
00073   bool lfaClkTurnoff    = false;
00074   RTC_Init_TypeDef init = RTC_INIT_DEFAULT;
00075   uint32_t rtcCtrl=0, rtcComp0=0, rtcComp1=0, rtcIen=0;
00076 
00077   /* Ensure LE modules are accessible */
00078   if ( !( CMU->HFCORECLKEN0 & CMU_HFCORECLKEN0_LE) )
00079   {
00080     CMU_ClockEnable(cmuClock_CORELE, true);
00081     leClkTurnoff = true;
00082   }
00083 
00084   lfaClkSel = CMU_ClockSelectGet(cmuClock_LFA);
00085 
00086   #if defined( UDELAY_LFXO )
00087     if ( !(CMU->STATUS & CMU_STATUS_LFXOENS) )
00088     {
00089       lfaClkTurnoff = true;
00090       CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
00091     }
00092 
00093     if ( lfaClkSel != cmuSelect_LFXO )
00094     {
00095       lfaClkSrcRestore = true;
00096       CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
00097     }
00098 
00099   #else
00100     if ( lfaClkSel != cmuSelect_LFRCO )
00101     {
00102       lfaClkSrcRestore = true;
00103     }
00104     if ( !(CMU->STATUS & CMU_STATUS_LFRCOENS) )
00105     {
00106       lfaClkTurnoff = true;
00107     }
00108     /* Enable LFACLK in CMU (will also enable oscillator if not enabled) */
00109     CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
00110   #endif
00111 
00112   /* Set up a reasonable prescaler. */
00113   rtcClkDiv = CMU_ClockDivGet(cmuClock_RTC);
00114   CMU_ClockDivSet(cmuClock_RTC, cmuClkDiv_256);
00115 
00116   if ( !(CMU->LFACLKEN0 & CMU_LFACLKEN0_RTC) )
00117   {
00118     /* Enable clock to RTC module */
00119     CMU_ClockEnable(cmuClock_RTC, true);
00120     rtcClkTurnoff = true;
00121   }
00122 
00123   INT_Disable();
00124 
00125   if ( RTC->CTRL & RTC_CTRL_EN )
00126   {
00127     /* Stash away current RTC settings. */
00128     rtcCtrl   = RTC->CTRL;
00129     rtcComp0  = RTC->COMP0;
00130     rtcComp1  = RTC->COMP1;
00131     rtcIen    = RTC->IEN;
00132 
00133     RTC->CTRL = _RTC_CTRL_RESETVALUE;
00134     RTC->IEN  = 0;
00135     RTC->IFC  = _RTC_IEN_MASK;
00136 
00137     NVIC_ClearPendingIRQ( RTC_IRQn );
00138 
00139     rtcRestore = true;
00140   }
00141 
00142   init.comp0Top = false;  /* Count to max before wrapping */
00143   RTC_Init(&init);        /* Start RTC counter. */
00144 
00145   calibrate_delay();      /* Calibrate the micro second delay loop. */
00146 
00147   INT_Enable();
00148 
00149   /* Restore all RTC related settings to how they were previously set. */
00150   if ( rtcRestore )
00151   {
00152     CMU_ClockDivSet(cmuClock_RTC, rtcClkDiv);
00153 
00154     RTC_FreezeEnable(true);
00155     #if defined(_EFM32_GECKO_FAMILY)
00156       RTC_Sync(RTC_SYNCBUSY_COMP0 | RTC_SYNCBUSY_COMP1 | RTC_SYNCBUSY_CTRL);
00157     #endif
00158     RTC->COMP0 = rtcComp0;
00159     RTC->COMP1 = rtcComp1;
00160     RTC->CTRL  = rtcCtrl;
00161     RTC->IEN   = rtcIen;
00162     RTC_FreezeEnable(false);
00163   }
00164   else
00165   {
00166     RTC_Enable(false);
00167   }
00168 
00169   if ( rtcClkTurnoff )
00170   {
00171     CMU_ClockEnable(cmuClock_RTC, false);
00172   }
00173 
00174   if ( lfaClkSrcRestore )
00175   {
00176     CMU_ClockSelectSet(cmuClock_LFA, lfaClkSel);
00177   }
00178 
00179   if ( lfaClkTurnoff )
00180   {
00181     #if defined( UDELAY_LFXO )
00182       CMU_OscillatorEnable(cmuOsc_LFXO, false, false);
00183     #else
00184       CMU_OscillatorEnable(cmuOsc_LFRCO, false, false);
00185     #endif
00186   }
00187 
00188   if ( leClkTurnoff )
00189   {
00190     CMU_ClockEnable(cmuClock_CORELE, false);
00191   }
00192 }
00193 
00194 #if defined(__GNUC__) /* GCC */
00195 /***************************************************************************/
00202 void UDELAY_Delay( uint32_t usecs )
00203 {
00204   __ASM volatile (
00205 #if defined(_EFM32_ZERO_FAMILY)
00206 "        .syntax unified           \n"
00207 "        .arch armv6-m             \n"
00208 #endif
00209 "        movs    r2, #0x88         \n"
00210 "        lsls    r2, r2, #8        \n"
00211 "        adds    r2, #0x00         \n"
00212 "        muls    %0, r2            \n"
00213 "                                  \n"
00214 "        ldr     r2, [%1]          \n"
00215 "        movs    r0, %0, lsr #11   \n"
00216 "        movs    r2, r2, lsr #11   \n"
00217 "                                  \n"
00218 "        muls    r0, r2            \n"
00219 "        movs    r0, r0, lsr #6    \n"
00220 "                                  \n"
00221 "        beq.n   2f                \n"
00222 "                                  \n"
00223 "1:      subs    r0, #1            \n"
00224 "        bhi     1b                \n"
00225 #if defined(_EFM32_ZERO_FAMILY)
00226 "2:                                \n"
00227 "        .syntax divided           \n" : : "r" (usecs), "r" (&loops_per_jiffy) );
00228 #else
00229 "2:                                \n" : : "r" (usecs), "r" (&loops_per_jiffy) );
00230 #endif
00231 }
00232 #endif /* defined(__GNUC__) */
00233 
00236 static void calibrate_delay(void)
00237 {
00238   /* From linux 2.4 source. */
00239   unsigned long loopbit;
00240   unsigned long ticks;
00241   int lps_precision = LPS_PREC;
00242 
00243   loops_per_jiffy = (1<<12);
00244 
00245   while (loops_per_jiffy <<= 1) {
00246     /* wait for "start of" clock tick */
00247     ticks = clock();
00248     while (ticks == clock())
00249       /* nothing */;
00250     /* Go .. */
00251     ticks = clock();
00252     _delay(loops_per_jiffy);
00253     ticks = clock() - ticks;
00254     if (ticks)
00255       break;
00256   }
00257 
00258   /* Do a binary approximation to get loops_per_jiffy set to equal one clock
00259      (up to lps_precision bits) */
00260 
00261   loops_per_jiffy >>= 1;
00262   loopbit = loops_per_jiffy;
00263   while ( lps_precision-- && (loopbit >>= 1) ) {
00264     loops_per_jiffy |= loopbit;
00265     ticks = clock();
00266     while (ticks == clock());
00267     ticks = clock();
00268     _delay(loops_per_jiffy);
00269     if (clock() != ticks)   /* longer than 1 tick */
00270       loops_per_jiffy &= ~loopbit;
00271   }
00272 }
00273 
00274 __STATIC_INLINE uint32_t clock(void)
00275 {
00276   return RTC_CounterGet();
00277 }
00278 
00279 #if defined(__ICCARM__) /* IAR */
00280 static void _delay( uint32_t delay)
00281 {
00282   __ASM volatile (
00283 "_delay_1:                         \n"
00284 "        subs    r0, #1            \n"
00285 "        bhi.n   _delay_1          \n" );
00286 }
00287 
00288 void UDELAY_Delay( uint32_t usecs )
00289 {
00290   __ASM volatile (
00291 "        movs    r2, #0x88         \n"
00292 "        lsls    r2, r2, #8        \n"
00293 "        adds    r2, #0x00         \n"
00294 "        muls    r0, r2            \n"
00295 "                                  \n"
00296 "        ldr     r2, [%0]          \n"
00297 "        movs    r0, r0, lsr #11   \n"
00298 "        movs    r2, r2, lsr #11   \n"
00299 "                                  \n"
00300 "        muls    r0, r2            \n"
00301 "        movs    r0, r0, lsr #6    \n"
00302 "                                  \n"
00303 "        bne.n   udelay_1          \n"
00304 "        bx      lr                \n"
00305 "                                  \n"
00306 "udelay_1:                         \n"
00307 "        subs    r0, #1            \n"
00308 "        bhi.n   udelay_1          \n" : : "r" (&loops_per_jiffy) );
00309 }
00310 #endif /* defined(__ICCARM__) */
00311 
00312 #if defined(__GNUC__) /* GCC */
00313 static void _delay( uint32_t delay )
00314 {
00315   __ASM volatile (
00316 #if defined(_EFM32_ZERO_FAMILY)
00317 "        .syntax unified           \n"
00318 "        .arch armv6-m             \n"
00319 #endif
00320 "1:      subs    %0, #1            \n"
00321 #if defined(_EFM32_ZERO_FAMILY)
00322 "        bhi.n   1b                \n"
00323 "        .syntax divided           \n" : : "r" (delay) );
00324 #else
00325 "        bhi.n   1b                \n" : : "r" (delay) );
00326 #endif
00327 }
00328 #endif /* defined(__GNUC__) */
00329 
00330 #if defined(__CC_ARM) /* Keil */
00331 static __ASM void _delay( uint32_t delay)
00332 {
00333 _delay_1
00334         subs    r0, #1
00335         bhi     _delay_1
00336         bx      lr
00337 }
00338 
00339 __ASM void UDELAY_Delay( uint32_t usecs __attribute__ ((unused)) )
00340 {
00341         IMPORT  loops_per_jiffy
00342 
00343         movs    r2, #0x88
00344         lsls    r2, r2, #8
00345         adds    r2, #0x00
00346         muls    r0, r2, r0
00347 
00348         ldr     r2, =loops_per_jiffy
00349         ldr     r2, [r2]
00350         movs    r0, r0, lsr #11
00351         movs    r2, r2, lsr #11
00352 
00353         muls    r0, r2, r0
00354         movs    r0, r0, lsr #6
00355 
00356         bne     udelay_1
00357         bx      lr
00358 
00359 udelay_1
00360         subs    r0, #1
00361         bhi     udelay_1
00362         bx      lr
00363 }
00364 #endif /* defined(__CC_ARM) */
00365