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