blob: 7dc7276ccf2572ef5461cebed13a0a5910b265ab [file] [log] [blame]
Christina Quast8be71e42014-12-02 13:06:01 +01001/* ----------------------------------------------------------------------------
2 * ATMEL Microcontroller Software Support
3 * ----------------------------------------------------------------------------
4 * Copyright (c) 2009, Atmel Corporation
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * - Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the disclaimer below.
13 *
14 * Atmel's name may not be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
20 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * ----------------------------------------------------------------------------
28 */
29
30/** \addtogroup flashd_module Flash Memory Interface
31 * The flash driver manages the programming, erasing, locking and unlocking sequences
32 * with dedicated commands.
33 *
34 * To implement flash programing operation, the user has to follow these few steps :
35 * <ul>
36 * <li>Configue flash wait states to initializes the flash. </li>
37 * <li>Checks whether a region to be programmed is locked. </li>
38 * <li>Unlocks the user region to be programmed if the region have locked before.</li>
39 * <li>Erases the user page before program (optional).</li>
40 * <li>Writes the user page from the page buffer.</li>
41 * <li>Locks the region of programmed area if any.</li>
42 * </ul>
43 *
44 * Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption.
45 * A check of this validity and padding for 32-bit alignment should be done in write algorithm.
46
47 * Lock/unlock range associated with the user address range is automatically translated.
48 *
49 * This security bit can be enabled through the command "Set General Purpose NVM Bit 0".
50 *
51 * A 128-bit factory programmed unique ID could be read to serve several purposes.
52 *
53 * The driver accesses the flash memory by calling the lowlevel module provided in \ref efc_module.
54 * For more accurate information, please look at the EEFC section of the Datasheet.
55 *
56 * Related files :\n
57 * \ref flashd.c\n
58 * \ref flashd.h.\n
59 * \ref efc.c\n
60 * \ref efc.h.\n
61*/
62/*@{*/
63/*@}*/
64
65
66/**
67 * \file
68 *
69 * The flash driver provides the unified interface for flash program operations.
70 *
71 */
72
73/*----------------------------------------------------------------------------
74 * Headers
75 *----------------------------------------------------------------------------*/
76#include "chip.h"
77
78#include <string.h>
79#include <assert.h>
80
81/*----------------------------------------------------------------------------
82 * Local variables
83 *----------------------------------------------------------------------------*/
84
85//static NO_INIT uint8_t _aucPageBuffer[IFLASH_PAGE_SIZE] ;
86static NO_INIT uint32_t _adwPageBuffer[IFLASH_PAGE_SIZE/4] ;
87static uint8_t* _aucPageBuffer = (uint8_t*)_adwPageBuffer;
88static NO_INIT uint32_t _dwUseIAP ;
89
90/*----------------------------------------------------------------------------
91 * Local macros
92 *----------------------------------------------------------------------------*/
93
94#define min( a, b ) (((a) < (b)) ? (a) : (b))
95
96/*----------------------------------------------------------------------------
97 * Local functions
98 *----------------------------------------------------------------------------*/
99
100
101/**
102 * \brief Computes the lock range associated with the given address range.
103 *
104 * \param dwStart Start address of lock range.
105 * \param dwEnd End address of lock range.
106 * \param pdwActualStart Actual start address of lock range.
107 * \param pdwActualEnd Actual end address of lock range.
108 */
109static void ComputeLockRange( uint32_t dwStart, uint32_t dwEnd, uint32_t *pdwActualStart, uint32_t *pdwActualEnd )
110{
111 Efc* pStartEfc ;
112 Efc* pEndEfc ;
113 uint16_t wStartPage ;
114 uint16_t wEndPage ;
115 uint16_t wNumPagesInRegion ;
116 uint16_t wActualStartPage ;
117 uint16_t wActualEndPage ;
118
119 // Convert start and end address in page numbers
120 EFC_TranslateAddress( &pStartEfc, dwStart, &wStartPage, 0 ) ;
121 EFC_TranslateAddress( &pEndEfc, dwEnd, &wEndPage, 0 ) ;
122
123 // Find out the first page of the first region to lock
124 wNumPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE ;
125 wActualStartPage = wStartPage - (wStartPage % wNumPagesInRegion) ;
126 wActualEndPage = wEndPage ;
127
128 if ( (wEndPage % wNumPagesInRegion) != 0 )
129 {
130 wActualEndPage += wNumPagesInRegion - (wEndPage % wNumPagesInRegion) ;
131 }
132 // Store actual page numbers
133 EFC_ComputeAddress( pStartEfc, wActualStartPage, 0, pdwActualStart ) ;
134 EFC_ComputeAddress( pEndEfc, wActualEndPage, 0, pdwActualEnd ) ;
135 TRACE_DEBUG( "Actual lock range is 0x%06X - 0x%06X\n\r", *pdwActualStart, *pdwActualEnd ) ;
136}
137
138
139/*----------------------------------------------------------------------------
140 * Exported functions
141 *----------------------------------------------------------------------------*/
142
143/**
144 * \brief Initializes the flash driver.
145 *
146 * \param mck Master clock frequency in Hz.
147 */
148
149extern void FLASHD_Initialize( uint32_t dwMCk, uint32_t dwUseIAP )
150{
151 EFC_DisableFrdyIt( EFC ) ;
152
153 if ( (dwMCk/1000000) >= 64 )
154 {
155 EFC_SetWaitState( EFC, 2 ) ;
156 }
157 else
158 {
159 if ( (dwMCk/1000000) >= 50 )
160 {
161 EFC_SetWaitState( EFC, 1 ) ;
162 }
163 else
164 {
165 EFC_SetWaitState( EFC, 0 ) ;
166 }
167 }
168
169 _dwUseIAP=dwUseIAP ;
170}
171
172/**
173 * \brief Erases the entire flash.
174 *
175 * \param address Flash start address.
176 * \return 0 if successful; otherwise returns an error code.
177 */
178extern uint32_t FLASHD_Erase( uint32_t dwAddress )
179{
180 Efc* pEfc ;
181 uint16_t wPage ;
182 uint16_t wOffset ;
183 uint32_t dwError ;
184
185 assert( (dwAddress >=IFLASH_ADDR) || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)) ) ;
186
187 // Translate write address
188 EFC_TranslateAddress( &pEfc, dwAddress, &wPage, &wOffset ) ;
189 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EA, 0, _dwUseIAP ) ;
190
191 return dwError ;
192}
193
194/**
195 * \brief Writes a data buffer in the internal flash
196 *
197 * \note This function works in polling mode, and thus only returns when the
198 * data has been effectively written.
199 * \param address Write address.
200 * \param pBuffer Data buffer.
201 * \param size Size of data buffer in bytes.
202 * \return 0 if successful, otherwise returns an error code.
203 */
204extern uint32_t FLASHD_Write( uint32_t dwAddress, const void *pvBuffer, uint32_t dwSize )
205{
206 Efc* pEfc ;
207 uint16_t page ;
208 uint16_t offset ;
209 uint32_t writeSize ;
210 uint32_t pageAddress ;
211 uint16_t padding ;
212 uint32_t dwError ;
213 uint32_t sizeTmp ;
214 uint32_t *pAlignedDestination ;
215 uint32_t *pAlignedSource ;
216
217 assert( pvBuffer ) ;
218 assert( dwAddress >=IFLASH_ADDR ) ;
219 assert( (dwAddress + dwSize) <= (IFLASH_ADDR + IFLASH_SIZE) ) ;
220
221 /* Translate write address */
222 EFC_TranslateAddress( &pEfc, dwAddress, &page, &offset ) ;
223
224 /* Write all pages */
225 while ( dwSize > 0 )
226 {
227 /* Copy data in temporary buffer to avoid alignment problems */
228 writeSize = min((uint32_t)IFLASH_PAGE_SIZE - offset, dwSize ) ;
229 EFC_ComputeAddress(pEfc, page, 0, &pageAddress ) ;
230 padding = IFLASH_PAGE_SIZE - offset - writeSize ;
231
232 /* Pre-buffer data */
233 memcpy( _aucPageBuffer, (void *) pageAddress, offset);
234
235 /* Buffer data */
236 memcpy( _aucPageBuffer + offset, pvBuffer, writeSize);
237
238 /* Post-buffer data */
239 memcpy( _aucPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding);
240
241 /* Write page
242 * Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption
243 */
244 pAlignedDestination = (uint32_t*)pageAddress ;
245 pAlignedSource = (uint32_t*)_adwPageBuffer ;
246 sizeTmp = IFLASH_PAGE_SIZE ;
247
248 while ( sizeTmp >= 4 )
249 {
250 *pAlignedDestination++ = *pAlignedSource++;
251 sizeTmp -= 4;
252 }
253
254 /* Send writing command */
255 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EWP, page, _dwUseIAP ) ;
256 if ( dwError )
257 {
258 return dwError ;
259 }
260
261 /* Progression */
262 dwAddress += IFLASH_PAGE_SIZE ;
263 pvBuffer = (void *)((uint32_t) pvBuffer + writeSize) ;
264 dwSize -= writeSize ;
265 page++;
266 offset = 0;
267 }
268
269 return 0 ;
270}
271/**
272 * \brief Locks all the regions in the given address range. The actual lock range is
273 * reported through two output parameters.
274 *
275 * \param start Start address of lock range.
276 * \param end End address of lock range.
277 * \param pActualStart Start address of the actual lock range (optional).
278 * \param pActualEnd End address of the actual lock range (optional).
279 * \return 0 if successful, otherwise returns an error code.
280 */
281extern uint32_t FLASHD_Lock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
282{
283 Efc *pEfc ;
284 uint32_t actualStart, actualEnd ;
285 uint16_t startPage, endPage ;
286 uint32_t dwError ;
287 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
288
289 /* Compute actual lock range and store it */
290 ComputeLockRange( start, end, &actualStart, &actualEnd ) ;
291 if ( pActualStart != NULL )
292 {
293 *pActualStart = actualStart ;
294 }
295 if ( pActualEnd != NULL )
296 {
297 *pActualEnd = actualEnd;
298 }
299
300 /* Compute page numbers */
301 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
302 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
303
304 /* Lock all pages */
305 while ( startPage < endPage )
306 {
307 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_SLB, startPage, _dwUseIAP ) ;
308 if ( dwError )
309 {
310 return dwError ;
311 }
312 startPage += numPagesInRegion;
313 }
314
315 return 0 ;
316}
317
318/**
319 * \brief Unlocks all the regions in the given address range. The actual unlock range is
320 * reported through two output parameters.
321 * \param start Start address of unlock range.
322 * \param end End address of unlock range.
323 * \param pActualStart Start address of the actual unlock range (optional).
324 * \param pActualEnd End address of the actual unlock range (optional).
325 * \return 0 if successful, otherwise returns an error code.
326 */
327extern uint32_t FLASHD_Unlock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
328{
329 Efc* pEfc ;
330 uint32_t actualStart, actualEnd ;
331 uint16_t startPage, endPage ;
332 uint32_t dwError ;
333 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
334
335 // Compute actual unlock range and store it
336 ComputeLockRange(start, end, &actualStart, &actualEnd);
337 if ( pActualStart != NULL )
338 {
339 *pActualStart = actualStart ;
340 }
341 if ( pActualEnd != NULL )
342 {
343 *pActualEnd = actualEnd ;
344 }
345
346 // Compute page numbers
347 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
348 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
349
350 // Unlock all pages
351 while ( startPage < endPage )
352 {
353 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_CLB, startPage, _dwUseIAP ) ;
354 if ( dwError )
355 {
356 return dwError ;
357 }
358 startPage += numPagesInRegion ;
359 }
360 return 0 ;
361}
362
363/**
364 * \brief Returns the number of locked regions inside the given address range.
365 *
366 * \param start Start address of range
367 * \param end End address of range.
368 */
369extern uint32_t FLASHD_IsLocked( uint32_t start, uint32_t end )
370{
371 Efc *pEfc ;
372 uint16_t startPage, endPage ;
373 uint8_t startRegion, endRegion ;
374 uint32_t numPagesInRegion ;
375 uint32_t status ;
376 uint32_t dwError ;
377 uint32_t numLockedRegions = 0 ;
378
379 assert( end >= start ) ;
380 assert( (start >=IFLASH_ADDR) && (end <= IFLASH_ADDR + IFLASH_SIZE) ) ;
381
382 // Compute page numbers
383 EFC_TranslateAddress( &pEfc, start, &startPage, 0 ) ;
384 EFC_TranslateAddress( 0, end, &endPage, 0 ) ;
385
386 // Compute region numbers
387 numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE ;
388 startRegion = startPage / numPagesInRegion ;
389 endRegion = endPage / numPagesInRegion ;
390 if ((endPage % numPagesInRegion) != 0)
391 {
392 endRegion++ ;
393 }
394
395 // Retrieve lock status
396 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_GLB, 0, _dwUseIAP ) ;
397 assert( !dwError ) ;
398 status = EFC_GetResult( pEfc ) ;
399
400 // Check status of each involved region
401 while ( startRegion < endRegion )
402 {
403 if ( (status & (1 << startRegion)) != 0 )
404 {
405 numLockedRegions++ ;
406 }
407 startRegion++ ;
408 }
409
410 return numLockedRegions ;
411}
412
413/**
414 * \brief Check if the given GPNVM bit is set or not.
415 *
416 * \param gpnvm GPNVM bit index.
417 * \returns 1 if the given GPNVM bit is currently set; otherwise returns 0.
418 */
419extern uint32_t FLASHD_IsGPNVMSet( uint8_t ucGPNVM )
420{
421 uint32_t dwError ;
422 uint32_t dwStatus ;
423
424 assert( ucGPNVM < 2 ) ;
425
426 /* Get GPNVMs status */
427 dwError = EFC_PerformCommand( EFC, EFC_FCMD_GFB, 0, _dwUseIAP ) ;
428 assert( !dwError ) ;
429 dwStatus = EFC_GetResult( EFC ) ;
430
431 /* Check if GPNVM is set */
432 if ( (dwStatus & (1 << ucGPNVM)) != 0 )
433 {
434 return 1 ;
435 }
436 else
437 {
438 return 0 ;
439 }
440}
441
442/**
443 * \brief Sets the selected GPNVM bit.
444 *
445 * \param gpnvm GPNVM bit index.
446 * \returns 0 if successful; otherwise returns an error code.
447 */
448extern uint32_t FLASHD_SetGPNVM( uint8_t ucGPNVM )
449{
450 assert( ucGPNVM < 2 ) ;
451
452 if ( !FLASHD_IsGPNVMSet( ucGPNVM ) )
453 {
454 return EFC_PerformCommand( EFC, EFC_FCMD_SFB, ucGPNVM, _dwUseIAP ) ;
455 }
456 else
457 {
458 return 0 ;
459 }
460}
461
462/**
463 * \brief Clears the selected GPNVM bit.
464 *
465 * \param gpnvm GPNVM bit index.
466 * \returns 0 if successful; otherwise returns an error code.
467 */
468extern uint32_t FLASHD_ClearGPNVM( uint8_t ucGPNVM )
469{
470 assert( ucGPNVM < 2 ) ;
471
472 if ( FLASHD_IsGPNVMSet( ucGPNVM ) )
473 {
474 return EFC_PerformCommand( EFC, EFC_FCMD_CFB, ucGPNVM, _dwUseIAP ) ;
475 }
476 else
477 {
478 return 0 ;
479 }
480}
481/**
482 * \brief Read the unique ID.
483 *
484 * \param uniqueID pointer on a 4bytes char containing the unique ID value.
485 * \returns 0 if successful; otherwise returns an error code.
486 */
487extern uint32_t FLASHD_ReadUniqueID( uint32_t* pdwUniqueID )
488{
489 uint32_t dwError ;
490
491 assert( pdwUniqueID != NULL ) ;
492
493 pdwUniqueID[0] = 0 ;
494 pdwUniqueID[1] = 0 ;
495 pdwUniqueID[2] = 0 ;
496 pdwUniqueID[3] = 0 ;
497
498 EFC_StartCommand( EFC, EFC_FCMD_STUI, 0 ) ;
499
500 pdwUniqueID[0] = *(uint32_t*) IFLASH_ADDR;
501 pdwUniqueID[1] = *(uint32_t*)(IFLASH_ADDR + 4) ;
502 pdwUniqueID[2] = *(uint32_t*)(IFLASH_ADDR + 8) ;
503 pdwUniqueID[3] = *(uint32_t*)(IFLASH_ADDR + 12) ;
504
505 dwError = EFC_PerformCommand( EFC, EFC_FCMD_SPUI, 0, _dwUseIAP ) ;
506 if ( dwError )
507 {
508 return dwError ;
509 }
510
511 return 0 ;
512}