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