1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2023 Nuvoton Technology Corporation.
5 */
6
7 #define DT_DRV_COMPAT nuvoton_numaker_fmc
8
9 #include <string.h>
10 #include <errno.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/flash.h>
14 #include <zephyr/logging/log.h>
15 #include "flash_priv.h"
16 #include <NuMicro.h>
17
18 LOG_MODULE_REGISTER(flash_numaker, CONFIG_FLASH_LOG_LEVEL);
19
20 #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
21 #define SOC_NV_FLASH_WRITE_BLOCK_SIZE DT_PROP_OR(SOC_NV_FLASH_NODE, write_block_size, 0x04)
22
23 struct flash_numaker_data {
24 FMC_T *fmc;
25 struct k_sem write_lock;
26 uint32_t flash_block_base;
27 };
28
29 static const struct flash_parameters flash_numaker_parameters = {
30 .write_block_size = SOC_NV_FLASH_WRITE_BLOCK_SIZE,
31 .erase_value = 0xff,
32 };
33
34 /* Validate offset and length */
flash_numaker_is_range_valid(off_t offset,size_t len)35 static bool flash_numaker_is_range_valid(off_t offset, size_t len)
36 {
37 uint32_t aprom_size = (FMC_APROM_END - FMC_APROM_BASE);
38
39 /* check for min value */
40 if ((offset < 0) || (len == 0)) {
41 return false;
42 }
43
44 /* check for max value */
45 if (offset >= aprom_size || len > aprom_size || (aprom_size - offset) < len) {
46 return false;
47 }
48
49 return true;
50 }
51
52 /*
53 * Erase a flash memory area.
54 *
55 * param dev Device struct
56 * param offset The address's offset
57 * param len The size of the buffer
58 * return 0 on success
59 * return -EINVAL erroneous code
60 */
61
flash_numaker_erase(const struct device * dev,off_t offset,size_t len)62 static int flash_numaker_erase(const struct device *dev, off_t offset, size_t len)
63 {
64 struct flash_numaker_data *dev_data = dev->data;
65 uint32_t rc = 0;
66 unsigned int key;
67 int page_nums = (len / FMC_FLASH_PAGE_SIZE);
68 uint32_t addr = dev_data->flash_block_base + offset;
69
70 /* return SUCCESS for len == 0 (required by tests/drivers/flash) */
71 if (!len) {
72 return 0;
73 }
74
75 /* Validate range */
76 if (!flash_numaker_is_range_valid(offset, len)) {
77 return -EINVAL;
78 }
79
80 /* check alignment and erase only by pages */
81 if (((addr % FMC_FLASH_PAGE_SIZE) != 0) || ((len % FMC_FLASH_PAGE_SIZE) != 0)) {
82 return -EINVAL;
83 }
84
85 /* take semaphore */
86 if (k_sem_take(&dev_data->write_lock, K_NO_WAIT)) {
87 return -EACCES;
88 }
89
90 SYS_UnlockReg();
91 key = irq_lock();
92 while (page_nums) {
93 if (((len >= FMC_BANK_SIZE)) && ((addr % FMC_BANK_SIZE) == 0)) {
94 if (FMC_Erase_Bank(addr)) {
95 LOG_ERR("Erase flash bank failed or erase time-out");
96 rc = -EIO;
97 goto done;
98 }
99 page_nums -= (FMC_BANK_SIZE / FMC_FLASH_PAGE_SIZE);
100 addr += FMC_BANK_SIZE;
101 } else {
102 /* erase page */
103 if (FMC_Erase(addr)) {
104 LOG_ERR("Erase flash page failed or erase time-out");
105 rc = -EIO;
106 goto done;
107 }
108 page_nums--;
109 addr += FMC_FLASH_PAGE_SIZE;
110 }
111 }
112
113 done:
114 SYS_LockReg();
115 irq_unlock(key);
116 /* release semaphore */
117 k_sem_give(&dev_data->write_lock);
118
119 return rc;
120 }
121
122 /*
123 * Read a flash memory area.
124 *
125 * param dev Device struct
126 * param offset The address's offset
127 * param data The buffer to store or read the value
128 * param length The size of the buffer
129 * return 0 on success,
130 * return -EIO erroneous code
131 */
flash_numaker_read(const struct device * dev,off_t offset,void * data,size_t len)132 static int flash_numaker_read(const struct device *dev, off_t offset, void *data, size_t len)
133 {
134 struct flash_numaker_data *dev_data = dev->data;
135 uint32_t addr = dev_data->flash_block_base + offset;
136
137 /* return SUCCESS for len == 0 (required by tests/drivers/flash) */
138 if (!len) {
139 return 0;
140 }
141
142 /* Validate range */
143 if (!flash_numaker_is_range_valid(offset, len)) {
144 return -EINVAL;
145 }
146
147 /* read flash */
148 memcpy(data, (void *)addr, len);
149
150 return 0;
151 }
152
flash_numaker_block_write(uint32_t u32_addr,uint8_t * pu8_data,int block_size)153 static int32_t flash_numaker_block_write(uint32_t u32_addr, uint8_t *pu8_data, int block_size)
154 {
155 int32_t retval;
156 uint32_t *pu32_data = (uint32_t *)pu8_data;
157
158 SYS_UnlockReg();
159 if (block_size == 4) {
160 retval = FMC_Write(u32_addr, *pu32_data);
161 } else if (block_size == 8) {
162 retval = FMC_Write8Bytes(u32_addr, *pu32_data, *(pu32_data + 1));
163 } else {
164 retval = -1;
165 }
166 SYS_LockReg();
167
168 return retval;
169 }
170
flash_numaker_write(const struct device * dev,off_t offset,const void * data,size_t len)171 static int flash_numaker_write(const struct device *dev, off_t offset, const void *data, size_t len)
172 {
173 struct flash_numaker_data *dev_data = dev->data;
174 uint32_t rc = 0;
175 unsigned int key;
176 uint32_t addr = dev_data->flash_block_base + offset;
177 int block_size = flash_numaker_parameters.write_block_size;
178 int blocks = (len / flash_numaker_parameters.write_block_size);
179 uint8_t *pu8_data = (uint8_t *)data;
180
181 /* return SUCCESS for len == 0 (required by tests/drivers/flash) */
182 if (!len) {
183 return 0;
184 }
185
186 /* Validate range */
187 if (!flash_numaker_is_range_valid(offset, len)) {
188 return -EINVAL;
189 }
190
191 /* Validate address alignment */
192 if ((addr % flash_numaker_parameters.write_block_size) != 0) {
193 return -EINVAL;
194 }
195
196 /* Validate write size be multiples of the write block size */
197 if ((len % block_size) != 0) {
198 return -EINVAL;
199 }
200
201 /* Validate offset be multiples of the write block size */
202 if ((offset % block_size) != 0) {
203 return -EINVAL;
204 }
205
206 if (k_sem_take(&dev_data->write_lock, K_FOREVER)) {
207 return -EACCES;
208 }
209
210 key = irq_lock();
211
212 while (blocks) {
213 if (flash_numaker_block_write(addr, pu8_data, block_size)) {
214 rc = -EIO;
215 goto done;
216 }
217 pu8_data += block_size;
218 addr += block_size;
219 blocks--;
220 }
221
222 done:
223 irq_unlock(key);
224
225 k_sem_give(&dev_data->write_lock);
226
227 return rc;
228 }
229
230 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
231 static const struct flash_pages_layout dev_layout = {
232 .pages_count =
233 DT_REG_SIZE(SOC_NV_FLASH_NODE) / DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
234 .pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
235 };
236
flash_numaker_pages_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)237 static void flash_numaker_pages_layout(const struct device *dev,
238 const struct flash_pages_layout **layout,
239 size_t *layout_size)
240 {
241 *layout = &dev_layout;
242 *layout_size = 1;
243 }
244 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
245
flash_numaker_get_parameters(const struct device * dev)246 static const struct flash_parameters *flash_numaker_get_parameters(const struct device *dev)
247 {
248 ARG_UNUSED(dev);
249
250 return &flash_numaker_parameters;
251 }
252
253 static struct flash_numaker_data flash_data;
254
255 static const struct flash_driver_api flash_numaker_api = {
256 .erase = flash_numaker_erase,
257 .write = flash_numaker_write,
258 .read = flash_numaker_read,
259 .get_parameters = flash_numaker_get_parameters,
260 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
261 .page_layout = flash_numaker_pages_layout,
262 #endif
263 };
264
flash_numaker_init(const struct device * dev)265 static int flash_numaker_init(const struct device *dev)
266 {
267 struct flash_numaker_data *dev_data = dev->data;
268
269 k_sem_init(&dev_data->write_lock, 1, 1);
270
271 /* Enable FMC ISP function */
272 SYS_UnlockReg();
273 FMC_Open();
274 /* Enable APROM update. */
275 FMC_ENABLE_AP_UPDATE();
276 SYS_LockReg();
277 dev_data->flash_block_base = (uint32_t)FMC_APROM_BASE;
278 dev_data->fmc = (FMC_T *)DT_REG_ADDR(DT_NODELABEL(fmc));
279
280 return 0;
281 }
282
283 DEVICE_DT_INST_DEFINE(0, flash_numaker_init, NULL, &flash_data, NULL, POST_KERNEL,
284 CONFIG_FLASH_INIT_PRIORITY, &flash_numaker_api);
285