SAMV71 Xplained Ultra Software Package 1.5

flashd.c

Go to the documentation of this file.
00001 /* ---------------------------------------------------------------------------- */
00002 /*                  Atmel Microcontroller Software Support                      */
00003 /*                       SAM Software Package License                           */
00004 /* ---------------------------------------------------------------------------- */
00005 /* Copyright (c) 2015, Atmel Corporation                                        */
00006 /*                                                                              */
00007 /* All rights reserved.                                                         */
00008 /*                                                                              */
00009 /* Redistribution and use in source and binary forms, with or without           */
00010 /* modification, are permitted provided that the following condition is met:    */
00011 /*                                                                              */
00012 /* - Redistributions of source code must retain the above copyright notice,     */
00013 /* this list of conditions and the disclaimer below.                            */
00014 /*                                                                              */
00015 /* Atmel's name may not be used to endorse or promote products derived from     */
00016 /* this software without specific prior written permission.                     */
00017 /*                                                                              */
00018 /* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
00019 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
00020 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
00021 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
00022 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
00023 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
00024 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
00025 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
00026 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
00027 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
00028 /* ---------------------------------------------------------------------------- */
00029 
00030 /** \addtogroup flashd_module Flash Memory Interface
00031  * The flash driver manages the programming, erasing, locking and unlocking
00032  * sequences with dedicated commands.
00033  *
00034  * To implement flash programming operation, the user has to follow these few
00035  * steps :
00036  * <ul>
00037  * <li>Configure flash wait states to initializes the flash. </li>
00038  * <li>Checks whether a region to be programmed is locked. </li>
00039  * <li>Unlocks the user region to be programmed if the region have locked
00040  * before.</li>
00041  * <li>Erases the user page before program (optional).</li>
00042  * <li>Writes the user page from the page buffer.</li>
00043  * <li>Locks the region of programmed area if any.</li>
00044  * </ul>
00045  *
00046  * Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable
00047  * data corruption.
00048  * A check of this validity and padding for 32-bit alignment should be done in
00049  * write algorithm.
00050  * Lock/unlock range associated with the user address range is automatically
00051  * translated.
00052  *
00053  * This security bit can be enabled through the command "Set General Purpose
00054  * NVM Bit 0".
00055  *
00056  * A 128-bit factory programmed unique ID could be read to serve several
00057  * purposes.
00058  *
00059  * The driver accesses the flash memory by calling the lowlevel module provided
00060  * in \ref efc_module.
00061  * For more accurate information, please look at the EEFC section of the
00062  * Datasheet.
00063  *
00064  * Related files :\n
00065  * \ref flashd.c\n
00066  * \ref flashd.h.\n
00067  * \ref efc.c\n
00068  * \ref efc.h.\n
00069  */
00070 /*@{*/
00071 /*@}*/
00072 
00073 
00074 /**
00075  * \file
00076  *
00077  * The flash driver provides the unified interface for flash program operations.
00078  *
00079  */
00080 
00081 /*----------------------------------------------------------------------------
00082  *        Headers
00083  *----------------------------------------------------------------------------*/
00084 #include "chip.h"
00085 
00086 #include <string.h>
00087 #include <assert.h>
00088 
00089 /*----------------------------------------------------------------------------
00090  *        Definitions
00091  *----------------------------------------------------------------------------*/
00092 
00093 #define GPNVM_NUM_MAX    9
00094 
00095 /*----------------------------------------------------------------------------
00096  *        Local variables
00097  *----------------------------------------------------------------------------*/
00098 
00099 static uint32_t _pdwPageBuffer[IFLASH_PAGE_SIZE / sizeof(uint32_t)];
00100 static uint32_t _dwUseIAP = 1; /* Use IAP interface by default. */
00101 
00102 
00103 /*----------------------------------------------------------------------------
00104  *        Local functions
00105  *----------------------------------------------------------------------------*/
00106 
00107 
00108 /**
00109  * \brief Computes the lock range associated with the given address range.
00110  *
00111  * \param dwStart  Start address of lock range.
00112  * \param dwEnd  End address of lock range.
00113  * \param pdwActualStart  Actual start address of lock range.
00114  * \param pdwActualEnd  Actual end address of lock range.
00115  */
00116 static void ComputeLockRange(uint32_t dwStart, uint32_t dwEnd,
00117                               uint32_t *pdwActualStart, uint32_t *pdwActualEnd)
00118 {
00119     Efc *pStartEfc;
00120     Efc *pEndEfc;
00121     uint16_t wStartPage;
00122     uint16_t wEndPage;
00123     uint16_t wNumPagesInRegion;
00124     uint16_t wActualStartPage;
00125     uint16_t wActualEndPage;
00126 
00127     /* Convert start and end address in page numbers */
00128     EFC_TranslateAddress(&pStartEfc, dwStart, &wStartPage, 0);
00129     EFC_TranslateAddress(&pEndEfc, dwEnd, &wEndPage, 0);
00130 
00131     /* Find out the first page of the first region to lock */
00132     wNumPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
00133     wActualStartPage = wStartPage - (wStartPage % wNumPagesInRegion);
00134     wActualEndPage = wEndPage;
00135 
00136     if ((wEndPage % wNumPagesInRegion) != 0)
00137         wActualEndPage += wNumPagesInRegion - (wEndPage % wNumPagesInRegion);
00138 
00139     /* Store actual page numbers */
00140     EFC_ComputeAddress(pStartEfc, wActualStartPage, 0, pdwActualStart);
00141     EFC_ComputeAddress(pEndEfc, wActualEndPage, 0, pdwActualEnd);
00142     TRACE_DEBUG("Actual lock range is 0x%06X - 0x%06X\n\r",
00143                  (unsigned int)*pdwActualStart, (unsigned int)*pdwActualEnd);
00144 }
00145 
00146 
00147 /*----------------------------------------------------------------------------
00148  *        Exported functions
00149  *----------------------------------------------------------------------------*/
00150 
00151 /**
00152  * \brief Initializes the flash driver.
00153  *
00154  * \param dwMCk     Master clock frequency in Hz.
00155  * \param dwUseIAP  0: use EEFC controller interface, 1: use IAP interface.
00156  *                  dwUseIAP should be set to 1 when running out of flash.
00157  */
00158 
00159 extern void FLASHD_Initialize(uint32_t dwMCk, uint32_t dwUseIAP)
00160 {
00161     dwMCk = dwMCk; /* avoid warnings */
00162 
00163     EFC_DisableFrdyIt(EFC);
00164     _dwUseIAP = dwUseIAP;
00165 }
00166 
00167 /**
00168  * \brief Erases the entire flash.
00169  *
00170  * \param dwAddress  Flash start address.
00171  * \return 0 if successful; otherwise returns an error code.
00172  */
00173 extern uint32_t FLASHD_Erase(uint32_t dwAddress)
00174 {
00175     Efc *pEfc;
00176     uint16_t wPage;
00177     uint16_t wOffset;
00178     uint32_t dwError;
00179 
00180     assert((dwAddress >= IFLASH_ADDR)
00181             || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)));
00182 
00183     /* Translate write address */
00184     EFC_TranslateAddress(&pEfc, dwAddress, &wPage, &wOffset);
00185     dwError = EFC_PerformCommand(pEfc, EFC_FCMD_EA, 0, _dwUseIAP);
00186 
00187     return dwError;
00188 }
00189 
00190 /**
00191  * \brief Erases flash by sector.
00192  *
00193  * \param dwAddress  Start address of be erased sector.
00194  *
00195  * \return 0 if successful; otherwise returns an error code.
00196  */
00197 extern uint32_t FLASHD_EraseSector(uint32_t dwAddress)
00198 {
00199     Efc *pEfc;
00200     uint16_t wPage;
00201     uint16_t wOffset;
00202     uint32_t dwError;
00203 
00204     assert((dwAddress >= IFLASH_ADDR)
00205             || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)));
00206 
00207     /* Translate write address */
00208     EFC_TranslateAddress(&pEfc, dwAddress, &wPage, &wOffset);
00209     dwError = EFC_PerformCommand(pEfc, EFC_FCMD_ES, wPage, _dwUseIAP);
00210 
00211     return dwError;
00212 }
00213 
00214 /**
00215  * \brief Erases flash by pages.
00216  *
00217  * \param dwAddress  Start address of be erased pages.
00218  * \param dwPageNum  Number of pages to be erased with EPA command (4, 8, 16, 32)
00219  *
00220  * \return 0 if successful; otherwise returns an error code.
00221  */
00222 extern uint32_t FLASHD_ErasePages(uint32_t dwAddress, uint32_t dwPageNum)
00223 {
00224     Efc *pEfc;
00225     uint16_t wPage;
00226     uint16_t wOffset;
00227     uint32_t dwError;
00228     static uint32_t dwFarg;
00229 
00230     assert((dwAddress >= IFLASH_ADDR)
00231             || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)));
00232 
00233     /* Translate write address */
00234     EFC_TranslateAddress(&pEfc, dwAddress, &wPage, &wOffset);
00235 
00236     /* Get FARG field for EPA command:
00237      * The first page to be erased is specified in the FARG[15:2] field of
00238      * the MC_FCR register. The first page number must be modulo 4, 8,16 or 32
00239      * according to the number of pages to erase at the same time.
00240      *
00241      * The 2 lowest bits of the FARG field define the number of pages to
00242      * be erased (FARG[1:0]).
00243      */
00244     if (dwPageNum == 32) {
00245         wPage &= ~(32u - 1u);
00246         dwFarg = (wPage) | 3; /* 32 pages */
00247     } else if (dwPageNum == 16) {
00248         wPage &= ~(16u - 1u);
00249         dwFarg = (wPage) | 2; /* 16 pages */
00250     } else if (dwPageNum == 8) {
00251         wPage &= ~(8u - 1u);
00252         dwFarg = (wPage) | 1; /* 8 pages */
00253     } else {
00254         wPage &= ~(4u - 1u);
00255         dwFarg = (wPage) | 0; /* 4 pages */
00256     }
00257 
00258     dwError = EFC_PerformCommand(pEfc, EFC_FCMD_EPA, dwFarg, _dwUseIAP);
00259 
00260     return dwError;
00261 }
00262 
00263 
00264 /**
00265  * \brief Writes a data buffer in the internal flash
00266  *
00267  * \note This function works in polling mode, and thus only returns when the
00268  * data has been effectively written.
00269  * \param address  Write address.
00270  * \param pBuffer  Data buffer.
00271  * \param size  Size of data buffer in bytes.
00272  * \return 0 if successful, otherwise returns an error code.
00273  */
00274 extern uint32_t FLASHD_Write(uint32_t dwAddress,
00275                               const void *pvBuffer, uint32_t dwSize)
00276 {
00277     Efc *pEfc;
00278     uint16_t page;
00279     uint16_t offset;
00280     uint32_t writeSize;
00281     uint32_t pageAddress;
00282     uint16_t padding;
00283     uint32_t dwError;
00284     uint32_t dwIdx;
00285     uint32_t *pAlignedDestination;
00286     uint8_t  *pucPageBuffer = (uint8_t *)_pdwPageBuffer;
00287 
00288     assert(pvBuffer);
00289     assert(dwAddress >= IFLASH_ADDR);
00290     assert((dwAddress + dwSize) <= (IFLASH_ADDR + IFLASH_SIZE));
00291 
00292     /* Translate write address */
00293     EFC_TranslateAddress(&pEfc, dwAddress, &page, &offset);
00294 
00295     /* Write all pages */
00296     while (dwSize > 0) {
00297         /* Copy data in temporary buffer to avoid alignment problems */
00298         writeSize = min((uint32_t)IFLASH_PAGE_SIZE - offset, dwSize);
00299         EFC_ComputeAddress(pEfc, page, 0, &pageAddress);
00300         padding = IFLASH_PAGE_SIZE - offset - writeSize;
00301 
00302         /* Pre-buffer data */
00303         memcpy(pucPageBuffer, (void *) pageAddress, offset);
00304 
00305         /* Buffer data */
00306         memcpy(pucPageBuffer + offset, pvBuffer, writeSize);
00307 
00308         /* Post-buffer data */
00309         memcpy(pucPageBuffer + offset + writeSize,
00310                 (void *) (pageAddress + offset + writeSize), padding);
00311 
00312         /* Write page
00313          * Writing 8-bit and 16-bit data is not allowed and may
00314             lead to unpredictable data corruption
00315          */
00316         pAlignedDestination = (uint32_t *)pageAddress;
00317 
00318         for (dwIdx = 0; dwIdx < (IFLASH_PAGE_SIZE / sizeof(uint32_t)); ++ dwIdx) {
00319             *pAlignedDestination++ = _pdwPageBuffer[dwIdx];
00320             memory_barrier()
00321         }
00322 
00323         /* Cache coherence operation before flash write*/
00324         SCB_CleanDCache_by_Addr((uint32_t *)pageAddress, IFLASH_PAGE_SIZE);
00325 
00326         /* Note: It is not possible to use Erase and write Command (EWP) on all Flash
00327         (this command is available on the First 2 Small Sector, 16K Bytes).
00328         For the next block, Erase them first then use Write page command. */
00329 
00330         /* Send writing command */
00331         dwError = EFC_PerformCommand(pEfc, EFC_FCMD_WP, page, _dwUseIAP);
00332 
00333         if (dwError)
00334             return dwError;
00335 
00336         /* Progression */
00337         pvBuffer = (void *)((uint32_t) pvBuffer + writeSize);
00338         dwSize -= writeSize;
00339         page++;
00340         offset = 0;
00341     }
00342 
00343     return 0;
00344 }
00345 
00346 /**
00347  * \brief Locks all the regions in the given address range. The actual lock
00348  * range is reported through two output parameters.
00349  *
00350  * \param start  Start address of lock range.
00351  * \param end    End address of lock range.
00352  * \param pActualStart  Start address of the actual lock range (optional).
00353  * \param pActualEnd  End address of the actual lock range (optional).
00354  * \return 0 if successful, otherwise returns an error code.
00355  */
00356 extern uint32_t FLASHD_Lock(uint32_t start, uint32_t end,
00357                              uint32_t *pActualStart, uint32_t *pActualEnd)
00358 {
00359     Efc *pEfc;
00360     uint32_t actualStart, actualEnd;
00361     uint16_t startPage, endPage;
00362     uint32_t dwError;
00363     uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
00364 
00365     /* Compute actual lock range and store it */
00366     ComputeLockRange(start, end, &actualStart, &actualEnd);
00367 
00368     if (pActualStart != NULL)
00369         *pActualStart = actualStart;
00370 
00371     if (pActualEnd != NULL)
00372         *pActualEnd = actualEnd;
00373 
00374     /* Compute page numbers */
00375     EFC_TranslateAddress(&pEfc, actualStart, &startPage, 0);
00376     EFC_TranslateAddress(0, actualEnd, &endPage, 0);
00377 
00378     /* Lock all pages */
00379     while (startPage < endPage) {
00380         dwError = EFC_PerformCommand(pEfc, EFC_FCMD_SLB, startPage, _dwUseIAP);
00381 
00382         if (dwError)
00383             return dwError;
00384 
00385         startPage += numPagesInRegion;
00386     }
00387 
00388     return 0;
00389 }
00390 
00391 /**
00392  * \brief Unlocks all the regions in the given address range. The actual unlock
00393  * range is reported through two output parameters.
00394  * \param start  Start address of unlock range.
00395  * \param end  End address of unlock range.
00396  * \param pActualStart  Start address of the actual unlock range (optional).
00397  * \param pActualEnd  End address of the actual unlock range (optional).
00398  * \return 0 if successful, otherwise returns an error code.
00399  */
00400 extern uint32_t FLASHD_Unlock(uint32_t start, uint32_t end,
00401                                uint32_t *pActualStart, uint32_t *pActualEnd)
00402 {
00403     Efc *pEfc;
00404     uint32_t actualStart, actualEnd;
00405     uint16_t startPage, endPage;
00406     uint32_t dwError;
00407     uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
00408 
00409     /* Compute actual unlock range and store it */
00410     ComputeLockRange(start, end, &actualStart, &actualEnd);
00411 
00412     if (pActualStart != NULL)
00413         *pActualStart = actualStart;
00414 
00415     if (pActualEnd != NULL)
00416         *pActualEnd = actualEnd;
00417 
00418     /* Compute page numbers */
00419     EFC_TranslateAddress(&pEfc, actualStart, &startPage, 0);
00420     EFC_TranslateAddress(0, actualEnd, &endPage, 0);
00421 
00422     /* Unlock all pages */
00423     while (startPage < endPage) {
00424         dwError = EFC_PerformCommand(pEfc, EFC_FCMD_CLB, startPage, _dwUseIAP);
00425 
00426         if (dwError)
00427             return dwError;
00428 
00429         startPage += numPagesInRegion;
00430     }
00431 
00432     return 0;
00433 }
00434 
00435 /**
00436  * \brief Returns the number of locked regions inside the given address range.
00437  *
00438  * \param start  Start address of range
00439  * \param end    End address of range.
00440  */
00441 extern uint32_t FLASHD_IsLocked(uint32_t start, uint32_t end)
00442 {
00443     uint32_t i, j;
00444     Efc *pEfc;
00445     uint16_t startPage, endPage;
00446     uint8_t startRegion, endRegion;
00447     uint32_t numPagesInRegion;
00448     uint32_t status[IFLASH_NB_OF_LOCK_BITS / 32u];
00449     uint32_t numLockedRegions = 0;
00450 
00451     assert(end >= start);
00452     assert((start >= IFLASH_ADDR) && (end <= IFLASH_ADDR + IFLASH_SIZE));
00453 
00454     /* Compute page numbers */
00455     EFC_TranslateAddress(&pEfc, start, &startPage, 0);
00456     EFC_TranslateAddress(0, end, &endPage, 0);
00457 
00458     /* Compute region numbers */
00459     numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
00460     startRegion = startPage / numPagesInRegion;
00461     endRegion = endPage / numPagesInRegion;
00462 
00463     if ((endPage % numPagesInRegion) != 0)
00464         endRegion++;
00465 
00466     /* Retrieve lock status */
00467     EFC_PerformCommand(pEfc, EFC_FCMD_GLB, 0, _dwUseIAP);
00468 
00469     for (i = 0; i < (IFLASH_NB_OF_LOCK_BITS / 32u); i++)
00470         status[i] = EFC_GetResult(pEfc);
00471 
00472     /* Check status of each involved region */
00473     while (startRegion < endRegion) {
00474         i = startRegion / 32u;
00475         j = startRegion % 32u;
00476 
00477         if ((status[i] & (1 << j)) != 0)
00478             numLockedRegions++;
00479 
00480         startRegion++;
00481     }
00482 
00483     return numLockedRegions;
00484 }
00485 
00486 /**
00487  * \brief Check if the given GPNVM bit is set or not.
00488  *
00489  * \param gpnvm  GPNVM bit index.
00490  * \returns 1 if the given GPNVM bit is currently set; otherwise returns 0.
00491  */
00492 extern uint32_t FLASHD_IsGPNVMSet(uint8_t ucGPNVM)
00493 {
00494     uint32_t dwStatus;
00495 
00496     assert(ucGPNVM < GPNVM_NUM_MAX);
00497 
00498     /* Get GPNVMs status */
00499     EFC_PerformCommand(EFC, EFC_FCMD_GFB, 0, _dwUseIAP);
00500     dwStatus = EFC_GetResult(EFC);
00501 
00502     /* Check if GPNVM is set */
00503     if ((dwStatus & (1 << ucGPNVM)) != 0)
00504         return 1;
00505     else
00506         return 0;
00507 }
00508 
00509 /**
00510  * \brief Sets the selected GPNVM bit.
00511  *
00512  * \param gpnvm  GPNVM bit index.
00513  * \returns 0 if successful; otherwise returns an error code.
00514  */
00515 extern uint32_t FLASHD_SetGPNVM(uint8_t ucGPNVM)
00516 {
00517     assert(ucGPNVM < GPNVM_NUM_MAX);
00518 
00519     if (!FLASHD_IsGPNVMSet(ucGPNVM))
00520         return EFC_PerformCommand(EFC, EFC_FCMD_SFB, ucGPNVM, _dwUseIAP);
00521     else
00522         return 0;
00523 }
00524 
00525 /**
00526  * \brief Clears the selected GPNVM bit.
00527  *
00528  * \param gpnvm  GPNVM bit index.
00529  * \returns 0 if successful; otherwise returns an error code.
00530  */
00531 extern uint32_t FLASHD_ClearGPNVM(uint8_t ucGPNVM)
00532 {
00533     assert(ucGPNVM < GPNVM_NUM_MAX);
00534 
00535     if (FLASHD_IsGPNVMSet(ucGPNVM))
00536         return EFC_PerformCommand(EFC, EFC_FCMD_CFB, ucGPNVM, _dwUseIAP);
00537     else
00538         return 0;
00539 }
00540 
00541 /**
00542  * \brief Read the unique ID.
00543  *
00544  * \param pdwUniqueID pointer on a 4bytes char containing the unique ID value.
00545  * \returns 0 if successful; otherwise returns an error code.
00546  */
00547 #ifdef __ICCARM__
00548     extern __ramfunc uint32_t FLASHD_ReadUniqueID(uint32_t *pdwUniqueID)
00549 #else
00550     __attribute__ ((section (".ramfunc")))
00551     uint32_t FLASHD_ReadUniqueID(uint32_t *pdwUniqueID)
00552 #endif
00553 {
00554     uint32_t status;
00555 
00556     if (pdwUniqueID == NULL)
00557         return 1;
00558 
00559     pdwUniqueID[0] = 0;
00560     pdwUniqueID[1] = 0;
00561     pdwUniqueID[2] = 0;
00562     pdwUniqueID[3] = 0;
00563 
00564     /* Send the Start Read unique Identifier command (STUI) by writing the Flash
00565        Command Register with the STUI command.*/
00566     EFC->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EFC_FCMD_STUI;
00567 
00568     /* When the Unique Identifier is ready to be read, the FRDY bit in the Flash
00569        Programming Status Register (EEFC_FSR) falls. */
00570     do {
00571         status = EFC->EEFC_FSR;
00572     } while ((status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY);
00573 
00574     /* The Unique Identifier is located in the first 128 bits of the Flash
00575        memory mapping. So, at the address 0x400000-0x40000F. */
00576     pdwUniqueID[0] = *(uint32_t *)IFLASH_ADDR;
00577     pdwUniqueID[1] = *(uint32_t *)(IFLASH_ADDR + 4);
00578     pdwUniqueID[2] = *(uint32_t *)(IFLASH_ADDR + 8);
00579     pdwUniqueID[3] = *(uint32_t *)(IFLASH_ADDR + 12);
00580 
00581     /* To stop the Unique Identifier mode, the user needs to send the Stop Read
00582        unique Identifier command (SPUI) by writing the Flash Command Register
00583        with the SPUI command. */
00584     EFC->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EFC_FCMD_SPUI;
00585 
00586     /* When the Stop read Unique Unique Identifier command (SPUI) has been
00587     performed, the FRDY bit in the Flash Programming Status Register (EEFC_FSR)
00588     rises. */
00589     do {
00590         status = EFC->EEFC_FSR;
00591     } while ((status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
00592 
00593     return 0;
00594 }
00595 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines