blob: 677ea30903a408a387655fdd1c0b0038692c597b [file] [log] [blame]
Kévin Redon93717e42018-07-08 13:26:15 +02001/* ----------------------------------------------------------------------------
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#include "flashd.h"
78#include "efc.h"
79
80#include <string.h>
81#include <assert.h>
82
83/*----------------------------------------------------------------------------
84 * Local variables
85 *----------------------------------------------------------------------------*/
86
87//static NO_INIT uint8_t _aucPageBuffer[IFLASH_PAGE_SIZE] ;
88static NO_INIT uint32_t _adwPageBuffer[IFLASH_PAGE_SIZE/4] ;
89static uint8_t* _aucPageBuffer = (uint8_t*)_adwPageBuffer;
90static NO_INIT uint32_t _dwUseIAP ;
91
92/*----------------------------------------------------------------------------
93 * Local macros
94 *----------------------------------------------------------------------------*/
95
96#define min( a, b ) (((a) < (b)) ? (a) : (b))
97
98/*----------------------------------------------------------------------------
99 * Local functions
100 *----------------------------------------------------------------------------*/
101
102
103/**
104 * \brief Computes the lock range associated with the given address range.
105 *
106 * \param dwStart Start address of lock range.
107 * \param dwEnd End address of lock range.
108 * \param pdwActualStart Actual start address of lock range.
109 * \param pdwActualEnd Actual end address of lock range.
110 */
111static void ComputeLockRange( uint32_t dwStart, uint32_t dwEnd, uint32_t *pdwActualStart, uint32_t *pdwActualEnd )
112{
113 Efc* pStartEfc ;
114 Efc* pEndEfc ;
115 uint16_t wStartPage ;
116 uint16_t wEndPage ;
117 uint16_t wNumPagesInRegion ;
118 uint16_t wActualStartPage ;
119 uint16_t wActualEndPage ;
120
121 // Convert start and end address in page numbers
122 EFC_TranslateAddress( &pStartEfc, dwStart, &wStartPage, 0 ) ;
123 EFC_TranslateAddress( &pEndEfc, dwEnd, &wEndPage, 0 ) ;
124
125 // Find out the first page of the first region to lock
126 wNumPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE ;
127 wActualStartPage = wStartPage - (wStartPage % wNumPagesInRegion) ;
128 wActualEndPage = wEndPage ;
129
130 if ( (wEndPage % wNumPagesInRegion) != 0 )
131 {
132 wActualEndPage += wNumPagesInRegion - (wEndPage % wNumPagesInRegion) ;
133 }
134 // Store actual page numbers
135 EFC_ComputeAddress( pStartEfc, wActualStartPage, 0, pdwActualStart ) ;
136 EFC_ComputeAddress( pEndEfc, wActualEndPage, 0, pdwActualEnd ) ;
137 TRACE_DEBUG( "Actual lock range is 0x%06X - 0x%06X\n\r", *pdwActualStart, *pdwActualEnd ) ;
138}
139
140
141/*----------------------------------------------------------------------------
142 * Exported functions
143 *----------------------------------------------------------------------------*/
144
145/**
146 * \brief Initializes the flash driver.
147 *
148 * \param mck Master clock frequency in Hz.
149 */
150
151extern void FLASHD_Initialize( uint32_t dwMCk, uint32_t dwUseIAP )
152{
153 EFC_DisableFrdyIt( EFC ) ;
154#if 1
155 /* See Revision A errata 46.1.1.3 */
156 EFC_SetWaitState(EFC, 6);
157#else
158 if ( (dwMCk/1000000) >= 64 )
159 {
160 EFC_SetWaitState( EFC, 2 ) ;
161 }
162 else
163 {
164 if ( (dwMCk/1000000) >= 50 )
165 {
166 EFC_SetWaitState( EFC, 1 ) ;
167 }
168 else
169 {
170 EFC_SetWaitState( EFC, 0 ) ;
171 }
172 }
173#endif
174 _dwUseIAP=dwUseIAP ;
175}
176
177/**
178 * \brief Erases the entire flash.
179 *
180 * \param address Flash start address.
181 * \return 0 if successful; otherwise returns an error code.
182 */
183extern uint32_t FLASHD_Erase( uint32_t dwAddress )
184{
185 Efc* pEfc ;
186 uint16_t wPage ;
187 uint16_t wOffset ;
188 uint32_t dwError ;
189
190 assert( (dwAddress >=IFLASH_ADDR) || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)) ) ;
191
192 // Translate write address
193 EFC_TranslateAddress( &pEfc, dwAddress, &wPage, &wOffset ) ;
194 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EA, 0, _dwUseIAP ) ;
195
196 return dwError ;
197}
198
199/**
200 * \brief Writes a data buffer in the internal flash
201 *
202 * \note This function works in polling mode, and thus only returns when the
203 * data has been effectively written.
204 * \param address Write address.
205 * \param pBuffer Data buffer.
206 * \param size Size of data buffer in bytes.
207 * \return 0 if successful, otherwise returns an error code.
208 */
209extern uint32_t FLASHD_Write( uint32_t dwAddress, const void *pvBuffer, uint32_t dwSize )
210{
211 Efc* pEfc ;
212 uint16_t page ;
213 uint16_t offset ;
214 uint32_t writeSize ;
215 uint32_t pageAddress ;
216 uint16_t padding ;
217 uint32_t dwError ;
218 uint32_t sizeTmp ;
219 uint32_t *pAlignedDestination ;
220 uint32_t *pAlignedSource ;
221
222 assert( pvBuffer ) ;
223 assert( dwAddress >=IFLASH_ADDR ) ;
224 assert( (dwAddress + dwSize) <= (IFLASH_ADDR + IFLASH_SIZE) ) ;
225
226 /* Translate write address */
227 EFC_TranslateAddress( &pEfc, dwAddress, &page, &offset ) ;
228
229 /* Write all pages */
230 while ( dwSize > 0 )
231 {
232 /* Copy data in temporary buffer to avoid alignment problems */
233 writeSize = min((uint32_t)IFLASH_PAGE_SIZE - offset, dwSize ) ;
234 EFC_ComputeAddress(pEfc, page, 0, &pageAddress ) ;
235 padding = IFLASH_PAGE_SIZE - offset - writeSize ;
236
237 /* Pre-buffer data */
238 memcpy( _aucPageBuffer, (void *) pageAddress, offset);
239
240 /* Buffer data */
241 memcpy( _aucPageBuffer + offset, pvBuffer, writeSize);
242
243 /* Post-buffer data */
244 memcpy( _aucPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding);
245
246 /* Write page
247 * Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption
248 */
249 pAlignedDestination = (uint32_t*)pageAddress ;
250 pAlignedSource = (uint32_t*)_adwPageBuffer ;
251 sizeTmp = IFLASH_PAGE_SIZE ;
252
253 while ( sizeTmp >= 4 )
254 {
255 *pAlignedDestination++ = *pAlignedSource++;
256 sizeTmp -= 4;
257 }
258
259 /* Send writing command */
260 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EWP, page, _dwUseIAP ) ;
261 if ( dwError )
262 {
263 return dwError ;
264 }
265
266 /* Progression */
267 dwAddress += IFLASH_PAGE_SIZE ;
268 pvBuffer = (void *)((uint32_t) pvBuffer + writeSize) ;
269 dwSize -= writeSize ;
270 page++;
271 offset = 0;
272 }
273
274 return 0 ;
275}
276/**
277 * \brief Locks all the regions in the given address range. The actual lock range is
278 * reported through two output parameters.
279 *
280 * \param start Start address of lock range.
281 * \param end End address of lock range.
282 * \param pActualStart Start address of the actual lock range (optional).
283 * \param pActualEnd End address of the actual lock range (optional).
284 * \return 0 if successful, otherwise returns an error code.
285 */
286extern uint32_t FLASHD_Lock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
287{
288 Efc *pEfc ;
289 uint32_t actualStart, actualEnd ;
290 uint16_t startPage, endPage ;
291 uint32_t dwError ;
292 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
293
294 /* Compute actual lock range and store it */
295 ComputeLockRange( start, end, &actualStart, &actualEnd ) ;
296 if ( pActualStart != NULL )
297 {
298 *pActualStart = actualStart ;
299 }
300 if ( pActualEnd != NULL )
301 {
302 *pActualEnd = actualEnd;
303 }
304
305 /* Compute page numbers */
306 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
307 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
308
309 /* Lock all pages */
310 while ( startPage < endPage )
311 {
312 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_SLB, startPage, _dwUseIAP ) ;
313 if ( dwError )
314 {
315 return dwError ;
316 }
317 startPage += numPagesInRegion;
318 }
319
320 return 0 ;
321}
322
323/**
324 * \brief Unlocks all the regions in the given address range. The actual unlock range is
325 * reported through two output parameters.
326 * \param start Start address of unlock range.
327 * \param end End address of unlock range.
328 * \param pActualStart Start address of the actual unlock range (optional).
329 * \param pActualEnd End address of the actual unlock range (optional).
330 * \return 0 if successful, otherwise returns an error code.
331 */
332extern uint32_t FLASHD_Unlock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
333{
334 Efc* pEfc ;
335 uint32_t actualStart, actualEnd ;
336 uint16_t startPage, endPage ;
337 uint32_t dwError ;
338 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
339
340 // Compute actual unlock range and store it
341 ComputeLockRange(start, end, &actualStart, &actualEnd);
342 if ( pActualStart != NULL )
343 {
344 *pActualStart = actualStart ;
345 }
346 if ( pActualEnd != NULL )
347 {
348 *pActualEnd = actualEnd ;
349 }
350
351 // Compute page numbers
352 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
353 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
354
355 // Unlock all pages
356 while ( startPage < endPage )
357 {
358 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_CLB, startPage, _dwUseIAP ) ;
359 if ( dwError )
360 {
361 return dwError ;
362 }
363 startPage += numPagesInRegion ;
364 }
365 return 0 ;
366}
367
368/**
369 * \brief Returns the number of locked regions inside the given address range.
370 *
371 * \param start Start address of range
372 * \param end End address of range.
373 */
374extern uint32_t FLASHD_IsLocked( uint32_t start, uint32_t end )
375{
376 Efc *pEfc ;
377 uint16_t startPage, endPage ;
378 uint8_t startRegion, endRegion ;
379 uint32_t numPagesInRegion ;
380 uint32_t status ;
381 uint32_t dwError ;
382 uint32_t numLockedRegions = 0 ;
383
384 assert( end >= start ) ;
385 assert( (start >=IFLASH_ADDR) && (end <= IFLASH_ADDR + IFLASH_SIZE) ) ;
386
387 // Compute page numbers
388 EFC_TranslateAddress( &pEfc, start, &startPage, 0 ) ;
389 EFC_TranslateAddress( 0, end, &endPage, 0 ) ;
390
391 // Compute region numbers
392 numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE ;
393 startRegion = startPage / numPagesInRegion ;
394 endRegion = endPage / numPagesInRegion ;
395 if ((endPage % numPagesInRegion) != 0)
396 {
397 endRegion++ ;
398 }
399
400 // Retrieve lock status
401 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_GLB, 0, _dwUseIAP ) ;
402 assert( !dwError ) ;
403 status = EFC_GetResult( pEfc ) ;
404
405 // Check status of each involved region
406 while ( startRegion < endRegion )
407 {
408 if ( (status & (1 << startRegion)) != 0 )
409 {
410 numLockedRegions++ ;
411 }
412 startRegion++ ;
413 }
414
415 return numLockedRegions ;
416}
417
418/**
419 * \brief Check if the given GPNVM bit is set or not.
420 *
421 * \param gpnvm GPNVM bit index.
422 * \returns 1 if the given GPNVM bit is currently set; otherwise returns 0.
423 */
424extern uint32_t FLASHD_IsGPNVMSet( uint8_t ucGPNVM )
425{
426 uint32_t dwError ;
427 uint32_t dwStatus ;
428
429 assert( ucGPNVM < 2 ) ;
430
431 /* Get GPNVMs status */
432 dwError = EFC_PerformCommand( EFC, EFC_FCMD_GFB, 0, _dwUseIAP ) ;
433 assert( !dwError ) ;
434 dwStatus = EFC_GetResult( EFC ) ;
435
436 /* Check if GPNVM is set */
437 if ( (dwStatus & (1 << ucGPNVM)) != 0 )
438 {
439 return 1 ;
440 }
441 else
442 {
443 return 0 ;
444 }
445}
446
447/**
448 * \brief Sets the selected GPNVM bit.
449 *
450 * \param gpnvm GPNVM bit index.
451 * \returns 0 if successful; otherwise returns an error code.
452 */
453extern uint32_t FLASHD_SetGPNVM( uint8_t ucGPNVM )
454{
455 assert( ucGPNVM < 2 ) ;
456
457 if ( !FLASHD_IsGPNVMSet( ucGPNVM ) )
458 {
459 return EFC_PerformCommand( EFC, EFC_FCMD_SFB, ucGPNVM, _dwUseIAP ) ;
460 }
461 else
462 {
463 return 0 ;
464 }
465}
466
467/**
468 * \brief Clears the selected GPNVM bit.
469 *
470 * \param gpnvm GPNVM bit index.
471 * \returns 0 if successful; otherwise returns an error code.
472 */
473extern uint32_t FLASHD_ClearGPNVM( uint8_t ucGPNVM )
474{
475 assert( ucGPNVM < 2 ) ;
476
477 if ( FLASHD_IsGPNVMSet( ucGPNVM ) )
478 {
479 return EFC_PerformCommand( EFC, EFC_FCMD_CFB, ucGPNVM, _dwUseIAP ) ;
480 }
481 else
482 {
483 return 0 ;
484 }
485}
486/**
487 * \brief Read the unique ID.
488 *
489 * \param uniqueID pointer on a 4bytes char containing the unique ID value.
490 * \returns 0 if successful; otherwise returns an error code.
491 */
492extern uint32_t FLASHD_ReadUniqueID( uint32_t* pdwUniqueID )
493{
494 uint32_t dwError ;
495
496 assert( pdwUniqueID != NULL ) ;
497
498 pdwUniqueID[0] = 0 ;
499 pdwUniqueID[1] = 0 ;
500 pdwUniqueID[2] = 0 ;
501 pdwUniqueID[3] = 0 ;
502
503 EFC_StartCommand( EFC, EFC_FCMD_STUI, 0 ) ;
504
505 pdwUniqueID[0] = *(uint32_t*) IFLASH_ADDR;
506 pdwUniqueID[1] = *(uint32_t*)(IFLASH_ADDR + 4) ;
507 pdwUniqueID[2] = *(uint32_t*)(IFLASH_ADDR + 8) ;
508 pdwUniqueID[3] = *(uint32_t*)(IFLASH_ADDR + 12) ;
509
510 dwError = EFC_PerformCommand( EFC, EFC_FCMD_SPUI, 0, _dwUseIAP ) ;
511 if ( dwError )
512 {
513 return dwError ;
514 }
515
516 return 0 ;
517}