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