em_msc.c

Go to the documentation of this file.
00001 /***************************************************************************/
00034 #include "em_msc.h"
00035 #if defined(MSC_COUNT) && (MSC_COUNT > 0)
00036 
00037 #include "em_system.h"
00038 #if defined( _MSC_TIMEBASE_MASK )
00039 #include "em_cmu.h"
00040 #endif
00041 #include "em_assert.h"
00042 
00045 #if defined( MSC_WRITECTRL_WDOUBLE )
00046 #define WORDS_PER_DATA_PHASE (FLASH_SIZE<(512*1024) ? 1 : 2)
00047 #else
00048 #define WORDS_PER_DATA_PHASE (1)
00049 #endif
00050 
00051 #ifdef __CC_ARM  /* MDK-ARM compiler */
00052 msc_Return_TypeDef MscLoadData(uint32_t *data, int num);
00053 msc_Return_TypeDef MscLoadAddress(uint32_t *address);
00054 #endif /* __CC_ARM */
00055 
00056 #ifdef __ICCARM__ /* IAR compiler */
00057 __ramfunc msc_Return_TypeDef MscLoadData(uint32_t *data, int num);
00058 __ramfunc msc_Return_TypeDef MscLoadAddress(uint32_t *address);
00059 #endif /* __ICCARM__ */
00060 
00061 #ifdef __GNUC__  /* GCC based compilers */
00062 #ifdef __CROSSWORKS_ARM  /* Rowley Crossworks */
00063 msc_Return_TypeDef MscLoadData(uint32_t *data, int num) __attribute__ ((section(".fast")));
00064 msc_Return_TypeDef MscLoadAddress(uint32_t *address) __attribute__ ((section(".fast")));
00065 #else /* Sourcery G++ */
00066 msc_Return_TypeDef MscLoadData(uint32_t *data, int num) __attribute__ ((section(".ram")));
00067 msc_Return_TypeDef MscLoadAddress(uint32_t *address) __attribute__ ((section(".ram")));
00068 #endif /* __CROSSWORKS_ARM */
00069 #endif /* __GNUC__ */
00070 
00073 /***************************************************************************/
00078 /***************************************************************************/
00084 /*******************************************************************************
00085  **************************   GLOBAL FUNCTIONS   *******************************
00086  ******************************************************************************/
00087 
00088 /***************************************************************************/
00095 void MSC_Init(void)
00096 {
00097 #if defined( _MSC_TIMEBASE_MASK )
00098   uint32_t freq, cycles;
00099 #endif
00100   /* Unlock the MSC */
00101   MSC->LOCK = MSC_UNLOCK_CODE;
00102   /* Disable writing to the flash */
00103   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00104 
00105 #if defined( _MSC_TIMEBASE_MASK )
00106   /* Configure MSC->TIMEBASE according to selected frequency */
00107   freq = CMU_ClockFreqGet(cmuClock_AUX);
00108 
00109   if (freq > 7000000)
00110   {
00111     /* Calculate number of clock cycles for 1us as base period */
00112     freq   = (freq * 11) / 10;
00113     cycles = (freq / 1000000) + 1;
00114 
00115     /* Configure clock cycles for flash timing */
00116     MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK |
00117                                        _MSC_TIMEBASE_PERIOD_MASK)) |
00118                     MSC_TIMEBASE_PERIOD_1US |
00119                     (cycles << _MSC_TIMEBASE_BASE_SHIFT);
00120   }
00121   else
00122   {
00123     /* Calculate number of clock cycles for 5us as base period */
00124     freq   = (freq * 5 * 11) / 10;
00125     cycles = (freq / 1000000) + 1;
00126 
00127     /* Configure clock cycles for flash timing */
00128     MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK |
00129                                        _MSC_TIMEBASE_PERIOD_MASK)) |
00130                     MSC_TIMEBASE_PERIOD_5US |
00131                     (cycles << _MSC_TIMEBASE_BASE_SHIFT);
00132   }
00133 #endif
00134 }
00135 
00136 /***************************************************************************/
00140 void MSC_Deinit(void)
00141 {
00142   /* Disable writing to the flash */
00143   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00144   /* Lock the MSC */
00145   MSC->LOCK = 0;
00146 }
00147 
00148 
00151 /***************************************************************************/
00174 #ifdef __CC_ARM  /* MDK-ARM compiler */
00175 #pragma arm section code="ram_code"
00176 #endif /* __CC_ARM */
00177 #if defined(__ICCARM__)
00178 /* Suppress warnings originating from use of EFM_ASSERT():              */
00179 /* "Call to a non __ramfunc function from within a __ramfunc function"  */
00180 /* "Possible rom access from within a __ramfunc function"               */
00181 #pragma diag_suppress=Ta022
00182 #pragma diag_suppress=Ta023
00183 #endif
00184 
00185 msc_Return_TypeDef MscLoadAddress(uint32_t* address)
00186 {
00187   uint32_t status;
00188   int      timeOut;
00189 
00190   /* Wait for the MSC to become ready. */
00191   timeOut = MSC_PROGRAM_TIMEOUT;
00192   while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
00193   {
00194     timeOut--;
00195   }
00196 
00197   /* Check for timeout */
00198   if (timeOut == 0)
00199     return mscReturnTimeOut;
00200 
00201   /* Load address */
00202   MSC->ADDRB    = (uint32_t) (address);
00203   MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00204 
00205   status = MSC->STATUS;
00206   if (status & (MSC_STATUS_INVADDR | MSC_STATUS_LOCKED))
00207   {
00208     /* Check for invalid address */
00209     if (status & MSC_STATUS_INVADDR)
00210       return mscReturnInvalidAddr;
00211     /* Check for write protected page */
00212     if (status & MSC_STATUS_LOCKED)
00213       return mscReturnLocked;
00214   }
00215   return mscReturnOk;
00216 }
00217 
00218 #if defined(__ICCARM__)
00219 #pragma diag_default=Ta022
00220 #pragma diag_default=Ta023
00221 #endif
00222 #ifdef __CC_ARM  /* MDK-ARM compiler */
00223 #pragma arm section code
00224 #endif /* __CC_ARM */
00225 
00226 
00227 
00228 /***************************************************************************/
00252 #ifdef __CC_ARM  /* MDK-ARM compiler */
00253 #pragma arm section code="ram_code"
00254 #endif /* __CC_ARM */
00255 #if defined(__ICCARM__)
00256 /* Suppress warnings originating from use of EFM_ASSERT():              */
00257 /* "Call to a non __ramfunc function from within a __ramfunc function"  */
00258 /* "Possible rom access from within a __ramfunc function"               */
00259 #pragma diag_suppress=Ta022
00260 #pragma diag_suppress=Ta023
00261 #endif
00262 
00263 msc_Return_TypeDef MscLoadData(uint32_t* data, int num)
00264 {
00265   int      timeOut  = MSC_PROGRAM_TIMEOUT;
00266   int      i;
00267   int      wordsPerDataPhase;
00268   msc_Return_TypeDef retval = mscReturnOk;
00269 
00270 #ifdef MSC_WRITECTRL_LPWRITE
00271   /* If LPWRITE (Low Power Write) is NOT enabled, set WDOUBLE (Write Double word) */
00272   if (0 == (MSC->WRITECTRL & MSC_WRITECTRL_LPWRITE))
00273   {
00274     /* If the number of words to be written are odd, we need to align by writing
00275        a single word first, before setting the WDOUBLE bit. */
00276     if (num & 0x1)
00277     {
00278       /* Wait for the msc to be ready for the next word. */
00279       timeOut = MSC_PROGRAM_TIMEOUT;
00280       while ((0 == (MSC->STATUS & MSC_STATUS_WDATAREADY)) && (timeOut != 0))
00281       {
00282         timeOut--;
00283       }
00284       /* Check for timeout */
00285       if (timeOut == 0)
00286         return mscReturnTimeOut;
00287 
00288       /* Clear double word option, in order to write one single word. */
00289       MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
00290       /* Write first data word. */
00291       MSC->WDATA = *data++;
00292       /* Execute the write command for the first word. We use the WRITETRIG
00293          command here, because we want the address to be updated, even though
00294          we do not intend to write the next word within the WDATAREADY timeout.
00295        */
00296       MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
00297 
00298       /* Wait for the transaction to finish. */
00299       timeOut = MSC_PROGRAM_TIMEOUT;
00300       while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
00301       {
00302         timeOut--;
00303       }
00304       /* Check for timeout */
00305       if (timeOut == 0)
00306         return mscReturnTimeOut;
00307 
00308       if (0 == --num)
00309       {
00310         retval = mscReturnOk;
00311         goto msc_load_data_exit;
00312       }
00313     }
00314 
00315     /* Now we can set the double word option in order to write two words per
00316        data phase. */
00317     MSC->WRITECTRL |= MSC_WRITECTRL_WDOUBLE;
00318     wordsPerDataPhase = 2;
00319   }
00320   else
00321 #endif
00322   {
00323     wordsPerDataPhase = 1;
00324   }
00325 
00326 
00327   /* Wait for the MSC to be ready for a new data word.
00328    * Due to the timing of this function, the MSC should
00329    * already be ready */
00330   timeOut = MSC_PROGRAM_TIMEOUT;
00331   while (((MSC->STATUS & MSC_STATUS_WDATAREADY) == 0) && (timeOut != 0))
00332   {
00333     timeOut--;
00334   }
00335 
00336   /* Check for timeout */
00337   if (timeOut == 0)
00338     return mscReturnTimeOut;
00339 
00340   /* Write first data word. */
00341   MSC->WDATA = *data;
00342 
00343   /* Execute the write command only for the first word. */
00344   MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
00345 
00346   /* Loop through the rest of the data to be written. */
00347   for (i=1, data++; i<num; i++, data++)
00348   {
00349     /* Only waut for WDATAREADY at the start of each data phase. */
00350     if (0 == (i&(wordsPerDataPhase-1)))
00351     {
00352       /* Wait for the msc to be ready for the next word. */
00353       timeOut = MSC_PROGRAM_TIMEOUT;
00354       while ((0 == (MSC->STATUS & MSC_STATUS_WDATAREADY)) && (timeOut != 0))
00355       {
00356         timeOut--;
00357       }
00358       /* Check for timeout */
00359       if (timeOut == 0)
00360         return mscReturnTimeOut;
00361     }
00362 
00363     /* Check if the WDATAREADY timeout has occurred. */
00364     if (MSC->STATUS & MSC_STATUS_WORDTIMEOUT)
00365     {
00366       retval = mscReturnTimeOut;
00367       goto msc_load_data_exit;
00368     }
00369 
00370     /* Write next word. */
00371     MSC->WDATA = *data;
00372   }
00373 
00374  msc_load_data_exit:
00375 
00376   /* Wait for the transaction to finish. */
00377   timeOut = MSC_PROGRAM_TIMEOUT;
00378   while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
00379   {
00380     timeOut--;
00381   }
00382   /* Check for timeout */
00383   if (timeOut == 0)
00384     retval = mscReturnTimeOut;
00385 
00386 #ifdef MSC_WRITECTRL_WDOUBLE
00387   /* Clear double word option, which should not be left on when returning. */
00388   MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
00389 #endif
00390 
00391   return retval;
00392 }
00393 #if defined(__ICCARM__)
00394 #pragma diag_default=Ta022
00395 #pragma diag_default=Ta023
00396 #endif
00397 #ifdef __CC_ARM  /* MDK-ARM compiler */
00398 #pragma arm section code
00399 #endif /* __CC_ARM */
00400 
00404 /***************************************************************************/
00426 #ifdef __CC_ARM  /* MDK-ARM compiler */
00427 #pragma arm section code="ram_code"
00428 #endif /* __CC_ARM */
00429 #if defined(__ICCARM__)
00430 /* Suppress warnings originating from use of EFM_ASSERT():              */
00431 /* "Call to a non __ramfunc function from within a __ramfunc function"  */
00432 /* "Possible rom access from within a __ramfunc function"               */
00433 #pragma diag_suppress=Ta022
00434 #pragma diag_suppress=Ta023
00435 #endif
00436 msc_Return_TypeDef MSC_ErasePage(uint32_t *startAddress)
00437 {
00438   int      timeOut  = MSC_PROGRAM_TIMEOUT;
00439 
00440   /* Address must be aligned to pages */
00441   EFM_ASSERT((((uint32_t) startAddress) & (FLASH_PAGE_SIZE - 1)) == 0);
00442 
00443   /* Enable writing to the MSC */
00444   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00445 
00446   /* Load address */
00447   MSC->ADDRB    = (uint32_t) startAddress;
00448   MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00449 
00450   /* Check for invalid address */
00451   if (MSC->STATUS & MSC_STATUS_INVADDR)
00452   {
00453     /* Disable writing to the MSC */
00454     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00455     return mscReturnInvalidAddr;
00456   }
00457 
00458   /* Check for write protected page */
00459   if (MSC->STATUS & MSC_STATUS_LOCKED)
00460   {
00461     /* Disable writing to the MSC */
00462     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00463     return mscReturnLocked;
00464   }
00465 
00466   /* Send erase page command */
00467   MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
00468 
00469   /* Wait for the erase to complete */
00470   while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0))
00471   {
00472     timeOut--;
00473   }
00474 
00475   if (timeOut == 0)
00476   {
00477     /* Disable writing to the MSC */
00478     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00479     return mscReturnTimeOut;
00480   }
00481 
00482   /* Disable writing to the MSC */
00483   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00484   return mscReturnOk;
00485 }
00486 #if defined(__ICCARM__)
00487 #pragma diag_default=Ta022
00488 #pragma diag_default=Ta023
00489 #endif
00490 #ifdef __CC_ARM  /* MDK-ARM compiler */
00491 #pragma arm section code
00492 #endif /* __CC_ARM */
00493 
00494 
00495 
00496 /***************************************************************************/
00528 #ifdef __CC_ARM  /* MDK-ARM compiler */
00529 #pragma arm section code="ram_code"
00530 #endif /* __CC_ARM */
00531 #if defined(__ICCARM__)
00532 /* Suppress warnings originating from use of EFM_ASSERT():              */
00533 /* "Call to a non __ramfunc function from within a __ramfunc function"  */
00534 /* "Possible rom access from within a __ramfunc function"               */
00535 #pragma diag_suppress=Ta022
00536 #pragma diag_suppress=Ta023
00537 #endif
00538 
00539 msc_Return_TypeDef MSC_WriteWord(uint32_t *address, void const *data, int numBytes)
00540 {
00541   int wordCount;
00542   int numWords;
00543   int pageWords;
00544   uint32_t* pData;
00545   msc_Return_TypeDef retval = mscReturnOk;
00546 
00547   /* Check alignment (Must be aligned to words) */
00548   EFM_ASSERT(((uint32_t) address & 0x3) == 0);
00549 
00550   /* Check number of bytes. Must be divisable by four */
00551   EFM_ASSERT((numBytes & 0x3) == 0);
00552 
00553   /* Enable writing to the MSC */
00554   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00555 
00556   /* Convert bytes to words */
00557   numWords = numBytes >> 2;
00558 
00559   /* The following loop splits the data into chunks corresponding to flash pages.
00560      The address is loaded only once per page, because the hardware automatically
00561      increments the address internally for each data load inside a page. */
00562   for (wordCount = 0, pData = (uint32_t*) data; wordCount < numWords; )
00563   {
00564     /* First we load address. The address is auto-incremented within a page.
00565        Therefore the address phase is only needed once for each page. */
00566     retval = MscLoadAddress(address+wordCount);
00567     if (mscReturnOk != retval)
00568       return retval;
00569 
00570     /* Compute the number of words to write to the current page. */
00571     pageWords =
00572       (FLASH_PAGE_SIZE - ((uint32_t) (address + wordCount)) % FLASH_PAGE_SIZE) /
00573       sizeof(uint32_t);
00574     if (pageWords > numWords-wordCount)
00575       pageWords = numWords-wordCount;
00576 
00577     /* Now write the data in the current page. */
00578     retval = MscLoadData(pData, pageWords);
00579     if (mscReturnOk != retval) goto msc_write_word_exit;
00580 
00581     wordCount += pageWords;
00582     pData += pageWords;
00583   }
00584 
00585  msc_write_word_exit:
00586 
00587   /* Disable writing to the MSC */
00588   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00589 
00590 #if (defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)) && (2==WORDS_PER_DATA_PHASE)
00591   /* Turn off double word write cycle support. */
00592   MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
00593 #endif
00594 
00595   return retval;
00596 }
00597 
00598 #if defined(__ICCARM__)
00599 #pragma diag_default=Ta022
00600 #pragma diag_default=Ta023
00601 #endif
00602 #ifdef __CC_ARM  /* MDK-ARM compiler */
00603 #pragma arm section code
00604 #endif /* __CC_ARM */
00605 
00606 
00607 #if defined( _MSC_MASSLOCK_MASK )
00608 /***************************************************************************/
00617 #ifdef __CC_ARM  /* MDK-ARM compiler */
00618 #pragma arm section code="ram_code"
00619 #endif /* __CC_ARM */
00620 msc_Return_TypeDef MSC_MassErase(void)
00621 {
00622   /* Enable writing to the MSC */
00623   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00624 
00625   /* Unlock device mass erase */
00626   MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_UNLOCK;
00627 
00628   /* Erase first 512K block */
00629   MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN0;
00630 
00631   /* Waiting for erase to complete */
00632   while ((MSC->STATUS & MSC_STATUS_BUSY))
00633   {
00634   }
00635 
00636 #if FLASH_SIZE >= (512 * 1024)
00637   /* Erase second 512K block */
00638   MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN1;
00639 
00640   /* Waiting for erase to complete */
00641   while ((MSC->STATUS & MSC_STATUS_BUSY))
00642   {
00643   }
00644 #endif
00645 
00646   /* Restore mass erase lock */
00647   MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_LOCK;
00648 
00649   /* This will only successfully return if calling function is also in SRAM */
00650   return mscReturnOk;
00651 }
00652 #ifdef __CC_ARM  /* MDK-ARM compiler */
00653 #pragma arm section code
00654 #endif /* __CC_ARM */
00655 #endif
00656 
00659 #endif /* defined(MSC_COUNT) && (MSC_COUNT > 0) */