diff --git a/soc/xtensa/intel_adsp/tools/cavstool.py b/soc/xtensa/intel_adsp/tools/cavstool.py index 9ea521c0dcb..c4053048978 100755 --- a/soc/xtensa/intel_adsp/tools/cavstool.py +++ b/soc/xtensa/intel_adsp/tools/cavstool.py @@ -20,7 +20,14 @@ PAGESZ = 4096 HUGEPAGESZ = 2 * 1024 * 1024 HUGEPAGE_FILE = "/dev/hugepages/cavs-fw-dma.tmp" -# Log is in the fourth window, they appear in 128k regions starting at 512k +# SRAM windows. Each appears in a 128k region starting at 512k. +# +# Window 0 is the FW_STATUS area, and 4k after that the IPC "outbox" +# Window 1 is the IPC "inbox" (host-writable memory, just 384 bytes currently) +# Window 2 is unused by this script +# Window 3 is winstream-formatted log output +OUTBOX_OFFSET = (512 + (0 * 128)) * 1024 + 4096 +INBOX_OFFSET = (512 + (1 * 128)) * 1024 WINSTREAM_OFFSET = (512 + (3 * 128)) * 1024 def map_regs(): @@ -346,6 +353,11 @@ def ipc_command(data, ext_data): ext_data = t - ipc_timestamp ipc_timestamp = t send_msg = True + elif data == 5: # copy word at outbox[ext_data >> 16] to inbox[ext_data & 0xffff] + src = OUTBOX_OFFSET + 4 * (ext_data >> 16) + dst = INBOX_OFFSET + 4 * (ext_data & 0xffff) + for i in range(4): + bar4_mmap[dst + i] = bar4_mmap[src + i] else: log.warning(f"cavstool: Unrecognized IPC command 0x{data:x} ext 0x{ext_data:x}") diff --git a/tests/boards/intel_adsp/smoke/CMakeLists.txt b/tests/boards/intel_adsp/smoke/CMakeLists.txt index a1de84f628a..2d994416b59 100644 --- a/tests/boards/intel_adsp/smoke/CMakeLists.txt +++ b/tests/boards/intel_adsp/smoke/CMakeLists.txt @@ -4,4 +4,4 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(intel_adsp) -target_sources(app PRIVATE src/main.c src/smpboot.c src/hostipc.c src/cpus.c) +target_sources(app PRIVATE src/main.c src/smpboot.c src/hostipc.c src/cpus.c src/ipm.c) diff --git a/tests/boards/intel_adsp/smoke/prj.conf b/tests/boards/intel_adsp/smoke/prj.conf index 30b23341a23..7daae72d676 100644 --- a/tests/boards/intel_adsp/smoke/prj.conf +++ b/tests/boards/intel_adsp/smoke/prj.conf @@ -2,3 +2,6 @@ CONFIG_ZTEST=y CONFIG_SMP=y CONFIG_SMP_BOOT_DELAY=y CONFIG_SCHED_CPU_MASK=y +CONFIG_IPM=y +CONFIG_IPM_CAVS_HOST=y +CONFIG_IPM_CAVS_IDC=n diff --git a/tests/boards/intel_adsp/smoke/src/ipm.c b/tests/boards/intel_adsp/smoke/src/ipm.c new file mode 100644 index 00000000000..1e80686547d --- /dev/null +++ b/tests/boards/intel_adsp/smoke/src/ipm.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2022 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "tests.h" + +#define IPM_DEV device_get_binding("ipm_cavs_host") + +/* Two values impossible to transmit in a cAVS ID */ +#define ID_INBOUND 0xfffffff0 +#define ID_INVALID 0xffffffff + +static K_SEM_DEFINE(ipm_sem, 0, 1); + +static const uint32_t msg[] = { 29, 15, 58, 71, 99 }; + +static uint32_t received_id = ID_INVALID; +static volatile uint32_t *received_data; + +static void ipm_msg(const struct device *ipmdev, void *user_data, + uint32_t id, volatile void *data) +{ + zassert_equal(ipmdev, IPM_DEV, "wrong device"); + zassert_equal(user_data, NULL, "wrong user_data pointer"); + zassert_equal(received_id, ID_INBOUND, "unexpected message"); + + received_id = id; + received_data = data; + + k_sem_give(&ipm_sem); +} + +static void msg_transact(bool do_wait) +{ + /* Send an IPCCMD_RETURN_MSG, this will send us a return + * message with msg[0] as the ID (on cAVS 1.8+, otherwise + * zero). + */ + received_id = ID_INBOUND; + ipm_send(IPM_DEV, do_wait, IPCCMD_RETURN_MSG, msg, sizeof(msg)); + + /* Wait for the return message */ + k_sem_take(&ipm_sem, K_FOREVER); + + zassert_equal(received_id, + IS_ENABLED(IPM_CAVS_HOST_REGWORD) ? msg[0] : 0, + "wrong return message ID"); + + received_id = ID_INVALID; + + /* Now whitebox the message protocol: copy the message buffer + * (on the host side!) from the outbox to the inbox. That + * will write into our "already received" inbox buffer memory. + * We do this using the underlying cavs_ipc API, which works + * only because we know it works. Note that on cAVS 1.8+, the + * actual in-use amount of the message will be one word + * shorter (because the first word is sent as IPC ext_data), + * but it won't be inspected below. + */ + for (int i = 0; i < ARRAY_SIZE(msg); i++) { + cavs_ipc_send_message_sync(CAVS_HOST_DEV, IPCCMD_WINCOPY, + (i << 16) | i, K_FOREVER); + } + + /* Validate data */ + for (int i = 0; i < ARRAY_SIZE(msg); i++) { + zassert_equal(msg[i], received_data[i], "wrong message data"); + } +} + +/* This is a little whiteboxey. It relies on the knowledge that an + * IPM message is nothing but a IPC message with the "id" parameter + * passed as data (and, on cAVS 1.8+ only, the first word of the + * message buffer passed as ext_data). + */ +void test_ipm_cavs_host(void) +{ + /* Restore IPM driver state (we've been mucking with cavs_ipc tests) */ + cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipm_handler, (void *)IPM_DEV); + cavs_ipc_set_done_handler(CAVS_HOST_DEV, NULL, NULL); + + ipm_register_callback(IPM_DEV, ipm_msg, NULL); + + /* Do it twice just for coverage on the wait parameter */ + msg_transact(true); + msg_transact(false); +} diff --git a/tests/boards/intel_adsp/smoke/src/main.c b/tests/boards/intel_adsp/smoke/src/main.c index 5b0c2de270d..b74cd37901d 100644 --- a/tests/boards/intel_adsp/smoke/src/main.c +++ b/tests/boards/intel_adsp/smoke/src/main.c @@ -6,6 +6,8 @@ #include #include "tests.h" +cavs_ipc_handler_t ipm_handler; + static bool clock_msg(const struct device *dev, void *arg, uint32_t data, uint32_t ext_data) { @@ -42,13 +44,18 @@ void test_clock_calibrate(void) void test_main(void) { + struct cavs_ipc_data *devdata = CAVS_HOST_DEV->data; + + ipm_handler = devdata->handle_message; + ztest_test_suite(intel_adsp, ztest_unit_test(test_smp_boot_delay), ztest_unit_test(test_cpu_halt), ztest_unit_test(test_post_boot_ipi), ztest_unit_test(test_cpu_behavior), ztest_unit_test(test_host_ipc), - ztest_unit_test(test_clock_calibrate) + ztest_unit_test(test_clock_calibrate), + ztest_unit_test(test_ipm_cavs_host) ); ztest_run_test_suite(intel_adsp); diff --git a/tests/boards/intel_adsp/smoke/src/tests.h b/tests/boards/intel_adsp/smoke/src/tests.h index 1f0840fc7db..19d32fe9900 100644 --- a/tests/boards/intel_adsp/smoke/src/tests.h +++ b/tests/boards/intel_adsp/smoke/src/tests.h @@ -38,6 +38,9 @@ enum cavstool_cmd { /* The host emits a (real/host time) timestamp into the log stream */ IPCCMD_TIMESTAMP, + + /* The host copies OUTBOX[ext_data >> 16] to INBOX[ext_data & 0xffff] */ + IPCCMD_WINCOPY, }; void test_post_boot_ipi(void); @@ -45,5 +48,12 @@ void test_smp_boot_delay(void); void test_host_ipc(void); void test_cpu_behavior(void); void test_cpu_halt(void); +void test_ipm_cavs_host(void); + +/* Cached copy of the ipm_cavs_host driver's handler. We save it at + * the start of the test because we want to do unit testing on the + * underlying cavs_ipc device, then recover it later. + */ +extern cavs_ipc_handler_t ipm_handler; #endif /* ZEPHYR_TESTS_INTEL_ADSP_TESTS_H */