1 /*
2  * Copyright (c) 2023 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/devicetree.h>
8 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(spi_rtio_loopback);
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/printk.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <zephyr/ztest.h>
17 
18 #include <zephyr/rtio/rtio.h>
19 #include <zephyr/drivers/spi.h>
20 
21 #define SPI_FAST_DEV	DT_COMPAT_GET_ANY_STATUS_OKAY(test_spi_loopback_fast)
22 #define SPI_SLOW_DEV	DT_COMPAT_GET_ANY_STATUS_OKAY(test_spi_loopback_slow)
23 
24 #if CONFIG_SPI_LOOPBACK_MODE_LOOP
25 #define MODE_LOOP SPI_MODE_LOOP
26 #else
27 #define MODE_LOOP 0
28 #endif
29 
30 #define SPI_OP SPI_OP_MODE_MASTER | SPI_MODE_CPOL | MODE_LOOP | \
31 	       SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE
32 
33 static SPI_DT_IODEV_DEFINE(spi_fast, SPI_FAST_DEV, SPI_OP, 0);
34 static SPI_DT_IODEV_DEFINE(spi_slow, SPI_FAST_DEV, SPI_OP, 0);
35 
36 RTIO_DEFINE(r, 8, 8);
37 
38 /* to run this test, connect MOSI pin to the MISO of the SPI */
39 
40 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
41 #define BUF_SIZE 17
42 #define BUF2_SIZE 36
43 
44 #if CONFIG_NOCACHE_MEMORY
45 static const char tx_data[BUF_SIZE] = "0123456789abcdef\0";
46 static __aligned(32) char buffer_tx[BUF_SIZE] __used __attribute__((__section__(".nocache")));
47 static __aligned(32) char buffer_rx[BUF_SIZE] __used __attribute__((__section__(".nocache")));
48 static const char tx2_data[BUF2_SIZE] = "Thequickbrownfoxjumpsoverthelazydog\0";
49 static __aligned(32) char buffer2_tx[BUF2_SIZE] __used __attribute__((__section__(".nocache")));
50 static __aligned(32) char buffer2_rx[BUF2_SIZE] __used __attribute__((__section__(".nocache")));
51 #else
52 /* this src memory shall be in RAM to support using as a DMA source pointer.*/
53 static uint8_t buffer_tx[] = "0123456789abcdef\0";
54 static uint8_t buffer_rx[BUF_SIZE] = {};
55 
56 static uint8_t buffer2_tx[] = "Thequickbrownfoxjumpsoverthelazydog\0";
57 static uint8_t buffer2_rx[BUF2_SIZE] = {};
58 #endif
59 
60 /*
61  * We need 5x(buffer size) + 1 to print a comma-separated list of each
62  * byte in hex, plus a null.
63  */
64 static uint8_t buffer_print_tx[BUF_SIZE * 5 + 1];
65 static uint8_t buffer_print_rx[BUF_SIZE * 5 + 1];
66 
67 static uint8_t buffer_print_tx2[BUF2_SIZE * 5 + 1];
68 static uint8_t buffer_print_rx2[BUF2_SIZE * 5 + 1];
69 
70 
71 
to_display_format(const uint8_t * src,size_t size,char * dst)72 static void to_display_format(const uint8_t *src, size_t size, char *dst)
73 {
74 	size_t i;
75 
76 	for (i = 0; i < size; i++) {
77 		sprintf(dst + 5 * i, "0x%02x,", src[i]);
78 	}
79 }
80 
81 /* test transferring different buffers on the same dma channels */
spi_complete_multiple(struct rtio_iodev * spi_iodev)82 static int spi_complete_multiple(struct rtio_iodev *spi_iodev)
83 {
84 	struct rtio_sqe *sqe;
85 	struct rtio_cqe *cqe;
86 	int ret;
87 
88 	sqe = rtio_sqe_acquire(&r);
89 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
90 				buffer_tx, buffer_rx, BUF_SIZE, NULL);
91 	sqe->flags |= RTIO_SQE_TRANSACTION;
92 	sqe = rtio_sqe_acquire(&r);
93 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
94 				 buffer2_tx, buffer2_rx, BUF2_SIZE, NULL);
95 
96 	LOG_INF("Start complete multiple");
97 	rtio_submit(&r, 1);
98 	cqe = rtio_cqe_consume(&r);
99 	ret = cqe->result;
100 	rtio_cqe_release(&r, cqe);
101 
102 	if (ret) {
103 		LOG_ERR("Code %d", ret);
104 		zassert_false(ret, "SPI transceive failed");
105 		return ret;
106 	}
107 
108 	if (memcmp(buffer_tx, buffer_rx, BUF_SIZE)) {
109 		to_display_format(buffer_tx, BUF_SIZE, buffer_print_tx);
110 		to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx);
111 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
112 		LOG_ERR("                           vs: %s", buffer_print_rx);
113 		zassert_false(1, "Buffer contents are different");
114 		return -1;
115 	}
116 
117 	if (memcmp(buffer2_tx, buffer2_rx, BUF2_SIZE)) {
118 		to_display_format(buffer2_tx, BUF2_SIZE, buffer_print_tx2);
119 		to_display_format(buffer2_rx, BUF2_SIZE, buffer_print_rx2);
120 		LOG_ERR("Buffer 2 contents are different: %s", buffer_print_tx2);
121 		LOG_ERR("                             vs: %s", buffer_print_rx2);
122 		zassert_false(1, "Buffer 2 contents are different");
123 		return -1;
124 	}
125 
126 	LOG_INF("Passed");
127 
128 	return 0;
129 }
130 
spi_complete_loop(struct rtio_iodev * spi_iodev)131 static int spi_complete_loop(struct rtio_iodev *spi_iodev)
132 {
133 	struct rtio_sqe *sqe;
134 	struct rtio_cqe *cqe;
135 	int ret;
136 
137 	sqe = rtio_sqe_acquire(&r);
138 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
139 				 buffer_tx,  buffer_rx, BUF_SIZE, NULL);
140 
141 	LOG_INF("Start complete loop");
142 
143 	rtio_submit(&r, 1);
144 	cqe = rtio_cqe_consume(&r);
145 	ret = cqe->result;
146 	rtio_cqe_release(&r, cqe);
147 
148 	if (ret) {
149 		LOG_ERR("Code %d", ret);
150 		zassert_false(ret, "SPI transceive failed");
151 		return ret;
152 	}
153 
154 	if (memcmp(buffer_tx, buffer_rx, BUF_SIZE)) {
155 		to_display_format(buffer_tx, BUF_SIZE, buffer_print_tx);
156 		to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx);
157 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
158 		LOG_ERR("                           vs: %s", buffer_print_rx);
159 		zassert_false(1, "Buffer contents are different");
160 		return -1;
161 	}
162 
163 	LOG_INF("Passed");
164 
165 	return 0;
166 }
167 
spi_null_tx_buf(struct rtio_iodev * spi_iodev)168 static int spi_null_tx_buf(struct rtio_iodev *spi_iodev)
169 {
170 	static const uint8_t EXPECTED_NOP_RETURN_BUF[BUF_SIZE] = { 0 };
171 
172 	struct rtio_sqe *sqe;
173 	struct rtio_cqe *cqe;
174 	int ret;
175 
176 	(void)memset(buffer_rx, 0x77, BUF_SIZE);
177 
178 	sqe = rtio_sqe_acquire(&r);
179 	rtio_sqe_prep_read(sqe, spi_iodev, RTIO_PRIO_NORM,
180 				 buffer_rx, BUF_SIZE,
181 				 NULL);
182 
183 	LOG_INF("Start null tx");
184 
185 	rtio_submit(&r, 1);
186 	cqe = rtio_cqe_consume(&r);
187 	ret = cqe->result;
188 	rtio_cqe_release(&r, cqe);
189 
190 	if (ret) {
191 		LOG_ERR("Code %d", ret);
192 		zassert_false(ret, "SPI transceive failed");
193 		return ret;
194 	}
195 
196 
197 	if (memcmp(buffer_rx, EXPECTED_NOP_RETURN_BUF, BUF_SIZE)) {
198 		to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx);
199 		LOG_ERR("Rx Buffer should contain NOP frames but got: %s",
200 			buffer_print_rx);
201 		zassert_false(1, "Buffer not as expected");
202 		return -1;
203 	}
204 
205 	LOG_INF("Passed");
206 
207 	return 0;
208 }
209 
spi_rx_half_start(struct rtio_iodev * spi_iodev)210 static int spi_rx_half_start(struct rtio_iodev *spi_iodev)
211 {
212 	struct rtio_sqe *sqe;
213 	struct rtio_cqe *cqe;
214 	int ret;
215 
216 	sqe = rtio_sqe_acquire(&r);
217 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
218 				 buffer_tx, buffer_rx, 8, NULL);
219 	sqe->flags |= RTIO_SQE_TRANSACTION;
220 	sqe = rtio_sqe_acquire(&r);
221 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
222 				 &buffer_tx[8], BUF_SIZE-8, NULL);
223 
224 
225 	LOG_INF("Start half start");
226 
227 	(void)memset(buffer_rx, 0, BUF_SIZE);
228 
229 	rtio_submit(&r, 1);
230 	cqe = rtio_cqe_consume(&r);
231 	ret = cqe->result;
232 	rtio_cqe_release(&r, cqe);
233 
234 	if (ret) {
235 		LOG_ERR("Code %d", ret);
236 		zassert_false(ret, "SPI transceive failed");
237 		return -1;
238 	}
239 
240 	if (memcmp(buffer_tx, buffer_rx, 8)) {
241 		to_display_format(buffer_tx, 8, buffer_print_tx);
242 		to_display_format(buffer_rx, 8, buffer_print_rx);
243 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
244 		LOG_ERR("                           vs: %s", buffer_print_rx);
245 		zassert_false(1, "Buffer contents are different");
246 		return -1;
247 	}
248 
249 	LOG_INF("Passed");
250 
251 	return 0;
252 }
253 
spi_rx_half_end(struct rtio_iodev * spi_iodev)254 static int spi_rx_half_end(struct rtio_iodev *spi_iodev)
255 {
256 	struct rtio_sqe *sqe;
257 	struct rtio_cqe *cqe;
258 	int ret;
259 
260 	if (IS_ENABLED(CONFIG_SPI_STM32_DMA)) {
261 		LOG_INF("Skip half end");
262 		return 0;
263 	}
264 
265 	sqe = rtio_sqe_acquire(&r);
266 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
267 				 buffer_tx, 8, NULL);
268 	sqe->flags |= RTIO_SQE_TRANSACTION;
269 
270 	sqe = rtio_sqe_acquire(&r);
271 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
272 				 &buffer_tx[8],
273 				 buffer_rx, 8,
274 				 NULL);
275 
276 	sqe = rtio_sqe_acquire(&r);
277 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
278 		&buffer_tx[16], BUF_SIZE-16, NULL);
279 
280 	LOG_INF("Start half end");
281 
282 	(void)memset(buffer_rx, 0, BUF_SIZE);
283 
284 	rtio_submit(&r, 1);
285 	cqe = rtio_cqe_consume(&r);
286 	ret = cqe->result;
287 	rtio_cqe_release(&r, cqe);
288 
289 	if (ret) {
290 		LOG_ERR("Code %d", ret);
291 		zassert_false(ret, "SPI transceive failed");
292 		return -1;
293 	}
294 
295 	if (memcmp(buffer_tx + 8, buffer_rx, 8)) {
296 		to_display_format(buffer_tx + 8, 8, buffer_print_tx);
297 		to_display_format(buffer_rx, 8, buffer_print_rx);
298 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
299 		LOG_ERR("                           vs: %s", buffer_print_rx);
300 		zassert_false(1, "Buffer contents are different");
301 		return -1;
302 	}
303 
304 	LOG_INF("Passed");
305 
306 	return 0;
307 }
308 
spi_rx_every_4(struct rtio_iodev * spi_iodev)309 static int spi_rx_every_4(struct rtio_iodev *spi_iodev)
310 {
311 	struct rtio_sqe *sqe;
312 	struct rtio_cqe *cqe;
313 	int ret;
314 
315 	if (IS_ENABLED(CONFIG_SPI_STM32_DMA)) {
316 		LOG_INF("Skip every 4");
317 		return 0;
318 	}
319 
320 	if (IS_ENABLED(CONFIG_DSPI_MCUX_EDMA)) {
321 		LOG_INF("Skip every 4");
322 		return 0;
323 	}
324 
325 	sqe = rtio_sqe_acquire(&r);
326 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
327 				 buffer_tx, 4,
328 				 NULL);
329 	sqe->flags |= RTIO_SQE_TRANSACTION;
330 	sqe = rtio_sqe_acquire(&r);
331 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
332 				 &buffer_tx[4], buffer_rx, 4, NULL);
333 	sqe->flags |= RTIO_SQE_TRANSACTION;
334 	sqe = rtio_sqe_acquire(&r);
335 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
336 				 &buffer_tx[8], (BUF_SIZE - 8),
337 				 NULL);
338 	sqe->flags |= RTIO_SQE_TRANSACTION;
339 	sqe = rtio_sqe_acquire(&r);
340 	rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM,
341 				 &buffer_tx[12], &buffer_rx[4], 4, NULL);
342 	sqe->flags |= RTIO_SQE_TRANSACTION;
343 	sqe = rtio_sqe_acquire(&r);
344 	rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM,
345 				 &buffer_tx[16], BUF_SIZE-16, NULL);
346 
347 	LOG_INF("Start every 4");
348 
349 	(void)memset(buffer_rx, 0, BUF_SIZE);
350 
351 	rtio_submit(&r, 1);
352 	cqe = rtio_cqe_consume(&r);
353 	ret = cqe->result;
354 	rtio_cqe_release(&r, cqe);
355 
356 	if (ret) {
357 		LOG_ERR("Code %d", ret);
358 		zassert_false(ret, "SPI transceive failed");
359 		return -1;
360 	}
361 
362 	if (memcmp(buffer_tx + 4, buffer_rx, 4)) {
363 		to_display_format(buffer_tx + 4, 4, buffer_print_tx);
364 		to_display_format(buffer_rx, 4, buffer_print_rx);
365 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
366 		LOG_ERR("                           vs: %s", buffer_print_rx);
367 		zassert_false(1, "Buffer contents are different");
368 		return -1;
369 	} else if (memcmp(buffer_tx + 12, buffer_rx + 4, 4)) {
370 		to_display_format(buffer_tx + 12, 4, buffer_print_tx);
371 		to_display_format(buffer_rx + 4, 4, buffer_print_rx);
372 		LOG_ERR("Buffer contents are different: %s", buffer_print_tx);
373 		LOG_ERR("                           vs: %s", buffer_print_rx);
374 		zassert_false(1, "Buffer contents are different");
375 		return -1;
376 	}
377 
378 	LOG_INF("Passed");
379 
380 	return 0;
381 }
382 
383 
ZTEST(spi_loopback_rtio,test_spi_loopback_rtio)384 ZTEST(spi_loopback_rtio, test_spi_loopback_rtio)
385 {
386 
387 	LOG_INF("SPI test on buffers TX/RX %p/%p", buffer_tx, buffer_rx);
388 
389 	zassert_true(spi_is_ready_iodev(&spi_slow), "Slow spi lookback device is not ready");
390 
391 	LOG_INF("SPI test slow config");
392 
393 	if (spi_complete_multiple(&spi_slow) ||
394 	    spi_complete_loop(&spi_slow) ||
395 	    spi_null_tx_buf(&spi_slow) ||
396 	    spi_rx_half_start(&spi_slow) ||
397 	    spi_rx_half_end(&spi_slow) ||
398 	    spi_rx_every_4(&spi_slow)
399 	    ) {
400 		goto end;
401 	}
402 
403 	zassert_true(spi_is_ready_iodev(&spi_fast), "Fast spi lookback device is not ready");
404 
405 	LOG_INF("SPI test fast config");
406 
407 	if (spi_complete_multiple(&spi_fast) ||
408 	    spi_complete_loop(&spi_fast) ||
409 	    spi_null_tx_buf(&spi_fast) ||
410 	    spi_rx_half_start(&spi_fast) ||
411 	    spi_rx_half_end(&spi_fast) ||
412 	    spi_rx_every_4(&spi_fast)
413 	    ) {
414 		goto end;
415 	}
416 
417 	LOG_INF("All tx/rx passed");
418 end:
419 }
420 
spi_loopback_setup(void)421 static void *spi_loopback_setup(void)
422 {
423 #if CONFIG_NOCACHE_MEMORY
424 	memset(buffer_tx, 0, sizeof(buffer_tx));
425 	memcpy(buffer_tx, tx_data, sizeof(tx_data));
426 	memset(buffer2_tx, 0, sizeof(buffer2_tx));
427 	memcpy(buffer2_tx, tx2_data, sizeof(tx2_data));
428 #endif
429 	return NULL;
430 }
431 
432 ZTEST_SUITE(spi_loopback_rtio, NULL, spi_loopback_setup, NULL, NULL, NULL);
433