1 /**************************************************************************//**
2  * @file     fmc.c
3  * @version  V1.00
4  * @brief    M480 series FMC driver source file
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  * @copyright (C) 2016-2020 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 
29 /**
30   * @brief Disable FMC ISP function.
31   * @return None
32   */
FMC_Close(void)33 void FMC_Close(void)
34 {
35     FMC->ISPCTL &= ~FMC_ISPCTL_ISPEN_Msk;
36 }
37 
38 /**
39   * @brief     Config XOM Region
40   * @param[in] u32XomNum    The XOM number(0~3)
41   * @param[in] u32XomBase   The XOM region base address.
42   * @param[in] u8XomPage   The XOM page number of region size.
43   *
44   * @retval   0   Success
45   * @retval   1   XOM is has already actived.
46   * @retval   -1  Program failed.
47   * @retval   -2  Invalid XOM number.
48   *
49   * @details  Program XOM base address and XOM size(page)
50   */
FMC_ConfigXOM(uint32_t u32XomNum,uint32_t u32XomBase,uint8_t u8XomPage)51 int32_t FMC_ConfigXOM(uint32_t u32XomNum, uint32_t u32XomBase, uint8_t u8XomPage)
52 {
53     int32_t  ret = 0;
54 
55     if(u32XomNum >= 4UL)
56     {
57         ret = -2;
58     }
59 
60     if(ret == 0)
61     {
62         ret = FMC_GetXOMState(u32XomNum);
63     }
64 
65     if(ret == 0)
66     {
67         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
68         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u);
69         FMC->ISPDAT = u32XomBase;
70         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
71         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) {}
72 
73         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
74         {
75             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
76             ret = -1;
77         }
78     }
79 
80     if(ret == 0)
81     {
82         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
83         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u + 0x04u);
84         FMC->ISPDAT = u8XomPage;
85         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
86         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) {}
87 
88         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
89         {
90             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
91             ret = -1;
92         }
93     }
94 
95     if(ret == 0)
96     {
97         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
98         FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u + 0x08u);
99         FMC->ISPDAT = 0u;
100         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
101         while(FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) {}
102 
103         if(FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
104         {
105             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
106             ret = -1;
107         }
108     }
109 
110     return ret;
111 }
112 
113 /**
114   * @brief Execute FMC_ISPCMD_PAGE_ERASE command to erase a flash page. The page size is 4096 bytes.
115   * @param[in]  u32PageAddr Address of the flash page to be erased.
116   *             It must be a 4096 bytes aligned address.
117   * @return ISP page erase success or not.
118   * @retval   0  Success
119   * @retval   -1  Erase failed
120   */
FMC_Erase(uint32_t u32PageAddr)121 int32_t FMC_Erase(uint32_t u32PageAddr)
122 {
123     int32_t  ret = 0;
124 
125     if (u32PageAddr == FMC_SPROM_BASE)
126     {
127         ret = FMC_Erase_SPROM();
128     }
129 
130     if (ret == 0)
131     {
132         FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
133         FMC->ISPADDR = u32PageAddr;
134         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
135 
136         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
137 
138         if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
139         {
140             FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
141             ret = -1;
142         }
143     }
144     return ret;
145 }
146 
147 
148 /**
149   * @brief Execute FMC_ISPCMD_PAGE_ERASE command to erase SPROM. The page size is 4096 bytes.
150   * @return   SPROM page erase success or not.
151   * @retval   0  Success
152   * @retval   -1  Erase failed
153   */
FMC_Erase_SPROM(void)154 int32_t FMC_Erase_SPROM(void)
155 {
156     int32_t  ret = 0;
157 
158     FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
159     FMC->ISPADDR = FMC_SPROM_BASE;
160     FMC->ISPDAT = 0x0055AA03UL;
161     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
162 
163     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
164 
165     if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
166     {
167         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
168         ret = -1;
169     }
170     return ret;
171 }
172 
173 /**
174   * @brief Execute FMC_ISPCMD_BLOCK_ERASE command to erase a flash block. The block size is 4 pages.
175   * @param[in]  u32BlockAddr  Address of the flash block to be erased.
176   *             It must be a 4 pages aligned address.
177   * @return ISP page erase success or not.
178   * @retval   0  Success
179   * @retval   -1  Erase failed
180   */
FMC_Erase_Block(uint32_t u32BlockAddr)181 int32_t FMC_Erase_Block(uint32_t u32BlockAddr)
182 {
183     int32_t  ret = 0;
184 
185     FMC->ISPCMD = FMC_ISPCMD_BLOCK_ERASE;
186     FMC->ISPADDR = u32BlockAddr;
187     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
188 
189     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
190 
191     if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
192     {
193         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
194         ret = -1;
195     }
196     return ret;
197 }
198 
199 /**
200   * @brief Execute FMC_ISPCMD_BANK_ERASE command to erase a flash block.
201   * @param[in]  u32BankAddr Base address of the flash bank to be erased.
202   * @return ISP page erase success or not.
203   * @retval   0  Success
204   * @retval   -1  Erase failed
205   */
FMC_Erase_Bank(uint32_t u32BankAddr)206 int32_t FMC_Erase_Bank(uint32_t u32BankAddr)
207 {
208     int32_t  ret = 0;
209 
210     FMC->ISPCMD = FMC_ISPCMD_BANK_ERASE;
211     FMC->ISPADDR = u32BankAddr;
212     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
213 
214     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
215 
216     if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
217     {
218         FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
219         ret = -1;
220     }
221     return ret;
222 }
223 
224 /**
225   * @brief  Execute Erase XOM Region
226   *
227   * @param[in]  u32XomNum  The XOMRn(n=0~3)
228   *
229   * @return   XOM erase success or not.
230   * @retval    0  Success
231   * @retval   -1  Erase failed
232   * @retval   -2  Invalid XOM number.
233   *
234   * @details Execute FMC_ISPCMD_PAGE_ERASE command to erase XOM.
235   */
FMC_EraseXOM(uint32_t u32XomNum)236 int32_t FMC_EraseXOM(uint32_t u32XomNum)
237 {
238     uint32_t u32Addr;
239     int32_t i32Active, err = 0;
240 
241     if(u32XomNum >= 4UL)
242     {
243         err = -2;
244     }
245 
246     if(err == 0)
247     {
248         i32Active = FMC_GetXOMState(u32XomNum);
249 
250         if(i32Active)
251         {
252             switch(u32XomNum)
253             {
254             case 0u:
255                 u32Addr = (FMC->XOMR0STS & 0xFFFFFF00u) >> 8u;
256                 break;
257             case 1u:
258                 u32Addr = (FMC->XOMR1STS & 0xFFFFFF00u) >> 8u;
259                 break;
260             case 2u:
261                 u32Addr = (FMC->XOMR2STS & 0xFFFFFF00u) >> 8u;
262                 break;
263             case 3u:
264                 u32Addr = (FMC->XOMR3STS & 0xFFFFFF00u) >> 8u;
265                 break;
266             default:
267                 break;
268             }
269             FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
270             FMC->ISPADDR = u32Addr;
271             FMC->ISPDAT = 0x55aa03u;
272             FMC->ISPTRG = 0x1u;
273 #if ISBEN
274             __ISB();
275 #endif
276             while(FMC->ISPTRG) {}
277 
278             /* Check ISPFF flag to know whether erase OK or fail. */
279             if(FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk)
280             {
281                 FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
282                 err = -1;
283             }
284         }
285         else
286         {
287             err = -1;
288         }
289     }
290     return err;
291 }
292 
293 /**
294   * @brief  Check the XOM is actived or not.
295   *
296   * @param[in] u32XomNum    The xom number(0~3).
297   *
298   * @retval   1   XOM is actived.
299   * @retval   0   XOM is not actived.
300   * @retval   -2  Invalid XOM number.
301   *
302   * @details To get specify XOMRn(n=0~3) active status
303   */
FMC_GetXOMState(uint32_t u32XomNum)304 int32_t FMC_GetXOMState(uint32_t u32XomNum)
305 {
306     uint32_t u32act;
307     int32_t  ret = 0;
308 
309     if(u32XomNum >= 4UL)
310     {
311         ret = -2;
312     }
313 
314     if(ret >= 0)
315     {
316         u32act = (((FMC->XOMSTS) & 0xful) & (1ul << u32XomNum)) >> u32XomNum;
317         ret = (int32_t)u32act;
318     }
319     return ret;
320 }
321 
322 /**
323   * @brief Get the current boot source.
324   * @return The current boot source.
325   * @retval   0  Is boot from APROM.
326   * @retval   1  Is boot from LDROM.
327   * @retval   2  Is boot from Boot Loader.
328   */
FMC_GetBootSource(void)329 int32_t FMC_GetBootSource (void)
330 {
331     if (FMC->ISPCTL & FMC_ISPCTL_BL_Msk)
332     {
333         return 2;
334     }
335     if (FMC->ISPCTL & FMC_ISPCTL_BS_Msk)
336     {
337         return 1;
338     }
339     return 0;
340 }
341 
342 
343 /**
344   * @brief Enable FMC ISP function
345   * @return None
346   */
FMC_Open(void)347 void FMC_Open(void)
348 {
349     FMC->ISPCTL |=  FMC_ISPCTL_ISPEN_Msk;
350 }
351 
352 
353 /**
354   * @brief Execute FMC_ISPCMD_READ command to read a word from flash.
355   * @param[in]  u32Addr Address of the flash location to be read.
356   *             It must be a word aligned address.
357   * @return The word data read from specified flash address.
358   */
FMC_Read(uint32_t u32Addr)359 uint32_t FMC_Read(uint32_t u32Addr)
360 {
361     FMC->ISPCMD = FMC_ISPCMD_READ;
362     FMC->ISPADDR = u32Addr;
363     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
364     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
365 
366     return FMC->ISPDAT;
367 }
368 
369 
370 /**
371   * @brief Execute FMC_ISPCMD_READ_64 command to read a double-word from flash.
372   * @param[in]  u32addr   Address of the flash location to be read.
373   *             It must be a double-word aligned address.
374   * @param[out] u32data0  Place holder of word 0 read from flash address u32addr.
375   * @param[out] u32data1  Place holder of word 0 read from flash address u32addr+4.
376   * @return   0   Success
377   * @return   -1  Failed
378   */
FMC_Read_64(uint32_t u32addr,uint32_t * u32data0,uint32_t * u32data1)379 int32_t FMC_Read_64(uint32_t u32addr, uint32_t * u32data0, uint32_t * u32data1)
380 {
381     int32_t  ret = 0;
382 
383     FMC->ISPCMD = FMC_ISPCMD_READ_64;
384     FMC->ISPADDR    = u32addr;
385     FMC->ISPDAT = 0x0UL;
386     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
387 
388     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
389 
390     if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
391     {
392         FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
393         ret = -1;
394     }
395     else
396     {
397         *u32data0 = FMC->MPDAT0;
398         *u32data1 = FMC->MPDAT1;
399     }
400     return ret;
401 }
402 
403 
404 /**
405   * @brief    Get the base address of Data Flash if enabled.
406   * @retval   The base address of Data Flash
407   */
FMC_ReadDataFlashBaseAddr(void)408 uint32_t FMC_ReadDataFlashBaseAddr(void)
409 {
410     return FMC->DFBA;
411 }
412 
413 /**
414   * @brief      Set boot source from LDROM or APROM after next software reset
415   * @param[in]  i32BootSrc
416   *                1: Boot from LDROM
417   *                0: Boot from APROM
418   * @return    None
419   * @details   This function is used to switch APROM boot or LDROM boot. User need to call
420   *            FMC_SetBootSource to select boot source first, then use CPU reset or
421   *            System Reset Request to reset system.
422   */
FMC_SetBootSource(int32_t i32BootSrc)423 void FMC_SetBootSource(int32_t i32BootSrc)
424 {
425     if(i32BootSrc)
426     {
427         FMC->ISPCTL |= FMC_ISPCTL_BS_Msk; /* Boot from LDROM */
428     }
429     else
430     {
431         FMC->ISPCTL &= ~FMC_ISPCTL_BS_Msk;/* Boot from APROM */
432     }
433 }
434 
435 /**
436   * @brief Execute ISP FMC_ISPCMD_PROGRAM to program a word to flash.
437   * @param[in]  u32Addr Address of the flash location to be programmed.
438   *             It must be a word aligned address.
439   * @param[in]  u32Data The word data to be programmed.
440   * @return None
441   */
FMC_Write(uint32_t u32Addr,uint32_t u32Data)442 void FMC_Write(uint32_t u32Addr, uint32_t u32Data)
443 {
444     FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
445     FMC->ISPADDR = u32Addr;
446     FMC->ISPDAT = u32Data;
447     FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
448     while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
449 }
450 
451 /**
452   * @brief Execute ISP FMC_ISPCMD_PROGRAM_64 to program a double-word to flash.
453   * @param[in]  u32addr Address of the flash location to be programmed.
454   *             It must be a double-word aligned address.
455   * @param[in]  u32data0   The word data to be programmed to flash address u32addr.
456   * @param[in]  u32data1   The word data to be programmed to flash address u32addr+4.
457   * @return   0   Success
458   * @return   -1  Failed
459   */
FMC_Write8Bytes(uint32_t u32addr,uint32_t u32data0,uint32_t u32data1)460 int32_t FMC_Write8Bytes(uint32_t u32addr, uint32_t u32data0, uint32_t u32data1)
461 {
462     int32_t  ret = 0;
463 
464     FMC->ISPCMD  = FMC_ISPCMD_PROGRAM_64;
465     FMC->ISPADDR = u32addr;
466     FMC->MPDAT0  = u32data0;
467     FMC->MPDAT1  = u32data1;
468     FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
469 
470     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
471 
472     if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
473     {
474         FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
475         ret = -1;
476     }
477     return ret;
478 }
479 
480 
481 /**
482   * @brief   Program Multi-Word data into specified address of flash.
483   * @param[in]  u32Addr    Start flash address in APROM where the data chunk to be programmed into.
484   *                        This address must be 8-bytes aligned to flash address.
485   * @param[in]  pu32Buf    Buffer that carry the data chunk.
486   * @param[in]  u32Len     Length of the data chunk in bytes.
487   * @retval   >=0  Number of data bytes were programmed.
488   * @return   -1   Invalid address.
489   */
FMC_WriteMultiple(uint32_t u32Addr,uint32_t pu32Buf[],uint32_t u32Len)490 int32_t FMC_WriteMultiple(uint32_t u32Addr, uint32_t pu32Buf[], uint32_t u32Len)
491 {
492     int   i, idx, retval = 0;
493 
494     if ((u32Addr >= FMC_APROM_END) || ((u32Addr % 8) != 0))
495     {
496         return -1;
497     }
498 
499     u32Len = u32Len - (u32Len % 8);         /* u32Len must be multiple of 8. */
500 
501     idx = 0;
502 
503     while (u32Len >= 8)
504     {
505         FMC->ISPADDR = u32Addr;
506         FMC->MPDAT0  = pu32Buf[idx++];
507         FMC->MPDAT1  = pu32Buf[idx++];
508         FMC->MPDAT2  = pu32Buf[idx++];
509         FMC->MPDAT3  = pu32Buf[idx++];
510         FMC->ISPCMD  = FMC_ISPCMD_PROGRAM_MUL;
511         FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
512 
513         for (i = 16; i < FMC_MULTI_WORD_PROG_LEN; )
514         {
515             while (FMC->MPSTS & (FMC_MPSTS_D0_Msk | FMC_MPSTS_D1_Msk))
516                 ;
517             retval += 8;
518             u32Len -= 8;
519             if (u32Len < 8)
520             {
521                 return retval;
522             }
523 
524             if (!(FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk))
525             {
526                 /* printf("    [WARNING] busy cleared after D0D1 cleared!\n"); */
527                 i += 8;
528                 break;
529             }
530 
531             FMC->MPDAT0 = pu32Buf[idx++];
532             FMC->MPDAT1 = pu32Buf[idx++];
533 
534             if (i == FMC_MULTI_WORD_PROG_LEN/4)
535                 break;           // done
536 
537             while (FMC->MPSTS & (FMC_MPSTS_D2_Msk | FMC_MPSTS_D3_Msk))
538                 ;
539             retval += 8;
540             u32Len -= 8;
541             if (u32Len < 8)
542             {
543                 return retval;
544             }
545 
546             if (!(FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk))
547             {
548                 /* printf("    [WARNING] busy cleared after D2D3 cleared!\n"); */
549                 i += 8;
550                 break;
551             }
552 
553             FMC->MPDAT2 = pu32Buf[idx++];
554             FMC->MPDAT3 = pu32Buf[idx++];
555         }
556 
557         if (i != FMC_MULTI_WORD_PROG_LEN)
558         {
559             /* printf("    [WARNING] Multi-word program interrupted at 0x%x !!\n", i); */
560             return retval;
561         }
562 
563         while (FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk) ;
564 
565         u32Addr += FMC_MULTI_WORD_PROG_LEN;
566     }
567     return retval;
568 }
569 
570 
571 /**
572   * @brief Program a 64-bits data to the specified OTP.
573   * @param[in] otp_num    The OTP number.
574   * @param[in] low_word   Low word of the 64-bits data.
575   * @param[in] high_word   Low word of the 64-bits data.
576   * @retval   0   Success
577   * @retval   -1  Program failed.
578   * @retval   -2  Invalid OTP number.
579   */
FMC_Write_OTP(uint32_t otp_num,uint32_t low_word,uint32_t high_word)580 int32_t FMC_Write_OTP(uint32_t otp_num, uint32_t low_word, uint32_t high_word)
581 {
582     int32_t  ret = 0;
583 
584     if (otp_num > 255UL)
585     {
586         ret = -2;
587     }
588 
589     if (ret == 0)
590     {
591         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
592         FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL;
593         FMC->ISPDAT = low_word;
594         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
595 
596         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
597 
598         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
599         {
600             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
601             ret = -1;
602         }
603     }
604 
605     if (ret == 0)
606     {
607         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
608         FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL + 4UL;
609         FMC->ISPDAT = high_word;
610         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
611 
612         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
613 
614         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
615         {
616             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
617             ret = -1;
618         }
619     }
620 
621     return ret;
622 }
623 
624 /**
625   * @brief  Read the 64-bits data from the specified OTP.
626   * @param[in] otp_num    The OTP number.
627   * @param[in] low_word   Low word of the 64-bits data.
628   * @param[in] high_word   Low word of the 64-bits data.
629   * @retval   0   Success
630   * @retval   -1  Read failed.
631   * @retval   -2  Invalid OTP number.
632   */
FMC_Read_OTP(uint32_t otp_num,uint32_t * low_word,uint32_t * high_word)633 int32_t FMC_Read_OTP(uint32_t otp_num, uint32_t *low_word, uint32_t *high_word)
634 {
635     int32_t  ret = 0;
636 
637     if (otp_num > 255UL)
638     {
639         ret = -2;
640     }
641 
642     if (ret == 0)
643     {
644         FMC->ISPCMD = FMC_ISPCMD_READ_64;
645         FMC->ISPADDR    = FMC_OTP_BASE + otp_num * 8UL ;
646         FMC->ISPDAT = 0x0UL;
647         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
648 
649         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
650 
651         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
652         {
653             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
654             ret = -1;
655         }
656         else
657         {
658             *low_word = FMC->MPDAT0;
659             *high_word = FMC->MPDAT1;
660         }
661     }
662     return ret;
663 }
664 
665 /**
666   * @brief  Lock the specified OTP.
667   * @param[in] otp_num    The OTP number.
668   * @retval   0   Success
669   * @retval   -1  Failed to write OTP lock bits.
670   * @retval   -2  Invalid OTP number.
671   */
FMC_Lock_OTP(uint32_t otp_num)672 int32_t FMC_Lock_OTP(uint32_t otp_num)
673 {
674     int32_t  ret = 0;
675 
676     if (otp_num > 255UL)
677     {
678         ret = -2;
679     }
680 
681     if (ret == 0)
682     {
683         FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
684         FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
685         FMC->ISPDAT = 0UL;
686         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
687 
688         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
689 
690         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
691         {
692             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
693             ret = -1;
694         }
695     }
696     return ret;
697 }
698 
699 /**
700   * @brief  Check the OTP is locked or not.
701   * @param[in] otp_num    The OTP number.
702   * @retval   1   OTP is locked.
703   * @retval   0   OTP is not locked.
704   * @retval   -1  Failed to read OTP lock bits.
705   * @retval   -2  Invalid OTP number.
706   */
FMC_Is_OTP_Locked(uint32_t otp_num)707 int32_t FMC_Is_OTP_Locked(uint32_t otp_num)
708 {
709     int32_t  ret = 0;
710 
711     if (otp_num > 255UL)
712     {
713         ret = -2;
714     }
715 
716     if (ret == 0)
717     {
718         FMC->ISPCMD = FMC_ISPCMD_READ;
719         FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
720         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
721 
722         while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
723 
724         if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk)
725         {
726             FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
727             ret = -1;
728         }
729         else
730         {
731             if (FMC->ISPDAT != 0xFFFFFFFFUL)
732             {
733                 ret = 1;   /* Lock work was progrmmed. OTP was locked. */
734             }
735         }
736     }
737     return ret;
738 }
739 
740 /**
741   * @brief Execute FMC_ISPCMD_READ command to read User Configuration.
742   * @param[out]  u32Config A two-word array.
743   *              u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
744   * @param[in] u32Count Available word count in u32Config.
745   * @return Success or not.
746   * @retval   0  Success.
747   * @retval   -1  Invalid parameter.
748   */
FMC_ReadConfig(uint32_t u32Config[],uint32_t u32Count)749 int32_t FMC_ReadConfig(uint32_t u32Config[], uint32_t u32Count)
750 {
751     int32_t   ret = 0;
752 
753     u32Config[0] = FMC_Read(FMC_CONFIG_BASE);
754 
755     if (u32Count < 2UL)
756     {
757         ret = -1;
758     }
759     else
760     {
761         u32Config[1] = FMC_Read(FMC_CONFIG_BASE+4UL);
762     }
763     return ret;
764 }
765 
766 
767 /**
768   * @brief Execute ISP commands to erase then write User Configuration.
769   * @param[in] u32Config   A two-word array.
770   *            u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
771   * @param[in] u32Count    The number of User Configuration words to be written.
772   * @return Success or not.
773   * @retval   0   Success
774   * @retval   -1  Failed
775   */
FMC_WriteConfig(uint32_t u32Config[],uint32_t u32Count)776 int32_t FMC_WriteConfig(uint32_t u32Config[], uint32_t u32Count)
777 {
778     int   i;
779 
780     FMC_ENABLE_CFG_UPDATE();
781     FMC_Erase(FMC_CONFIG_BASE);
782 
783     if ((FMC_Read(FMC_CONFIG_BASE) != 0xFFFFFFFF) || (FMC_Read(FMC_CONFIG_BASE+4) != 0xFFFFFFFF) ||
784             (FMC_Read(FMC_CONFIG_BASE+8) != 0xFFFF5A5A))
785     {
786         FMC_DISABLE_CFG_UPDATE();
787         return -1;
788     }
789 
790     for (i = 0; i < u32Count; i++)
791     {
792         FMC_Write(FMC_CONFIG_BASE+i*4UL, u32Config[1]);
793 
794         if (FMC_Read(FMC_CONFIG_BASE+i*4UL) != u32Config[1])
795         {
796             FMC_DISABLE_CFG_UPDATE();
797             return -1;
798         }
799     }
800 
801     FMC_DISABLE_CFG_UPDATE();
802     return 0;
803 }
804 
805 
806 /**
807   * @brief Run CRC32 checksum calculation and get result.
808   * @param[in] u32addr   Starting flash address. It must be a page aligned address.
809   * @param[in] u32count  Byte count of flash to be calculated. It must be multiple of 512 bytes.
810   * @return Success or not.
811   * @retval   0           Success.
812   * @retval   0xFFFFFFFF  Invalid parameter.
813   */
FMC_GetChkSum(uint32_t u32addr,uint32_t u32count)814 uint32_t  FMC_GetChkSum(uint32_t u32addr, uint32_t u32count)
815 {
816     uint32_t   ret;
817 
818     if ((u32addr % 512UL) || (u32count % 512UL))
819     {
820         ret = 0xFFFFFFFF;
821     }
822     else
823     {
824         FMC->ISPCMD  = FMC_ISPCMD_RUN_CKS;
825         FMC->ISPADDR = u32addr;
826         FMC->ISPDAT  = u32count;
827         FMC->ISPTRG  = FMC_ISPTRG_ISPGO_Msk;
828 
829         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
830 
831         FMC->ISPCMD = FMC_ISPCMD_READ_CKS;
832         FMC->ISPADDR    = u32addr;
833         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
834 
835         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
836 
837         ret = FMC->ISPDAT;
838     }
839 
840     return ret;
841 }
842 
843 
844 /**
845   * @brief Run flash all one verification and get result.
846   * @param[in] u32addr   Starting flash address. It must be a page aligned address.
847   * @param[in] u32count  Byte count of flash to be calculated. It must be multiple of 512 bytes.
848   * @retval   READ_ALLONE_YES      The contents of verified flash area are 0xFFFFFFFF.
849   * @retval   READ_ALLONE_NOT  Some contents of verified flash area are not 0xFFFFFFFF.
850   * @retval   READ_ALLONE_CMD_FAIL  Unexpected error occurred.
851   */
FMC_CheckAllOne(uint32_t u32addr,uint32_t u32count)852 uint32_t  FMC_CheckAllOne(uint32_t u32addr, uint32_t u32count)
853 {
854     uint32_t  ret = READ_ALLONE_CMD_FAIL;
855 
856     FMC->ISPSTS = 0x80UL;   /* clear check all one bit */
857 
858     FMC->ISPCMD   = FMC_ISPCMD_RUN_ALL1;
859     FMC->ISPADDR  = u32addr;
860     FMC->ISPDAT   = u32count;
861     FMC->ISPTRG   = FMC_ISPTRG_ISPGO_Msk;
862 
863     while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
864 
865     do
866     {
867         FMC->ISPCMD = FMC_ISPCMD_READ_ALL1;
868         FMC->ISPADDR    = u32addr;
869         FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
870         while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
871     }
872     while (FMC->ISPDAT == 0UL);
873 
874     if (FMC->ISPDAT == READ_ALLONE_YES)
875     {
876         ret = FMC->ISPDAT;
877     }
878 
879     if (FMC->ISPDAT == READ_ALLONE_NOT)
880     {
881         ret = FMC->ISPDAT;
882     }
883 
884     return ret;
885 }
886 
887 
888 /**
889   * @brief    Setup security key.
890   * @param[in] key      Key 0~2 to be setup.
891   * @param[in] kpmax    Maximum unmatched power-on counting number.
892   * @param[in] kemax    Maximum unmatched counting number.
893   * @param[in] lock_CONFIG   1: Security key lock CONFIG to write-protect. 0: Don't lock CONFIG.
894   * @param[in] lock_SPROM    1: Security key lock SPROM to write-protect. 0: Don't lock SPROM.
895   * @retval   0     Success.
896   * @retval   -1    Key is locked. Cannot overwrite the current key.
897   * @retval   -2    Failed to erase flash.
898   * @retval   -3    Failed to program key.
899   * @retval   -4    Key lock function failed.
900   * @retval   -5    CONFIG lock function failed.
901   * @retval   -6    SPROM lock function failed.
902   * @retval   -7    KPMAX function failed.
903   * @retval   -8    KEMAX function failed.
904   */
FMC_SetSPKey(uint32_t key[3],uint32_t kpmax,uint32_t kemax,const int32_t lock_CONFIG,const int32_t lock_SPROM)905 int32_t  FMC_SetSPKey(uint32_t key[3], uint32_t kpmax, uint32_t kemax,
906                       const int32_t lock_CONFIG, const int32_t lock_SPROM)
907 {
908     uint32_t  lock_ctrl = 0UL;
909     uint32_t  u32KeySts;
910     int32_t   ret = 0;
911 
912     if (FMC->KPKEYSTS != 0x200UL)
913     {
914         ret = -1;
915     }
916 
917     if (FMC_Erase(FMC_KPROM_BASE))
918     {
919         ret = -2;
920     }
921 
922     if (FMC_Erase(FMC_KPROM_BASE+0x200UL))
923     {
924         ret = -3;
925     }
926 
927     if (!lock_CONFIG)
928     {
929         lock_ctrl |= 0x1UL;
930     }
931 
932     if (!lock_SPROM)
933     {
934         lock_ctrl |= 0x2UL;
935     }
936 
937     if (ret == 0)
938     {
939         FMC_Write(FMC_KPROM_BASE, key[0]);
940         FMC_Write(FMC_KPROM_BASE+0x4UL, key[1]);
941         FMC_Write(FMC_KPROM_BASE+0x8UL, key[2]);
942         FMC_Write(FMC_KPROM_BASE+0xCUL, kpmax);
943         FMC_Write(FMC_KPROM_BASE+0x10UL, kemax);
944         FMC_Write(FMC_KPROM_BASE+0x14UL, lock_ctrl);
945 
946         while (FMC->KPKEYSTS & FMC_KPKEYSTS_KEYBUSY_Msk) { }
947 
948         u32KeySts = FMC->KPKEYSTS;
949 
950         if (!(u32KeySts & FMC_KPKEYSTS_KEYLOCK_Msk))
951         {
952             /* Security key lock failed! */
953             ret = -4;
954         }
955         else if ((lock_CONFIG && (!(u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk))) ||
956                  ((!lock_CONFIG) && (u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk)))
957         {
958             /* CONFIG lock failed! */
959             ret = -5;
960         }
961         else if ((lock_SPROM && (!(u32KeySts & FMC_KPKEYSTS_SPFLAG_Msk))) ||
962                  ((!lock_SPROM) && (u32KeySts & FMC_KPKEYSTS_SPFLAG_Msk)))
963         {
964             /* CONFIG lock failed! */
965             ret = -6;
966         }
967         else if (((FMC->KPCNT & FMC_KPCNT_KPMAX_Msk) >> FMC_KPCNT_KPMAX_Pos) != kpmax)
968         {
969             /* KPMAX failed! */
970             ret = -7;
971         }
972         else if (((FMC->KPKEYCNT & FMC_KPKEYCNT_KPKEMAX_Msk) >> FMC_KPKEYCNT_KPKEMAX_Pos) != kemax)
973         {
974             /* KEMAX failed! */
975             ret = -8;
976         }
977     }
978     return ret;
979 }
980 
981 
982 /**
983   * @brief    Execute security key comparison.
984   * @param[in] key  Key 0~2 to be compared.
985   * @retval   0     Key matched.
986   * @retval   -1    Forbidden. Times of key comparison mismatch reach the maximum count.
987   * @retval   -2    Key mismatched.
988   * @retval   -3    No security key lock. Key comparison is not required.
989   */
FMC_CompareSPKey(uint32_t key[3])990 int32_t  FMC_CompareSPKey(uint32_t key[3])
991 {
992     uint32_t  u32KeySts;
993     int32_t   ret = 0;
994 
995     if (FMC->KPKEYSTS & FMC_KPKEYSTS_FORBID_Msk)
996     {
997         /* FMC_CompareSPKey - FORBID!  */
998         ret = -1;
999     }
1000 
1001     if (!(FMC->KPKEYSTS & FMC_KPKEYSTS_KEYLOCK_Msk))
1002     {
1003         /* FMC_CompareSPKey - key is not locked!  */
1004         ret = -3;
1005     }
1006 
1007     if (ret == 0)
1008     {
1009         FMC->KPKEY0 = key[0];
1010         FMC->KPKEY1 = key[1];
1011         FMC->KPKEY2 = key[2];
1012         FMC->KPKEYTRG = FMC_KPKEYTRG_KPKEYGO_Msk | FMC_KPKEYTRG_TCEN_Msk;
1013 
1014         while (FMC->KPKEYSTS & FMC_KPKEYSTS_KEYBUSY_Msk) { }
1015 
1016         u32KeySts = FMC->KPKEYSTS;
1017 
1018         if (!(u32KeySts & FMC_KPKEYSTS_KEYMATCH_Msk))
1019         {
1020             /* Key mismatched! */
1021             ret = -2;
1022         }
1023         else if (u32KeySts & FMC_KPKEYSTS_KEYLOCK_Msk)
1024         {
1025             /* Key matched, but still be locked! */
1026             ret = -2;
1027         }
1028     }
1029     return ret;
1030 }
1031 
1032 
1033 /*@}*/ /* end of group FMC_EXPORTED_FUNCTIONS */
1034 
1035 /*@}*/ /* end of group FMC_Driver */
1036 
1037 /*@}*/ /* end of group Standard_Driver */
1038 
1039 /*** (C) COPYRIGHT 2016 Nuvoton Technology Corp. ***/
1040 
1041 
1042