/* dma.c - DMA test source file */ /* * Copyright (c) 2016 Intel Corporation. * Copyright (c) 2021 Linaro Limited. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Verify zephyr dma memory to memory transfer loops * @details * - Test Steps * -# Set dma channel configuration including source/dest addr, burstlen * -# Set direction memory-to-memory * -# Start transfer * -# Move to next dest addr * -# Back to first step * - Expected Results * -# Data is transferred correctly from src to dest, for each loop */ #include #include #include #include #include /* in millisecond */ #define SLEEPTIME 250 #define TRANSFER_LOOPS (4) static __aligned(32) uint8_t tx_data[CONFIG_DMA_LOOP_TRANSFER_SIZE]; static __aligned(32) uint8_t rx_data[TRANSFER_LOOPS][CONFIG_DMA_LOOP_TRANSFER_SIZE] = { { 0 } }; volatile uint32_t transfer_count; volatile uint32_t done; static struct dma_config dma_cfg = {0}; static struct dma_block_config dma_block_cfg = {0}; static int test_case_id; static void test_transfer(const struct device *dev, uint32_t id) { transfer_count++; if (transfer_count < TRANSFER_LOOPS) { dma_block_cfg.block_size = sizeof(tx_data); #ifdef CONFIG_DMA_64BIT dma_block_cfg.source_address = (uint64_t)tx_data; dma_block_cfg.dest_address = (uint64_t)rx_data[transfer_count]; #else dma_block_cfg.source_address = (uint32_t)tx_data; dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count]; #endif zassert_ok(dma_config(dev, id, &dma_cfg), "Not able to config transfer %d", transfer_count + 1); zassert_ok(dma_start(dev, id), "Not able to start next transfer %d", transfer_count + 1); } } static void dma_user_callback(const struct device *dma_dev, void *arg, uint32_t id, int status) { /* test case is done so ignore the interrupt */ if (done) { return; } zassert_false(status < 0, "DMA could not proceed, an error occurred\n"); #ifdef CONFIG_DMAMUX_STM32 /* the channel is the DMAMUX's one * the device is the DMAMUX, given through * the stream->user_data by the dma_stm32_irq_handler */ test_transfer((const struct device *)arg, id); #else test_transfer(dma_dev, id); #endif /* CONFIG_DMAMUX_STM32 */ } static int test_loop(const struct device *dma) { static int chan_id; test_case_id = 0; TC_PRINT("DMA memory to memory transfer started\n"); memset(tx_data, 0, sizeof(tx_data)); for (int i = 0; i < CONFIG_DMA_LOOP_TRANSFER_SIZE; i++) { tx_data[i] = i; } memset(rx_data, 0, sizeof(rx_data)); if (!device_is_ready(dma)) { TC_PRINT("dma controller device is not ready\n"); return TC_FAIL; } TC_PRINT("Preparing DMA Controller: %s\n", dma->name); dma_cfg.channel_direction = MEMORY_TO_MEMORY; dma_cfg.source_data_size = 1U; dma_cfg.dest_data_size = 1U; dma_cfg.source_burst_length = 1U; dma_cfg.dest_burst_length = 1U; #ifdef CONFIG_DMAMUX_STM32 dma_cfg.user_data = (void *)dma; #else dma_cfg.user_data = NULL; #endif /* CONFIG_DMAMUX_STM32 */ dma_cfg.dma_callback = dma_user_callback; dma_cfg.block_count = 1U; dma_cfg.head_block = &dma_block_cfg; #ifdef CONFIG_DMA_MCUX_TEST_SLOT_START dma_cfg.dma_slot = CONFIG_DMA_MCUX_TEST_SLOT_START; #endif chan_id = dma_request_channel(dma, NULL); if (chan_id < 0) { TC_PRINT("this platform do not support the dma channel\n"); chan_id = CONFIG_DMA_LOOP_TRANSFER_CHANNEL_NR; } transfer_count = 0; done = 0; TC_PRINT("Starting the transfer on channel %d and waiting for 1 second\n", chan_id); dma_block_cfg.block_size = sizeof(tx_data); #ifdef CONFIG_DMA_64BIT dma_block_cfg.source_address = (uint64_t)tx_data; dma_block_cfg.dest_address = (uint64_t)rx_data[transfer_count]; #else dma_block_cfg.source_address = (uint32_t)tx_data; dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count]; #endif if (dma_config(dma, chan_id, &dma_cfg)) { TC_PRINT("ERROR: transfer config (%d)\n", chan_id); return TC_FAIL; } if (dma_start(dma, chan_id)) { TC_PRINT("ERROR: transfer start (%d)\n", chan_id); return TC_FAIL; } k_sleep(K_MSEC(SLEEPTIME)); if (transfer_count < TRANSFER_LOOPS) { transfer_count = TRANSFER_LOOPS; TC_PRINT("ERROR: unfinished transfer\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } TC_PRINT("Each RX buffer should contain the full TX buffer string.\n"); for (int i = 0; i < TRANSFER_LOOPS; i++) { TC_PRINT("RX data Loop %d\n", i); if (memcmp(tx_data, rx_data[i], CONFIG_DMA_LOOP_TRANSFER_SIZE)) { return TC_FAIL; } } TC_PRINT("Finished DMA: %s\n", dma->name); return TC_PASS; } static int test_loop_suspend_resume(const struct device *dma) { static int chan_id; int res = 0; test_case_id = 1; TC_PRINT("DMA memory to memory transfer started\n"); memset(tx_data, 0, sizeof(tx_data)); for (int i = 0; i < CONFIG_DMA_LOOP_TRANSFER_SIZE; i++) { tx_data[i] = i; } memset(rx_data, 0, sizeof(rx_data)); if (!device_is_ready(dma)) { TC_PRINT("dma controller device is not ready\n"); return TC_FAIL; } TC_PRINT("Preparing DMA Controller: %s\n", dma->name); dma_cfg.channel_direction = MEMORY_TO_MEMORY; dma_cfg.source_data_size = 1U; dma_cfg.dest_data_size = 1U; dma_cfg.source_burst_length = 1U; dma_cfg.dest_burst_length = 1U; #ifdef CONFIG_DMAMUX_STM32 dma_cfg.user_data = (struct device *)dma; #else dma_cfg.user_data = NULL; #endif /* CONFIG_DMAMUX_STM32 */ dma_cfg.dma_callback = dma_user_callback; dma_cfg.block_count = 1U; dma_cfg.head_block = &dma_block_cfg; #ifdef CONFIG_DMA_MCUX_TEST_SLOT_START dma_cfg.dma_slot = CONFIG_DMA_MCUX_TEST_SLOT_START; #endif chan_id = dma_request_channel(dma, NULL); if (chan_id < 0) { TC_PRINT("this platform do not support the dma channel\n"); chan_id = CONFIG_DMA_LOOP_TRANSFER_CHANNEL_NR; } transfer_count = 0; done = 0; TC_PRINT("Starting the transfer on channel %d and waiting for 1 second\n", chan_id); dma_block_cfg.block_size = sizeof(tx_data); #ifdef CONFIG_DMA_64BIT dma_block_cfg.source_address = (uint64_t)tx_data; dma_block_cfg.dest_address = (uint64_t)rx_data[transfer_count]; #else dma_block_cfg.source_address = (uint32_t)tx_data; dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count]; #endif unsigned int irq_key; if (dma_config(dma, chan_id, &dma_cfg)) { TC_PRINT("ERROR: transfer config (%d)\n", chan_id); return TC_FAIL; } if (dma_start(dma, chan_id)) { TC_PRINT("ERROR: transfer start (%d)\n", chan_id); return TC_FAIL; } /* Try multiple times to suspend the transfers */ uint32_t tc = transfer_count; do { irq_key = irq_lock(); res = dma_suspend(dma, chan_id); if (res == -ENOSYS) { done = 1; TC_PRINT("suspend not supported\n"); dma_stop(dma, chan_id); ztest_test_skip(); return TC_SKIP; } tc = transfer_count; irq_unlock(irq_key); k_busy_wait(100); } while (tc != transfer_count); /* If we failed to suspend we failed */ if (transfer_count == TRANSFER_LOOPS) { TC_PRINT("ERROR: failed to suspend transfers\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } TC_PRINT("suspended after %d transfers occurred\n", transfer_count); /* Now sleep */ k_sleep(K_MSEC(SLEEPTIME)); /* If we failed to suspend we failed */ if (transfer_count == TRANSFER_LOOPS) { TC_PRINT("ERROR: failed to suspend transfers\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } TC_PRINT("resuming after %d transfers occurred\n", transfer_count); res = dma_resume(dma, chan_id); TC_PRINT("Resumed transfers\n"); if (res != 0) { TC_PRINT("ERROR: resume failed, channel %d, result %d", chan_id, res); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } k_sleep(K_MSEC(SLEEPTIME)); TC_PRINT("Transfer count %d\n", transfer_count); if (transfer_count < TRANSFER_LOOPS) { transfer_count = TRANSFER_LOOPS; TC_PRINT("ERROR: unfinished transfer\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } TC_PRINT("Each RX buffer should contain the full TX buffer string.\n"); for (int i = 0; i < TRANSFER_LOOPS; i++) { TC_PRINT("RX data Loop %d\n", i); if (memcmp(tx_data, rx_data[i], CONFIG_DMA_LOOP_TRANSFER_SIZE)) { return TC_FAIL; } } TC_PRINT("Finished DMA: %s\n", dma->name); return TC_PASS; } /** * @brief Check if the device is in valid power state. * * @param dev Device instance. * @param expected Device expected power state. * * @retval true If device is in correct power state. * @retval false If device is not in correct power state. */ static bool check_dev_power_state(const struct device *dev, enum pm_device_state expected) { #if CONFIG_PM_DEVICE_RUNTIME enum pm_device_state state; if (pm_device_state_get(dev, &state) == 0) { if (expected != state) { TC_PRINT("ERROR: device %s is incorrect power state" " (current state = %s, expected = %s)\n", dev->name, pm_device_state_str(state), pm_device_state_str(expected)); return false; } return true; } TC_PRINT("ERROR: unable to get power state of %s", dev->name); return false; #else return true; #endif /* CONFIG_PM_DEVICE_RUNTIME */ } static int test_loop_repeated_start_stop(const struct device *dma) { static int chan_id; enum pm_device_state init_state = pm_device_on_power_domain(dma) ? PM_DEVICE_STATE_OFF : PM_DEVICE_STATE_SUSPENDED; test_case_id = 0; TC_PRINT("DMA memory to memory transfer started\n"); TC_PRINT("Preparing DMA Controller\n"); memset(tx_data, 0, sizeof(tx_data)); memset(rx_data, 0, sizeof(rx_data)); if (!device_is_ready(dma)) { TC_PRINT("dma controller device is not ready\n"); return TC_FAIL; } dma_cfg.channel_direction = MEMORY_TO_MEMORY; dma_cfg.source_data_size = 1U; dma_cfg.dest_data_size = 1U; dma_cfg.source_burst_length = 1U; dma_cfg.dest_burst_length = 1U; #ifdef CONFIG_DMAMUX_STM32 dma_cfg.user_data = (void *)dma; #else dma_cfg.user_data = NULL; #endif /* CONFIG_DMAMUX_STM32 */ dma_cfg.dma_callback = dma_user_callback; dma_cfg.block_count = 1U; dma_cfg.head_block = &dma_block_cfg; #ifdef CONFIG_DMA_MCUX_TEST_SLOT_START dma_cfg.dma_slot = CONFIG_DMA_MCUX_TEST_SLOT_START; #endif if (!check_dev_power_state(dma, PM_DEVICE_STATE_OFF)) { return TC_FAIL; } chan_id = dma_request_channel(dma, NULL); if (chan_id < 0) { TC_PRINT("this platform do not support the dma channel\n"); chan_id = CONFIG_DMA_LOOP_TRANSFER_CHANNEL_NR; } transfer_count = 0; done = 0; TC_PRINT("Starting the transfer on channel %d and waiting for 1 second\n", chan_id); dma_block_cfg.block_size = sizeof(tx_data); #ifdef CONFIG_DMA_64BIT dma_block_cfg.source_address = (uint64_t)tx_data; dma_block_cfg.dest_address = (uint64_t)rx_data[transfer_count]; #else dma_block_cfg.source_address = (uint32_t)tx_data; dma_block_cfg.dest_address = (uint32_t)rx_data[transfer_count]; #endif if (dma_config(dma, chan_id, &dma_cfg)) { TC_PRINT("ERROR: transfer config (%d)\n", chan_id); return TC_FAIL; } if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop on stopped channel (%d)\n", chan_id); return TC_FAIL; } if (!check_dev_power_state(dma, init_state)) { return TC_FAIL; } if (dma_start(dma, chan_id)) { TC_PRINT("ERROR: transfer start (%d)\n", chan_id); return TC_FAIL; } if (!check_dev_power_state(dma, PM_DEVICE_STATE_ACTIVE)) { return TC_FAIL; } k_sleep(K_MSEC(SLEEPTIME)); if (transfer_count < TRANSFER_LOOPS) { transfer_count = TRANSFER_LOOPS; TC_PRINT("ERROR: unfinished transfer\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop\n"); } return TC_FAIL; } TC_PRINT("Each RX buffer should contain the full TX buffer string.\n"); for (int i = 0; i < TRANSFER_LOOPS; i++) { TC_PRINT("RX data Loop %d\n", i); if (memcmp(tx_data, rx_data[i], CONFIG_DMA_LOOP_TRANSFER_SIZE)) { return TC_FAIL; } } TC_PRINT("Finished: DMA\n"); if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: transfer stop (%d)\n", chan_id); return TC_FAIL; } if (!check_dev_power_state(dma, init_state)) { return TC_FAIL; } if (dma_stop(dma, chan_id)) { TC_PRINT("ERROR: repeated transfer stop (%d)\n", chan_id); return TC_FAIL; } return TC_PASS; } #define DMA_NAME(i, _) tst_dma ## i #define DMA_LIST LISTIFY(CONFIG_DMA_LOOP_TRANSFER_NUMBER_OF_DMAS, DMA_NAME, (,)) #define TEST_LOOP(dma_name) \ ZTEST(dma_m2m_loop, test_ ## dma_name ## _m2m_loop) \ { \ const struct device *dma = DEVICE_DT_GET(DT_NODELABEL(dma_name)); \ zassert_true((test_loop(dma) == TC_PASS)); \ } FOR_EACH(TEST_LOOP, (), DMA_LIST); #define TEST_LOOP_SUSPEND_RESUME(dma_name) \ ZTEST(dma_m2m_loop, test_ ## dma_name ## _m2m_loop_suspend_resume) \ { \ const struct device *dma = DEVICE_DT_GET(DT_NODELABEL(dma_name)); \ zassert_true((test_loop_suspend_resume(dma) == TC_PASS)); \ } FOR_EACH(TEST_LOOP_SUSPEND_RESUME, (), DMA_LIST); #define TEST_LOOP_REPEATED_START_STOP(dma_name) \ ZTEST(dma_m2m_loop, test_ ## dma_name ## _m2m_loop_repeated_start_stop) \ { \ const struct device *dma = DEVICE_DT_GET(DT_NODELABEL(dma_name)); \ zassert_true((test_loop_repeated_start_stop(dma) == TC_PASS)); \ } FOR_EACH(TEST_LOOP_REPEATED_START_STOP, (), DMA_LIST);