/* * Copyright (c) 2023 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL #include LOG_MODULE_REGISTER(spi_rtio_loopback); #include #include #include #include #include #include #include #define SPI_FAST_DEV DT_COMPAT_GET_ANY_STATUS_OKAY(test_spi_loopback_fast) #define SPI_SLOW_DEV DT_COMPAT_GET_ANY_STATUS_OKAY(test_spi_loopback_slow) #if CONFIG_SPI_LOOPBACK_MODE_LOOP #define MODE_LOOP SPI_MODE_LOOP #else #define MODE_LOOP 0 #endif #define SPI_OP SPI_OP_MODE_MASTER | SPI_MODE_CPOL | MODE_LOOP | \ SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE static SPI_DT_IODEV_DEFINE(spi_fast, SPI_FAST_DEV, SPI_OP, 0); static SPI_DT_IODEV_DEFINE(spi_slow, SPI_FAST_DEV, SPI_OP, 0); RTIO_DEFINE(r, 8, 8); /* to run this test, connect MOSI pin to the MISO of the SPI */ #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) #define BUF_SIZE 17 #define BUF2_SIZE 36 #if CONFIG_NOCACHE_MEMORY static const char tx_data[BUF_SIZE] = "0123456789abcdef\0"; static __aligned(32) char buffer_tx[BUF_SIZE] __used __attribute__((__section__(".nocache"))); static __aligned(32) char buffer_rx[BUF_SIZE] __used __attribute__((__section__(".nocache"))); static const char tx2_data[BUF2_SIZE] = "Thequickbrownfoxjumpsoverthelazydog\0"; static __aligned(32) char buffer2_tx[BUF2_SIZE] __used __attribute__((__section__(".nocache"))); static __aligned(32) char buffer2_rx[BUF2_SIZE] __used __attribute__((__section__(".nocache"))); #else /* this src memory shall be in RAM to support using as a DMA source pointer.*/ static uint8_t buffer_tx[] = "0123456789abcdef\0"; static uint8_t buffer_rx[BUF_SIZE] = {}; static uint8_t buffer2_tx[] = "Thequickbrownfoxjumpsoverthelazydog\0"; static uint8_t buffer2_rx[BUF2_SIZE] = {}; #endif /* * We need 5x(buffer size) + 1 to print a comma-separated list of each * byte in hex, plus a null. */ static uint8_t buffer_print_tx[BUF_SIZE * 5 + 1]; static uint8_t buffer_print_rx[BUF_SIZE * 5 + 1]; static uint8_t buffer_print_tx2[BUF2_SIZE * 5 + 1]; static uint8_t buffer_print_rx2[BUF2_SIZE * 5 + 1]; static void to_display_format(const uint8_t *src, size_t size, char *dst) { size_t i; for (i = 0; i < size; i++) { sprintf(dst + 5 * i, "0x%02x,", src[i]); } } /* test transferring different buffers on the same dma channels */ static int spi_complete_multiple(struct rtio_iodev *spi_iodev) { struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_tx, buffer_rx, BUF_SIZE, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, buffer2_tx, buffer2_rx, BUF2_SIZE, NULL); LOG_INF("Start complete multiple"); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return ret; } if (memcmp(buffer_tx, buffer_rx, BUF_SIZE)) { to_display_format(buffer_tx, BUF_SIZE, buffer_print_tx); to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } if (memcmp(buffer2_tx, buffer2_rx, BUF2_SIZE)) { to_display_format(buffer2_tx, BUF2_SIZE, buffer_print_tx2); to_display_format(buffer2_rx, BUF2_SIZE, buffer_print_rx2); LOG_ERR("Buffer 2 contents are different: %s", buffer_print_tx2); LOG_ERR(" vs: %s", buffer_print_rx2); zassert_false(1, "Buffer 2 contents are different"); return -1; } LOG_INF("Passed"); return 0; } static int spi_complete_loop(struct rtio_iodev *spi_iodev) { struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_tx, buffer_rx, BUF_SIZE, NULL); LOG_INF("Start complete loop"); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return ret; } if (memcmp(buffer_tx, buffer_rx, BUF_SIZE)) { to_display_format(buffer_tx, BUF_SIZE, buffer_print_tx); to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } LOG_INF("Passed"); return 0; } static int spi_null_tx_buf(struct rtio_iodev *spi_iodev) { static const uint8_t EXPECTED_NOP_RETURN_BUF[BUF_SIZE] = { 0 }; struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; (void)memset(buffer_rx, 0x77, BUF_SIZE); sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_read(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_rx, BUF_SIZE, NULL); LOG_INF("Start null tx"); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return ret; } if (memcmp(buffer_rx, EXPECTED_NOP_RETURN_BUF, BUF_SIZE)) { to_display_format(buffer_rx, BUF_SIZE, buffer_print_rx); LOG_ERR("Rx Buffer should contain NOP frames but got: %s", buffer_print_rx); zassert_false(1, "Buffer not as expected"); return -1; } LOG_INF("Passed"); return 0; } static int spi_rx_half_start(struct rtio_iodev *spi_iodev) { struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_tx, buffer_rx, 8, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[8], BUF_SIZE-8, NULL); LOG_INF("Start half start"); (void)memset(buffer_rx, 0, BUF_SIZE); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return -1; } if (memcmp(buffer_tx, buffer_rx, 8)) { to_display_format(buffer_tx, 8, buffer_print_tx); to_display_format(buffer_rx, 8, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } LOG_INF("Passed"); return 0; } static int spi_rx_half_end(struct rtio_iodev *spi_iodev) { struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; if (IS_ENABLED(CONFIG_SPI_STM32_DMA)) { LOG_INF("Skip half end"); return 0; } sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_tx, 8, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[8], buffer_rx, 8, NULL); sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[16], BUF_SIZE-16, NULL); LOG_INF("Start half end"); (void)memset(buffer_rx, 0, BUF_SIZE); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return -1; } if (memcmp(buffer_tx + 8, buffer_rx, 8)) { to_display_format(buffer_tx + 8, 8, buffer_print_tx); to_display_format(buffer_rx, 8, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } LOG_INF("Passed"); return 0; } static int spi_rx_every_4(struct rtio_iodev *spi_iodev) { struct rtio_sqe *sqe; struct rtio_cqe *cqe; int ret; if (IS_ENABLED(CONFIG_SPI_STM32_DMA)) { LOG_INF("Skip every 4"); return 0; } if (IS_ENABLED(CONFIG_DSPI_MCUX_EDMA)) { LOG_INF("Skip every 4"); return 0; } sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, buffer_tx, 4, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[4], buffer_rx, 4, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[8], (BUF_SIZE - 8), NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_transceive(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[12], &buffer_rx[4], 4, NULL); sqe->flags |= RTIO_SQE_TRANSACTION; sqe = rtio_sqe_acquire(&r); rtio_sqe_prep_write(sqe, spi_iodev, RTIO_PRIO_NORM, &buffer_tx[16], BUF_SIZE-16, NULL); LOG_INF("Start every 4"); (void)memset(buffer_rx, 0, BUF_SIZE); rtio_submit(&r, 1); cqe = rtio_cqe_consume(&r); ret = cqe->result; rtio_cqe_release(&r, cqe); if (ret) { LOG_ERR("Code %d", ret); zassert_false(ret, "SPI transceive failed"); return -1; } if (memcmp(buffer_tx + 4, buffer_rx, 4)) { to_display_format(buffer_tx + 4, 4, buffer_print_tx); to_display_format(buffer_rx, 4, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } else if (memcmp(buffer_tx + 12, buffer_rx + 4, 4)) { to_display_format(buffer_tx + 12, 4, buffer_print_tx); to_display_format(buffer_rx + 4, 4, buffer_print_rx); LOG_ERR("Buffer contents are different: %s", buffer_print_tx); LOG_ERR(" vs: %s", buffer_print_rx); zassert_false(1, "Buffer contents are different"); return -1; } LOG_INF("Passed"); return 0; } ZTEST(spi_loopback_rtio, test_spi_loopback_rtio) { LOG_INF("SPI test on buffers TX/RX %p/%p", buffer_tx, buffer_rx); zassert_true(spi_is_ready_iodev(&spi_slow), "Slow spi lookback device is not ready"); LOG_INF("SPI test slow config"); if (spi_complete_multiple(&spi_slow) || spi_complete_loop(&spi_slow) || spi_null_tx_buf(&spi_slow) || spi_rx_half_start(&spi_slow) || spi_rx_half_end(&spi_slow) || spi_rx_every_4(&spi_slow) ) { goto end; } zassert_true(spi_is_ready_iodev(&spi_fast), "Fast spi lookback device is not ready"); LOG_INF("SPI test fast config"); if (spi_complete_multiple(&spi_fast) || spi_complete_loop(&spi_fast) || spi_null_tx_buf(&spi_fast) || spi_rx_half_start(&spi_fast) || spi_rx_half_end(&spi_fast) || spi_rx_every_4(&spi_fast) ) { goto end; } LOG_INF("All tx/rx passed"); end: } static void *spi_loopback_setup(void) { #if CONFIG_NOCACHE_MEMORY memset(buffer_tx, 0, sizeof(buffer_tx)); memcpy(buffer_tx, tx_data, sizeof(tx_data)); memset(buffer2_tx, 0, sizeof(buffer2_tx)); memcpy(buffer2_tx, tx2_data, sizeof(tx2_data)); #endif return NULL; } ZTEST_SUITE(spi_loopback_rtio, NULL, spi_loopback_setup, NULL, NULL, NULL);