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