1 /*
2  * Copyright (c) 2024 Ambiq Micro Inc. <www.ambiq.com>
3  * SPDX-License-Identifier: Apache-2.0
4  * Emulate a memory device on MSPI emulator bus
5  */
6 #define DT_DRV_COMPAT zephyr_mspi_emul_flash
7 
8 #include <zephyr/logging/log.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/emul.h>
13 #include <zephyr/drivers/mspi.h>
14 #include <zephyr/drivers/mspi_emul.h>
15 #include <zephyr/drivers/flash.h>
16 #include "spi_nor.h"
17 
18 LOG_MODULE_REGISTER(zephyr_mspi_emul_flash, CONFIG_FLASH_LOG_LEVEL);
19 
20 /* add else if for other SoC platforms */
21 #if defined(CONFIG_SOC_POSIX)
22 typedef struct mspi_timing_cfg mspi_timing_cfg;
23 typedef enum mspi_timing_param mspi_timing_param;
24 #endif
25 
26 struct flash_mspi_emul_device_config {
27 	uint32_t                            size;
28 	struct flash_parameters             flash_param;
29 	struct flash_pages_layout           page_layout;
30 
31 	struct mspi_dev_id                  dev_id;
32 	struct mspi_dev_cfg                 tar_dev_cfg;
33 	struct mspi_xip_cfg                 tar_xip_cfg;
34 	struct mspi_scramble_cfg            tar_scramble_cfg;
35 
36 	bool                                sw_multi_periph;
37 };
38 
39 struct flash_mspi_emul_device_data {
40 	const struct device                 *bus;
41 	struct mspi_dev_cfg                 dev_cfg;
42 	struct mspi_xip_cfg                 xip_cfg;
43 	struct mspi_scramble_cfg            scramble_cfg;
44 	mspi_timing_cfg                     timing_cfg;
45 
46 	struct mspi_xfer                    xfer;
47 	struct mspi_xfer_packet             packet;
48 
49 	struct k_sem                        lock;
50 	uint8_t                             *mem;
51 };
52 
53 /**
54  * Acquire the device lock.
55  *
56  * @param flash MSPI emulation flash device.
57  */
acquire(const struct device * flash)58 static void acquire(const struct device *flash)
59 {
60 	const struct flash_mspi_emul_device_config *cfg = flash->config;
61 	struct flash_mspi_emul_device_data *data = flash->data;
62 
63 	k_sem_take(&data->lock, K_FOREVER);
64 	if (cfg->sw_multi_periph) {
65 		while (mspi_dev_config(data->bus, &cfg->dev_id,
66 				       MSPI_DEVICE_CONFIG_ALL, &data->dev_cfg)) {
67 			;
68 		}
69 	} else {
70 		while (mspi_dev_config(data->bus, &cfg->dev_id,
71 				       MSPI_DEVICE_CONFIG_NONE, NULL)) {
72 			;
73 		}
74 	}
75 }
76 
77 /**
78  * Release the device lock.
79  *
80  * @param flash MSPI emulation flash device.
81  */
release(const struct device * flash)82 static void release(const struct device *flash)
83 {
84 	struct flash_mspi_emul_device_data *data = flash->data;
85 
86 	while (mspi_get_channel_status(data->bus, 0)) {
87 
88 	}
89 
90 	k_sem_give(&data->lock);
91 }
92 
93 /**
94  * API implementation of emul_mspi_dev_api_transceive transceive.
95  *
96  * @param target Pointer to MSPI device emulator.
97  * @param dev_id Pointer to the device ID structure from a device.
98  * @param xfer Pointer to the MSPI transfer started by dev_id.
99  *
100  * @retval 0 if successful.
101  * @retval -ESTALE device ID don't match, need to call mspi_dev_config first.
102  * @retval -Error transfer failed.
103  */
emul_mspi_device_transceive(const struct emul * target,const struct mspi_xfer_packet * packets,uint32_t num_packet,bool async,uint32_t timeout)104 static int emul_mspi_device_transceive(const struct emul *target,
105 				       const struct mspi_xfer_packet *packets,
106 				       uint32_t num_packet,
107 				       bool async,
108 				       uint32_t timeout)
109 {
110 	ARG_UNUSED(timeout);
111 	const struct flash_mspi_emul_device_config *cfg = target->dev->config;
112 	struct flash_mspi_emul_device_data *data = target->dev->data;
113 	struct emul_mspi_driver_api *api = (struct emul_mspi_driver_api *)data->bus->api;
114 
115 	__ASSERT_NO_MSG(api);
116 	__ASSERT_NO_MSG(api->trigger_event);
117 
118 	for (uint32_t count = 0; count < num_packet; ++count) {
119 		const struct mspi_xfer_packet *packet = &packets[count];
120 
121 		if (packet->address > cfg->size ||
122 		    packet->address + packet->num_bytes > cfg->size) {
123 			return -ENOMEM;
124 		}
125 
126 		if (packet->dir == MSPI_RX) {
127 			memcpy(packet->data_buf, data->mem + packet->address,
128 			       packet->num_bytes);
129 		} else if (packet->dir == MSPI_TX) {
130 			memcpy(data->mem + packet->address, packet->data_buf,
131 			       packet->num_bytes);
132 		}
133 
134 		if (async) {
135 			if (packet->cb_mask == MSPI_BUS_XFER_COMPLETE_CB) {
136 				api->trigger_event(data->bus, MSPI_BUS_XFER_COMPLETE);
137 			}
138 		}
139 	}
140 
141 	return 0;
142 }
143 
flash_mspi_emul_erase(const struct device * flash,off_t offset,size_t size)144 static int flash_mspi_emul_erase(const struct device *flash, off_t offset, size_t size)
145 {
146 	const struct flash_mspi_emul_device_config *cfg = flash->config;
147 	struct flash_mspi_emul_device_data *data = flash->data;
148 
149 	const size_t num_sectors = size / SPI_NOR_SECTOR_SIZE;
150 	const size_t num_blocks = size / SPI_NOR_BLOCK_SIZE;
151 
152 	int i;
153 
154 	acquire(flash);
155 
156 	if (offset % SPI_NOR_SECTOR_SIZE) {
157 		LOG_ERR("Invalid offset");
158 		return -EINVAL;
159 	}
160 
161 	if (size % SPI_NOR_SECTOR_SIZE) {
162 		LOG_ERR("Invalid size");
163 		return -EINVAL;
164 	}
165 
166 	if ((offset == 0) && (size == cfg->size)) {
167 
168 		memset(data->mem, cfg->flash_param.erase_value, size);
169 
170 	} else if ((0 == (offset % SPI_NOR_BLOCK_SIZE)) && (0 == (size % SPI_NOR_BLOCK_SIZE))) {
171 		for (i = 0; i < num_blocks; i++) {
172 			memset(data->mem + offset, cfg->flash_param.erase_value,
173 			       SPI_NOR_BLOCK_SIZE);
174 			offset += SPI_NOR_BLOCK_SIZE;
175 		}
176 	} else {
177 		for (i = 0; i < num_sectors; i++) {
178 			memset(data->mem + offset, cfg->flash_param.erase_value,
179 			       SPI_NOR_SECTOR_SIZE);
180 			offset += SPI_NOR_SECTOR_SIZE;
181 		}
182 	}
183 
184 	release(flash);
185 
186 	return 0;
187 }
188 
189 /**
190  * API implementation of flash write.
191  *
192  * @param flash Pointer to MSPI flash device.
193  * @param offset Flash device address.
194  * @param wdata Pointer to the write data buffer.
195  * @param len Number of bytes to write.
196  *
197  * @retval 0 if successful.
198  * @retval -Error flash read fail.
199  */
flash_mspi_emul_write(const struct device * flash,off_t offset,const void * wdata,size_t len)200 static int flash_mspi_emul_write(const struct device *flash, off_t offset,
201 				 const void *wdata, size_t len)
202 {
203 	const struct flash_mspi_emul_device_config *cfg = flash->config;
204 	struct flash_mspi_emul_device_data *data = flash->data;
205 
206 	int ret;
207 	uint8_t *src = (uint8_t *)wdata;
208 	int i;
209 
210 	acquire(flash);
211 
212 	data->xfer.async               = false;
213 	data->xfer.xfer_mode           = MSPI_DMA;
214 	data->xfer.tx_dummy            = data->dev_cfg.tx_dummy;
215 	data->xfer.cmd_length          = data->dev_cfg.cmd_length;
216 	data->xfer.addr_length         = data->dev_cfg.addr_length;
217 	data->xfer.hold_ce             = false;
218 	data->xfer.priority            = 1;
219 	data->xfer.packets             = &data->packet;
220 	data->xfer.num_packet          = 1;
221 	data->xfer.timeout             = CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE;
222 
223 	while (len) {
224 		/* If the offset isn't a multiple of the NOR page size, we first need
225 		 * to write the remaining part that fits, otherwise the write could
226 		 * be wrapped around within the same page
227 		 */
228 		i = MIN(SPI_NOR_PAGE_SIZE - (offset % SPI_NOR_PAGE_SIZE), len);
229 
230 		data->packet.dir               = MSPI_TX;
231 		data->packet.cmd               = data->dev_cfg.write_cmd;
232 		data->packet.address           = offset;
233 		data->packet.data_buf          = src;
234 		data->packet.num_bytes         = i;
235 
236 		LOG_DBG("Write %d bytes to 0x%08zx", i, (ssize_t)offset);
237 
238 		ret = mspi_transceive(data->bus, &cfg->dev_id,
239 				      (const struct mspi_xfer *)&data->xfer);
240 		if (ret) {
241 			LOG_ERR("%u, MSPI write transaction failed with code: %d", __LINE__, ret);
242 			return -EIO;
243 		}
244 
245 		/* emulate flash write busy wait */
246 		k_busy_wait(100);
247 
248 		src += i;
249 		offset += i;
250 		len -= i;
251 	}
252 
253 	release(flash);
254 
255 	return ret;
256 }
257 
258 /**
259  * API implementation of flash read.
260  *
261  * @param flash Pointer to MSPI flash device.
262  * @param offset Flash device address.
263  * @param rdata Pointer to the read data buffer.
264  * @param len Number of bytes to read.
265  *
266  * @retval 0 if successful.
267  * @retval -Error flash read fail.
268  */
flash_mspi_emul_read(const struct device * flash,off_t offset,void * rdata,size_t len)269 static int flash_mspi_emul_read(const struct device *flash, off_t offset,
270 				void *rdata, size_t len)
271 {
272 	const struct flash_mspi_emul_device_config *cfg = flash->config;
273 	struct flash_mspi_emul_device_data *data = flash->data;
274 
275 	int ret;
276 
277 	acquire(flash);
278 
279 	data->packet.dir               = MSPI_RX;
280 	data->packet.cmd               = data->dev_cfg.read_cmd;
281 	data->packet.address           = offset;
282 	data->packet.data_buf          = rdata;
283 	data->packet.num_bytes         = len;
284 
285 	data->xfer.async               = false;
286 	data->xfer.xfer_mode           = MSPI_DMA;
287 	data->xfer.rx_dummy            = data->dev_cfg.rx_dummy;
288 	data->xfer.cmd_length          = data->dev_cfg.cmd_length;
289 	data->xfer.addr_length         = data->dev_cfg.addr_length;
290 	data->xfer.hold_ce             = false;
291 	data->xfer.priority            = 1;
292 	data->xfer.packets             = &data->packet;
293 	data->xfer.num_packet          = 1;
294 	data->xfer.timeout             = CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE;
295 
296 	LOG_DBG("Read %d bytes from 0x%08zx", len, (ssize_t)offset);
297 
298 	ret = mspi_transceive(data->bus, &cfg->dev_id, (const struct mspi_xfer *)&data->xfer);
299 	if (ret) {
300 		LOG_ERR("%u, MSPI read transaction failed with code: %d", __LINE__, ret);
301 		return -EIO;
302 	}
303 
304 	release(flash);
305 
306 	return ret;
307 }
308 
309 /**
310  * API implementation of flash get_parameters.
311  *
312  * @param flash Pointer to MSPI flash device.
313  *
314  * @retval @ref flash_parameters.
315  */
flash_mspi_emul_get_parameters(const struct device * flash)316 static const struct flash_parameters *flash_mspi_emul_get_parameters(const struct device *flash)
317 {
318 	const struct flash_mspi_emul_device_config *cfg = flash->config;
319 
320 	return &cfg->flash_param;
321 }
322 
323 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
324 /**
325  * API implementation of flash pages_layout.
326  *
327  * @param flash Pointer to MSPI flash device.
328  * @param layout @ref flash_pages_layout.
329  * @param layout_size
330  */
flash_mspi_emul_pages_layout(const struct device * flash,const struct flash_pages_layout ** layout,size_t * layout_size)331 static void flash_mspi_emul_pages_layout(const struct device *flash,
332 					 const struct flash_pages_layout **layout,
333 					 size_t *layout_size)
334 {
335 	const struct flash_mspi_emul_device_config *cfg = flash->config;
336 
337 	*layout = &cfg->page_layout;
338 	*layout_size = 1;
339 }
340 #endif /* CONFIG_FLASH_PAGE_LAYOUT */
341 
342 static DEVICE_API(flash, flash_mspi_emul_device_api) = {
343 	.erase = flash_mspi_emul_erase,
344 	.write = flash_mspi_emul_write,
345 	.read = flash_mspi_emul_read,
346 	.get_parameters = flash_mspi_emul_get_parameters,
347 #if defined(CONFIG_FLASH_PAGE_LAYOUT)
348 	.page_layout = flash_mspi_emul_pages_layout,
349 #endif
350 };
351 
352 static const struct emul_mspi_device_api emul_mspi_dev_api = {
353 	.transceive = emul_mspi_device_transceive,
354 };
355 
356 /**
357  * Set up a new MSPI device emulator
358  *
359  * @param emul The MSPI device emulator instance itself
360  * @param bus The MSPI bus emulator instance
361  * @return 0 If successful
362  */
emul_mspi_device_init(const struct emul * emul_flash,const struct device * bus)363 static int emul_mspi_device_init(const struct emul *emul_flash, const struct device *bus)
364 {
365 	const struct flash_mspi_emul_device_config *cfg = emul_flash->dev->config;
366 	struct flash_mspi_emul_device_data *data = emul_flash->dev->data;
367 
368 	data->bus = bus;
369 
370 	if (mspi_dev_config(data->bus, &cfg->dev_id, MSPI_DEVICE_CONFIG_ALL,
371 			    &cfg->tar_dev_cfg)) {
372 		LOG_ERR("%u, Failed to config mspi controller", __LINE__);
373 		return -EIO;
374 	}
375 	data->dev_cfg = cfg->tar_dev_cfg;
376 
377 #if CONFIG_MSPI_XIP
378 	if (cfg->tar_xip_cfg.enable) {
379 		if (mspi_xip_config(data->bus, &cfg->dev_id, &cfg->tar_xip_cfg)) {
380 			LOG_ERR("%u, Failed to enable XIP.", __LINE__);
381 			return -EIO;
382 		}
383 		data->xip_cfg = cfg->tar_xip_cfg;
384 	}
385 #endif
386 
387 #if CONFIG_MSPI_SCRAMBLE
388 	if (cfg->tar_scramble_cfg.enable) {
389 		if (mspi_scramble_config(data->bus, &cfg->dev_id, &cfg->tar_scramble_cfg)) {
390 			LOG_ERR("%u, Failed to enable scrambling.", __LINE__);
391 			return -EIO;
392 		}
393 		data->scramble_cfg = cfg->tar_scramble_cfg;
394 	}
395 #endif
396 
397 #if CONFIG_MSPI_TIMING
398 	if (mspi_timing_config(data->bus, &cfg->dev_id,
399 			       MSPI_TIMING_PARAM_DUMMY, &data->timing_cfg)) {
400 		LOG_ERR("%u, Failed to configure timing.", __LINE__);
401 		return -EIO;
402 	}
403 #endif
404 
405 	release(emul_flash->dev);
406 
407 	return 0;
408 }
409 
flash_mspi_emul_device_init_stub(const struct device * dev)410 static int flash_mspi_emul_device_init_stub(const struct device *dev)
411 {
412 	ARG_UNUSED(dev);
413 	return 0;
414 }
415 
416 #define FLASH_MSPI_EMUL_DEVICE(n)                                                                 \
417 	static uint8_t flash_mspi_emul_device_mem##n[DT_INST_PROP(n, size) / 8];                  \
418 	static const struct flash_mspi_emul_device_config flash_mspi_emul_device_config_##n = {   \
419 		.size                     = DT_INST_PROP(n, size) / 8,                            \
420 		.flash_param =                                                                    \
421 			{                                                                         \
422 				.write_block_size = 1,                                            \
423 				.erase_value      = 0xff,                                         \
424 			},                                                                        \
425 		.page_layout =                                                                    \
426 			{                                                                         \
427 				.pages_count      = DT_INST_PROP(n, size) / 8 / SPI_NOR_PAGE_SIZE,\
428 				.pages_size       = SPI_NOR_PAGE_SIZE,                            \
429 			},                                                                        \
430 		.dev_id                   = MSPI_DEVICE_ID_DT_INST(n),                            \
431 		.tar_dev_cfg              = MSPI_DEVICE_CONFIG_DT_INST(n),                        \
432 		.tar_xip_cfg              = MSPI_XIP_CONFIG_DT_INST(n),                           \
433 		.tar_scramble_cfg         = MSPI_SCRAMBLE_CONFIG_DT_INST(n),                      \
434 		.sw_multi_periph          = DT_PROP(DT_INST_BUS(n), software_multiperipheral)     \
435 	};                                                                                        \
436 	static struct flash_mspi_emul_device_data flash_mspi_emul_device_data_##n = {             \
437 		.lock = Z_SEM_INITIALIZER(flash_mspi_emul_device_data_##n.lock, 0, 1),            \
438 		.mem  = (uint8_t *)flash_mspi_emul_device_mem##n,                                 \
439 	};                                                                                        \
440 	DEVICE_DT_INST_DEFINE(n,                                                                  \
441 			      flash_mspi_emul_device_init_stub,                                   \
442 			      NULL,                                                               \
443 			      &flash_mspi_emul_device_data_##n,                                   \
444 			      &flash_mspi_emul_device_config_##n,                                 \
445 			      POST_KERNEL,                                                        \
446 			      CONFIG_FLASH_INIT_PRIORITY,                                         \
447 			      &flash_mspi_emul_device_api);
448 
449 #define EMUL_TEST(n)                                                                              \
450 	EMUL_DT_INST_DEFINE(n,                                                                    \
451 			    emul_mspi_device_init,                                                \
452 			    NULL,                                                                 \
453 			    NULL,                                                                 \
454 			    &emul_mspi_dev_api,                                                   \
455 			    NULL);
456 
457 DT_INST_FOREACH_STATUS_OKAY(EMUL_TEST);
458 
459 DT_INST_FOREACH_STATUS_OKAY(FLASH_MSPI_EMUL_DEVICE);
460