1 /*
2 * Copyright (c) 2022 BrainCo Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "flash_gd32.h"
8
9 #include <zephyr/logging/log.h>
10 #include <zephyr/kernel.h>
11 #include <gd32_fmc.h>
12
13 LOG_MODULE_DECLARE(flash_gd32);
14
15 #define GD32_NV_FLASH_V3_NODE DT_INST(0, gd_gd32_nv_flash_v3)
16 #define GD32_NV_FLASH_V3_TIMEOUT DT_PROP(GD32_NV_FLASH_V3_NODE, max_erase_time_ms)
17
18 /**
19 * @brief GD32 FMC v3 flash memory layout for GD32F4xx series.
20 */
21 #if defined(CONFIG_FLASH_PAGE_LAYOUT) && \
22 defined(CONFIG_SOC_SERIES_GD32F4XX)
23 #if (PRE_KB(512) == SOC_NV_FLASH_SIZE)
24 static const struct flash_pages_layout gd32_fmc_v3_layout[] = {
25 {.pages_count = 4, .pages_size = KB(16)},
26 {.pages_count = 1, .pages_size = KB(64)},
27 {.pages_count = 3, .pages_size = KB(128)},
28 };
29 #elif (PRE_KB(1024) == SOC_NV_FLASH_SIZE)
30 static const struct flash_pages_layout gd32_fmc_v3_layout[] = {
31 {.pages_count = 4, .pages_size = KB(16)},
32 {.pages_count = 1, .pages_size = KB(64)},
33 {.pages_count = 7, .pages_size = KB(128)},
34 };
35 #elif (PRE_KB(2048) == SOC_NV_FLASH_SIZE)
36 static const struct flash_pages_layout gd32_fmc_v3_layout[] = {
37 {.pages_count = 4, .pages_size = KB(16)},
38 {.pages_count = 1, .pages_size = KB(64)},
39 {.pages_count = 7, .pages_size = KB(128)},
40 {.pages_count = 4, .pages_size = KB(16)},
41 {.pages_count = 1, .pages_size = KB(64)},
42 {.pages_count = 7, .pages_size = KB(128)},
43 };
44 #elif (PRE_KB(3072) == SOC_NV_FLASH_SIZE)
45 static const struct flash_pages_layout gd32_fmc_v3_layout[] = {
46 {.pages_count = 4, .pages_size = KB(16)},
47 {.pages_count = 1, .pages_size = KB(64)},
48 {.pages_count = 7, .pages_size = KB(128)},
49 {.pages_count = 4, .pages_size = KB(16)},
50 {.pages_count = 1, .pages_size = KB(64)},
51 {.pages_count = 7, .pages_size = KB(128)},
52 {.pages_count = 4, .pages_size = KB(256)},
53 };
54 #else
55 #error "Unknown FMC layout for GD32F4xx series."
56 #endif
57 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
58
59 #define gd32_fmc_v3_WRITE_ERR (FMC_STAT_PGMERR | FMC_STAT_PGSERR | FMC_STAT_WPERR)
60 #define gd32_fmc_v3_ERASE_ERR FMC_STAT_OPERR
61
62 /* SN bits in FMC_CTL are not continue values, use table below to map them. */
63 static uint8_t gd32_fmc_v3_sectors[] = {
64 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U,
65 16U, 17U, 18U, 19U, 20U, 21U, 22U, 23U, 24U, 25U, 26U, 27U,
66 12U, 13U, 14U, 15U
67 };
68
gd32_fmc_v3_unlock(void)69 static inline void gd32_fmc_v3_unlock(void)
70 {
71 FMC_KEY = UNLOCK_KEY0;
72 FMC_KEY = UNLOCK_KEY1;
73 }
74
gd32_fmc_v3_lock(void)75 static inline void gd32_fmc_v3_lock(void)
76 {
77 FMC_CTL |= FMC_CTL_LK;
78 }
79
gd32_fmc_v3_wait_idle(void)80 static int gd32_fmc_v3_wait_idle(void)
81 {
82 const int64_t expired_time = k_uptime_get() + GD32_NV_FLASH_V3_TIMEOUT;
83
84 while (FMC_STAT & FMC_STAT_BUSY) {
85 if (k_uptime_get() > expired_time) {
86 return -ETIMEDOUT;
87 }
88 }
89
90 return 0;
91 }
92
flash_gd32_valid_range(off_t offset,uint32_t len,bool write)93 bool flash_gd32_valid_range(off_t offset, uint32_t len, bool write)
94 {
95 const struct flash_pages_layout *page_layout;
96 uint32_t cur = 0U, next = 0U;
97
98 if ((offset > SOC_NV_FLASH_SIZE) ||
99 ((offset + len) > SOC_NV_FLASH_SIZE)) {
100 return false;
101 }
102
103 if (write) {
104 /* Check offset and len aligned to write-block-size. */
105 if ((offset % sizeof(flash_prg_t)) ||
106 (len % sizeof(flash_prg_t))) {
107 return false;
108 }
109
110 } else {
111 for (size_t i = 0; i < ARRAY_SIZE(gd32_fmc_v3_layout); i++) {
112 page_layout = &gd32_fmc_v3_layout[i];
113
114 for (size_t j = 0; j < page_layout->pages_count; j++) {
115 cur = next;
116
117 next += page_layout->pages_size;
118
119 /* Check bad offset. */
120 if ((offset > cur) && (offset < next)) {
121 return false;
122 }
123
124 /* Check bad len. */
125 if (((offset + len) > cur) &&
126 ((offset + len) < next)) {
127 return false;
128 }
129
130 if ((offset + len) == next) {
131 return true;
132 }
133 }
134 }
135 }
136
137 return true;
138 }
139
flash_gd32_write_range(off_t offset,const void * data,size_t len)140 int flash_gd32_write_range(off_t offset, const void *data, size_t len)
141 {
142 flash_prg_t *prg_flash = (flash_prg_t *)((uint8_t *)SOC_NV_FLASH_ADDR + offset);
143 flash_prg_t *prg_data = (flash_prg_t *)data;
144 int ret = 0;
145
146 gd32_fmc_v3_unlock();
147
148 if (FMC_STAT & FMC_STAT_BUSY) {
149 return -EBUSY;
150 }
151
152 FMC_CTL |= FMC_CTL_PG;
153
154 FMC_CTL &= ~FMC_CTL_PSZ;
155 FMC_CTL |= CTL_PSZ(sizeof(flash_prg_t) - 1);
156
157 for (size_t i = 0U; i < (len / sizeof(flash_prg_t)); i++) {
158 *prg_flash++ = *prg_data++;
159 }
160
161 ret = gd32_fmc_v3_wait_idle();
162 if (ret < 0) {
163 goto expired_out;
164 }
165
166 if (FMC_STAT & gd32_fmc_v3_WRITE_ERR) {
167 ret = -EIO;
168 FMC_STAT |= gd32_fmc_v3_WRITE_ERR;
169 LOG_ERR("FMC programming failed");
170 }
171
172 expired_out:
173 FMC_CTL &= ~FMC_CTL_PG;
174
175 gd32_fmc_v3_lock();
176
177 return ret;
178 }
179
gd32_fmc_v3_sector_erase(uint8_t sector)180 static int gd32_fmc_v3_sector_erase(uint8_t sector)
181 {
182 int ret = 0;
183
184 gd32_fmc_v3_unlock();
185
186 if (FMC_STAT & FMC_STAT_BUSY) {
187 return -EBUSY;
188 }
189
190 FMC_CTL |= FMC_CTL_SER;
191
192 FMC_CTL &= ~FMC_CTL_SN;
193 FMC_CTL |= CTL_SN(sector);
194
195 FMC_CTL |= FMC_CTL_START;
196
197 ret = gd32_fmc_v3_wait_idle();
198 if (ret < 0) {
199 goto expired_out;
200 }
201
202 if (FMC_STAT & gd32_fmc_v3_ERASE_ERR) {
203 ret = -EIO;
204 FMC_STAT |= gd32_fmc_v3_ERASE_ERR;
205 LOG_ERR("FMC sector %u erase failed", sector);
206 }
207
208 expired_out:
209 FMC_CTL &= ~FMC_CTL_SER;
210
211 gd32_fmc_v3_lock();
212
213 return ret;
214 }
215
flash_gd32_erase_block(off_t offset,size_t size)216 int flash_gd32_erase_block(off_t offset, size_t size)
217 {
218 const struct flash_pages_layout *page_layout;
219 uint32_t erase_offset = 0U;
220 uint8_t counter = 0U;
221 int ret = 0;
222
223 for (size_t i = 0; i < ARRAY_SIZE(gd32_fmc_v3_layout); i++) {
224 page_layout = &gd32_fmc_v3_layout[i];
225
226 for (size_t j = 0; j < page_layout->pages_count; j++) {
227 if (erase_offset < offset) {
228 counter++;
229 erase_offset += page_layout->pages_size;
230
231 continue;
232 }
233
234 uint8_t sector = gd32_fmc_v3_sectors[counter++];
235
236 ret = gd32_fmc_v3_sector_erase(sector);
237 if (ret < 0) {
238 return ret;
239 }
240
241 erase_offset += page_layout->pages_size;
242
243 if (erase_offset - offset >= size) {
244 return 0;
245 }
246 }
247 }
248
249 return 0;
250 }
251
252 #ifdef CONFIG_FLASH_PAGE_LAYOUT
flash_gd32_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)253 void flash_gd32_pages_layout(const struct device *dev,
254 const struct flash_pages_layout **layout,
255 size_t *layout_size)
256 {
257 ARG_UNUSED(dev);
258
259 *layout = gd32_fmc_v3_layout;
260 *layout_size = ARRAY_SIZE(gd32_fmc_v3_layout);
261 }
262 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
263