1 /**************************************************************************//**
2  * @file     fmc.c
3  * @version  V1.00
4  * @brief    M460 series FMC driver source file
5  *
6  * @copyright SPDX-License-Identifier: Apache-2.0
7  * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
8 *****************************************************************************/
9 
10 #include <stdio.h>
11 
12 #include "NuMicro.h"
13 
14 
15 /** @addtogroup Standard_Driver Standard Driver
16   @{
17 */
18 
19 /** @addtogroup FMC_Driver FMC Driver
20   @{
21 */
22 
23 
24 /** @addtogroup FMC_EXPORTED_FUNCTIONS FMC Exported Functions
25   @{
26 */
27 
28 int32_t g_FMC_i32ErrCode = 0; /*!< FMC global error code */
29 
30 /**
31   * @brief Disable FMC ISP function.
32   * @return None
33   */
FMC_Close(void)34 void FMC_Close(void)
35 {
36     FMC->ISPCTL &= ~FMC_ISPCTL_ISPEN_Msk;
37 }
38 
39 /**
40   * @brief     Config XOM Region
41   * @param[in] u32XomNum    The XOM number(0~3)
42   * @param[in] u32XomBase   The XOM region base address.
43   * @param[in] u8XomPage    The XOM page number of region size.
44   *
45   * @retval   0   Success
46   * @retval   1   XOM is has already actived.
47   * @retval   -1  Program failed.
48   * @retval   -2  Invalid XOM number.
49   *
50   * @details  Program XOM base address and XOM size(page)
51   * @note     Global error code g_FMC_i32ErrCode
52   *           -1  Program failed or program time-out
53   *           -2  Invalid XOM number.
54   */
FMC_ConfigXOM(uint32_t u32XomNum,uint32_t u32XomBase,uint8_t u8XomPage)55 int32_t FMC_ConfigXOM(uint32_t u32XomNum, uint32_t u32XomBase, uint8_t u8XomPage)
56 {
57     int32_t  ret = 0;
58     int32_t i32TimeOutCnt;
59 
60     g_FMC_i32ErrCode = 0;
61 
62     if(u32XomNum >= 4UL)
63     {
64         g_FMC_i32ErrCode = -2;
65         ret = -2;
66     }
67 
68     if(ret == 0)
69     {
70         ret = FMC_GetXOMState(u32XomNum);
71     }
72 
73     if(ret == 0)
74     {
75         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
76         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u);
77         FMC->ISPDAT = u32XomBase;
78         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
79         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
80         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
81         {
82             if( i32TimeOutCnt-- <= 0)
83             {
84                 g_FMC_i32ErrCode = -1;
85                 ret = -1;
86                 break;
87             }
88         }
89 
90         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
91         {
92             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
93             g_FMC_i32ErrCode = -1;
94             ret = -1;
95         }
96     }
97 
98     if(ret == 0)
99     {
100         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
101         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u + 0x04u);
102         FMC->ISPDAT = u8XomPage;
103         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
104         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
105         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
106         {
107             if( i32TimeOutCnt-- <= 0)
108             {
109                 g_FMC_i32ErrCode = -1;
110                 ret = -1;
111                 break;
112             }
113         }
114 
115         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
116         {
117             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
118             g_FMC_i32ErrCode = -1;
119             ret = -1;
120         }
121     }
122 
123     if(ret == 0)
124     {
125         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
126         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u + 0x08u);
127         FMC->ISPDAT = 0u;
128         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
129         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
130         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
131         {
132             if( i32TimeOutCnt-- <= 0)
133             {
134                 g_FMC_i32ErrCode = -1;
135                 ret = -1;
136                 break;
137             }
138         }
139 
140         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
141         {
142             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
143             g_FMC_i32ErrCode = -1;
144             ret = -1;
145         }
146     }
147 
148     return ret;
149 }
150 
151 /**
152   * @brief Execute FMC_ISPCMD_PAGE_ERASE command to erase a flash page. The page size is 4096 bytes.
153   * @param[in]  u32PageAddr Address of the flash page to be erased.
154   *             It must be a 4096 bytes aligned address.
155   * @return ISP page erase success or not.
156   * @retval   0  Success
157   * @retval   -1  Erase failed
158   *
159   * @note     Global error code g_FMC_i32ErrCode
160   *           -1  Erase failed or erase time-out
161   */
FMC_Erase(uint32_t u32PageAddr)162 int32_t FMC_Erase(uint32_t u32PageAddr)
163 {
164     int32_t  ret = 0;
165     int32_t i32TimeOutCnt;
166 
167     g_FMC_i32ErrCode = 0;
168 
169     FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
170     FMC->ISPADDR = u32PageAddr;
171     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
172 
173     i32TimeOutCnt = FMC_TIMEOUT_ERASE;
174     while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
175     {
176         if( i32TimeOutCnt-- <= 0)
177         {
178             g_FMC_i32ErrCode = -1;
179             ret = -1;
180             break;
181         }
182     }
183 
184     if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
185     {
186         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
187         g_FMC_i32ErrCode = -1;
188         ret = -1;
189     }
190 
191     return ret;
192 }
193 
194 
195 /**
196   * @brief Execute FMC_ISPCMD_BANK_ERASE command to erase a flash block.
197   * @param[in]  u32BankAddr Base address of the flash bank to be erased.
198   * @return ISP page erase success or not.
199   * @retval   0  Success
200   * @retval   -1  Erase failed
201   *
202   * @note     Global error code g_FMC_i32ErrCode
203   *           -1  Erase failed or erase time-out
204   */
FMC_Erase_Bank(uint32_t u32BankAddr)205 int32_t FMC_Erase_Bank(uint32_t u32BankAddr)
206 {
207     int32_t  ret = 0;
208     int32_t i32TimeOutCnt;
209 
210     g_FMC_i32ErrCode = 0;
211 
212     FMC->ISPCMD = FMC_ISPCMD_BANK_ERASE;
213     FMC->ISPADDR = u32BankAddr;
214     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
215 
216     i32TimeOutCnt = FMC_TIMEOUT_ERASE;
217     while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
218     {
219         if( i32TimeOutCnt-- <= 0)
220         {
221             g_FMC_i32ErrCode = -1;
222             ret = -1;
223             break;
224         }
225     }
226 
227     if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
228     {
229         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
230         g_FMC_i32ErrCode = -1;
231         ret = -1;
232     }
233     return ret;
234 }
235 
236 /**
237   * @brief  Execute Erase XOM Region
238   *
239   * @param[in]  u32XomNum  The XOMRn(n=0~3)
240   *
241   * @return   XOM erase success or not.
242   * @retval    0  Success
243   * @retval   -1  Erase failed
244   * @retval   -2  Invalid XOM number.
245   *
246   * @details Execute FMC_ISPCMD_PAGE_ERASE command to erase XOM.
247   *
248   * @note     Global error code g_FMC_i32ErrCode
249   *           -1  Program failed or program time-out
250   *           -2  Invalid XOM number.
251   */
FMC_EraseXOM(uint32_t u32XomNum)252 int32_t FMC_EraseXOM(uint32_t u32XomNum)
253 {
254     uint32_t u32Addr;
255     int32_t i32Active, err = 0;
256     int32_t i32TimeOutCnt;
257 
258     g_FMC_i32ErrCode = 0;
259 
260     if(u32XomNum >= 4UL)
261     {
262         err = -2;
263     }
264 
265     if(err == 0)
266     {
267         i32Active = FMC_GetXOMState(u32XomNum);
268 
269         if(i32Active)
270         {
271             switch(u32XomNum)
272             {
273             case 0u:
274                 u32Addr = (FMC->XOMR0STS & 0xFFFFFF00u) >> 8u;
275                 break;
276             case 1u:
277                 u32Addr = (FMC->XOMR1STS & 0xFFFFFF00u) >> 8u;
278                 break;
279             case 2u:
280                 u32Addr = (FMC->XOMR2STS & 0xFFFFFF00u) >> 8u;
281                 break;
282             case 3u:
283                 u32Addr = (FMC->XOMR3STS & 0xFFFFFF00u) >> 8u;
284                 break;
285             default:
286                 /* Should not be here */
287                 err = -2;
288                 goto lexit;
289             }
290             FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
291             FMC->ISPADDR = u32Addr;
292             FMC->ISPDAT = 0x55aa03u;
293             FMC->ISPTRG = 0x1u;
294 #if ISBEN
295             __ISB();
296 #endif
297             i32TimeOutCnt = FMC_TIMEOUT_ERASE;
298             while(FMC->ISPTRG)
299             {
300                 if( i32TimeOutCnt-- <= 0)
301                 {
302                     err = -1;
303                     break;
304                 }
305             }
306 
307             /* Check ISPFF flag to know whether erase OK or fail. */
308             if(FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
309             {
310                 FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
311                 err = -1;
312             }
313         }
314         else
315         {
316             err = -1;
317         }
318     }
319 
320 lexit:
321     g_FMC_i32ErrCode = err;
322     return err;
323 }
324 
325 /**
326   * @brief  Check the XOM is actived or not.
327   *
328   * @param[in] u32XomNum    The xom number(0~3).
329   *
330   * @retval   1   XOM is actived.
331   * @retval   0   XOM is not actived.
332   * @retval   -2  Invalid XOM number.
333   *
334   * @details To get specify XOMRn(n=0~3) active status
335   */
FMC_GetXOMState(uint32_t u32XomNum)336 int32_t FMC_GetXOMState(uint32_t u32XomNum)
337 {
338     uint32_t u32act;
339     int32_t  ret = 0;
340 
341     if(u32XomNum >= 4UL)
342     {
343         ret = -2;
344     }
345 
346     if(ret >= 0)
347     {
348         u32act = (((FMC->XOMSTS) & 0xful) & (1ul << u32XomNum)) >> u32XomNum;
349         ret = (int32_t)u32act;
350     }
351     return ret;
352 }
353 
354 /**
355   * @brief Get the current boot source.
356   * @return The current boot source.
357   * @retval   0  Is boot from APROM.
358   * @retval   1  Is boot from LDROM.
359   * @retval   2  Is boot from Boot Loader.
360   */
FMC_GetBootSource(void)361 int32_t FMC_GetBootSource (void)
362 {
363     if (FMC->ISPCTL & FMC_ISPCTL_BL_Msk)
364     {
365         return 2;
366     }
367     if (FMC->ISPCTL & FMC_ISPCTL_BS_Msk)
368     {
369         return 1;
370     }
371     return 0;
372 }
373 
374 
375 /**
376   * @brief Enable FMC ISP function
377   * @return None
378   */
FMC_Open(void)379 void FMC_Open(void)
380 {
381     FMC->ISPCTL |=  FMC_ISPCTL_ISPEN_Msk;
382 }
383 
384 
385 /**
386   * @brief Execute FMC_ISPCMD_READ command to read a word from flash.
387   * @param[in]  u32Addr Address of the flash location to be read.
388   *             It must be a word aligned address.
389   * @return The word data read from specified flash address.
390   *         Return 0xFFFFFFFF if read failed.
391   * @note   Global error code g_FMC_i32ErrCode
392   *         -1  Read time-out
393   */
FMC_Read(uint32_t u32Addr)394 uint32_t FMC_Read(uint32_t u32Addr)
395 {
396     int32_t i32TimeOutCnt;
397 
398     g_FMC_i32ErrCode = 0;
399     FMC->ISPCMD = FMC_ISPCMD_READ;
400     FMC->ISPADDR = u32Addr;
401     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
402 
403     i32TimeOutCnt = FMC_TIMEOUT_READ;
404     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
405     {
406         if( i32TimeOutCnt-- <= 0)
407         {
408             g_FMC_i32ErrCode = -1;
409             return 0xFFFFFFFF;
410         }
411     }
412 
413     return FMC->ISPDAT;
414 }
415 
416 
417 /**
418   * @brief Execute FMC_ISPCMD_READ_64 command to read a double-word from flash.
419   * @param[in]  u32addr   Address of the flash location to be read.
420   *             It must be a double-word aligned address.
421   * @param[out] u32data0  Place holder of word 0 read from flash address u32addr.
422   * @param[out] u32data1  Place holder of word 0 read from flash address u32addr+4.
423   * @return   0   Success
424   * @return   -1  Failed
425   *
426   * @note     Global error code g_FMC_i32ErrCode
427   *           -1  Read time-out
428   */
FMC_Read_64(uint32_t u32addr,uint32_t * u32data0,uint32_t * u32data1)429 int32_t FMC_Read_64(uint32_t u32addr, uint32_t * u32data0, uint32_t * u32data1)
430 {
431     int32_t  ret = 0;
432     int32_t i32TimeOutCnt;
433 
434     g_FMC_i32ErrCode = 0;
435     FMC->ISPCMD = FMC_ISPCMD_READ_64;
436     FMC->ISPADDR = u32addr;
437     FMC->ISPDAT = 0x0UL;
438     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
439 
440     i32TimeOutCnt = FMC_TIMEOUT_READ;
441     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
442     {
443         if( i32TimeOutCnt-- <= 0)
444         {
445             g_FMC_i32ErrCode = -1;
446             ret = -1;
447             break;
448         }
449     }
450 
451     if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
452     {
453         FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
454         g_FMC_i32ErrCode = -1;
455         ret = -1;
456     }
457     else
458     {
459         *u32data0 = FMC->MPDAT0;
460         *u32data1 = FMC->MPDAT1;
461     }
462     return ret;
463 }
464 
465 
466 /**
467   * @brief    Get the base address of Data Flash if enabled.
468   * @retval   The base address of Data Flash
469   */
FMC_ReadDataFlashBaseAddr(void)470 uint32_t FMC_ReadDataFlashBaseAddr(void)
471 {
472     return FMC->DFBA;
473 }
474 
475 /**
476   * @brief      Set boot source from LDROM or APROM after next software reset
477   * @param[in]  i32BootSrc
478   *                1: Boot from LDROM
479   *                0: Boot from APROM
480   * @return    None
481   * @details   This function is used to switch APROM boot or LDROM boot. User need to call
482   *            FMC_SetBootSource to select boot source first, then use CPU reset or
483   *            System Reset Request to reset system.
484   */
FMC_SetBootSource(int32_t i32BootSrc)485 void FMC_SetBootSource(int32_t i32BootSrc)
486 {
487     if(i32BootSrc)
488     {
489         FMC->ISPCTL |= FMC_ISPCTL_BS_Msk; /* Boot from LDROM */
490     }
491     else
492     {
493         FMC->ISPCTL &= ~FMC_ISPCTL_BS_Msk;/* Boot from APROM */
494     }
495 }
496 
497 /**
498   * @brief Execute ISP FMC_ISPCMD_PROGRAM to program a word to flash.
499   * @param[in]  u32Addr Address of the flash location to be programmed.
500   *             It must be a word aligned address.
501   * @param[in]  u32Data The word data to be programmed.
502   * @return   0   Success
503   * @return   -1  Failed
504   *
505   * @note     Global error code g_FMC_i32ErrCode
506   *           -1  Program failed or time-out
507   */
FMC_Write(uint32_t u32Addr,uint32_t u32Data)508 int32_t FMC_Write(uint32_t u32Addr, uint32_t u32Data)
509 {
510     int32_t i32TimeOutCnt;
511 
512     g_FMC_i32ErrCode = 0;
513     FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
514     FMC->ISPADDR = u32Addr;
515     FMC->ISPDAT = u32Data;
516     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
517 
518     i32TimeOutCnt = FMC_TIMEOUT_WRITE;
519     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
520     {
521         if( i32TimeOutCnt-- <= 0)
522         {
523             g_FMC_i32ErrCode = -1;
524             return -1;
525         }
526     }
527 
528     return 0;
529 }
530 
531 /**
532   * @brief Execute ISP FMC_ISPCMD_PROGRAM_64 to program a double-word to flash.
533   * @param[in]  u32addr Address of the flash location to be programmed.
534   *             It must be a double-word aligned address.
535   * @param[in]  u32data0   The word data to be programmed to flash address u32addr.
536   * @param[in]  u32data1   The word data to be programmed to flash address u32addr+4.
537   * @return   0   Success
538   * @return   -1  Failed
539   *
540   * @note     Global error code g_FMC_i32ErrCode
541   *           -1  Program failed or time-out
542   */
FMC_Write8Bytes(uint32_t u32addr,uint32_t u32data0,uint32_t u32data1)543 int32_t FMC_Write8Bytes(uint32_t u32addr, uint32_t u32data0, uint32_t u32data1)
544 {
545     int32_t  ret = 0;
546     int32_t i32TimeOutCnt;
547 
548     g_FMC_i32ErrCode = 0;
549     FMC->ISPCMD  = FMC_ISPCMD_PROGRAM_64;
550     FMC->ISPADDR = u32addr;
551     FMC->MPDAT0  = u32data0;
552     FMC->MPDAT1  = u32data1;
553     FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
554 
555     i32TimeOutCnt = FMC_TIMEOUT_WRITE;
556     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
557     {
558         if( i32TimeOutCnt-- <= 0)
559         {
560             g_FMC_i32ErrCode = -1;
561             ret = -1;
562         }
563     }
564 
565     if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
566     {
567         FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
568         g_FMC_i32ErrCode = -1;
569         ret = -1;
570     }
571     return ret;
572 }
573 
574 
575 /**
576   * @brief   Program Multi-Word data into specified address of flash.
577   * @param[in]  u32Addr    Start flash address in APROM where the data chunk to be programmed into.
578   *                        This address must be 8-bytes aligned to flash address.
579   * @param[in]  pu32Buf    Buffer that carry the data chunk.
580   * @param[in]  u32Len     Length of the data chunk in bytes.
581   * @retval   >=0  Number of data bytes were programmed.
582   * @retval   -1   Program failed.
583   * @retval   -2   Invalid address.
584   *
585   * @note     Global error code g_FMC_i32ErrCode
586   *           -1  Program failed or time-out
587   *           -2  Invalid address
588   */
FMC_WriteMultiple(uint32_t u32Addr,uint32_t pu32Buf[],uint32_t u32Len)589 int32_t FMC_WriteMultiple(uint32_t u32Addr, uint32_t pu32Buf[], uint32_t u32Len)
590 {
591     int   i, idx, retval = 0;
592     int32_t i32TimeOutCnt;
593 
594     g_FMC_i32ErrCode = 0;
595 
596     if ((u32Addr >= FMC_APROM_END) || ((u32Addr % 8) != 0))
597     {
598         g_FMC_i32ErrCode = -2;
599         return -2;
600     }
601 
602     u32Len = u32Len - (u32Len % 8);         /* u32Len must be multiple of 8. */
603 
604     idx = 0;
605 
606     while (u32Len >= 8)
607     {
608         FMC->ISPADDR = u32Addr;
609         FMC->MPDAT0  = pu32Buf[idx++];
610         FMC->MPDAT1  = pu32Buf[idx++];
611         FMC->MPDAT2  = pu32Buf[idx++];
612         FMC->MPDAT3  = pu32Buf[idx++];
613         FMC->ISPCMD  = FMC_ISPCMD_PROGRAM_MUL;
614         FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
615 
616         for (i = 16; i < FMC_MULTI_WORD_PROG_LEN; )
617         {
618             i32TimeOutCnt = FMC_TIMEOUT_WRITE;
619             while (FMC->MPSTS & (FMC_MPSTS_D0_Msk | FMC_MPSTS_D1_Msk))
620             {
621                 if( i32TimeOutCnt-- <= 0)
622                 {
623                     g_FMC_i32ErrCode = -1;
624                     return -1;
625                 }
626             }
627 
628             retval += 8;
629             u32Len -= 8;
630             if (u32Len < 8)
631             {
632                 return retval;
633             }
634 
635             if (!(FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk))
636             {
637                 /* printf("    [WARNING] busy cleared after D0D1 cleared!\n"); */
638                 i += 8;
639                 break;
640             }
641 
642             FMC->MPDAT0 = pu32Buf[idx++];
643             FMC->MPDAT1 = pu32Buf[idx++];
644 
645             if (i == FMC_MULTI_WORD_PROG_LEN/4)
646                 break;           // done
647 
648             i32TimeOutCnt = FMC_TIMEOUT_WRITE;
649             while (FMC->MPSTS & (FMC_MPSTS_D2_Msk | FMC_MPSTS_D3_Msk))
650             {
651                 if( i32TimeOutCnt-- <= 0)
652                 {
653                     g_FMC_i32ErrCode = -1;
654                     return -1;
655                 }
656             }
657 
658             retval += 8;
659             u32Len -= 8;
660             if (u32Len < 8)
661             {
662                 return retval;
663             }
664 
665             if (!(FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk))
666             {
667                 /* printf("    [WARNING] busy cleared after D2D3 cleared!\n"); */
668                 i += 8;
669                 break;
670             }
671 
672             FMC->MPDAT2 = pu32Buf[idx++];
673             FMC->MPDAT3 = pu32Buf[idx++];
674         }
675 
676         if (i != FMC_MULTI_WORD_PROG_LEN)
677         {
678             /* printf("    [WARNING] Multi-word program interrupted at 0x%x !!\n", i); */
679             return retval;
680         }
681 
682         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
683         while (FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk)
684         {
685             if( i32TimeOutCnt-- <= 0)
686             {
687                 g_FMC_i32ErrCode = -1;
688                 return -1;
689             }
690         }
691 
692         u32Addr += FMC_MULTI_WORD_PROG_LEN;
693     }
694     return retval;
695 }
696 
697 
698 /**
699   * @brief Program a 64-bits data to the specified OTP.
700   * @param[in] otp_num    The OTP number.
701   * @param[in] low_word   Low word of the 64-bits data.
702   * @param[in] high_word  High word of the 64-bits data.
703   * @retval   0   Success
704   * @retval   -1  Program failed.
705   * @retval   -2  Invalid OTP number.
706   *
707   * @note     Global error code g_FMC_i32ErrCode
708   *           -1  Program failed or time-out
709   *           -2  Invalid OTP number
710   */
FMC_WriteOTP(uint32_t otp_num,uint32_t low_word,uint32_t high_word)711 int32_t FMC_WriteOTP(uint32_t otp_num, uint32_t low_word, uint32_t high_word)
712 {
713     int32_t  ret = 0;
714     int32_t i32TimeOutCnt;
715 
716     g_FMC_i32ErrCode = 0;
717 
718     if (otp_num > 255UL)
719     {
720         g_FMC_i32ErrCode = -2;
721         ret = -2;
722     }
723 
724     if (ret == 0)
725     {
726         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
727         FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL;
728         FMC->ISPDAT = low_word;
729         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
730 
731         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
732         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
733         {
734             if( i32TimeOutCnt-- <= 0)
735             {
736                 g_FMC_i32ErrCode = -1;
737                 ret = -1;
738                 break;
739             }
740         }
741 
742         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
743         {
744             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
745             g_FMC_i32ErrCode = -1;
746             ret = -1;
747         }
748     }
749 
750     if (ret == 0)
751     {
752         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
753         FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL + 4UL;
754         FMC->ISPDAT = high_word;
755         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
756 
757         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
758         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
759         {
760             if( i32TimeOutCnt-- <= 0)
761             {
762                 g_FMC_i32ErrCode = -1;
763                 ret = -1;
764                 break;
765             }
766         }
767 
768         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
769         {
770             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
771             g_FMC_i32ErrCode = -1;
772             ret = -1;
773         }
774     }
775 
776     return ret;
777 }
778 
779 /**
780   * @brief  Read the 64-bits data from the specified OTP.
781   * @param[in] otp_num    The OTP number.
782   * @param[in] low_word   Low word of the 64-bits data.
783   * @param[in] high_word  High word of the 64-bits data.
784   * @retval   0   Success
785   * @retval   -1  Read failed.
786   * @retval   -2  Invalid OTP number.
787   *
788   * @note     Global error code g_FMC_i32ErrCode
789   *           -1  Read failed or time-out
790   *           -2  Invalid OTP number
791   */
FMC_ReadOTP(uint32_t otp_num,uint32_t * low_word,uint32_t * high_word)792 int32_t FMC_ReadOTP(uint32_t otp_num, uint32_t *low_word, uint32_t *high_word)
793 {
794     int32_t  ret = 0;
795     int32_t i32TimeOutCnt;
796 
797     g_FMC_i32ErrCode = 0;
798 
799     if (otp_num > 255UL)
800     {
801         g_FMC_i32ErrCode = -2;
802         ret = -2;
803     }
804 
805     if (ret == 0)
806     {
807         FMC->ISPCMD = FMC_ISPCMD_READ_64;
808         FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL ;
809         FMC->ISPDAT = 0x0UL;
810         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
811 
812         i32TimeOutCnt = FMC_TIMEOUT_READ;
813         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
814         {
815             if( i32TimeOutCnt-- <= 0)
816             {
817                 g_FMC_i32ErrCode = -1;
818                 ret = -1;
819                 break;
820             }
821         }
822 
823         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
824         {
825             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
826             g_FMC_i32ErrCode = -1;
827             ret = -1;
828         }
829         else
830         {
831             *low_word = FMC->MPDAT0;
832             *high_word = FMC->MPDAT1;
833         }
834     }
835     return ret;
836 }
837 
838 /**
839   * @brief  Lock the specified OTP.
840   * @param[in] otp_num    The OTP number.
841   * @retval    0  Success
842   * @retval   -1  Failed to write OTP lock bits.
843   * @retval   -2  Invalid OTP number.
844   *
845   * @note     Global error code g_FMC_i32ErrCode
846   *           -1  Failed to write OTP lock bits or write time-out
847   *           -2  Invalid OTP number
848   */
FMC_LockOTP(uint32_t otp_num)849 int32_t FMC_LockOTP(uint32_t otp_num)
850 {
851     int32_t  ret = 0;
852     int32_t i32TimeOutCnt;
853 
854     g_FMC_i32ErrCode = 0;
855 
856     if (otp_num > 255UL)
857     {
858         g_FMC_i32ErrCode = -2;
859         ret = -2;
860     }
861 
862     if (ret == 0)
863     {
864         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
865         FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
866         FMC->ISPDAT = 0UL;
867         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
868 
869         i32TimeOutCnt = FMC_TIMEOUT_WRITE;
870         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
871         {
872             if( i32TimeOutCnt-- <= 0)
873             {
874                 g_FMC_i32ErrCode = -1;
875                 ret = -1;
876                 break;
877             }
878         }
879 
880         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
881         {
882             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
883             g_FMC_i32ErrCode = -1;
884             ret = -1;
885         }
886     }
887     return ret;
888 }
889 
890 /**
891   * @brief  Check the OTP is locked or not.
892   * @param[in] otp_num    The OTP number.
893   * @retval   1   OTP is locked.
894   * @retval   0   OTP is not locked.
895   * @retval   -1  Failed to read OTP lock bits.
896   * @retval   -2  Invalid OTP number.
897   *
898   * @note     Global error code g_FMC_i32ErrCode
899   *           -1  Failed to read OTP lock bits or read time-out
900   *           -2  Invalid OTP number
901   */
FMC_IsOTPLocked(uint32_t otp_num)902 int32_t FMC_IsOTPLocked(uint32_t otp_num)
903 {
904     int32_t  ret = 0;
905     int32_t i32TimeOutCnt;
906 
907     g_FMC_i32ErrCode = 0;
908 
909     if (otp_num > 255UL)
910     {
911         g_FMC_i32ErrCode = -2;
912         ret = -2;
913     }
914 
915     if (ret == 0)
916     {
917         FMC->ISPCMD = FMC_ISPCMD_READ;
918         FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
919         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
920 
921         i32TimeOutCnt = FMC_TIMEOUT_READ;
922         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
923         {
924             if( i32TimeOutCnt-- <= 0)
925             {
926                 g_FMC_i32ErrCode = -1;
927                 ret = -1;
928                 break;
929             }
930         }
931 
932         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
933         {
934             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
935             g_FMC_i32ErrCode = -1;
936             ret = -1;
937         }
938         else
939         {
940             if (FMC->ISPDAT != 0xFFFFFFFFUL)
941             {
942                 g_FMC_i32ErrCode = -1;
943                 ret = 1;   /* Lock work was progrmmed. OTP was locked. */
944             }
945         }
946     }
947     return ret;
948 }
949 
950 /**
951   * @brief Execute FMC_ISPCMD_READ command to read User Configuration.
952   * @param[out]  u32Config A two-word array.
953   *              u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
954   * @param[in] u32Count Available word count in u32Config.
955   * @return Success or not.
956   * @retval   0  Success.
957   * @retval   -1  Read failed
958   * @retval   -2  Invalid parameter.
959   *
960   * @note     Global error code g_FMC_i32ErrCode
961   *           -1  Read failed
962   *           -2  Invalid parameter
963   */
FMC_ReadConfig(uint32_t u32Config[],uint32_t u32Count)964 int32_t FMC_ReadConfig(uint32_t u32Config[], uint32_t u32Count)
965 {
966     int32_t   ret = 0;
967 
968     u32Config[0] = FMC_Read(FMC_CONFIG_BASE);
969 
970     if (g_FMC_i32ErrCode != 0)
971         return g_FMC_i32ErrCode;
972 
973     if (u32Count < 2UL)
974     {
975         ret = -2;
976     }
977     else
978     {
979         u32Config[1] = FMC_Read(FMC_CONFIG_BASE+4UL);
980     }
981     return ret;
982 }
983 
984 
985 /**
986   * @brief Execute ISP commands to erase then write User Configuration.
987   * @param[in] u32Config   A two-word array.
988   *            u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
989   * @param[in] u32Count    The number of User Configuration words to be written.
990   * @return Success or not.
991   * @retval   0   Success
992   * @retval   -1  Erase/program/read/verify failed
993   *
994   * @note     Global error code g_FMC_i32ErrCode
995   *           < 0  Errors caused by erase/program/read failed or time-out
996   */
FMC_WriteConfig(uint32_t u32Config[],uint32_t u32Count)997 int32_t FMC_WriteConfig(uint32_t u32Config[], uint32_t u32Count)
998 {
999     int   i;
1000 
1001     FMC_ENABLE_CFG_UPDATE();
1002 
1003     if (FMC_Erase(FMC_CONFIG_BASE) != 0)
1004         return -1;
1005 
1006     if ((FMC_Read(FMC_CONFIG_BASE) != 0xFFFFFFFF) || (FMC_Read(FMC_CONFIG_BASE+4) != 0xFFFFFFFF) ||
1007             (FMC_Read(FMC_CONFIG_BASE+8) != 0xFFFF5A5A))
1008     {
1009         FMC_DISABLE_CFG_UPDATE();
1010         return -1;
1011     }
1012 
1013     if (g_FMC_i32ErrCode != 0)
1014     {
1015         FMC_DISABLE_CFG_UPDATE();
1016         return -1;
1017     }
1018 
1019     for (i = 0; i < u32Count; i++)
1020     {
1021         if (FMC_Write(FMC_CONFIG_BASE+i*4UL, u32Config[i]) != 0)
1022         {
1023             FMC_DISABLE_CFG_UPDATE();
1024             return -1;
1025         }
1026 
1027         if (FMC_Read(FMC_CONFIG_BASE+i*4UL) != u32Config[i])
1028         {
1029             FMC_DISABLE_CFG_UPDATE();
1030             return -1;
1031         }
1032 
1033         if (g_FMC_i32ErrCode != 0)
1034         {
1035             FMC_DISABLE_CFG_UPDATE();
1036             return -1;
1037         }
1038     }
1039 
1040     FMC_DISABLE_CFG_UPDATE();
1041     return 0;
1042 }
1043 
1044 
1045 /**
1046   * @brief Run CRC32 checksum calculation and get result.
1047   * @param[in] u32addr   Starting flash address. It must be a page aligned address.
1048   * @param[in] u32count  Byte count of flash to be calculated. It must be multiple of 512 bytes.
1049   * @return Success or not.
1050   * @retval   0           Success.
1051   * @retval   0xFFFFFFFF  Invalid parameter or command failed.
1052   *
1053   * @note     Global error code g_FMC_i32ErrCode
1054   *           -1  Run/Read check sum time-out failed
1055   *           -2  u32addr or u32count must be aligned with 512
1056   */
FMC_GetChkSum(uint32_t u32addr,uint32_t u32count)1057 uint32_t  FMC_GetChkSum(uint32_t u32addr, uint32_t u32count)
1058 {
1059     uint32_t   ret;
1060     int32_t i32TimeOutCnt;
1061 
1062     g_FMC_i32ErrCode = 0;
1063 
1064     if ((u32addr % 512UL) || (u32count % 512UL))
1065     {
1066         g_FMC_i32ErrCode = -2;
1067         ret = 0xFFFFFFFF;
1068     }
1069     else
1070     {
1071         FMC->ISPCMD  = FMC_ISPCMD_RUN_CKS;
1072         FMC->ISPADDR = u32addr;
1073         FMC->ISPDAT  = u32count;
1074         FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
1075 
1076         i32TimeOutCnt = FMC_TIMEOUT_CHKSUM;
1077         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
1078         {
1079             if( i32TimeOutCnt-- <= 0)
1080             {
1081                 g_FMC_i32ErrCode = -1;
1082                 return 0xFFFFFFFF;
1083             }
1084         }
1085 
1086         FMC->ISPCMD = FMC_ISPCMD_READ_CKS;
1087         FMC->ISPADDR    = u32addr;
1088         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
1089 
1090         i32TimeOutCnt = FMC_TIMEOUT_CHKSUM;
1091         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
1092         {
1093             if( i32TimeOutCnt-- <= 0)
1094             {
1095                 g_FMC_i32ErrCode = -1;
1096                 return 0xFFFFFFFF;
1097             }
1098         }
1099 
1100         ret = FMC->ISPDAT;
1101     }
1102 
1103     return ret;
1104 }
1105 
1106 
1107 /**
1108   * @brief Run flash all one verification and get result.
1109   * @param[in] u32addr   Starting flash address. It must be a page aligned address.
1110   * @param[in] u32count  Byte count of flash to be calculated. It must be multiple of 512 bytes.
1111   * @retval   READ_ALLONE_YES      The contents of verified flash area are 0xFFFFFFFF.
1112   * @retval   READ_ALLONE_NOT  Some contents of verified flash area are not 0xFFFFFFFF.
1113   * @retval   READ_ALLONE_CMD_FAIL  Unexpected error occurred.
1114   *
1115   * @note     Global error code g_FMC_i32ErrCode
1116   *           -1  RUN_ALL_ONE or CHECK_ALL_ONE commands time-out
1117   */
FMC_CheckAllOne(uint32_t u32addr,uint32_t u32count)1118 uint32_t  FMC_CheckAllOne(uint32_t u32addr, uint32_t u32count)
1119 {
1120     uint32_t  ret = READ_ALLONE_CMD_FAIL;
1121     int32_t i32TimeOutCnt0, i32TimeOutCnt1;
1122 
1123     g_FMC_i32ErrCode = 0;
1124 
1125     FMC->ISPSTS = 0x80UL;   /* clear check all one bit */
1126 
1127     FMC->ISPCMD   = FMC_ISPCMD_RUN_ALL1;
1128     FMC->ISPADDR  = u32addr;
1129     FMC->ISPDAT   = u32count;
1130     FMC->ISPTRG   = FMC_ISPTRG_ISPGO_Msk;
1131 
1132     i32TimeOutCnt0 = FMC_TIMEOUT_CHKALLONE;
1133     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
1134     {
1135         if( i32TimeOutCnt0-- <= 0)
1136         {
1137             g_FMC_i32ErrCode = -1;
1138             break;
1139         }
1140     }
1141 
1142     if(g_FMC_i32ErrCode == 0)
1143     {
1144         i32TimeOutCnt1 = FMC_TIMEOUT_CHKALLONE;
1145         do
1146         {
1147             FMC->ISPCMD = FMC_ISPCMD_READ_ALL1;
1148             FMC->ISPADDR    = u32addr;
1149             FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
1150 
1151             i32TimeOutCnt0 = FMC_TIMEOUT_CHKALLONE;
1152             while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk)
1153             {
1154                 if( i32TimeOutCnt0-- <= 0)
1155                 {
1156                     g_FMC_i32ErrCode = -1;
1157                     break;
1158                 }
1159             }
1160 
1161             if( i32TimeOutCnt1-- <= 0)
1162             {
1163                 g_FMC_i32ErrCode = -1;
1164             }
1165         }
1166         while ( (FMC->ISPDAT == 0UL) && (g_FMC_i32ErrCode == 0));
1167     }
1168 
1169     if(g_FMC_i32ErrCode == 0)
1170     {
1171         if(FMC->ISPDAT == READ_ALLONE_YES)
1172             ret = READ_ALLONE_YES;
1173         else if(FMC->ISPDAT == READ_ALLONE_NOT)
1174             ret = READ_ALLONE_NOT;
1175         else
1176             g_FMC_i32ErrCode = -1;
1177     }
1178 
1179     return ret;
1180 }
1181 
1182 /**
1183   * @brief     Remap Bank0/Bank1
1184   *
1185   * @param[in] u32Bank    Bank Num which will remap to.
1186   *
1187   * @retval    0   Success
1188   * @retval    -1  Program failed.
1189   *
1190   * @details  Remap Bank0/Bank1
1191   *
1192   * @note     Global error code g_FMC_i32ErrCode
1193   *           -1  Program failed or time-out
1194   */
FMC_RemapBank(uint32_t u32Bank)1195 int32_t FMC_RemapBank(uint32_t u32Bank)
1196 {
1197     int32_t  ret = 0;
1198     int32_t i32TimeOutCnt;
1199 
1200     g_FMC_i32ErrCode = 0;
1201 
1202     FMC->ISPCMD = FMC_ISPCMD_BANK_REMAP;
1203     FMC->ISPADDR = u32Bank;
1204     FMC->ISPDAT = 0x5AA55AA5UL;
1205     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
1206 
1207     i32TimeOutCnt = FMC_TIMEOUT_WRITE;
1208     while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk)
1209     {
1210         if( i32TimeOutCnt-- <= 0)
1211         {
1212             g_FMC_i32ErrCode = -1;
1213             ret = -1;
1214             break;
1215         }
1216     }
1217 
1218     if(FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
1219     {
1220         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
1221         g_FMC_i32ErrCode = -1;
1222         ret = -1;
1223     }
1224     return ret;
1225 }
1226 
1227 
1228 /*@}*/ /* end of group FMC_EXPORTED_FUNCTIONS */
1229 
1230 /*@}*/ /* end of group FMC_Driver */
1231 
1232 /*@}*/ /* end of group Standard_Driver */
1233