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