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