1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-03-28     luobeihai    first version
9  *
10  */
11 
12 #include "board.h"
13 
14 #ifdef BSP_USING_ON_CHIP_FLASH
15 #include "drv_flash.h"
16 
17 #if defined(RT_USING_FAL)
18 #include "fal.h"
19 #endif
20 
21 #define DRV_DEBUG
22 #define LOG_TAG                "drv.flash"
23 #include <drv_log.h>
24 
25 #if defined(APM32E10X_HD)
26 #define FLASH_PAGE_SIZE          0x800U
27 #endif
28 
29 /**
30   * @brief  Gets the page of a given address
31   * @param  Addr: Address of the FLASH Memory
32   * @retval The page of a given address
33   */
GetPage(uint32_t addr)34 static uint32_t GetPage(uint32_t addr)
35 {
36     uint32_t page = 0;
37     page = RT_ALIGN_DOWN(addr, FLASH_PAGE_SIZE);
38     return page;
39 }
40 
41 /**
42  * Read data from flash.
43  * @note This operation's units is word.
44  *
45  * @param addr flash address
46  * @param buf buffer to store read data
47  * @param size read bytes size
48  *
49  * @return result
50  */
apm32_flash_read(rt_uint32_t addr,rt_uint8_t * buf,size_t size)51 int apm32_flash_read(rt_uint32_t addr, rt_uint8_t *buf, size_t size)
52 {
53     size_t i;
54 
55     if ((addr + size) > APM32_FLASH_END_ADDRESS)
56     {
57         LOG_E("read outrange flash size! addr is (0x%p)", (void *)(addr + size));
58         return -RT_EINVAL;
59     }
60 
61     for (i = 0; i < size; i++, buf++, addr++)
62     {
63         *buf = *(rt_uint8_t *) addr;
64     }
65 
66     return size;
67 }
68 
69 /**
70  * Write data to flash.
71  * @note This operation's units is word.
72  * @note This operation must after erase. @see flash_erase.
73  *
74  * @param addr flash address
75  * @param buf the write data buffer
76  * @param size write bytes size
77  *
78  * @return result
79  */
apm32_flash_write(rt_uint32_t addr,const rt_uint8_t * buf,size_t size)80 int apm32_flash_write(rt_uint32_t addr, const rt_uint8_t *buf, size_t size)
81 {
82     rt_err_t result        = RT_EOK;
83     rt_uint32_t end_addr   = addr + size;
84 
85     if (addr % 4 != 0)
86     {
87         LOG_E("write addr must be 4-byte alignment");
88         return -RT_EINVAL;
89     }
90 
91     if ((end_addr) > APM32_FLASH_END_ADDRESS)
92     {
93         LOG_E("write outrange flash size! addr is (0x%p)", (void *)(addr + size));
94         return -RT_EINVAL;
95     }
96 
97     FMC_Unlock();
98 
99     while (addr < end_addr)
100     {
101         if (FMC_ProgramWord(addr, *((rt_uint32_t *)buf)) == FMC_STATUS_COMPLETE)
102         {
103             if (*(rt_uint32_t *)addr != *(rt_uint32_t *)buf)
104             {
105                 result = -RT_ERROR;
106                 break;
107             }
108             addr += 4;
109             buf  += 4;
110         }
111         else
112         {
113             result = -RT_ERROR;
114             break;
115         }
116     }
117 
118     FMC_Lock();
119 
120     if (result != RT_EOK)
121     {
122         return result;
123     }
124 
125     return size;
126 }
127 
128 /**
129  * @brief erase data on flash .
130  * @note this operation is irreversible.
131  * @note this operation's units is different which on many chips.
132  *
133  * @param addr flash address
134  * @param size erase bytes size
135  *
136  * @return result
137  */
apm32_flash_erase(rt_uint32_t addr,size_t size)138 int apm32_flash_erase(rt_uint32_t addr, size_t size)
139 {
140     rt_err_t result = RT_EOK;
141     rt_uint32_t start_addr = addr;
142     rt_uint32_t end_addr = addr + size;
143     rt_uint32_t page_addr = 0;
144 
145     FMC_Unlock();
146 
147     if ((end_addr) > APM32_FLASH_END_ADDRESS)
148     {
149         LOG_E("erase outrange flash size! addr is (0x%p)", (void *)(addr + size));
150         return -RT_EINVAL;
151     }
152 
153     /* clear program error flag */
154     if (FMC_ReadStatus() == FMC_STATUS_ERROR_PG)
155     {
156         FMC_ClearStatusFlag(FMC_FLAG_PE);
157     }
158 
159     while(addr < end_addr)
160     {
161         page_addr = GetPage(addr);
162 
163         if(FMC_ErasePage(page_addr) != FMC_STATUS_COMPLETE)
164         {
165             result = -RT_ERROR;
166             goto __exit;
167         }
168 
169         addr += FLASH_PAGE_SIZE;
170     }
171 
172 __exit:
173     FMC_Lock();
174 
175     if(result != RT_EOK)
176     {
177         return result;
178     }
179 
180     LOG_D("erase done: addr (0x%p), size %d", (void *)start_addr, size);
181 
182     return size;
183 }
184 
185 
186 #if defined(RT_USING_FAL)
187 
188 static int fal_flash_read(long offset, rt_uint8_t *buf, size_t size);
189 static int fal_flash_write(long offset, const rt_uint8_t *buf, size_t size);
190 static int fal_flash_erase(long offset, size_t size);
191 
192 const struct fal_flash_dev apm32_onchip_flash = { "onchip_flash", APM32_FLASH_START_ADRESS, APM32_FLASH_SIZE, FLASH_PAGE_SIZE, {NULL, fal_flash_read, fal_flash_write, fal_flash_erase} };
193 
fal_flash_read(long offset,rt_uint8_t * buf,size_t size)194 static int fal_flash_read(long offset, rt_uint8_t *buf, size_t size)
195 {
196     return apm32_flash_read(apm32_onchip_flash.addr + offset, buf, size);
197 }
198 
fal_flash_write(long offset,const rt_uint8_t * buf,size_t size)199 static int fal_flash_write(long offset, const rt_uint8_t *buf, size_t size)
200 {
201     return apm32_flash_write(apm32_onchip_flash.addr + offset, buf, size);
202 }
203 
fal_flash_erase(long offset,size_t size)204 static int fal_flash_erase(long offset, size_t size)
205 {
206     return apm32_flash_erase(apm32_onchip_flash.addr + offset, size);
207 }
208 
209 #endif
210 #endif /* BSP_USING_ON_CHIP_FLASH */
211