blob: 6247139dad5540a6528200977f451c159cf7b689 [file] [log] [blame]
Harald Weltee9eaf902017-02-26 12:52: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"
Harald Weltea05ccc92017-02-26 12:52:28 +010077#include "flashd.h"
78#include "efc.h"
Harald Weltee9eaf902017-02-26 12:52:01 +010079
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
155 if ( (dwMCk/1000000) >= 64 )
156 {
157 EFC_SetWaitState( EFC, 2 ) ;
158 }
159 else
160 {
161 if ( (dwMCk/1000000) >= 50 )
162 {
163 EFC_SetWaitState( EFC, 1 ) ;
164 }
165 else
166 {
167 EFC_SetWaitState( EFC, 0 ) ;
168 }
169 }
170
171 _dwUseIAP=dwUseIAP ;
172}
173
174/**
175 * \brief Erases the entire flash.
176 *
177 * \param address Flash start address.
178 * \return 0 if successful; otherwise returns an error code.
179 */
180extern uint32_t FLASHD_Erase( uint32_t dwAddress )
181{
182 Efc* pEfc ;
183 uint16_t wPage ;
184 uint16_t wOffset ;
185 uint32_t dwError ;
186
187 assert( (dwAddress >=IFLASH_ADDR) || (dwAddress <= (IFLASH_ADDR + IFLASH_SIZE)) ) ;
188
189 // Translate write address
190 EFC_TranslateAddress( &pEfc, dwAddress, &wPage, &wOffset ) ;
191 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EA, 0, _dwUseIAP ) ;
192
193 return dwError ;
194}
195
196/**
197 * \brief Writes a data buffer in the internal flash
198 *
199 * \note This function works in polling mode, and thus only returns when the
200 * data has been effectively written.
201 * \param address Write address.
202 * \param pBuffer Data buffer.
203 * \param size Size of data buffer in bytes.
204 * \return 0 if successful, otherwise returns an error code.
205 */
206extern uint32_t FLASHD_Write( uint32_t dwAddress, const void *pvBuffer, uint32_t dwSize )
207{
208 Efc* pEfc ;
209 uint16_t page ;
210 uint16_t offset ;
211 uint32_t writeSize ;
212 uint32_t pageAddress ;
213 uint16_t padding ;
214 uint32_t dwError ;
215 uint32_t sizeTmp ;
216 uint32_t *pAlignedDestination ;
217 uint32_t *pAlignedSource ;
218
219 assert( pvBuffer ) ;
220 assert( dwAddress >=IFLASH_ADDR ) ;
221 assert( (dwAddress + dwSize) <= (IFLASH_ADDR + IFLASH_SIZE) ) ;
222
223 /* Translate write address */
224 EFC_TranslateAddress( &pEfc, dwAddress, &page, &offset ) ;
225
226 /* Write all pages */
227 while ( dwSize > 0 )
228 {
229 /* Copy data in temporary buffer to avoid alignment problems */
230 writeSize = min((uint32_t)IFLASH_PAGE_SIZE - offset, dwSize ) ;
231 EFC_ComputeAddress(pEfc, page, 0, &pageAddress ) ;
232 padding = IFLASH_PAGE_SIZE - offset - writeSize ;
233
234 /* Pre-buffer data */
235 memcpy( _aucPageBuffer, (void *) pageAddress, offset);
236
237 /* Buffer data */
238 memcpy( _aucPageBuffer + offset, pvBuffer, writeSize);
239
240 /* Post-buffer data */
241 memcpy( _aucPageBuffer + offset + writeSize, (void *) (pageAddress + offset + writeSize), padding);
242
243 /* Write page
244 * Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption
245 */
246 pAlignedDestination = (uint32_t*)pageAddress ;
247 pAlignedSource = (uint32_t*)_adwPageBuffer ;
248 sizeTmp = IFLASH_PAGE_SIZE ;
249
250 while ( sizeTmp >= 4 )
251 {
252 *pAlignedDestination++ = *pAlignedSource++;
253 sizeTmp -= 4;
254 }
255
256 /* Send writing command */
257 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_EWP, page, _dwUseIAP ) ;
258 if ( dwError )
259 {
260 return dwError ;
261 }
262
263 /* Progression */
264 dwAddress += IFLASH_PAGE_SIZE ;
265 pvBuffer = (void *)((uint32_t) pvBuffer + writeSize) ;
266 dwSize -= writeSize ;
267 page++;
268 offset = 0;
269 }
270
271 return 0 ;
272}
273/**
274 * \brief Locks all the regions in the given address range. The actual lock range is
275 * reported through two output parameters.
276 *
277 * \param start Start address of lock range.
278 * \param end End address of lock range.
279 * \param pActualStart Start address of the actual lock range (optional).
280 * \param pActualEnd End address of the actual lock range (optional).
281 * \return 0 if successful, otherwise returns an error code.
282 */
283extern uint32_t FLASHD_Lock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
284{
285 Efc *pEfc ;
286 uint32_t actualStart, actualEnd ;
287 uint16_t startPage, endPage ;
288 uint32_t dwError ;
289 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
290
291 /* Compute actual lock range and store it */
292 ComputeLockRange( start, end, &actualStart, &actualEnd ) ;
293 if ( pActualStart != NULL )
294 {
295 *pActualStart = actualStart ;
296 }
297 if ( pActualEnd != NULL )
298 {
299 *pActualEnd = actualEnd;
300 }
301
302 /* Compute page numbers */
303 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
304 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
305
306 /* Lock all pages */
307 while ( startPage < endPage )
308 {
309 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_SLB, startPage, _dwUseIAP ) ;
310 if ( dwError )
311 {
312 return dwError ;
313 }
314 startPage += numPagesInRegion;
315 }
316
317 return 0 ;
318}
319
320/**
321 * \brief Unlocks all the regions in the given address range. The actual unlock range is
322 * reported through two output parameters.
323 * \param start Start address of unlock range.
324 * \param end End address of unlock range.
325 * \param pActualStart Start address of the actual unlock range (optional).
326 * \param pActualEnd End address of the actual unlock range (optional).
327 * \return 0 if successful, otherwise returns an error code.
328 */
329extern uint32_t FLASHD_Unlock( uint32_t start, uint32_t end, uint32_t *pActualStart, uint32_t *pActualEnd )
330{
331 Efc* pEfc ;
332 uint32_t actualStart, actualEnd ;
333 uint16_t startPage, endPage ;
334 uint32_t dwError ;
335 uint16_t numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE;
336
337 // Compute actual unlock range and store it
338 ComputeLockRange(start, end, &actualStart, &actualEnd);
339 if ( pActualStart != NULL )
340 {
341 *pActualStart = actualStart ;
342 }
343 if ( pActualEnd != NULL )
344 {
345 *pActualEnd = actualEnd ;
346 }
347
348 // Compute page numbers
349 EFC_TranslateAddress( &pEfc, actualStart, &startPage, 0 ) ;
350 EFC_TranslateAddress( 0, actualEnd, &endPage, 0 ) ;
351
352 // Unlock all pages
353 while ( startPage < endPage )
354 {
355 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_CLB, startPage, _dwUseIAP ) ;
356 if ( dwError )
357 {
358 return dwError ;
359 }
360 startPage += numPagesInRegion ;
361 }
362 return 0 ;
363}
364
365/**
366 * \brief Returns the number of locked regions inside the given address range.
367 *
368 * \param start Start address of range
369 * \param end End address of range.
370 */
371extern uint32_t FLASHD_IsLocked( uint32_t start, uint32_t end )
372{
373 Efc *pEfc ;
374 uint16_t startPage, endPage ;
375 uint8_t startRegion, endRegion ;
376 uint32_t numPagesInRegion ;
377 uint32_t status ;
378 uint32_t dwError ;
379 uint32_t numLockedRegions = 0 ;
380
381 assert( end >= start ) ;
382 assert( (start >=IFLASH_ADDR) && (end <= IFLASH_ADDR + IFLASH_SIZE) ) ;
383
384 // Compute page numbers
385 EFC_TranslateAddress( &pEfc, start, &startPage, 0 ) ;
386 EFC_TranslateAddress( 0, end, &endPage, 0 ) ;
387
388 // Compute region numbers
389 numPagesInRegion = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE ;
390 startRegion = startPage / numPagesInRegion ;
391 endRegion = endPage / numPagesInRegion ;
392 if ((endPage % numPagesInRegion) != 0)
393 {
394 endRegion++ ;
395 }
396
397 // Retrieve lock status
398 dwError = EFC_PerformCommand( pEfc, EFC_FCMD_GLB, 0, _dwUseIAP ) ;
399 assert( !dwError ) ;
400 status = EFC_GetResult( pEfc ) ;
401
402 // Check status of each involved region
403 while ( startRegion < endRegion )
404 {
405 if ( (status & (1 << startRegion)) != 0 )
406 {
407 numLockedRegions++ ;
408 }
409 startRegion++ ;
410 }
411
412 return numLockedRegions ;
413}
414
415/**
416 * \brief Check if the given GPNVM bit is set or not.
417 *
418 * \param gpnvm GPNVM bit index.
419 * \returns 1 if the given GPNVM bit is currently set; otherwise returns 0.
420 */
421extern uint32_t FLASHD_IsGPNVMSet( uint8_t ucGPNVM )
422{
423 uint32_t dwError ;
424 uint32_t dwStatus ;
425
426 assert( ucGPNVM < 2 ) ;
427
428 /* Get GPNVMs status */
429 dwError = EFC_PerformCommand( EFC, EFC_FCMD_GFB, 0, _dwUseIAP ) ;
430 assert( !dwError ) ;
431 dwStatus = EFC_GetResult( EFC ) ;
432
433 /* Check if GPNVM is set */
434 if ( (dwStatus & (1 << ucGPNVM)) != 0 )
435 {
436 return 1 ;
437 }
438 else
439 {
440 return 0 ;
441 }
442}
443
444/**
445 * \brief Sets the selected GPNVM bit.
446 *
447 * \param gpnvm GPNVM bit index.
448 * \returns 0 if successful; otherwise returns an error code.
449 */
450extern uint32_t FLASHD_SetGPNVM( uint8_t ucGPNVM )
451{
452 assert( ucGPNVM < 2 ) ;
453
454 if ( !FLASHD_IsGPNVMSet( ucGPNVM ) )
455 {
456 return EFC_PerformCommand( EFC, EFC_FCMD_SFB, ucGPNVM, _dwUseIAP ) ;
457 }
458 else
459 {
460 return 0 ;
461 }
462}
463
464/**
465 * \brief Clears the selected GPNVM bit.
466 *
467 * \param gpnvm GPNVM bit index.
468 * \returns 0 if successful; otherwise returns an error code.
469 */
470extern uint32_t FLASHD_ClearGPNVM( uint8_t ucGPNVM )
471{
472 assert( ucGPNVM < 2 ) ;
473
474 if ( FLASHD_IsGPNVMSet( ucGPNVM ) )
475 {
476 return EFC_PerformCommand( EFC, EFC_FCMD_CFB, ucGPNVM, _dwUseIAP ) ;
477 }
478 else
479 {
480 return 0 ;
481 }
482}
483/**
484 * \brief Read the unique ID.
485 *
486 * \param uniqueID pointer on a 4bytes char containing the unique ID value.
487 * \returns 0 if successful; otherwise returns an error code.
488 */
489extern uint32_t FLASHD_ReadUniqueID( uint32_t* pdwUniqueID )
490{
491 uint32_t dwError ;
492
493 assert( pdwUniqueID != NULL ) ;
494
495 pdwUniqueID[0] = 0 ;
496 pdwUniqueID[1] = 0 ;
497 pdwUniqueID[2] = 0 ;
498 pdwUniqueID[3] = 0 ;
499
500 EFC_StartCommand( EFC, EFC_FCMD_STUI, 0 ) ;
501
502 pdwUniqueID[0] = *(uint32_t*) IFLASH_ADDR;
503 pdwUniqueID[1] = *(uint32_t*)(IFLASH_ADDR + 4) ;
504 pdwUniqueID[2] = *(uint32_t*)(IFLASH_ADDR + 8) ;
505 pdwUniqueID[3] = *(uint32_t*)(IFLASH_ADDR + 12) ;
506
507 dwError = EFC_PerformCommand( EFC, EFC_FCMD_SPUI, 0, _dwUseIAP ) ;
508 if ( dwError )
509 {
510 return dwError ;
511 }
512
513 return 0 ;
514}