From a734f8460080e0acb675ccb2fbb30bb06eb867f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Wed, 23 Apr 2025 11:40:47 +0200 Subject: [PATCH 01/21] [nrf fromlist] drivers: flash: nrf_qspi_nor: Prevent CPU hang when XIP is re-enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a simple non-XIP transaction before deactivating the QSPI after a XIP transaction is performed. This prevents a CPU hang from occuring when another XIP transaction is attempted after the QSPI is activated again. Upstream PR #: 88967 Signed-off-by: Andrzej Głąbek --- drivers/flash/nrf_qspi_nor.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/flash/nrf_qspi_nor.c b/drivers/flash/nrf_qspi_nor.c index f60ca2d2c0ea..12eeb3f4a3e2 100644 --- a/drivers/flash/nrf_qspi_nor.c +++ b/drivers/flash/nrf_qspi_nor.c @@ -1359,6 +1359,18 @@ void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable) #endif if (enable) { (void)nrfx_qspi_activate(false); + } else { + /* It turns out that when the QSPI peripheral is deactivated + * after a XIP transaction, it cannot be later successfully + * reactivated and an attempt to perform another XIP transaction + * results in the CPU being hung; even a debug session cannot be + * started then and the SoC has to be recovered. + * As a workaround, at least until the cause of such behavior + * is fully clarified, perform a simple non-XIP transaction + * (a read of the status register) before deactivating the QSPI. + * This prevents the issue from occurring. + */ + (void)qspi_rdsr(dev, 1); } dev_data->xip_enabled = enable; From 07e10c68dbdd53929226357bbdd507474752a4b9 Mon Sep 17 00:00:00 2001 From: Gordon Klaus Date: Tue, 8 Apr 2025 17:56:00 +0200 Subject: [PATCH 02/21] [nrf noup] tests: bluetooth: tester: Enable PSA RNG on nRF54H20 The PSA is a cryptographically secure random number generator. It will be enabled by default, eventually, For now, enable it manually. Signed-off-by: Gordon Klaus --- .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 7 ++++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 14 +++++++ tests/bluetooth/tester/sysbuild.cmake | 7 ---- .../boards/nrf54h20dk_nrf54h20_cpurad.conf | 40 +++++++++++++++++++ .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 13 ++++++ .../tester/sysbuild/hci_ipc/prj.conf | 23 +++++++++++ 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.conf create mode 100644 tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.overlay create mode 100644 tests/bluetooth/tester/sysbuild/hci_ipc/prj.conf diff --git a/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.conf index 5bb1c4d82846..51b0ef5fa8d4 100644 --- a/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -22,3 +22,10 @@ CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_BTTESTER_LOG_LEVEL_DBG=y CONFIG_UART_INTERRUPT_DRIVEN=y + +# Enable PSA RNG +CONFIG_PSA_CRYPTO_DRIVER_OBERON=n +CONFIG_PSA_SSF_CRYPTO_CLIENT=y +CONFIG_SSF_PSA_CRYPTO_SERVICE_ENABLED=y +CONFIG_MBEDTLS_PSA_CRYPTO_C=y +CONFIG_NRF_SECURITY=y diff --git a/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index e6d4f675f57a..4f9de686b7e5 100644 --- a/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/bluetooth/tester/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -12,3 +12,17 @@ status = "okay"; hw-flow-control; }; + +// Enable PSA RNG +/ { + chosen { + zephyr,entropy = &psa_rng; + }; + + psa_rng: psa-rng { + compatible = "zephyr,psa-crypto-rng"; + status = "okay"; + }; + + /delete-node/ prng; +}; diff --git a/tests/bluetooth/tester/sysbuild.cmake b/tests/bluetooth/tester/sysbuild.cmake index e2fc2ed0de86..8b9402cfcddc 100644 --- a/tests/bluetooth/tester/sysbuild.cmake +++ b/tests/bluetooth/tester/sysbuild.cmake @@ -22,13 +22,6 @@ if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC) ) endif() - if(SB_CONFIG_SOC_NRF54H20_CPUAPP) - set(${NET_APP}_CONF_FILE - ${NET_APP_SRC_DIR}/nrf54h20_cpurad-bt_ll_softdevice.conf - CACHE INTERNAL "" - ) - endif() - set(${NET_APP}_EXTRA_CONF_FILE ${APP_DIR}/hci_ipc_cpunet.conf CACHE INTERNAL "" diff --git a/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.conf b/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.conf new file mode 100644 index 000000000000..b7d64a9e6a08 --- /dev/null +++ b/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.conf @@ -0,0 +1,40 @@ +CONFIG_IPC_SERVICE=y +CONFIG_MBOX=y + +CONFIG_ISR_STACK_SIZE=1024 +CONFIG_IDLE_STACK_SIZE=256 +CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512 +CONFIG_IPC_SERVICE_BACKEND_RPMSG_WQ_STACK_SIZE=512 +CONFIG_HEAP_MEM_POOL_SIZE=8192 + +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y + +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_CMD_TX_SIZE=255 + +# Host +CONFIG_BT_BROADCASTER=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_PER_ADV=y +CONFIG_BT_PER_ADV_SYNC=y + +# Controller +CONFIG_BT_LL_SW_SPLIT=n +CONFIG_BT_LL_SOFTDEVICE=y +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 +CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=191 + +# Enable PSA RNG +CONFIG_PSA_CRYPTO_DRIVER_OBERON=n +CONFIG_PSA_SSF_CRYPTO_CLIENT=y +CONFIG_SSF_PSA_CRYPTO_SERVICE_ENABLED=y +CONFIG_MBEDTLS_PSA_CRYPTO_C=y +CONFIG_NRF_SECURITY=y diff --git a/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 000000000000..e34567fe834a --- /dev/null +++ b/tests/bluetooth/tester/sysbuild/hci_ipc/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,13 @@ +// Enable PSA RNG +/ { + chosen { + zephyr,entropy = &psa_rng; + }; + + psa_rng: psa-rng { + compatible = "zephyr,psa-crypto-rng"; + status = "okay"; + }; + + /delete-node/ prng; +}; diff --git a/tests/bluetooth/tester/sysbuild/hci_ipc/prj.conf b/tests/bluetooth/tester/sysbuild/hci_ipc/prj.conf new file mode 100644 index 000000000000..08b1aed9e7f6 --- /dev/null +++ b/tests/bluetooth/tester/sysbuild/hci_ipc/prj.conf @@ -0,0 +1,23 @@ +CONFIG_IPC_SERVICE=y +CONFIG_MBOX=y + +CONFIG_HEAP_MEM_POOL_SIZE=4096 + +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512 + +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_MAX_CONN=16 + + +# Workaround: Unable to allocate command buffer when using K_NO_WAIT since +# Host number of completed commands does not follow normal flow control. +CONFIG_BT_BUF_CMD_TX_COUNT=10 + +# Enable and adjust the below value as necessary +# CONFIG_BT_BUF_EVT_RX_COUNT=16 +# CONFIG_BT_BUF_EVT_RX_SIZE=255 +# CONFIG_BT_BUF_ACL_RX_SIZE=255 +# CONFIG_BT_BUF_ACL_TX_SIZE=251 +# CONFIG_BT_BUF_CMD_TX_SIZE=255 From 0afd70bfd6e65c619b1fdfd4f08cb063922c0b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stasiak?= Date: Wed, 23 Apr 2025 12:47:16 +0200 Subject: [PATCH 03/21] [nrf fromlist] tests: drivers: clock_control_api: Adjust to nRF54L09 and nRF54L20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nRF54L09 and nRF54L20 need double the time for startup of their clocks. Upstream PR #: 88956 Signed-off-by: Michał Stasiak --- .../clock_control/clock_control_api/src/nrf_device_subsys.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 6d660bda9473..78dafa2a3b34 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -11,8 +11,9 @@ static const struct device_subsys_data subsys_data[] = { { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, .startup_us = - IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? - 3000 : 500 + (IS_ENABLED(CONFIG_SOC_NRF54L20_ENGA) || + IS_ENABLED(CONFIG_SOC_NRF54L09_ENGA)) ? + 1000 : (IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? 3000 : 500) }, #ifndef CONFIG_SOC_NRF52832 /* On nrf52832 LF clock cannot be stopped because it leads From 785502f7ab97d046c0561c0411b16ba14fb318b4 Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Wed, 23 Apr 2025 17:13:36 +0200 Subject: [PATCH 04/21] Revert "[nrf fromlist] tests: drivers: i2s: Align tests to TDM peripheral" This reverts commit 3c0b18489134cb3a3dc7882e6034d55a73604b3b. Signed-off-by: Adam Kondraciuk --- .../nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml | 1 - .../nrf54l20pdk_nrf54l20_cpuapp.yaml | 1 - tests/drivers/i2s/i2s_api/Kconfig | 6 ++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 31 ------------------- .../nrf54l20pdk_nrf54l20_cpuapp.overlay | 30 ------------------ tests/drivers/i2s/i2s_speed/Kconfig | 6 ++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 31 ------------------- .../nrf54l20pdk_nrf54l20_cpuapp.overlay | 30 ------------------ 8 files changed, 6 insertions(+), 130 deletions(-) delete mode 100644 tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay delete mode 100644 tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay delete mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay delete mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml index 02acf129ef1e..a64f8cf6398b 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml @@ -18,7 +18,6 @@ supported: - counter - gpio - i2c - - i2s - pwm - retained_mem - spi diff --git a/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml b/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml index 2f3e9d15fb3a..d4bc78288220 100644 --- a/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml +++ b/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml @@ -18,7 +18,6 @@ supported: - dmic - gpio - i2c - - i2s - pwm - spi - watchdog diff --git a/tests/drivers/i2s/i2s_api/Kconfig b/tests/drivers/i2s/i2s_api/Kconfig index ff78064206ea..538157f8a794 100644 --- a/tests/drivers/i2s/i2s_api/Kconfig +++ b/tests/drivers/i2s/i2s_api/Kconfig @@ -15,7 +15,7 @@ config I2S_TEST_SEPARATE_DEVICES config I2S_TEST_USE_I2S_DIR_BOTH bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED help Use the I2S_DIR_BOTH enumeration value to trigger commands in test cases involving both reception and transmission. Use of this option @@ -24,7 +24,7 @@ config I2S_TEST_USE_I2S_DIR_BOTH config I2S_TEST_USE_GPIO_LOOPBACK bool "Use GPIO loopback" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED help Use wiring between the data-out and data-in pins for looping back data. This option is intended to be used for devices that do not @@ -32,7 +32,7 @@ config I2S_TEST_USE_GPIO_LOOPBACK config I2S_TEST_ALLOWED_DATA_OFFSET int "Allowed offset in received data" - default 10 if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED default 0 help Maximum allowed offset between sent and received samples. Non-zero diff --git a/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay deleted file mode 100644 index 0625be930fa6..000000000000 --- a/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* i2s-node0 is the transmitter/receiver */ - -/ { - aliases { - i2s-node0 = &tdm130; - }; -}; - -&pinctrl { - tdm130_default_alt: tdm130_default_alt { - group1 { - psels = , - , - , - ; - }; - }; -}; - -&tdm130 { - status = "okay"; - pinctrl-0 = <&tdm130_default_alt>; - pinctrl-names = "default"; - memory-regions = <&cpuapp_dma_region>; -}; diff --git a/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay b/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay deleted file mode 100644 index 09a2cb0459c8..000000000000 --- a/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* i2s-node0 is the transmitter/receiver */ - -/ { - aliases { - i2s-node0 = &tdm; - }; -}; - -&pinctrl { - tdm_default_alt: tdm_default_alt { - group1 { - psels = , - , - , - ; - }; - }; -}; - -&tdm { - status = "okay"; - pinctrl-0 = <&tdm_default_alt>; - pinctrl-names = "default"; -}; diff --git a/tests/drivers/i2s/i2s_speed/Kconfig b/tests/drivers/i2s/i2s_speed/Kconfig index 4e074f18c102..165fc6279b27 100644 --- a/tests/drivers/i2s/i2s_speed/Kconfig +++ b/tests/drivers/i2s/i2s_speed/Kconfig @@ -15,7 +15,7 @@ config I2S_TEST_SEPARATE_DEVICES config I2S_TEST_USE_I2S_DIR_BOTH bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED help Use the I2S_DIR_BOTH enumeration value to trigger commands in test cases involving both reception and transmission. Use of this option @@ -24,7 +24,7 @@ config I2S_TEST_USE_I2S_DIR_BOTH config I2S_TEST_USE_GPIO_LOOPBACK bool "Use GPIO loopback" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED help Use wiring between the data-out and data-in pins for looping back data. This option is intended to be used for devices that do not @@ -32,7 +32,7 @@ config I2S_TEST_USE_GPIO_LOOPBACK config I2S_TEST_ALLOWED_DATA_OFFSET int "Allowed offset in received data" - default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED + default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED default 0 help Maximum allowed offset between sent and received samples. Non-zero diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay deleted file mode 100644 index 0625be930fa6..000000000000 --- a/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* i2s-node0 is the transmitter/receiver */ - -/ { - aliases { - i2s-node0 = &tdm130; - }; -}; - -&pinctrl { - tdm130_default_alt: tdm130_default_alt { - group1 { - psels = , - , - , - ; - }; - }; -}; - -&tdm130 { - status = "okay"; - pinctrl-0 = <&tdm130_default_alt>; - pinctrl-names = "default"; - memory-regions = <&cpuapp_dma_region>; -}; diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay deleted file mode 100644 index 09a2cb0459c8..000000000000 --- a/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* i2s-node0 is the transmitter/receiver */ - -/ { - aliases { - i2s-node0 = &tdm; - }; -}; - -&pinctrl { - tdm_default_alt: tdm_default_alt { - group1 { - psels = , - , - , - ; - }; - }; -}; - -&tdm { - status = "okay"; - pinctrl-0 = <&tdm_default_alt>; - pinctrl-names = "default"; -}; From 5c1fbf9e1facd429a1bfedded141c57c1a5cedd6 Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Wed, 23 Apr 2025 17:13:56 +0200 Subject: [PATCH 05/21] Revert "[nrf fromlist] drivers: i2s: Add support for nRF TDM peripherals" This reverts commit f07fb31d8033a32a3d0d3f0ebcd3621001a5aad2. Signed-off-by: Adam Kondraciuk --- drivers/i2s/CMakeLists.txt | 1 - drivers/i2s/Kconfig.nrfx | 22 - drivers/i2s/i2s_nrfx_tdm.c | 1116 ------------------------------------ 3 files changed, 1139 deletions(-) delete mode 100644 drivers/i2s/i2s_nrfx_tdm.c diff --git a/drivers/i2s/CMakeLists.txt b/drivers/i2s/CMakeLists.txt index 29a9da412cea..7857b6e863b5 100644 --- a/drivers/i2s/CMakeLists.txt +++ b/drivers/i2s/CMakeLists.txt @@ -11,7 +11,6 @@ zephyr_library_sources_ifdef(CONFIG_I2S_STM32 i2s_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_I2S_LITEX i2s_litex.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_FLEXCOMM i2s_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_I2S_NRFX i2s_nrfx.c) -zephyr_library_sources_ifdef(CONFIG_TDM_NRFX i2s_nrfx_tdm.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_SAI i2s_mcux_sai.c) zephyr_library_sources_ifdef(CONFIG_I2S_ESP32 i2s_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2S_TEST i2s_test.c) diff --git a/drivers/i2s/Kconfig.nrfx b/drivers/i2s/Kconfig.nrfx index 6c92cda39936..b36f3eb9c641 100644 --- a/drivers/i2s/Kconfig.nrfx +++ b/drivers/i2s/Kconfig.nrfx @@ -22,25 +22,3 @@ config I2S_NRFX_TX_BLOCK_COUNT default 4 endif # I2S_NRFX - -menuconfig TDM_NRFX - bool "nRF TDM nrfx driver" - default y - depends on DT_HAS_NORDIC_NRF_TDM_ENABLED - select NRFX_TDM130 if HAS_HW_NRF_TDM130 - select CLOCK_CONTROL - select PINCTRL - help - Enable support for nrfx TDM driver for nRF MCU series. - -if TDM_NRFX - -config TDM_NRFX_RX_BLOCK_COUNT - int "RX queue length" - default 4 - -config TDM_NRFX_TX_BLOCK_COUNT - int "TX queue length" - default 4 - -endif # TDM_NRFX diff --git a/drivers/i2s/i2s_nrfx_tdm.c b/drivers/i2s/i2s_nrfx_tdm.c deleted file mode 100644 index 4ca14ccd48f9..000000000000 --- a/drivers/i2s/i2s_nrfx_tdm.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(tdm_nrfx, CONFIG_I2S_LOG_LEVEL); - -/* The application must provide buffers that are to be used in the next - * part of the transfer. - */ -#define NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED BIT(0) - -/* The TDM peripheral has been stopped and all buffers that were passed - * to the driver have been released. - */ -#define NRFX_TDM_STATUS_TRANSFER_STOPPED BIT(1) - -#define NODE_AUDIOPLL DT_NODELABEL(audiopll) - -#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) -static const struct device *audiopll = DEVICE_DT_GET(NODE_AUDIOPLL); -const struct nrf_clock_spec aclk_spec = { - .frequency = DT_PROP_OR(NODE_AUDIOPLL, frequency, 0), - .accuracy = 0, - .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, -}; -#endif - -typedef struct { - uint32_t *p_rx_buffer; - uint32_t const *p_tx_buffer; - void *p_tx_mem_slab; - void *p_rx_mem_slab; - uint16_t buffer_size; -} tdm_buffers_t; - -typedef void (*tdm_data_handler_t)(tdm_buffers_t const *p_released, uint32_t status); - -typedef struct { - tdm_data_handler_t handler; - bool use_rx: 1; - bool use_tx: 1; - bool rx_ready: 1; - bool tx_ready: 1; - bool buffers_needed: 1; - bool buffers_reused: 1; - tdm_buffers_t next_buffers; - tdm_buffers_t current_buffers; -} tdm_ctrl_t; - -struct stream_cfg { - struct i2s_config cfg; - nrf_tdm_config_t nrfx_cfg; -}; - -struct tdm_buf { - void *mem_block; - size_t size; - void *dmm_buf; -}; - -struct tdm_drv_cfg { - tdm_data_handler_t data_handler; - const struct pinctrl_dev_config *pcfg; - NRF_TDM_Type *p_reg; - void *mem_reg; - tdm_ctrl_t *control_data; - uint32_t mck_frequency; - uint32_t pclk_frequency; - enum clock_source { - PCLK, - ACLK - } clk_src; -}; - -struct tdm_drv_data { - struct onoff_client clk_cli; - struct stream_cfg tx; - struct k_msgq tx_queue; - struct stream_cfg rx; - struct k_msgq rx_queue; - const struct tdm_drv_cfg *drv_cfg; - const uint32_t *last_tx_buffer; - void *last_tx_mem_slab; - enum i2s_state state; - enum i2s_dir active_dir; - bool stop; /* stop after the current (TX or RX) block */ - bool discard_rx; /* discard further RX blocks */ - volatile bool next_tx_buffer_needed; - bool tx_configured: 1; - bool rx_configured: 1; - bool request_clock: 1; -}; - -static int audio_clock_request(struct tdm_drv_data *drv_data) -{ -#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) - return nrf_clock_control_request(audiopll, &aclk_spec, &drv_data->clk_cli); -#else - (void)drv_data; - - return -ENOTSUP; -#endif -} - -static int audio_clock_release(void) -{ -#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) - return nrf_clock_control_release(audiopll, &aclk_spec); -#else - return -ENOTSUP; -#endif -} - -void tdm_irq_handler(const struct device *dev) -{ - const struct tdm_drv_cfg *drv_cfg = dev->config; - NRF_TDM_Type *p_reg = drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_cfg->control_data; - uint32_t event_mask = 0; - - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_MAXCNT)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_MAXCNT); - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_TXPTRUPD)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_TXPTRUPD); - ctrl_data->tx_ready = true; - if (ctrl_data->use_tx && ctrl_data->buffers_needed) { - ctrl_data->buffers_reused = true; - } - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_RXPTRUPD)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_RXPTRUPD); - ctrl_data->rx_ready = true; - if (ctrl_data->use_rx && ctrl_data->buffers_needed) { - ctrl_data->buffers_reused = true; - } - } - if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_STOPPED)) { - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); - event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_STOPPED); - nrf_tdm_int_disable(p_reg, NRF_TDM_INT_STOPPED_MASK_MASK); - nrf_tdm_disable(p_reg); - /* When stopped, release all buffers, including these scheduled for - * the next part of the transfer, and signal that the transfer has - * finished. - */ - ctrl_data->handler(&ctrl_data->current_buffers, 0); - ctrl_data->handler(&ctrl_data->next_buffers, NRFX_TDM_STATUS_TRANSFER_STOPPED); - } else { - /* Check if the requested transfer has been completed: - * - full-duplex mode - */ - if ((ctrl_data->use_tx && ctrl_data->use_rx && ctrl_data->tx_ready && - ctrl_data->rx_ready) || - /* - TX only mode */ - (!ctrl_data->use_rx && ctrl_data->tx_ready) || - /* - RX only mode */ - (!ctrl_data->use_tx && ctrl_data->rx_ready)) { - ctrl_data->tx_ready = false; - ctrl_data->rx_ready = false; - - /* If the application did not supply the buffers for the next - * part of the transfer until this moment, the current buffers - * cannot be released, since the I2S peripheral already started - * using them. Signal this situation to the application by - * passing NULL instead of the structure with released buffers. - */ - if (ctrl_data->buffers_reused) { - ctrl_data->buffers_reused = false; - /* This will most likely be set at this point. However, there is - * a small time window between TXPTRUPD and RXPTRUPD events, - * and it is theoretically possible that next buffers will be - * set in this window, so to be sure this flag is set to true, - * set it explicitly. - */ - ctrl_data->buffers_needed = true; - ctrl_data->handler(NULL, NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); - } else { - /* Buffers that have been used by the I2S peripheral (current) - * are now released and will be returned to the application, - * and the ones scheduled to be used as next become the current - * ones. - */ - tdm_buffers_t released_buffers = ctrl_data->current_buffers; - - ctrl_data->current_buffers = ctrl_data->next_buffers; - ctrl_data->next_buffers.p_rx_buffer = NULL; - ctrl_data->next_buffers.p_tx_buffer = NULL; - ctrl_data->buffers_needed = true; - ctrl_data->handler(&released_buffers, - NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); - } - } - } -} - -static uint32_t div_calculate(uint32_t src_freq, uint32_t requested_clk_freq) -{ - enum { - MCKCONST = 1048576 - }; - /* As specified in the PS: - * - * DIV = 4096 * floor(f_MCK * 1048576 / - * (f_source + f_MCK / 2)) - * f_actual = f_source / - * floor(1048576 * 4096 / DIV) - */ - - uint32_t ck_div = (uint32_t)(((uint64_t)requested_clk_freq * MCKCONST) / - (src_freq + requested_clk_freq / 2)); - return (ck_div * 4096); -} - -static bool get_next_tx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) -{ - struct tdm_buf buf; - int ret = k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT); - - if (ret != 0) { - return false; - } - buffers->p_tx_buffer = buf.dmm_buf; - buffers->p_tx_mem_slab = buf.mem_block; - buffers->buffer_size = buf.size / sizeof(uint32_t); - return true; -} - -static bool get_next_rx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - int ret = k_mem_slab_alloc(drv_data->rx.cfg.mem_slab, &buffers->p_rx_mem_slab, K_NO_WAIT); - - if (ret < 0) { - LOG_ERR("Failed to allocate next RX buffer: %d", ret); - return false; - } - ret = dmm_buffer_in_prepare(drv_cfg->mem_reg, buffers->p_rx_mem_slab, - buffers->buffer_size * sizeof(uint32_t), - (void **)&buffers->p_rx_buffer); - if (ret < 0) { - LOG_ERR("Failed to prepare buffer: %d", ret); - return false; - } - - return true; -} - -static void free_tx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - (void)dmm_buffer_out_release(drv_cfg->mem_reg, buf->dmm_buf); - k_mem_slab_free(drv_data->tx.cfg.mem_slab, buf->mem_block); - LOG_DBG("Freed TX %p", buf->mem_block); -} - -static void free_rx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf->mem_block, buf->size, buf->dmm_buf); - k_mem_slab_free(drv_data->rx.cfg.mem_slab, buf->mem_block); - LOG_DBG("Freed RX %p", buf->mem_block); -} - -static void tdm_start(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_initial_buffers) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - - __ASSERT_NO_MSG(p_initial_buffers->p_rx_buffer != NULL || - p_initial_buffers->p_tx_buffer != NULL); - ctrl_data->use_rx = (p_initial_buffers->p_rx_buffer != NULL); - ctrl_data->use_tx = (p_initial_buffers->p_tx_buffer != NULL); - ctrl_data->rx_ready = false; - ctrl_data->tx_ready = false; - ctrl_data->buffers_needed = false; - - ctrl_data->next_buffers = *p_initial_buffers; - ctrl_data->current_buffers.p_rx_buffer = NULL; - ctrl_data->current_buffers.p_tx_buffer = NULL; - nrf_tdm_enable(p_reg); - - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - - nrf_tdm_int_enable( - p_reg, - (p_initial_buffers->p_rx_buffer ? NRF_TDM_INT_RXPTRUPD_MASK_MASK : 0UL) | - (p_initial_buffers->p_tx_buffer ? NRF_TDM_INT_TXPTRUPD_MASK_MASK : 0UL) | - NRF_TDM_INT_STOPPED_MASK_MASK); - - nrf_tdm_tx_count_set(p_reg, p_initial_buffers->buffer_size); - nrf_tdm_rx_count_set(p_reg, p_initial_buffers->buffer_size); - nrf_tdm_rx_buffer_set(p_reg, p_initial_buffers->p_rx_buffer); - nrf_tdm_tx_buffer_set(p_reg, p_initial_buffers->p_tx_buffer); - nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_START); -} - -static void tdm_stop(NRF_TDM_Type *p_reg) -{ - nrf_tdm_int_disable(p_reg, NRF_TDM_INT_RXPTRUPD_MASK_MASK | NRF_TDM_INT_TXPTRUPD_MASK_MASK); - - nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_STOP); -} - -static bool next_buffers_set(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_buffers) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - nrf_tdm_rxtxen_t dir = NRF_TDM_RXTXEN_DUPLEX; - - __ASSERT_NO_MSG(p_buffers->p_rx_buffer != NULL || p_buffers->p_tx_buffer != NULL); - - if (!ctrl_data->buffers_needed) { - return false; - } - - nrf_tdm_tx_count_set(p_reg, p_buffers->buffer_size); - nrf_tdm_rx_count_set(p_reg, p_buffers->buffer_size); - nrf_tdm_rx_buffer_set(p_reg, p_buffers->p_rx_buffer); - nrf_tdm_tx_buffer_set(p_reg, p_buffers->p_tx_buffer); - - if (p_buffers->p_rx_buffer == NULL) { - dir = NRF_TDM_RXTXEN_TX; - } else if (p_buffers->p_tx_buffer == NULL) { - dir = NRF_TDM_RXTXEN_RX; - } - nrf_tdm_transfer_direction_set(p_reg, dir); - - ctrl_data->next_buffers = *p_buffers; - ctrl_data->buffers_needed = false; - - return true; -} - -static bool supply_next_buffers(struct tdm_drv_data *drv_data, tdm_buffers_t *next) -{ - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - - if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */ - if (!get_next_rx_buffer(drv_data, next)) { - drv_data->state = I2S_STATE_ERROR; - tdm_stop(drv_cfg->p_reg); - return false; - } - /* Set buffer size if there is no TX buffer (which effectively - * controls how many bytes will be received). - */ - if (drv_data->active_dir == I2S_DIR_RX) { - next->buffer_size = drv_data->rx.cfg.block_size / sizeof(uint32_t); - } - } - - drv_data->last_tx_buffer = next->p_tx_buffer; - drv_data->last_tx_mem_slab = next->p_tx_mem_slab; - - LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer); - return next_buffers_set(drv_data, next); -} - -static void purge_queue(const struct device *dev, enum i2s_dir dir) -{ - struct tdm_drv_data *drv_data = dev->data; - struct tdm_buf buf; - - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - while (k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT) == 0) { - free_tx_buffer(drv_data, &buf); - } - } - - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - while (k_msgq_get(&drv_data->rx_queue, &buf, K_NO_WAIT) == 0) { - free_rx_buffer(drv_data, &buf); - } - } -} - -static void tdm_uninit(struct tdm_drv_data *drv_data) -{ - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - - tdm_stop(p_reg); - NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg)); -} - -static int tdm_nrfx_configure(const struct device *dev, enum i2s_dir dir, - const struct i2s_config *tdm_cfg) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - nrf_tdm_config_t nrfx_cfg; - uint32_t chan_mask = 0; - - if (drv_data->state != I2S_STATE_READY) { - LOG_ERR("Cannot configure in state: %d", drv_data->state); - return -EINVAL; - } - - if (tdm_cfg->frame_clk_freq == 0) { /* -> reset state */ - purge_queue(dev, dir); - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - drv_data->tx_configured = false; - memset(&drv_data->tx, 0, sizeof(drv_data->tx)); - } - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - drv_data->rx_configured = false; - memset(&drv_data->rx, 0, sizeof(drv_data->rx)); - } - return 0; - } - - __ASSERT_NO_MSG(tdm_cfg->mem_slab != NULL && tdm_cfg->block_size != 0); - - if ((tdm_cfg->block_size % sizeof(uint32_t)) != 0) { - LOG_ERR("This device can transfer only full 32-bit words"); - return -EINVAL; - } - - switch (tdm_cfg->word_size) { - case 8: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_8BIT; - break; - case 16: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_16BIT; - break; - case 24: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_24BIT; - break; - case 32: - nrfx_cfg.sample_width = NRF_TDM_SWIDTH_32BIT; - break; - default: - LOG_ERR("Unsupported word size: %u", tdm_cfg->word_size); - return -EINVAL; - } - - switch (tdm_cfg->format & I2S_FMT_DATA_FORMAT_MASK) { - case I2S_FMT_DATA_FORMAT_I2S: - nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_NEGEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_1CK; - break; - case I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED: - nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; - break; - case I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED: - nrfx_cfg.alignment = NRF_TDM_ALIGN_RIGHT; - nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; - nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; - nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; - break; - default: - LOG_ERR("Unsupported data format: 0x%02x", tdm_cfg->format); - return -EINVAL; - } - - if ((tdm_cfg->format & I2S_FMT_DATA_ORDER_LSB) || (tdm_cfg->format & I2S_FMT_BIT_CLK_INV) || - (tdm_cfg->format & I2S_FMT_FRAME_CLK_INV)) { - LOG_ERR("Unsupported stream format: 0x%02x", tdm_cfg->format); - return -EINVAL; - } - - if (tdm_cfg->channels == 2) { - nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_2; - } else if (tdm_cfg->channels == 1) { - nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_1; - } else { - LOG_ERR("Unsupported number of channels: %u", tdm_cfg->channels); - return -EINVAL; - } - chan_mask = BIT_MASK(tdm_cfg->channels); - - if ((tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && - (tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { - nrfx_cfg.mode = NRF_TDM_MODE_SLAVE; - } else if (!(tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && - !(tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { - nrfx_cfg.mode = NRF_TDM_MODE_MASTER; - } else { - LOG_ERR("Unsupported operation mode: 0x%02x", tdm_cfg->options); - return -EINVAL; - } - - nrfx_cfg.mck_setup = 0; - if (nrfx_cfg.mode == NRF_TDM_MODE_MASTER) { - uint32_t sck = tdm_cfg->word_size * tdm_cfg->frame_clk_freq * tdm_cfg->channels; - const uint32_t src_freq = (drv_cfg->clk_src == ACLK) - ? DT_PROP_OR(NODE_AUDIOPLL, frequency, 0) - : drv_cfg->pclk_frequency; - - /* Unless the PCLK source is used, - * it is required to request the proper clock to be running - * before starting the transfer itself. - */ - drv_data->request_clock = (drv_cfg->clk_src != PCLK); - nrfx_cfg.sck_setup = div_calculate(src_freq, sck); - - if (((nrf_tdm_mck_pin_get(drv_cfg->p_reg) & TDM_PSEL_MCK_CONNECT_Msk) == - TDM_PSEL_MCK_CONNECT_Connected << TDM_PSEL_MCK_CONNECT_Pos) && - drv_cfg->mck_frequency != 0) { - nrfx_cfg.mck_setup = div_calculate(src_freq, drv_cfg->mck_frequency); - } - } else { - drv_data->request_clock = false; - } - - if ((tdm_cfg->options & I2S_OPT_LOOPBACK) || (tdm_cfg->options & I2S_OPT_PINGPONG)) { - LOG_ERR("Unsupported options: 0x%02x", tdm_cfg->options); - return -EINVAL; - } - - if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { - nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Tx0Enable_Pos); - drv_data->tx.cfg = *tdm_cfg; - drv_data->tx.nrfx_cfg = nrfx_cfg; - drv_data->tx_configured = true; - } - - if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { - nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Rx0Enable_Pos); - drv_data->rx.cfg = *tdm_cfg; - drv_data->rx.nrfx_cfg = nrfx_cfg; - drv_data->rx_configured = true; - } - return 0; -} - -static const struct i2s_config *tdm_nrfx_config_get(const struct device *dev, enum i2s_dir dir) -{ - struct tdm_drv_data *drv_data = dev->data; - - if (dir == I2S_DIR_TX && drv_data->tx_configured) { - return &drv_data->tx.cfg; - } - if (dir == I2S_DIR_RX && drv_data->rx_configured) { - return &drv_data->rx.cfg; - } - - return NULL; -} - -static int tdm_nrfx_read(const struct device *dev, void **mem_block, size_t *size) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; - struct tdm_buf buf; - int ret; - - if (!drv_data->rx_configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - ret = k_msgq_get(&drv_data->rx_queue, &buf, - (drv_data->state == I2S_STATE_ERROR) - ? K_NO_WAIT - : SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout)); - if (ret == -ENOMSG) { - return -EIO; - } - - LOG_DBG("Released RX %p", buf.mem_block); - - if (ret == 0) { - (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf.mem_block, buf.size, buf.dmm_buf); - *mem_block = buf.mem_block; - *size = buf.size; - } - return ret; -} - -static int tdm_nrfx_write(const struct device *dev, void *mem_block, size_t size) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - struct tdm_buf buf = {.mem_block = mem_block, .size = size}; - int ret; - - if (!drv_data->tx_configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - - if (drv_data->state != I2S_STATE_RUNNING && drv_data->state != I2S_STATE_READY) { - LOG_ERR("Cannot write in state: %d", drv_data->state); - return -EIO; - } - - if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) { - LOG_ERR("This device can only write blocks up to %u bytes", - drv_data->tx.cfg.block_size); - return -EIO; - } - ret = dmm_buffer_out_prepare(drv_cfg->mem_reg, buf.mem_block, buf.size, - (void **)&buf.dmm_buf); - ret = k_msgq_put(&drv_data->tx_queue, &buf, SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout)); - if (ret < 0) { - return ret; - } - - /* Check if interrupt wanted to get next TX buffer before current buffer - * was queued. Do not move this check before queuing because doing so - * opens the possibility for a race condition between this function and - * data_handler() that is called in interrupt context. - */ - if (drv_data->state == I2S_STATE_RUNNING && drv_data->next_tx_buffer_needed) { - tdm_buffers_t next = {0}; - - if (!get_next_tx_buffer(drv_data, &next)) { - /* Log error because this is definitely unexpected. - * Do not return error because the caller is no longer - * responsible for releasing the buffer. - */ - LOG_ERR("Cannot reacquire queued buffer"); - return 0; - } - - drv_data->next_tx_buffer_needed = false; - - LOG_DBG("Next TX %p", next.p_tx_buffer); - - if (!supply_next_buffers(drv_data, &next)) { - LOG_ERR("Cannot supply buffer"); - return -EIO; - } - } - return 0; -} - -static int start_transfer(struct tdm_drv_data *drv_data) -{ - tdm_buffers_t initial_buffers = {0}; - int ret = 0; - - if (drv_data->active_dir != I2S_DIR_RX && /* -> TX to be started */ - !get_next_tx_buffer(drv_data, &initial_buffers)) { - LOG_ERR("No TX buffer available"); - ret = -ENOMEM; - } else if (drv_data->active_dir != I2S_DIR_TX && /* -> RX to be started */ - !get_next_rx_buffer(drv_data, &initial_buffers)) { - /* Failed to allocate next RX buffer */ - ret = -ENOMEM; - } else { - /* It is necessary to set buffer size here only for I2S_DIR_RX, - * because only then the get_next_tx_buffer() call in the if - * condition above gets short-circuited. - */ - if (drv_data->active_dir == I2S_DIR_RX) { - initial_buffers.buffer_size = - drv_data->rx.cfg.block_size / sizeof(uint32_t); - } - - drv_data->last_tx_buffer = initial_buffers.p_tx_buffer; - drv_data->last_tx_mem_slab = initial_buffers.p_tx_mem_slab; - - tdm_start(drv_data, &initial_buffers); - } - if (ret < 0) { - tdm_uninit(drv_data); - if (drv_data->request_clock) { - (void)audio_clock_release(); - } - - if (initial_buffers.p_tx_buffer) { - struct tdm_buf buf = {.mem_block = (void *)initial_buffers.p_tx_mem_slab, - .dmm_buf = (void *)initial_buffers.p_tx_buffer, - .size = initial_buffers.buffer_size * - sizeof(uint32_t)}; - free_tx_buffer(drv_data, &buf); - } - if (initial_buffers.p_rx_buffer) { - struct tdm_buf buf = {.mem_block = initial_buffers.p_rx_mem_slab, - .dmm_buf = (void *)initial_buffers.p_rx_buffer, - .size = initial_buffers.buffer_size * - sizeof(uint32_t)}; - free_rx_buffer(drv_data, &buf); - } - - drv_data->state = I2S_STATE_ERROR; - } - return ret; -} - -static void tdm_init(struct tdm_drv_data *drv_data, nrf_tdm_config_t const *p_config, - tdm_data_handler_t handler) -{ - tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; - NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; - - nrf_tdm_configure(p_reg, p_config); - nrf_tdm_mck_set(p_reg, p_config->mck_setup != 0); - - ctrl_data->handler = handler; - - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); - nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); - NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg)); -} - -static void clock_started_callback(struct onoff_manager *mgr, struct onoff_client *cli, - uint32_t state, int res) -{ - struct tdm_drv_data *drv_data = CONTAINER_OF(cli, struct tdm_drv_data, clk_cli); - - /* The driver state can be set back to READY at this point if the DROP - * command was triggered before the clock has started. Do not start - * the actual transfer in such case. - */ - if (drv_data->state == I2S_STATE_READY) { - tdm_uninit(drv_data); - (void)audio_clock_release(); - } else { - (void)start_transfer(drv_data); - } -} - -static int trigger_start(const struct device *dev) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - int ret; - const nrf_tdm_config_t *nrfx_cfg = (drv_data->active_dir == I2S_DIR_TX) - ? &drv_data->tx.nrfx_cfg - : &drv_data->rx.nrfx_cfg; - - tdm_init(drv_data, nrfx_cfg, drv_cfg->data_handler); - - drv_data->state = I2S_STATE_RUNNING; - - nrf_tdm_sck_configure(drv_cfg->p_reg, - drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, - false); - - nrf_tdm_mck_configure(drv_cfg->p_reg, - drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, - false); - /* If it is required to use certain HF clock, request it to be running - * first. If not, start the transfer directly. - */ - if (drv_data->request_clock) { - sys_notify_init_callback(&drv_data->clk_cli.notify, clock_started_callback); - ret = audio_clock_request(drv_data); - if (ret < 0) { - tdm_uninit(drv_data); - drv_data->state = I2S_STATE_READY; - - LOG_ERR("Failed to request clock: %d", ret); - return -EIO; - } - } else { - ret = start_transfer(drv_data); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -static int tdm_nrfx_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - bool configured = false; - bool cmd_allowed; - - /* This driver does not use the I2S_STATE_NOT_READY value. - * Instead, if a given stream is not configured, the respective - * flag (tx_configured or rx_configured) is cleared. - */ - drv_data->tx.nrfx_cfg.channels |= drv_data->rx.nrfx_cfg.channels; - drv_data->rx.nrfx_cfg.channels |= drv_data->tx.nrfx_cfg.channels; - if (dir == I2S_DIR_BOTH) { - - configured = drv_data->tx_configured && drv_data->rx_configured; - } else if (dir == I2S_DIR_TX) { - configured = drv_data->tx_configured; - } else if (dir == I2S_DIR_RX) { - configured = drv_data->rx_configured; - } - - if (!configured) { - LOG_ERR("Device is not configured"); - return -EIO; - } - - if (dir == I2S_DIR_BOTH && (memcmp(&drv_data->tx.nrfx_cfg, &drv_data->rx.nrfx_cfg, - sizeof(drv_data->rx.nrfx_cfg)) != 0 || - (drv_data->tx.cfg.block_size != drv_data->rx.cfg.block_size))) { - LOG_ERR("TX and RX configurations are different"); - return -EIO; - } - - switch (cmd) { - case I2S_TRIGGER_START: - cmd_allowed = (drv_data->state == I2S_STATE_READY); - break; - case I2S_TRIGGER_STOP: - case I2S_TRIGGER_DRAIN: - cmd_allowed = (drv_data->state == I2S_STATE_RUNNING); - break; - case I2S_TRIGGER_DROP: - cmd_allowed = configured; - break; - case I2S_TRIGGER_PREPARE: - cmd_allowed = (drv_data->state == I2S_STATE_ERROR); - break; - default: - LOG_ERR("Invalid trigger: %d", cmd); - return -EINVAL; - } - - if (!cmd_allowed) { - LOG_ERR("Not allowed"); - return -EIO; - } - - /* For triggers applicable to the RUNNING state (i.e. STOP, DRAIN, - * and DROP), ensure that the command is applied to the streams - * that are currently active (this device cannot e.g. stop only TX - * without stopping RX). - */ - if (drv_data->state == I2S_STATE_RUNNING && drv_data->active_dir != dir) { - LOG_ERR("Inappropriate trigger (%d/%d), active stream(s): %d", cmd, dir, - drv_data->active_dir); - return -EINVAL; - } - - switch (cmd) { - case I2S_TRIGGER_START: - drv_data->stop = false; - drv_data->discard_rx = false; - drv_data->active_dir = dir; - drv_data->next_tx_buffer_needed = false; - return trigger_start(dev); - - case I2S_TRIGGER_STOP: - drv_data->state = I2S_STATE_STOPPING; - drv_data->stop = true; - return 0; - - case I2S_TRIGGER_DRAIN: - drv_data->state = I2S_STATE_STOPPING; - /* If only RX is active, DRAIN is equivalent to STOP. */ - drv_data->stop = (drv_data->active_dir == I2S_DIR_RX); - return 0; - - case I2S_TRIGGER_DROP: - if (drv_data->state != I2S_STATE_READY) { - drv_data->discard_rx = true; - tdm_stop(drv_cfg->p_reg); - } - purge_queue(dev, dir); - drv_data->state = I2S_STATE_READY; - return 0; - - case I2S_TRIGGER_PREPARE: - purge_queue(dev, dir); - drv_data->state = I2S_STATE_READY; - return 0; - - default: - LOG_ERR("Invalid trigger: %d", cmd); - return -EINVAL; - } -} - -static void data_handler(const struct device *dev, const tdm_buffers_t *released, uint32_t status) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - bool stop_transfer = false; - struct tdm_buf buf = {.mem_block = NULL, .dmm_buf = NULL, .size = 0}; - - if (released != NULL) { - buf.size = released->buffer_size * sizeof(uint32_t); - } - - if (status & NRFX_TDM_STATUS_TRANSFER_STOPPED) { - if (drv_data->state == I2S_STATE_STOPPING) { - drv_data->state = I2S_STATE_READY; - } - if (drv_data->last_tx_buffer) { - /* Usually, these pointers are equal, i.e. the last TX - * buffer that were to be transferred is released by the - * driver after it stops. The last TX buffer pointer is - * then set to NULL here so that the buffer can be freed - * below, just as any other TX buffer released by the - * driver. However, it may happen that the buffer is not - * released this way, for example, when the transfer - * ends with an error because an RX buffer allocation - * fails. In such case, the last TX buffer needs to be - * freed here. - */ - - if ((released != NULL) != 0 && - (drv_data->last_tx_buffer != released->p_tx_buffer)) { - buf.dmm_buf = (void *)drv_data->last_tx_buffer; - buf.mem_block = (void *)drv_data->last_tx_mem_slab; - free_tx_buffer(drv_data, &buf); - } - drv_data->last_tx_buffer = NULL; - } - tdm_uninit(drv_data); - if (drv_data->request_clock) { - (void)audio_clock_release(); - } - } - - if (released == NULL) { - /* This means that buffers for the next part of the transfer - * were not supplied and the previous ones cannot be released - * yet, as pointers to them were latched in the I2S registers. - * It is not an error when the transfer is to be stopped (those - * buffers will be released after the transfer actually stops). - */ - if (drv_data->state != I2S_STATE_STOPPING) { - drv_data->state = I2S_STATE_ERROR; - } - tdm_stop(drv_cfg->p_reg); - return; - } - if (released->p_rx_buffer) { - buf.mem_block = (void *)released->p_rx_mem_slab; - buf.dmm_buf = (void *)released->p_rx_buffer; - if (drv_data->discard_rx) { - free_rx_buffer(drv_data, &buf); - } else { - int ret = k_msgq_put(&drv_data->rx_queue, &buf, K_NO_WAIT); - - if (ret < 0) { - LOG_ERR("No room in RX queue"); - drv_data->state = I2S_STATE_ERROR; - stop_transfer = true; - - free_rx_buffer(drv_data, &buf); - } else { - - /* If the TX direction is not active and - * the transfer should be stopped after - * the current block, stop the reception. - */ - if (drv_data->active_dir == I2S_DIR_RX && drv_data->stop) { - drv_data->discard_rx = true; - stop_transfer = true; - } - } - } - } - - if (released->p_tx_buffer) { - buf.mem_block = (void *)released->p_tx_mem_slab; - buf.dmm_buf = (void *)released->p_tx_buffer; - /* If the last buffer that was to be transferred has just been - * released, it is time to stop the transfer. - */ - if (released->p_tx_buffer == drv_data->last_tx_buffer) { - drv_data->discard_rx = true; - stop_transfer = true; - } else { - free_tx_buffer(drv_data, &buf); - } - } - - if (stop_transfer) { - tdm_stop(drv_cfg->p_reg); - } else if (status & NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED) { - tdm_buffers_t next = {0}; - - if (drv_data->active_dir != I2S_DIR_RX) { /* -> TX active */ - if (drv_data->stop) { - /* If the stream is to be stopped, don't get - * the next TX buffer from the queue, instead - * supply the one used last time (it won't be - * transferred, the stream will stop right - * before this buffer would be started again). - */ - next.p_tx_buffer = drv_data->last_tx_buffer; - next.p_tx_mem_slab = drv_data->last_tx_mem_slab; - next.buffer_size = 1; - } else if (get_next_tx_buffer(drv_data, &next)) { - /* Next TX buffer successfully retrieved from - * the queue, nothing more to do here. - */ - } else if (drv_data->state == I2S_STATE_STOPPING) { - /* If there are no more TX blocks queued and - * the current state is STOPPING (so the DRAIN - * command was triggered) it is time to finish - * the transfer. - */ - drv_data->stop = true; - /* Supply the same buffer as last time; it will - * not be transferred anyway, as the transfer - * will be stopped earlier. - */ - next.p_tx_buffer = drv_data->last_tx_buffer; - next.p_tx_mem_slab = drv_data->last_tx_mem_slab; - next.buffer_size = 1; - } else { - /* Next TX buffer cannot be supplied now. - * Defer it to when the user writes more data. - */ - drv_data->next_tx_buffer_needed = true; - return; - } - } - (void)supply_next_buffers(drv_data, &next); - } -} - -static int data_init(const struct device *dev) -{ - struct tdm_drv_data *drv_data = dev->data; - const struct tdm_drv_cfg *drv_cfg = dev->config; - - drv_data->state = I2S_STATE_READY; - int err = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); - - if (err < 0) { - return err; - } - drv_data->drv_cfg = drv_cfg; - return err; -} - -static const struct i2s_driver_api tdm_nrf_drv_api = { - .configure = tdm_nrfx_configure, - .config_get = tdm_nrfx_config_get, - .read = tdm_nrfx_read, - .write = tdm_nrfx_write, - .trigger = tdm_nrfx_trigger, -}; - -#define TDM(idx) DT_NODELABEL(tdm##idx) -#define TDM_CLK_SRC(idx) DT_STRING_TOKEN(TDM(idx), clock_source) -#define PCLK_NODE(idx) DT_CLOCKS_CTLR(TDM(idx)) - -#define TDM_NRFX_DEVICE(idx) \ - static tdm_ctrl_t tdm##idx##data; \ - static struct tdm_buf tx_msgs##idx[CONFIG_TDM_NRFX_TX_BLOCK_COUNT]; \ - static struct tdm_buf rx_msgs##idx[CONFIG_TDM_NRFX_RX_BLOCK_COUNT]; \ - static void tdm_##idx##_irq_handler(const struct device *dev) \ - { \ - tdm_irq_handler(dev); \ - } \ - static void tdm_##idx##data_handler(tdm_buffers_t const *p_released, uint32_t status) \ - { \ - data_handler(DEVICE_DT_GET(TDM(idx)), p_released, status); \ - } \ - PINCTRL_DT_DEFINE(TDM(idx)); \ - static const struct tdm_drv_cfg tdm_nrfx_cfg##idx = { \ - .data_handler = tdm_##idx##data_handler, \ - .pcfg = PINCTRL_DT_DEV_CONFIG_GET(TDM(idx)), \ - .clk_src = TDM_CLK_SRC(idx), \ - .mck_frequency = DT_PROP_OR(TDM(idx), mck_frequency, 0), \ - .pclk_frequency = DT_PROP(PCLK_NODE(idx), clock_frequency), \ - .p_reg = NRF_TDM##idx, \ - .control_data = &tdm##idx##data, \ - .mem_reg = DMM_DEV_TO_REG(TDM(idx)), \ - }; \ - static struct tdm_drv_data tdm_nrfx_data##idx; \ - static int tdm_nrfx_init##idx(const struct device *dev) \ - { \ - IRQ_CONNECT(DT_IRQN(TDM(idx)), DT_IRQ(TDM(idx), priority), \ - tdm_##idx##_irq_handler, DEVICE_DT_GET(TDM(idx)), 0); \ - \ - int err = data_init(dev); \ - if (err < 0) { \ - return err; \ - } \ - k_msgq_init(&tdm_nrfx_data##idx.tx_queue, (char *)tx_msgs##idx, \ - sizeof(struct tdm_buf), ARRAY_SIZE(tx_msgs##idx)); \ - k_msgq_init(&tdm_nrfx_data##idx.rx_queue, (char *)rx_msgs##idx, \ - sizeof(struct tdm_buf), ARRAY_SIZE(rx_msgs##idx)); \ - return 0; \ - } \ - BUILD_ASSERT(TDM_CLK_SRC(idx) != ACLK || DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL), \ - "Clock source ACLK requires the audiopll node."); \ - DEVICE_DT_DEFINE(TDM(idx), tdm_nrfx_init##idx, NULL, &tdm_nrfx_data##idx, \ - &tdm_nrfx_cfg##idx, POST_KERNEL, CONFIG_I2S_INIT_PRIORITY, \ - &tdm_nrf_drv_api); - -/* Execute macro f(x) for all instances. */ -#define TDM_FOR_EACH_INSTANCE(f, sep, off_code, ...) \ - NRFX_FOREACH_PRESENT(TDM, f, sep, off_code, __VA_ARGS__) - -#define COND_TDM_NRFX_DEVICE(unused, prefix, i, _) \ - IF_ENABLED(CONFIG_HAS_HW_NRF_TDM##prefix##i, (TDM_NRFX_DEVICE(prefix##i);)) - -TDM_FOR_EACH_INSTANCE(COND_TDM_NRFX_DEVICE, (), ()) From e22c04216256e370e375a481a0685ad6ffa843c1 Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Wed, 27 Nov 2024 16:19:17 +0100 Subject: [PATCH 06/21] [nrf fromlist] drivers: i2s: Add support for nRF TDM peripherals Add a shim that allows using the nRF TDM (Time division multiplexed audio interface) HAL by I2S Zephyr API. Upstream PR #: 82144 Signed-off-by: Adam Kondraciuk --- drivers/i2s/CMakeLists.txt | 1 + drivers/i2s/Kconfig.nrfx | 22 + drivers/i2s/i2s_nrfx_tdm.c | 1117 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1140 insertions(+) create mode 100644 drivers/i2s/i2s_nrfx_tdm.c diff --git a/drivers/i2s/CMakeLists.txt b/drivers/i2s/CMakeLists.txt index 7857b6e863b5..43284026f314 100644 --- a/drivers/i2s/CMakeLists.txt +++ b/drivers/i2s/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_I2S_STM32 i2s_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_I2S_LITEX i2s_litex.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_FLEXCOMM i2s_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_I2S_NRFX i2s_nrfx.c) +zephyr_library_sources_ifdef(CONFIG_TDM_NRF i2s_nrfx_tdm.c) zephyr_library_sources_ifdef(CONFIG_I2S_MCUX_SAI i2s_mcux_sai.c) zephyr_library_sources_ifdef(CONFIG_I2S_ESP32 i2s_esp32.c) zephyr_library_sources_ifdef(CONFIG_I2S_TEST i2s_test.c) diff --git a/drivers/i2s/Kconfig.nrfx b/drivers/i2s/Kconfig.nrfx index b36f3eb9c641..92966c306baf 100644 --- a/drivers/i2s/Kconfig.nrfx +++ b/drivers/i2s/Kconfig.nrfx @@ -22,3 +22,25 @@ config I2S_NRFX_TX_BLOCK_COUNT default 4 endif # I2S_NRFX + +menuconfig TDM_NRF + bool "nRF TDM nrfx driver" + default y + depends on DT_HAS_NORDIC_NRF_TDM_ENABLED + select NRFX_TDM130 if HAS_HW_NRF_TDM130 + select CLOCK_CONTROL + select PINCTRL + help + Enable support for nrfx TDM driver for nRF MCU series. + +if TDM_NRF + +config TDM_NRFX_RX_BLOCK_COUNT + int "RX queue length" + default 4 + +config TDM_NRFX_TX_BLOCK_COUNT + int "TX queue length" + default 4 + +endif # TDM_NRF diff --git a/drivers/i2s/i2s_nrfx_tdm.c b/drivers/i2s/i2s_nrfx_tdm.c new file mode 100644 index 000000000000..1116b22e94b7 --- /dev/null +++ b/drivers/i2s/i2s_nrfx_tdm.c @@ -0,0 +1,1117 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(tdm_nrfx, CONFIG_I2S_LOG_LEVEL); + +/* The application must provide buffers that are to be used in the next + * part of the transfer. + */ +#define NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED BIT(0) + +/* The TDM peripheral has been stopped and all buffers that were passed + * to the driver have been released. + */ +#define NRFX_TDM_STATUS_TRANSFER_STOPPED BIT(1) + +#define NODE_AUDIOPLL DT_NODELABEL(audiopll) + +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) +static const struct device *audiopll = DEVICE_DT_GET(NODE_AUDIOPLL); +const struct nrf_clock_spec aclk_spec = { + .frequency = DT_PROP_OR(NODE_AUDIOPLL, frequency, 0), + .accuracy = 0, + .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, +}; +#endif + +typedef struct { + uint32_t *p_rx_buffer; + uint32_t const *p_tx_buffer; + void *p_tx_mem_slab; + void *p_rx_mem_slab; + uint16_t buffer_size; +} tdm_buffers_t; + +typedef void (*tdm_data_handler_t)(tdm_buffers_t const *p_released, uint32_t status); + +typedef struct { + tdm_data_handler_t handler; + bool use_rx: 1; + bool use_tx: 1; + bool rx_ready: 1; + bool tx_ready: 1; + bool buffers_needed: 1; + bool buffers_reused: 1; + tdm_buffers_t next_buffers; + tdm_buffers_t current_buffers; +} tdm_ctrl_t; + +struct stream_cfg { + struct i2s_config cfg; + nrf_tdm_config_t nrfx_cfg; +}; + +struct tdm_buf { + void *mem_block; + size_t size; + void *dmm_buf; +}; + +struct tdm_drv_cfg { + tdm_data_handler_t data_handler; + const struct pinctrl_dev_config *pcfg; + NRF_TDM_Type *p_reg; + void *mem_reg; + tdm_ctrl_t *control_data; + uint32_t mck_frequency; + uint32_t pclk_frequency; + enum clock_source { + PCLK, + ACLK + } clk_src; +}; + +struct tdm_drv_data { + struct onoff_client clk_cli; + struct stream_cfg tx; + struct k_msgq tx_queue; + struct stream_cfg rx; + struct k_msgq rx_queue; + const struct tdm_drv_cfg *drv_cfg; + const uint32_t *last_tx_buffer; + void *last_tx_mem_slab; + enum i2s_state state; + enum i2s_dir active_dir; + bool stop; /* stop after the current (TX or RX) block */ + bool discard_rx; /* discard further RX blocks */ + volatile bool next_tx_buffer_needed; + bool tx_configured: 1; + bool rx_configured: 1; + bool request_clock: 1; +}; + +static int audio_clock_request(struct tdm_drv_data *drv_data) +{ +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) + return nrf_clock_control_request(audiopll, &aclk_spec, &drv_data->clk_cli); +#else + (void)drv_data; + + return -ENOTSUP; +#endif +} + +static int audio_clock_release(void) +{ +#if DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL) + return nrf_clock_control_release(audiopll, &aclk_spec); +#else + return -ENOTSUP; +#endif +} + +void tdm_irq_handler(const struct device *dev) +{ + const struct tdm_drv_cfg *drv_cfg = dev->config; + NRF_TDM_Type *p_reg = drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_cfg->control_data; + uint32_t event_mask = 0; + + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_MAXCNT)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_MAXCNT); + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_TXPTRUPD)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_TXPTRUPD); + ctrl_data->tx_ready = true; + if (ctrl_data->use_tx && ctrl_data->buffers_needed) { + ctrl_data->buffers_reused = true; + } + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_RXPTRUPD)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_RXPTRUPD); + ctrl_data->rx_ready = true; + if (ctrl_data->use_rx && ctrl_data->buffers_needed) { + ctrl_data->buffers_reused = true; + } + } + if (nrf_tdm_event_check(p_reg, NRF_TDM_EVENT_STOPPED)) { + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); + event_mask |= NRFY_EVENT_TO_INT_BITMASK(NRF_TDM_EVENT_STOPPED); + nrf_tdm_int_disable(p_reg, NRF_TDM_INT_STOPPED_MASK_MASK); + nrf_tdm_disable(p_reg); + /* When stopped, release all buffers, including these scheduled for + * the next part of the transfer, and signal that the transfer has + * finished. + */ + ctrl_data->handler(&ctrl_data->current_buffers, 0); + ctrl_data->handler(&ctrl_data->next_buffers, NRFX_TDM_STATUS_TRANSFER_STOPPED); + } else { + /* Check if the requested transfer has been completed: + * - full-duplex mode + */ + if ((ctrl_data->use_tx && ctrl_data->use_rx && ctrl_data->tx_ready && + ctrl_data->rx_ready) || + /* - TX only mode */ + (!ctrl_data->use_rx && ctrl_data->tx_ready) || + /* - RX only mode */ + (!ctrl_data->use_tx && ctrl_data->rx_ready)) { + ctrl_data->tx_ready = false; + ctrl_data->rx_ready = false; + + /* If the application did not supply the buffers for the next + * part of the transfer until this moment, the current buffers + * cannot be released, since the I2S peripheral already started + * using them. Signal this situation to the application by + * passing NULL instead of the structure with released buffers. + */ + if (ctrl_data->buffers_reused) { + ctrl_data->buffers_reused = false; + /* This will most likely be set at this point. However, there is + * a small time window between TXPTRUPD and RXPTRUPD events, + * and it is theoretically possible that next buffers will be + * set in this window, so to be sure this flag is set to true, + * set it explicitly. + */ + ctrl_data->buffers_needed = true; + ctrl_data->handler(NULL, NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); + } else { + /* Buffers that have been used by the I2S peripheral (current) + * are now released and will be returned to the application, + * and the ones scheduled to be used as next become the current + * ones. + */ + tdm_buffers_t released_buffers = ctrl_data->current_buffers; + + ctrl_data->current_buffers = ctrl_data->next_buffers; + ctrl_data->next_buffers.p_rx_buffer = NULL; + ctrl_data->next_buffers.p_tx_buffer = NULL; + ctrl_data->buffers_needed = true; + ctrl_data->handler(&released_buffers, + NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED); + } + } + } +} + +static uint32_t div_calculate(uint32_t src_freq, uint32_t requested_clk_freq) +{ + enum { + MCKCONST = 1048576 + }; + /* As specified in the PS: + * + * DIV = 4096 * floor(f_MCK * 1048576 / + * (f_source + f_MCK / 2)) + * f_actual = f_source / + * floor(1048576 * 4096 / DIV) + */ + + uint32_t ck_div = (uint32_t)(((uint64_t)requested_clk_freq * MCKCONST) / + (src_freq + requested_clk_freq / 2)); + return (ck_div * 4096); +} + +static bool get_next_tx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) +{ + struct tdm_buf buf; + int ret = k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT); + + if (ret != 0) { + return false; + } + buffers->p_tx_buffer = buf.dmm_buf; + buffers->p_tx_mem_slab = buf.mem_block; + buffers->buffer_size = buf.size / sizeof(uint32_t); + return true; +} + +static bool get_next_rx_buffer(struct tdm_drv_data *drv_data, tdm_buffers_t *buffers) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + int ret = k_mem_slab_alloc(drv_data->rx.cfg.mem_slab, &buffers->p_rx_mem_slab, K_NO_WAIT); + + if (ret < 0) { + LOG_ERR("Failed to allocate next RX buffer: %d", ret); + return false; + } + ret = dmm_buffer_in_prepare(drv_cfg->mem_reg, buffers->p_rx_mem_slab, + buffers->buffer_size * sizeof(uint32_t), + (void **)&buffers->p_rx_buffer); + if (ret < 0) { + LOG_ERR("Failed to prepare buffer: %d", ret); + return false; + } + + return true; +} + +static void free_tx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + (void)dmm_buffer_out_release(drv_cfg->mem_reg, buf->dmm_buf); + k_mem_slab_free(drv_data->tx.cfg.mem_slab, buf->mem_block); + LOG_DBG("Freed TX %p", buf->mem_block); +} + +static void free_rx_buffer(struct tdm_drv_data *drv_data, struct tdm_buf *buf) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf->mem_block, buf->size, buf->dmm_buf); + k_mem_slab_free(drv_data->rx.cfg.mem_slab, buf->mem_block); + LOG_DBG("Freed RX %p", buf->mem_block); +} + +static void tdm_start(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_initial_buffers) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + + __ASSERT_NO_MSG(p_initial_buffers->p_rx_buffer != NULL || + p_initial_buffers->p_tx_buffer != NULL); + ctrl_data->use_rx = (p_initial_buffers->p_rx_buffer != NULL); + ctrl_data->use_tx = (p_initial_buffers->p_tx_buffer != NULL); + ctrl_data->rx_ready = false; + ctrl_data->tx_ready = false; + ctrl_data->buffers_needed = false; + ctrl_data->buffers_reused = false; + + ctrl_data->next_buffers = *p_initial_buffers; + ctrl_data->current_buffers.p_rx_buffer = NULL; + ctrl_data->current_buffers.p_tx_buffer = NULL; + nrf_tdm_enable(p_reg); + + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + + nrf_tdm_int_enable( + p_reg, + (p_initial_buffers->p_rx_buffer ? NRF_TDM_INT_RXPTRUPD_MASK_MASK : 0UL) | + (p_initial_buffers->p_tx_buffer ? NRF_TDM_INT_TXPTRUPD_MASK_MASK : 0UL) | + NRF_TDM_INT_STOPPED_MASK_MASK); + + nrf_tdm_tx_count_set(p_reg, p_initial_buffers->buffer_size); + nrf_tdm_rx_count_set(p_reg, p_initial_buffers->buffer_size); + nrf_tdm_rx_buffer_set(p_reg, p_initial_buffers->p_rx_buffer); + nrf_tdm_tx_buffer_set(p_reg, p_initial_buffers->p_tx_buffer); + nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_START); +} + +static void tdm_stop(NRF_TDM_Type *p_reg) +{ + nrf_tdm_int_disable(p_reg, NRF_TDM_INT_RXPTRUPD_MASK_MASK | NRF_TDM_INT_TXPTRUPD_MASK_MASK); + + nrf_tdm_task_trigger(p_reg, NRF_TDM_TASK_STOP); +} + +static bool next_buffers_set(struct tdm_drv_data *drv_data, tdm_buffers_t const *p_buffers) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + nrf_tdm_rxtxen_t dir = NRF_TDM_RXTXEN_DUPLEX; + + __ASSERT_NO_MSG(p_buffers->p_rx_buffer != NULL || p_buffers->p_tx_buffer != NULL); + + if (!ctrl_data->buffers_needed) { + return false; + } + + nrf_tdm_tx_count_set(p_reg, p_buffers->buffer_size); + nrf_tdm_rx_count_set(p_reg, p_buffers->buffer_size); + nrf_tdm_rx_buffer_set(p_reg, p_buffers->p_rx_buffer); + nrf_tdm_tx_buffer_set(p_reg, p_buffers->p_tx_buffer); + + if (p_buffers->p_rx_buffer == NULL) { + dir = NRF_TDM_RXTXEN_TX; + } else if (p_buffers->p_tx_buffer == NULL) { + dir = NRF_TDM_RXTXEN_RX; + } + nrf_tdm_transfer_direction_set(p_reg, dir); + + ctrl_data->next_buffers = *p_buffers; + ctrl_data->buffers_needed = false; + + return true; +} + +static bool supply_next_buffers(struct tdm_drv_data *drv_data, tdm_buffers_t *next) +{ + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + + if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */ + if (!get_next_rx_buffer(drv_data, next)) { + drv_data->state = I2S_STATE_ERROR; + tdm_stop(drv_cfg->p_reg); + return false; + } + /* Set buffer size if there is no TX buffer (which effectively + * controls how many bytes will be received). + */ + if (drv_data->active_dir == I2S_DIR_RX) { + next->buffer_size = drv_data->rx.cfg.block_size / sizeof(uint32_t); + } + } + + drv_data->last_tx_buffer = next->p_tx_buffer; + drv_data->last_tx_mem_slab = next->p_tx_mem_slab; + + LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer); + return next_buffers_set(drv_data, next); +} + +static void purge_queue(const struct device *dev, enum i2s_dir dir) +{ + struct tdm_drv_data *drv_data = dev->data; + struct tdm_buf buf; + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + while (k_msgq_get(&drv_data->tx_queue, &buf, K_NO_WAIT) == 0) { + free_tx_buffer(drv_data, &buf); + } + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + while (k_msgq_get(&drv_data->rx_queue, &buf, K_NO_WAIT) == 0) { + free_rx_buffer(drv_data, &buf); + } + } +} + +static void tdm_uninit(struct tdm_drv_data *drv_data) +{ + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + + tdm_stop(p_reg); + NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg)); +} + +static int tdm_nrfx_configure(const struct device *dev, enum i2s_dir dir, + const struct i2s_config *tdm_cfg) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + nrf_tdm_config_t nrfx_cfg; + uint32_t chan_mask = 0; + + if (drv_data->state != I2S_STATE_READY) { + LOG_ERR("Cannot configure in state: %d", drv_data->state); + return -EINVAL; + } + + if (tdm_cfg->frame_clk_freq == 0) { /* -> reset state */ + purge_queue(dev, dir); + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + drv_data->tx_configured = false; + memset(&drv_data->tx, 0, sizeof(drv_data->tx)); + } + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + drv_data->rx_configured = false; + memset(&drv_data->rx, 0, sizeof(drv_data->rx)); + } + return 0; + } + + __ASSERT_NO_MSG(tdm_cfg->mem_slab != NULL && tdm_cfg->block_size != 0); + + if ((tdm_cfg->block_size % sizeof(uint32_t)) != 0) { + LOG_ERR("This device can transfer only full 32-bit words"); + return -EINVAL; + } + + switch (tdm_cfg->word_size) { + case 8: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_8BIT; + break; + case 16: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_16BIT; + break; + case 24: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_24BIT; + break; + case 32: + nrfx_cfg.sample_width = NRF_TDM_SWIDTH_32BIT; + break; + default: + LOG_ERR("Unsupported word size: %u", tdm_cfg->word_size); + return -EINVAL; + } + + switch (tdm_cfg->format & I2S_FMT_DATA_FORMAT_MASK) { + case I2S_FMT_DATA_FORMAT_I2S: + nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_NEGEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_1CK; + break; + case I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED: + nrfx_cfg.alignment = NRF_TDM_ALIGN_LEFT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; + break; + case I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED: + nrfx_cfg.alignment = NRF_TDM_ALIGN_RIGHT; + nrfx_cfg.fsync_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.sck_polarity = NRF_TDM_POLARITY_POSEDGE; + nrfx_cfg.fsync_duration = NRF_TDM_FSYNC_DURATION_CHANNEL; + nrfx_cfg.channel_delay = NRF_TDM_CHANNEL_DELAY_NONE; + break; + default: + LOG_ERR("Unsupported data format: 0x%02x", tdm_cfg->format); + return -EINVAL; + } + + if ((tdm_cfg->format & I2S_FMT_DATA_ORDER_LSB) || (tdm_cfg->format & I2S_FMT_BIT_CLK_INV) || + (tdm_cfg->format & I2S_FMT_FRAME_CLK_INV)) { + LOG_ERR("Unsupported stream format: 0x%02x", tdm_cfg->format); + return -EINVAL; + } + + if (tdm_cfg->channels == 2) { + nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_2; + } else if (tdm_cfg->channels == 1) { + nrfx_cfg.num_of_channels = NRF_TDM_CHANNELS_COUNT_1; + } else { + LOG_ERR("Unsupported number of channels: %u", tdm_cfg->channels); + return -EINVAL; + } + chan_mask = BIT_MASK(tdm_cfg->channels); + + if ((tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && + (tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { + nrfx_cfg.mode = NRF_TDM_MODE_SLAVE; + } else if (!(tdm_cfg->options & I2S_OPT_BIT_CLK_SLAVE) && + !(tdm_cfg->options & I2S_OPT_FRAME_CLK_SLAVE)) { + nrfx_cfg.mode = NRF_TDM_MODE_MASTER; + } else { + LOG_ERR("Unsupported operation mode: 0x%02x", tdm_cfg->options); + return -EINVAL; + } + + nrfx_cfg.mck_setup = 0; + if (nrfx_cfg.mode == NRF_TDM_MODE_MASTER) { + uint32_t sck = tdm_cfg->word_size * tdm_cfg->frame_clk_freq * tdm_cfg->channels; + const uint32_t src_freq = (drv_cfg->clk_src == ACLK) + ? DT_PROP_OR(NODE_AUDIOPLL, frequency, 0) + : drv_cfg->pclk_frequency; + + /* Unless the PCLK source is used, + * it is required to request the proper clock to be running + * before starting the transfer itself. + */ + drv_data->request_clock = (drv_cfg->clk_src != PCLK); + nrfx_cfg.sck_setup = div_calculate(src_freq, sck); + + if (((nrf_tdm_mck_pin_get(drv_cfg->p_reg) & TDM_PSEL_MCK_CONNECT_Msk) == + TDM_PSEL_MCK_CONNECT_Connected << TDM_PSEL_MCK_CONNECT_Pos) && + drv_cfg->mck_frequency != 0) { + nrfx_cfg.mck_setup = div_calculate(src_freq, drv_cfg->mck_frequency); + } + } else { + drv_data->request_clock = false; + } + + if ((tdm_cfg->options & I2S_OPT_LOOPBACK) || (tdm_cfg->options & I2S_OPT_PINGPONG)) { + LOG_ERR("Unsupported options: 0x%02x", tdm_cfg->options); + return -EINVAL; + } + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Tx0Enable_Pos); + drv_data->tx.cfg = *tdm_cfg; + drv_data->tx.nrfx_cfg = nrfx_cfg; + drv_data->tx_configured = true; + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + nrfx_cfg.channels = (chan_mask << TDM_CONFIG_CHANNEL_MASK_Rx0Enable_Pos); + drv_data->rx.cfg = *tdm_cfg; + drv_data->rx.nrfx_cfg = nrfx_cfg; + drv_data->rx_configured = true; + } + return 0; +} + +static const struct i2s_config *tdm_nrfx_config_get(const struct device *dev, enum i2s_dir dir) +{ + struct tdm_drv_data *drv_data = dev->data; + + if (dir == I2S_DIR_TX && drv_data->tx_configured) { + return &drv_data->tx.cfg; + } + if (dir == I2S_DIR_RX && drv_data->rx_configured) { + return &drv_data->rx.cfg; + } + + return NULL; +} + +static int tdm_nrfx_read(const struct device *dev, void **mem_block, size_t *size) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = drv_data->drv_cfg; + struct tdm_buf buf; + int ret; + + if (!drv_data->rx_configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + ret = k_msgq_get(&drv_data->rx_queue, &buf, + (drv_data->state == I2S_STATE_ERROR) + ? K_NO_WAIT + : SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout)); + if (ret == -ENOMSG) { + return -EIO; + } + + LOG_DBG("Released RX %p", buf.mem_block); + + if (ret == 0) { + (void)dmm_buffer_in_release(drv_cfg->mem_reg, buf.mem_block, buf.size, buf.dmm_buf); + *mem_block = buf.mem_block; + *size = buf.size; + } + return ret; +} + +static int tdm_nrfx_write(const struct device *dev, void *mem_block, size_t size) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + struct tdm_buf buf = {.mem_block = mem_block, .size = size}; + int ret; + + if (!drv_data->tx_configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + + if (drv_data->state != I2S_STATE_RUNNING && drv_data->state != I2S_STATE_READY) { + LOG_ERR("Cannot write in state: %d", drv_data->state); + return -EIO; + } + + if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) { + LOG_ERR("This device can only write blocks up to %u bytes", + drv_data->tx.cfg.block_size); + return -EIO; + } + ret = dmm_buffer_out_prepare(drv_cfg->mem_reg, buf.mem_block, buf.size, + (void **)&buf.dmm_buf); + ret = k_msgq_put(&drv_data->tx_queue, &buf, SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout)); + if (ret < 0) { + return ret; + } + + /* Check if interrupt wanted to get next TX buffer before current buffer + * was queued. Do not move this check before queuing because doing so + * opens the possibility for a race condition between this function and + * data_handler() that is called in interrupt context. + */ + if (drv_data->state == I2S_STATE_RUNNING && drv_data->next_tx_buffer_needed) { + tdm_buffers_t next = {0}; + + if (!get_next_tx_buffer(drv_data, &next)) { + /* Log error because this is definitely unexpected. + * Do not return error because the caller is no longer + * responsible for releasing the buffer. + */ + LOG_ERR("Cannot reacquire queued buffer"); + return 0; + } + + drv_data->next_tx_buffer_needed = false; + + LOG_DBG("Next TX %p", next.p_tx_buffer); + + if (!supply_next_buffers(drv_data, &next)) { + LOG_ERR("Cannot supply buffer"); + return -EIO; + } + } + return 0; +} + +static int start_transfer(struct tdm_drv_data *drv_data) +{ + tdm_buffers_t initial_buffers = {0}; + int ret = 0; + + if (drv_data->active_dir != I2S_DIR_RX && /* -> TX to be started */ + !get_next_tx_buffer(drv_data, &initial_buffers)) { + LOG_ERR("No TX buffer available"); + ret = -ENOMEM; + } else if (drv_data->active_dir != I2S_DIR_TX && /* -> RX to be started */ + !get_next_rx_buffer(drv_data, &initial_buffers)) { + /* Failed to allocate next RX buffer */ + ret = -ENOMEM; + } else { + /* It is necessary to set buffer size here only for I2S_DIR_RX, + * because only then the get_next_tx_buffer() call in the if + * condition above gets short-circuited. + */ + if (drv_data->active_dir == I2S_DIR_RX) { + initial_buffers.buffer_size = + drv_data->rx.cfg.block_size / sizeof(uint32_t); + } + + drv_data->last_tx_buffer = initial_buffers.p_tx_buffer; + drv_data->last_tx_mem_slab = initial_buffers.p_tx_mem_slab; + + tdm_start(drv_data, &initial_buffers); + } + if (ret < 0) { + tdm_uninit(drv_data); + if (drv_data->request_clock) { + (void)audio_clock_release(); + } + + if (initial_buffers.p_tx_buffer) { + struct tdm_buf buf = {.mem_block = (void *)initial_buffers.p_tx_mem_slab, + .dmm_buf = (void *)initial_buffers.p_tx_buffer, + .size = initial_buffers.buffer_size * + sizeof(uint32_t)}; + free_tx_buffer(drv_data, &buf); + } + if (initial_buffers.p_rx_buffer) { + struct tdm_buf buf = {.mem_block = initial_buffers.p_rx_mem_slab, + .dmm_buf = (void *)initial_buffers.p_rx_buffer, + .size = initial_buffers.buffer_size * + sizeof(uint32_t)}; + free_rx_buffer(drv_data, &buf); + } + + drv_data->state = I2S_STATE_ERROR; + } + return ret; +} + +static void tdm_init(struct tdm_drv_data *drv_data, nrf_tdm_config_t const *p_config, + tdm_data_handler_t handler) +{ + tdm_ctrl_t *ctrl_data = drv_data->drv_cfg->control_data; + NRF_TDM_Type *p_reg = drv_data->drv_cfg->p_reg; + + nrf_tdm_configure(p_reg, p_config); + nrf_tdm_mck_set(p_reg, p_config->mck_setup != 0); + + ctrl_data->handler = handler; + + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_RXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_TXPTRUPD); + nrf_tdm_event_clear(p_reg, NRF_TDM_EVENT_STOPPED); + NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg)); +} + +static void clock_started_callback(struct onoff_manager *mgr, struct onoff_client *cli, + uint32_t state, int res) +{ + struct tdm_drv_data *drv_data = CONTAINER_OF(cli, struct tdm_drv_data, clk_cli); + + /* The driver state can be set back to READY at this point if the DROP + * command was triggered before the clock has started. Do not start + * the actual transfer in such case. + */ + if (drv_data->state == I2S_STATE_READY) { + tdm_uninit(drv_data); + (void)audio_clock_release(); + } else { + (void)start_transfer(drv_data); + } +} + +static int trigger_start(const struct device *dev) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + int ret; + const nrf_tdm_config_t *nrfx_cfg = (drv_data->active_dir == I2S_DIR_TX) + ? &drv_data->tx.nrfx_cfg + : &drv_data->rx.nrfx_cfg; + + tdm_init(drv_data, nrfx_cfg, drv_cfg->data_handler); + + drv_data->state = I2S_STATE_RUNNING; + + nrf_tdm_sck_configure(drv_cfg->p_reg, + drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, + false); + + nrf_tdm_mck_configure(drv_cfg->p_reg, + drv_cfg->clk_src == ACLK ? NRF_TDM_SRC_ACLK : NRF_TDM_SRC_PCLK32M, + false); + /* If it is required to use certain HF clock, request it to be running + * first. If not, start the transfer directly. + */ + if (drv_data->request_clock) { + sys_notify_init_callback(&drv_data->clk_cli.notify, clock_started_callback); + ret = audio_clock_request(drv_data); + if (ret < 0) { + tdm_uninit(drv_data); + drv_data->state = I2S_STATE_READY; + + LOG_ERR("Failed to request clock: %d", ret); + return -EIO; + } + } else { + ret = start_transfer(drv_data); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int tdm_nrfx_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + bool configured = false; + bool cmd_allowed; + + /* This driver does not use the I2S_STATE_NOT_READY value. + * Instead, if a given stream is not configured, the respective + * flag (tx_configured or rx_configured) is cleared. + */ + drv_data->tx.nrfx_cfg.channels |= drv_data->rx.nrfx_cfg.channels; + drv_data->rx.nrfx_cfg.channels |= drv_data->tx.nrfx_cfg.channels; + if (dir == I2S_DIR_BOTH) { + + configured = drv_data->tx_configured && drv_data->rx_configured; + } else if (dir == I2S_DIR_TX) { + configured = drv_data->tx_configured; + } else if (dir == I2S_DIR_RX) { + configured = drv_data->rx_configured; + } + + if (!configured) { + LOG_ERR("Device is not configured"); + return -EIO; + } + + if (dir == I2S_DIR_BOTH && (memcmp(&drv_data->tx.nrfx_cfg, &drv_data->rx.nrfx_cfg, + sizeof(drv_data->rx.nrfx_cfg)) != 0 || + (drv_data->tx.cfg.block_size != drv_data->rx.cfg.block_size))) { + LOG_ERR("TX and RX configurations are different"); + return -EIO; + } + + switch (cmd) { + case I2S_TRIGGER_START: + cmd_allowed = (drv_data->state == I2S_STATE_READY); + break; + case I2S_TRIGGER_STOP: + case I2S_TRIGGER_DRAIN: + cmd_allowed = (drv_data->state == I2S_STATE_RUNNING); + break; + case I2S_TRIGGER_DROP: + cmd_allowed = configured; + break; + case I2S_TRIGGER_PREPARE: + cmd_allowed = (drv_data->state == I2S_STATE_ERROR); + break; + default: + LOG_ERR("Invalid trigger: %d", cmd); + return -EINVAL; + } + + if (!cmd_allowed) { + LOG_ERR("Not allowed"); + return -EIO; + } + + /* For triggers applicable to the RUNNING state (i.e. STOP, DRAIN, + * and DROP), ensure that the command is applied to the streams + * that are currently active (this device cannot e.g. stop only TX + * without stopping RX). + */ + if (drv_data->state == I2S_STATE_RUNNING && drv_data->active_dir != dir) { + LOG_ERR("Inappropriate trigger (%d/%d), active stream(s): %d", cmd, dir, + drv_data->active_dir); + return -EINVAL; + } + + switch (cmd) { + case I2S_TRIGGER_START: + drv_data->stop = false; + drv_data->discard_rx = false; + drv_data->active_dir = dir; + drv_data->next_tx_buffer_needed = false; + return trigger_start(dev); + + case I2S_TRIGGER_STOP: + drv_data->state = I2S_STATE_STOPPING; + drv_data->stop = true; + return 0; + + case I2S_TRIGGER_DRAIN: + drv_data->state = I2S_STATE_STOPPING; + /* If only RX is active, DRAIN is equivalent to STOP. */ + drv_data->stop = (drv_data->active_dir == I2S_DIR_RX); + return 0; + + case I2S_TRIGGER_DROP: + if (drv_data->state != I2S_STATE_READY) { + drv_data->discard_rx = true; + tdm_stop(drv_cfg->p_reg); + } + purge_queue(dev, dir); + drv_data->state = I2S_STATE_READY; + return 0; + + case I2S_TRIGGER_PREPARE: + purge_queue(dev, dir); + drv_data->state = I2S_STATE_READY; + return 0; + + default: + LOG_ERR("Invalid trigger: %d", cmd); + return -EINVAL; + } +} + +static void data_handler(const struct device *dev, const tdm_buffers_t *released, uint32_t status) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + bool stop_transfer = false; + struct tdm_buf buf = {.mem_block = NULL, .dmm_buf = NULL, .size = 0}; + + if (released != NULL) { + buf.size = released->buffer_size * sizeof(uint32_t); + } + + if (status & NRFX_TDM_STATUS_TRANSFER_STOPPED) { + if (drv_data->state == I2S_STATE_STOPPING) { + drv_data->state = I2S_STATE_READY; + } + if (drv_data->last_tx_buffer) { + /* Usually, these pointers are equal, i.e. the last TX + * buffer that were to be transferred is released by the + * driver after it stops. The last TX buffer pointer is + * then set to NULL here so that the buffer can be freed + * below, just as any other TX buffer released by the + * driver. However, it may happen that the buffer is not + * released this way, for example, when the transfer + * ends with an error because an RX buffer allocation + * fails. In such case, the last TX buffer needs to be + * freed here. + */ + + if ((released != NULL) != 0 && + (drv_data->last_tx_buffer != released->p_tx_buffer)) { + buf.dmm_buf = (void *)drv_data->last_tx_buffer; + buf.mem_block = (void *)drv_data->last_tx_mem_slab; + free_tx_buffer(drv_data, &buf); + } + drv_data->last_tx_buffer = NULL; + } + tdm_uninit(drv_data); + if (drv_data->request_clock) { + (void)audio_clock_release(); + } + } + + if (released == NULL) { + /* This means that buffers for the next part of the transfer + * were not supplied and the previous ones cannot be released + * yet, as pointers to them were latched in the I2S registers. + * It is not an error when the transfer is to be stopped (those + * buffers will be released after the transfer actually stops). + */ + if (drv_data->state != I2S_STATE_STOPPING) { + drv_data->state = I2S_STATE_ERROR; + } + tdm_stop(drv_cfg->p_reg); + return; + } + if (released->p_rx_buffer) { + buf.mem_block = (void *)released->p_rx_mem_slab; + buf.dmm_buf = (void *)released->p_rx_buffer; + if (drv_data->discard_rx) { + free_rx_buffer(drv_data, &buf); + } else { + int ret = k_msgq_put(&drv_data->rx_queue, &buf, K_NO_WAIT); + + if (ret < 0) { + LOG_ERR("No room in RX queue"); + drv_data->state = I2S_STATE_ERROR; + stop_transfer = true; + + free_rx_buffer(drv_data, &buf); + } else { + + /* If the TX direction is not active and + * the transfer should be stopped after + * the current block, stop the reception. + */ + if (drv_data->active_dir == I2S_DIR_RX && drv_data->stop) { + drv_data->discard_rx = true; + stop_transfer = true; + } + } + } + } + + if (released->p_tx_buffer) { + buf.mem_block = (void *)released->p_tx_mem_slab; + buf.dmm_buf = (void *)released->p_tx_buffer; + /* If the last buffer that was to be transferred has just been + * released, it is time to stop the transfer. + */ + if (released->p_tx_buffer == drv_data->last_tx_buffer) { + drv_data->discard_rx = true; + stop_transfer = true; + } else { + free_tx_buffer(drv_data, &buf); + } + } + + if (stop_transfer) { + tdm_stop(drv_cfg->p_reg); + } else if (status & NRFX_TDM_STATUS_NEXT_BUFFERS_NEEDED) { + tdm_buffers_t next = {0}; + + if (drv_data->active_dir != I2S_DIR_RX) { /* -> TX active */ + if (drv_data->stop) { + /* If the stream is to be stopped, don't get + * the next TX buffer from the queue, instead + * supply the one used last time (it won't be + * transferred, the stream will stop right + * before this buffer would be started again). + */ + next.p_tx_buffer = drv_data->last_tx_buffer; + next.p_tx_mem_slab = drv_data->last_tx_mem_slab; + next.buffer_size = 1; + } else if (get_next_tx_buffer(drv_data, &next)) { + /* Next TX buffer successfully retrieved from + * the queue, nothing more to do here. + */ + } else if (drv_data->state == I2S_STATE_STOPPING) { + /* If there are no more TX blocks queued and + * the current state is STOPPING (so the DRAIN + * command was triggered) it is time to finish + * the transfer. + */ + drv_data->stop = true; + /* Supply the same buffer as last time; it will + * not be transferred anyway, as the transfer + * will be stopped earlier. + */ + next.p_tx_buffer = drv_data->last_tx_buffer; + next.p_tx_mem_slab = drv_data->last_tx_mem_slab; + next.buffer_size = 1; + } else { + /* Next TX buffer cannot be supplied now. + * Defer it to when the user writes more data. + */ + drv_data->next_tx_buffer_needed = true; + return; + } + } + (void)supply_next_buffers(drv_data, &next); + } +} + +static int data_init(const struct device *dev) +{ + struct tdm_drv_data *drv_data = dev->data; + const struct tdm_drv_cfg *drv_cfg = dev->config; + + drv_data->state = I2S_STATE_READY; + int err = pinctrl_apply_state(drv_cfg->pcfg, PINCTRL_STATE_DEFAULT); + + if (err < 0) { + return err; + } + drv_data->drv_cfg = drv_cfg; + return err; +} + +static const struct i2s_driver_api tdm_nrf_drv_api = { + .configure = tdm_nrfx_configure, + .config_get = tdm_nrfx_config_get, + .read = tdm_nrfx_read, + .write = tdm_nrfx_write, + .trigger = tdm_nrfx_trigger, +}; + +#define TDM(idx) DT_NODELABEL(tdm##idx) +#define TDM_CLK_SRC(idx) DT_STRING_TOKEN(TDM(idx), clock_source) +#define PCLK_NODE(idx) DT_CLOCKS_CTLR(TDM(idx)) + +#define TDM_NRFX_DEVICE(idx) \ + static tdm_ctrl_t tdm##idx##data; \ + static struct tdm_buf tx_msgs##idx[CONFIG_TDM_NRFX_TX_BLOCK_COUNT]; \ + static struct tdm_buf rx_msgs##idx[CONFIG_TDM_NRFX_RX_BLOCK_COUNT]; \ + static void tdm_##idx##_irq_handler(const struct device *dev) \ + { \ + tdm_irq_handler(dev); \ + } \ + static void tdm_##idx##data_handler(tdm_buffers_t const *p_released, uint32_t status) \ + { \ + data_handler(DEVICE_DT_GET(TDM(idx)), p_released, status); \ + } \ + PINCTRL_DT_DEFINE(TDM(idx)); \ + static const struct tdm_drv_cfg tdm_nrfx_cfg##idx = { \ + .data_handler = tdm_##idx##data_handler, \ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(TDM(idx)), \ + .clk_src = TDM_CLK_SRC(idx), \ + .mck_frequency = DT_PROP_OR(TDM(idx), mck_frequency, 0), \ + .pclk_frequency = DT_PROP(PCLK_NODE(idx), clock_frequency), \ + .p_reg = NRF_TDM##idx, \ + .control_data = &tdm##idx##data, \ + .mem_reg = DMM_DEV_TO_REG(TDM(idx)), \ + }; \ + static struct tdm_drv_data tdm_nrfx_data##idx; \ + static int tdm_nrfx_init##idx(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_IRQN(TDM(idx)), DT_IRQ(TDM(idx), priority), \ + tdm_##idx##_irq_handler, DEVICE_DT_GET(TDM(idx)), 0); \ + \ + int err = data_init(dev); \ + if (err < 0) { \ + return err; \ + } \ + k_msgq_init(&tdm_nrfx_data##idx.tx_queue, (char *)tx_msgs##idx, \ + sizeof(struct tdm_buf), ARRAY_SIZE(tx_msgs##idx)); \ + k_msgq_init(&tdm_nrfx_data##idx.rx_queue, (char *)rx_msgs##idx, \ + sizeof(struct tdm_buf), ARRAY_SIZE(rx_msgs##idx)); \ + return 0; \ + } \ + BUILD_ASSERT(TDM_CLK_SRC(idx) != ACLK || DT_NODE_HAS_STATUS_OKAY(NODE_AUDIOPLL), \ + "Clock source ACLK requires the audiopll node."); \ + DEVICE_DT_DEFINE(TDM(idx), tdm_nrfx_init##idx, NULL, &tdm_nrfx_data##idx, \ + &tdm_nrfx_cfg##idx, POST_KERNEL, CONFIG_I2S_INIT_PRIORITY, \ + &tdm_nrf_drv_api); + +/* Execute macro f(x) for all instances. */ +#define TDM_FOR_EACH_INSTANCE(f, sep, off_code, ...) \ + NRFX_FOREACH_PRESENT(TDM, f, sep, off_code, __VA_ARGS__) + +#define COND_TDM_NRFX_DEVICE(unused, prefix, i, _) \ + IF_ENABLED(CONFIG_HAS_HW_NRF_TDM##prefix##i, (TDM_NRFX_DEVICE(prefix##i);)) + +TDM_FOR_EACH_INSTANCE(COND_TDM_NRFX_DEVICE, (), ()) From 7221211d6f275c8d43d82f7be6817f0aa75263e3 Mon Sep 17 00:00:00 2001 From: Adam Kondraciuk Date: Tue, 14 Jan 2025 14:26:06 +0100 Subject: [PATCH 07/21] [nrf fromlist] tests: drivers: i2s: Align tests to TDM peripheral Some of nRF54's has TDM peripheral instead of I2S. Upstream PR #: 82144 Signed-off-by: Adam Kondraciuk --- .../nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml | 1 + .../nrf54l20pdk_nrf54l20_cpuapp.yaml | 1 + tests/drivers/i2s/i2s_api/Kconfig | 6 ++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 31 +++++++++++++++++++ .../nrf54l20pdk_nrf54l20_cpuapp.overlay | 30 ++++++++++++++++++ tests/drivers/i2s/i2s_speed/Kconfig | 6 ++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 31 +++++++++++++++++++ .../nrf54l20pdk_nrf54l20_cpuapp.overlay | 30 ++++++++++++++++++ 8 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml index a64f8cf6398b..02acf129ef1e 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_0_9_0.yaml @@ -18,6 +18,7 @@ supported: - counter - gpio - i2c + - i2s - pwm - retained_mem - spi diff --git a/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml b/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml index d4bc78288220..2f3e9d15fb3a 100644 --- a/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml +++ b/boards/nordic/nrf54l20pdk/nrf54l20pdk_nrf54l20_cpuapp.yaml @@ -18,6 +18,7 @@ supported: - dmic - gpio - i2c + - i2s - pwm - spi - watchdog diff --git a/tests/drivers/i2s/i2s_api/Kconfig b/tests/drivers/i2s/i2s_api/Kconfig index 538157f8a794..ad08f2cc4574 100644 --- a/tests/drivers/i2s/i2s_api/Kconfig +++ b/tests/drivers/i2s/i2s_api/Kconfig @@ -15,7 +15,7 @@ config I2S_TEST_SEPARATE_DEVICES config I2S_TEST_USE_I2S_DIR_BOTH bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED help Use the I2S_DIR_BOTH enumeration value to trigger commands in test cases involving both reception and transmission. Use of this option @@ -24,7 +24,7 @@ config I2S_TEST_USE_I2S_DIR_BOTH config I2S_TEST_USE_GPIO_LOOPBACK bool "Use GPIO loopback" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED help Use wiring between the data-out and data-in pins for looping back data. This option is intended to be used for devices that do not @@ -32,7 +32,7 @@ config I2S_TEST_USE_GPIO_LOOPBACK config I2S_TEST_ALLOWED_DATA_OFFSET int "Allowed offset in received data" - default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED + default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED default 0 help Maximum allowed offset between sent and received samples. Non-zero diff --git a/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 000000000000..0625be930fa6 --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* i2s-node0 is the transmitter/receiver */ + +/ { + aliases { + i2s-node0 = &tdm130; + }; +}; + +&pinctrl { + tdm130_default_alt: tdm130_default_alt { + group1 { + psels = , + , + , + ; + }; + }; +}; + +&tdm130 { + status = "okay"; + pinctrl-0 = <&tdm130_default_alt>; + pinctrl-names = "default"; + memory-regions = <&cpuapp_dma_region>; +}; diff --git a/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay b/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay new file mode 100644 index 000000000000..09a2cb0459c8 --- /dev/null +++ b/tests/drivers/i2s/i2s_api/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* i2s-node0 is the transmitter/receiver */ + +/ { + aliases { + i2s-node0 = &tdm; + }; +}; + +&pinctrl { + tdm_default_alt: tdm_default_alt { + group1 { + psels = , + , + , + ; + }; + }; +}; + +&tdm { + status = "okay"; + pinctrl-0 = <&tdm_default_alt>; + pinctrl-names = "default"; +}; diff --git a/tests/drivers/i2s/i2s_speed/Kconfig b/tests/drivers/i2s/i2s_speed/Kconfig index 165fc6279b27..4e074f18c102 100644 --- a/tests/drivers/i2s/i2s_speed/Kconfig +++ b/tests/drivers/i2s/i2s_speed/Kconfig @@ -15,7 +15,7 @@ config I2S_TEST_SEPARATE_DEVICES config I2S_TEST_USE_I2S_DIR_BOTH bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED help Use the I2S_DIR_BOTH enumeration value to trigger commands in test cases involving both reception and transmission. Use of this option @@ -24,7 +24,7 @@ config I2S_TEST_USE_I2S_DIR_BOTH config I2S_TEST_USE_GPIO_LOOPBACK bool "Use GPIO loopback" - default y if DT_HAS_NORDIC_NRF_I2S_ENABLED + default y if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED help Use wiring between the data-out and data-in pins for looping back data. This option is intended to be used for devices that do not @@ -32,7 +32,7 @@ config I2S_TEST_USE_GPIO_LOOPBACK config I2S_TEST_ALLOWED_DATA_OFFSET int "Allowed offset in received data" - default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED + default 2 if DT_HAS_NORDIC_NRF_I2S_ENABLED || DT_HAS_NORDIC_NRF_TDM_ENABLED default 0 help Maximum allowed offset between sent and received samples. Non-zero diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 000000000000..0625be930fa6 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* i2s-node0 is the transmitter/receiver */ + +/ { + aliases { + i2s-node0 = &tdm130; + }; +}; + +&pinctrl { + tdm130_default_alt: tdm130_default_alt { + group1 { + psels = , + , + , + ; + }; + }; +}; + +&tdm130 { + status = "okay"; + pinctrl-0 = <&tdm130_default_alt>; + pinctrl-names = "default"; + memory-regions = <&cpuapp_dma_region>; +}; diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay new file mode 100644 index 000000000000..09a2cb0459c8 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf54l20pdk_nrf54l20_cpuapp.overlay @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* i2s-node0 is the transmitter/receiver */ + +/ { + aliases { + i2s-node0 = &tdm; + }; +}; + +&pinctrl { + tdm_default_alt: tdm_default_alt { + group1 { + psels = , + , + , + ; + }; + }; +}; + +&tdm { + status = "okay"; + pinctrl-0 = <&tdm_default_alt>; + pinctrl-names = "default"; +}; From d771a3a303787a9e4c4915c9705bc2735a2622ef Mon Sep 17 00:00:00 2001 From: Jonathan Nilsen Date: Tue, 22 Apr 2025 14:27:43 +0200 Subject: [PATCH 08/21] Revert "[nrf noup] moduled: hal_nordic: require nrf-regtool" This reverts commit d6cefa7cf72bfb85b7c185928044e880cf64e8f8. Signed-off-by: Jonathan Nilsen --- modules/hal_nordic/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal_nordic/CMakeLists.txt b/modules/hal_nordic/CMakeLists.txt index 6e6d5d4d741e..cce0688a9121 100644 --- a/modules/hal_nordic/CMakeLists.txt +++ b/modules/hal_nordic/CMakeLists.txt @@ -12,7 +12,7 @@ if(CONFIG_NRF_REGTOOL_GENERATE_UICR) list(APPEND nrf_regtool_components GENERATE:UICR) endif() if(DEFINED nrf_regtool_components) - find_package(nrf-regtool 9.0.1 REQUIRED + find_package(nrf-regtool 9.0.1 COMPONENTS ${nrf_regtool_components} PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool NO_CMAKE_PATH From 2d72746ebd4593c93aa4ca81d7d155ba974ba6e7 Mon Sep 17 00:00:00 2001 From: Jonathan Nilsen Date: Tue, 22 Apr 2025 14:18:50 +0200 Subject: [PATCH 09/21] [nrf fromlist] modules: hal_nordic: bump regtool to 9.1.0 Upstream PR #: 88898 Changes for this package version: * CTRLSEL is now set appropriately for the SPIS121 pins. * DPPI.LINK configuration is now skipped when processing the 'dppic130' devicetree node. Signed-off-by: Jonathan Nilsen --- modules/hal_nordic/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal_nordic/CMakeLists.txt b/modules/hal_nordic/CMakeLists.txt index cce0688a9121..11c28246828b 100644 --- a/modules/hal_nordic/CMakeLists.txt +++ b/modules/hal_nordic/CMakeLists.txt @@ -12,7 +12,7 @@ if(CONFIG_NRF_REGTOOL_GENERATE_UICR) list(APPEND nrf_regtool_components GENERATE:UICR) endif() if(DEFINED nrf_regtool_components) - find_package(nrf-regtool 9.0.1 + find_package(nrf-regtool 9.1.0 COMPONENTS ${nrf_regtool_components} PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool NO_CMAKE_PATH From 4e9869f5f21c10202fe10ae5c500c16ad65dcbcf Mon Sep 17 00:00:00 2001 From: Jonathan Nilsen Date: Tue, 22 Apr 2025 14:34:11 +0200 Subject: [PATCH 10/21] [nrf noup] modules: hal_nordic: require nrf-regtool Same as commit 6ec9d10 but with the REQUIRED keyword on its own line to attempt to avoid a merge conflict when reverting/reapplying this patch. Signed-off-by: Jonathan Nilsen --- modules/hal_nordic/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/hal_nordic/CMakeLists.txt b/modules/hal_nordic/CMakeLists.txt index 11c28246828b..f4242b2d67bf 100644 --- a/modules/hal_nordic/CMakeLists.txt +++ b/modules/hal_nordic/CMakeLists.txt @@ -13,6 +13,7 @@ if(CONFIG_NRF_REGTOOL_GENERATE_UICR) endif() if(DEFINED nrf_regtool_components) find_package(nrf-regtool 9.1.0 + REQUIRED COMPONENTS ${nrf_regtool_components} PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool NO_CMAKE_PATH From 60cac621057f133cff25fcb75679f56167d3475d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stasiak?= Date: Thu, 24 Apr 2025 12:57:41 +0200 Subject: [PATCH 11/21] Revert "[nrf fromlist] tests: drivers: clock_control_api: Adjust to nRF54L09 and nRF54L20" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0afd70bfd6e65c619b1fdfd4f08cb063922c0b3e. Signed-off-by: Michał Stasiak --- .../clock_control/clock_control_api/src/nrf_device_subsys.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 78dafa2a3b34..6d660bda9473 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -11,9 +11,8 @@ static const struct device_subsys_data subsys_data[] = { { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, .startup_us = - (IS_ENABLED(CONFIG_SOC_NRF54L20_ENGA) || - IS_ENABLED(CONFIG_SOC_NRF54L09_ENGA)) ? - 1000 : (IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? 3000 : 500) + IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? + 3000 : 500 }, #ifndef CONFIG_SOC_NRF52832 /* On nrf52832 LF clock cannot be stopped because it leads From 0955031caffedc507a775a000d608419ad3dd25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Stasiak?= Date: Thu, 24 Apr 2025 12:15:03 +0200 Subject: [PATCH 12/21] [nrf fromlist] tests: drivers: clock_control_api: move startup time to Kconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved target dependent startup time from header file to Kconfig option to allow adding new targets with new .conf file rather that modyfing source of the test. Adjusted startup time for nRF54L09 and nRF54L20. Upstream PR #: 88956 Signed-off-by: Michał Stasiak --- .../drivers/clock_control/clock_control_api/Kconfig | 13 +++++++++++++ .../boards/nrf54l09pdk_nrf54l09_cpuapp.conf | 1 + .../boards/nrf54l20pdk_nrf54l20_cpuapp.conf | 1 + .../clock_control_api/src/nrf_device_subsys.h | 4 +--- 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/drivers/clock_control/clock_control_api/Kconfig create mode 100644 tests/drivers/clock_control/clock_control_api/boards/nrf54l09pdk_nrf54l09_cpuapp.conf create mode 100644 tests/drivers/clock_control/clock_control_api/boards/nrf54l20pdk_nrf54l20_cpuapp.conf diff --git a/tests/drivers/clock_control/clock_control_api/Kconfig b/tests/drivers/clock_control/clock_control_api/Kconfig new file mode 100644 index 000000000000..dc1e976d824c --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/Kconfig @@ -0,0 +1,13 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config TEST_NRF_HF_STARTUP_TIME_US + int "Delay required for HF clock startup." + default 3000 if CONFIG_SOC_SERIES_NRF91X + default 500 + depends on SOC_FAMILY_NORDIC_NRF + help + Delay in microseconds required for high-frequency + clock startup. + +source "Kconfig.zephyr" diff --git a/tests/drivers/clock_control/clock_control_api/boards/nrf54l09pdk_nrf54l09_cpuapp.conf b/tests/drivers/clock_control/clock_control_api/boards/nrf54l09pdk_nrf54l09_cpuapp.conf new file mode 100644 index 000000000000..11d42321cbc3 --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/boards/nrf54l09pdk_nrf54l09_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_TEST_NRF_HF_STARTUP_TIME_US=1000 diff --git a/tests/drivers/clock_control/clock_control_api/boards/nrf54l20pdk_nrf54l20_cpuapp.conf b/tests/drivers/clock_control/clock_control_api/boards/nrf54l20pdk_nrf54l20_cpuapp.conf new file mode 100644 index 000000000000..11d42321cbc3 --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/boards/nrf54l20pdk_nrf54l20_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_TEST_NRF_HF_STARTUP_TIME_US=1000 diff --git a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h index 6d660bda9473..47a3060d630e 100644 --- a/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h +++ b/tests/drivers/clock_control/clock_control_api/src/nrf_device_subsys.h @@ -10,9 +10,7 @@ static const struct device_subsys_data subsys_data[] = { { .subsys = CLOCK_CONTROL_NRF_SUBSYS_HF, - .startup_us = - IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? - 3000 : 500 + .startup_us = CONFIG_TEST_NRF_HF_STARTUP_TIME_US }, #ifndef CONFIG_SOC_NRF52832 /* On nrf52832 LF clock cannot be stopped because it leads From c82f74129f225752e6f5e685262f564838538df0 Mon Sep 17 00:00:00 2001 From: Sudan Landge Date: Tue, 24 Dec 2024 12:31:52 +0000 Subject: [PATCH 13/21] [nrf fromtree] modules: avoid fetching external repo in TF-M What is changed? - Use the updated TF-M that is compatible with the Zephyr's latest Ethos-U driver repo. - Change the default behavior of TF-M builds to use Ethos driver locally fetched by Zephyr, using west update, instead of downloading it from external repo. Why is this change required? - This is to be inline with Zephyr's rules to not fetch code from external repo. Fixes #81656 Signed-off-by: Sudan Landge (cherry picked from commit 9b0623e63aa9df6ae64bf0df595e9a5d2f7871ba) --- modules/trusted-firmware-m/CMakeLists.txt | 2 ++ modules/trusted-firmware-m/Kconfig.tfm | 11 +++++++++++ west.yml | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index a34ed5fc7098..3388389b034d 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -257,6 +257,8 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DTFM_TESTS_REVISION_CHECKS=OFF) + list(APPEND TFM_CMAKE_ARGS -DETHOS_DRIVER_PATH=${CONFIG_TFM_ETHOS_DRIVER_PATH_LOCAL}) + file(MAKE_DIRECTORY ${TFM_BINARY_DIR}) add_custom_target(tfm_cmake DEPENDS ${TFM_BINARY_DIR}/CMakeCache.txt diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index 94e65f232a79..c8f8b656e287 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -311,6 +311,17 @@ config TFM_MCUBOOT_PATH_DOWNLOAD endchoice +config TFM_ETHOS_DRIVER_PATH_LOCAL + string "Path to a locally available Ethos-U driver or an empty string" + default "${ZEPHYR_HAL_ETHOS_U_MODULE_DIR}" + help + Path to a locally available Ethos-U driver to be used for TF-M builds or + an empty string to allow TF-M to automatically fetch the Ethos-U + driver from an external repository at build time. + By default Zephyr's Ethos-U driver will be used. It is present in + the hal_ethos_u module. + Alternatively, applications can point to their own paths for Ethos-U driver. + config TFM_QCBOR_PATH string prompt "Path to QCBOR or DOWNLOAD to fetch automatically" diff --git a/west.yml b/west.yml index a97e6558ffa1..969f437904ae 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: fa020a8b001843bb5a115bc4692eaf6787e3d1de + revision: 3fb1f9e536b8fae5879c482ddd0f728052c3b509 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From a97c5a16d776ea057aade81e480b6000da0e7a3b Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 4 Feb 2025 10:42:12 +0100 Subject: [PATCH 14/21] [nrf fromtree] west.yml : update modules/tee/tf-m/trusted-firmware-m Updates the https://github.com/zephyrproject-rtos/trusted-firmware-m/ to fix the build error on stm32u5 due to gcc revision Signed-off-by: Francois Ramu (cherry picked from commit 676c3d2dbb3093216cb632f8756128504a85cad7) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 969f437904ae..31c4c1e65cfb 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 3fb1f9e536b8fae5879c482ddd0f728052c3b509 + revision: 2f138475047d566f1446f62657aa546b0e0b52c2 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 6e16b34836de02622e1b342b13f3be6d96b3173f Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 17 Feb 2025 16:46:28 +1000 Subject: [PATCH 15/21] [nrf fromtree] modules: tf-m: fix build verbosity Only set CMAKE_INSTALL_MESSAGE in a single location, remove the unconditional overrides of the value in other locations. This prevents dozens of rather pointless messages appearing in the build log when CONFIG_TFM_BUILD_LOG_QUIET is set. ``` Installing: /home/jordan/code/workspace/build/nrf5340dk/... ``` Signed-off-by: Jordan Yates (cherry picked from commit b74b092f22ea29950f291104a84e3433f4efd7d6) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 31c4c1e65cfb..fbc08f2ee511 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 2f138475047d566f1446f62657aa546b0e0b52c2 + revision: 918f32d9fce5e0ee59fc33844b5317b7626ce37a path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 5b1d1dca7d10fdf681d9d715559e5f8b7cc306a6 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Wed, 26 Feb 2025 13:19:26 +0000 Subject: [PATCH 16/21] [nrf fromtree] west.yml: Update TrustedFirmware-M with Flash Area cherry-picks Update brings in flash_area_get_sector. Signed-off-by: Dominik Ermel (cherry picked from commit 789e3f340e5019c18d973b25aa281b89afae1449) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index fbc08f2ee511..be8d6bb8c807 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 918f32d9fce5e0ee59fc33844b5317b7626ce37a + revision: f04edd12b5e5de2e2291f6e0a15efc03924dcbc3 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 0ae7d3d100fe2a12b902449df46057d29e0ddb0e Mon Sep 17 00:00:00 2001 From: Georgios Vasilakis Date: Thu, 20 Feb 2025 14:35:49 +0100 Subject: [PATCH 17/21] [nrf fromtree] manifest: Bring TF-M with nRF54L15 upstream support Update west Signed-off-by: Georgios Vasilakis (cherry picked from commit d8b87d62799febd0e8327f7937133eb5a609a015) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index be8d6bb8c807..dbb0710d9da9 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: f04edd12b5e5de2e2291f6e0a15efc03924dcbc3 + revision: 25dff7328c77b51e314ef9d4625044072040c96d path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 8ac2b70671722e1ec368e5ce5eccd839a4431ea4 Mon Sep 17 00:00:00 2001 From: Georgios Vasilakis Date: Thu, 10 Apr 2025 16:50:39 +0200 Subject: [PATCH 18/21] [nrf fromtree] manifest: Bring TF-M with initial nRF54L10 support Brings some commits from the upstream TF-M which introduce nRF54L10 ns support. Signed-off-by: Georgios Vasilakis (cherry picked from commit 27174731c769a3382e53810335cfe0ef5581959e) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index dbb0710d9da9..602d5fda8679 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 25dff7328c77b51e314ef9d4625044072040c96d + revision: 857f3697ece6f44a663f007de71205a7b34974ef path: modules/tee/tf-m/trusted-firmware-m groups: - tee From 532da2eef14295953945e7dca74e476c8013a7ea Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Mon, 14 Apr 2025 16:16:22 +0300 Subject: [PATCH 19/21] [nrf fromtree] manifest: tf-m: update to 2.1.2 Update TF-M to 2.1.2 from version 2.1.1. Signed-off-by: Tomi Fontanilles (cherry picked from commit dfd0dd04c1857d038107b78b325ae992016a66df) --- submanifests/optional.yaml | 2 +- west.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 4794b5fbe1e0..0480cce6321e 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -41,7 +41,7 @@ manifest: groups: - optional - name: tf-m-tests - revision: 502ea90105ee18f20c78f710e2ba2ded0fc0756e + revision: c712761dd5391bf3f38033643d28a736cae89a19 path: modules/tee/tf-m/tf-m-tests remote: upstream groups: diff --git a/west.yml b/west.yml index 602d5fda8679..ca468e297ac9 100644 --- a/west.yml +++ b/west.yml @@ -350,7 +350,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 857f3697ece6f44a663f007de71205a7b34974ef + revision: e2288c13ee0abc16163186523897e7910b03dd31 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From a6f08b2eae4ec01312e3825d1632ecbc00832e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Tue, 22 Apr 2025 17:16:55 +0200 Subject: [PATCH 20/21] [nrf fromlist] west: runners: nrf_common: Temp hack for NRF54H20_IRON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temp hack while waiting for NRF54H20_IRON support for Network in nrfutil. Upstream PR #: 88910 Signed-off-by: Sebastian Bøe --- scripts/west_commands/runners/nrf_common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/west_commands/runners/nrf_common.py b/scripts/west_commands/runners/nrf_common.py index 75d39b271c9d..3ed8fb65a068 100644 --- a/scripts/west_commands/runners/nrf_common.py +++ b/scripts/west_commands/runners/nrf_common.py @@ -418,6 +418,10 @@ def program_hex(self): if self.hex_refers_region(xip_start, xip_end): ext_mem_erase_opt = erase_arg + # Temp hack while waiting for NRF54H20_IRON support for Network in nrfutil + if self.build_conf.get('CONFIG_SOC_NRF54H20_IRON') and core == "Network": + core = "Application" + self.op_program(self.hex_, erase_arg, ext_mem_erase_opt, defer=True, core=core) self.flush(force=False) From 310e0436a9b9648a6e1cef179755af9104c68413 Mon Sep 17 00:00:00 2001 From: Arkadiusz Balys Date: Fri, 18 Apr 2025 16:07:55 +0200 Subject: [PATCH 21/21] openthread: Move OpenThread implementation from net to modules Move OpenThread-related code from zephyr/subsys/net/l2/openthread/openthread.c to zephyr/modules/openthread/platform/openthread.c. The primary goal of this refactor is to enable the use of OpenThread as an independent module, without the necessity of Zephyr's networking layer. This change is particularly beneficial for simple applications that have their own implementation of the IEEE802.15.4 driver and do not require a networking layer. These applications can now disable Zephyr's L2 and IEEE802.15.4 shim layers and directly use the OpenThread module, saving valuable kilobytes of memory. In this approach if the CONFIG_NET_L2_OPENTHREAD Kconfig option is set, Zephyr's L2 and IEEE802.15.4 layer will be used, and everything will function as before. The main difference is the Zephyr's L2 layer now uses the OpenThread module, no longer implementing it. While most of the functions in include/net/openthread.h have been deprecated, they are still available for use to maintain backwards compatibility. Signed-off-by: Arkadiusz Balys --- include/zephyr/net/openthread.h | 183 ++++++- modules/openthread/platform/CMakeLists.txt | 1 + modules/openthread/platform/openthread.c | 425 ++++++++++++++++ modules/openthread/platform/shell.c | 5 +- subsys/net/l2/openthread/CMakeLists.txt | 2 +- subsys/net/l2/openthread/openthread.c | 513 ++++---------------- subsys/net/l2/openthread/openthread_utils.c | 22 +- 7 files changed, 688 insertions(+), 463 deletions(-) create mode 100644 modules/openthread/platform/openthread.c diff --git a/include/zephyr/net/openthread.h b/include/zephyr/net/openthread.h index 0d83109e15b9..e20fb5d7015f 100644 --- a/include/zephyr/net/openthread.h +++ b/include/zephyr/net/openthread.h @@ -21,10 +21,11 @@ */ #include - #include +#include #include +#include #ifdef __cplusplus extern "C" { @@ -44,8 +45,10 @@ struct pkt_list_elem { * @brief OpenThread l2 private data. */ struct openthread_context { - /** Pointer to OpenThread stack instance */ - otInstance *instance; + /** @deprecated Pointer to OpenThread stack instance. This is deprecated and will be removed + * in a future release. This field must not be used outside anymore. + */ + __deprecated otInstance *instance; /** Pointer to OpenThread network interface */ struct net_if *iface; @@ -62,22 +65,40 @@ struct openthread_context { /** Array for storing net_pkt for OpenThread internal usage */ struct pkt_list_elem pkt_list[CONFIG_OPENTHREAD_PKT_LIST_SIZE]; - /** A mutex to protect API calls from being preempted. */ - struct k_mutex api_lock; + /** @deprecated A mutex to protect API calls from being preempted. This is deprecated and + * will be removed in a future release. This field must not be used outside anymore. + */ + __deprecated struct k_mutex api_lock; - /** A work queue for all OpenThread activity */ - struct k_work_q work_q; + /** @deprecated A work queue for all OpenThread activity. This is deprecated and will be + * removed in a future release. This field must not be used outside anymore. + */ + __deprecated struct k_work_q work_q; - /** Work object for OpenThread internal usage */ - struct k_work api_work; + /** @deprecated Work object for OpenThread internal usage. This is deprecated and will be + * removed in a future release. This field must not be used outside anymore. + */ + __deprecated struct k_work api_work; - /** A list for state change callbacks */ + /** @deprecated A list for state change callbacks. This is deprecated and will be removed in + * a future release. + */ sys_slist_t state_change_cbs; }; /** * INTERNAL_HIDDEN @endcond */ +/** + * @brief The common callback type for IPv4 (translated by NAT64) and IPv6 datagrams. + * + * This callback is called when a datagram is received. + * + * @param aMessage The message to receive. + * @param aContext The context to pass to the callback. + */ +typedef void (*openthread_receive_cb)(otMessage *aMessage, void *aContext); + /** OpenThread state change callback */ /** @@ -88,6 +109,38 @@ struct openthread_context { * are unique pointers of struct openthread_state_changed_cb. * Beware such structure should not be allocated on stack. */ +struct openthread_state_changed_callback { + /** + * @brief Callback for notifying configuration or state changes. + * + * @param flags as per OpenThread otStateChangedCallback() aFlags parameter. + * See https://openthread.io/reference/group/api-instance#otstatechangedcallback + * @param instance the OpenThread instance the callback is registered with. + * @param user_data Data to pass to the callback. + */ + void (*openthread_state_changed_cb)(otChangedFlags flags, struct otInstance *instance, + void *user_data); + + /** User data if required */ + void *user_data; + + /** + * Internally used field for list handling + * - user must not directly modify + */ + sys_snode_t node; +}; + +/** + * @deprecated use @ref openthread_state_changed_callback instead. + * + * @brief OpenThread state change callback structure + * + * Used to register a callback in the callback list. As many + * callbacks as needed can be added as long as each of them + * are unique pointers of struct openthread_state_changed_cb. + * Beware such structure should not be allocated on stack. + */ struct openthread_state_changed_cb { /** * @brief Callback for notifying configuration or state changes. @@ -111,23 +164,44 @@ struct openthread_state_changed_cb { }; /** + * @brief Registers callbacks which will be called when certain configuration + * or state changes occur within OpenThread. + * + * @param cb callback struct to register. + */ +int openthread_state_change_callback_register(struct openthread_state_changed_callback *cb); + +/** + * @brief Unregisters OpenThread configuration or state changed callbacks. + * + * @param cb callback struct to unregister. + */ +int openthread_state_change_callback_unregister(struct openthread_state_changed_callback *cb); + +/** + * @deprecated use @ref openthread_platform_state_changed_cb_register from modules/openthread + * instead. + * * @brief Registers callbacks which will be called when certain configuration * or state changes occur within OpenThread. * * @param ot_context the OpenThread context to register the callback with. * @param cb callback struct to register. */ -int openthread_state_changed_cb_register(struct openthread_context *ot_context, - struct openthread_state_changed_cb *cb); +__deprecated int openthread_state_changed_cb_register(struct openthread_context *ot_context, + struct openthread_state_changed_cb *cb); /** + * @deprecated use @ref openthread_platform_state_changed_cb_unregister from modules/openthread + * instead. + * * @brief Unregisters OpenThread configuration or state changed callbacks. * * @param ot_context the OpenThread context to unregister the callback from. * @param cb callback struct to unregister. */ -int openthread_state_changed_cb_unregister(struct openthread_context *ot_context, - struct openthread_state_changed_cb *cb); +__deprecated int openthread_state_changed_cb_unregister(struct openthread_context *ot_context, + struct openthread_state_changed_cb *cb); /** * @brief Get OpenThread thread identification. @@ -151,6 +225,46 @@ struct openthread_context *openthread_get_default_context(void); struct otInstance *openthread_get_default_instance(void); /** + * @brief Initialize the OpenThread module. + * + * This function: + * - Initializes the OpenThread module. + * - Creates an OpenThread single instance. + * - Starts the shell. + * - Enables the UART and NCP HDLC for coprocessor purposes. + * - Initializes the NAT64 translator. + * - Creates a work queue for the OpenThread module. + * - Initializes the state change callback list. + * + * @note This function is automatically called by Zephyr's networking layer. + * If you want to initialize the OpenThread independently, call this function + * in your application init code and then call openthread_run() to prepare and run + * the OpenThread network. + * + * @param rx_handler The receive callback for the OpenThread module. + * @param context The context to pass to the callback. + * @return true if initialization succeeded, false otherwise. + */ +bool openthread_init(openthread_receive_cb rx_handler, void *context); + +/** + * @brief Runs the OpenThread network. + * + * @details Prepares the OpenThread network and enables it. + * Depends on active settings: it uses stored network configuration, + * start joining procedure or uses default network configuration. Additionally + * when the device is MTD, it sets the SED mode to properly attach the network. + */ +int openthread_run(void); + +/** + * @brief Disables the OpenThread network. + */ +int openthread_stop(void); + +/** + * @deprecated use @ref openthread_run instead. + * * @brief Starts the OpenThread network. * * @details Depends on active settings: it uses stored network configuration, @@ -159,7 +273,7 @@ struct otInstance *openthread_get_default_instance(void); * * @param ot_context */ -int openthread_start(struct openthread_context *ot_context); +__deprecated int openthread_start(struct openthread_context *ot_context); /** * @brief Lock internal mutex before accessing OT API. @@ -170,9 +284,40 @@ int openthread_start(struct openthread_context *ot_context); * * @param ot_context Context to lock. */ -void openthread_api_mutex_lock(struct openthread_context *ot_context); +void openthread_mutex_lock(void); /** + * @brief Try to lock internal mutex before accessing OT API. + * + * @details This function behaves like openthread_mutex_lock() provided that + * the internal mutex is unlocked. Otherwise, it exists immediately and returns + * a negative value. + */ +int openthread_mutex_try_lock(void); + +/** + * @brief Unlock internal mutex after accessing OT API. + * + * @param ot_context Context to unlock. + */ +void openthread_mutex_unlock(void); + +/** + * @deprecated use @ref openthread_mutex_lock. + * + * @brief Lock internal mutex before accessing OT API. + * + * @details OpenThread API is not thread-safe, therefore before accessing any + * API function, it's needed to lock the internal mutex, to prevent the + * OpenThread thread from preempting the API call. + * + * @param ot_context Context to lock. + */ +__deprecated void openthread_api_mutex_lock(struct openthread_context *ot_context); + +/** + * @deprecated use @ref openthread_mutex_try_lock instead. + * * @brief Try to lock internal mutex before accessing OT API. * * @details This function behaves like openthread_api_mutex_lock() provided that @@ -183,14 +328,16 @@ void openthread_api_mutex_lock(struct openthread_context *ot_context); * @retval 0 On success. * @retval <0 On failure. */ -int openthread_api_mutex_try_lock(struct openthread_context *ot_context); +__deprecated int openthread_api_mutex_try_lock(struct openthread_context *ot_context); /** + * @deprecated use @ref openthread_mutex_unlock instead. + * * @brief Unlock internal mutex after accessing OT API. * * @param ot_context Context to unlock. */ -void openthread_api_mutex_unlock(struct openthread_context *ot_context); +__deprecated void openthread_api_mutex_unlock(struct openthread_context *ot_context); /** @cond INTERNAL_HIDDEN */ diff --git a/modules/openthread/platform/CMakeLists.txt b/modules/openthread/platform/CMakeLists.txt index bedd51c84eef..188db2da05aa 100644 --- a/modules/openthread/platform/CMakeLists.txt +++ b/modules/openthread/platform/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library_sources( entropy.c misc.c platform.c + openthread.c ) zephyr_library_sources_ifndef(CONFIG_HDLC_RCP_IF diff --git a/modules/openthread/platform/openthread.c b/modules/openthread/platform/openthread.c new file mode 100644 index 000000000000..62d1a5056870 --- /dev/null +++ b/modules/openthread/platform/openthread.c @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file + * This file implements the OpenThread module initialization and state change handling. + * + */ + +#include +LOG_MODULE_REGISTER(net_openthread_platform, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include "platform-zephyr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) +#include +#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ + +#define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE) + +#if defined(CONFIG_OPENTHREAD_THREAD_PREEMPTIVE) +#define OT_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#else +#define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#endif + +#if defined(CONFIG_OPENTHREAD_NETWORK_NAME) +#define OT_NETWORK_NAME CONFIG_OPENTHREAD_NETWORK_NAME +#else +#define OT_NETWORK_NAME "" +#endif + +#if defined(CONFIG_OPENTHREAD_CHANNEL) +#define OT_CHANNEL CONFIG_OPENTHREAD_CHANNEL +#else +#define OT_CHANNEL 0 +#endif + +#if defined(CONFIG_OPENTHREAD_PANID) +#define OT_PANID CONFIG_OPENTHREAD_PANID +#else +#define OT_PANID 0 +#endif + +#if defined(CONFIG_OPENTHREAD_XPANID) +#define OT_XPANID CONFIG_OPENTHREAD_XPANID +#else +#define OT_XPANID "" +#endif + +#if defined(CONFIG_OPENTHREAD_NETWORKKEY) +#define OT_NETWORKKEY CONFIG_OPENTHREAD_NETWORKKEY +#else +#define OT_NETWORKKEY "" +#endif + +#if defined(CONFIG_OPENTHREAD_JOINER_PSKD) +#define OT_JOINER_PSKD CONFIG_OPENTHREAD_JOINER_PSKD +#else +#define OT_JOINER_PSKD "" +#endif + +#if defined(CONFIG_OPENTHREAD_PLATFORM_INFO) +#define OT_PLATFORM_INFO CONFIG_OPENTHREAD_PLATFORM_INFO +#else +#define OT_PLATFORM_INFO "" +#endif + +#if defined(CONFIG_OPENTHREAD_POLL_PERIOD) +#define OT_POLL_PERIOD CONFIG_OPENTHREAD_POLL_PERIOD +#else +#define OT_POLL_PERIOD 0 +#endif + +#define ZEPHYR_PACKAGE_NAME "Zephyr" +#define PACKAGE_VERSION KERNEL_VERSION_STRING + +/* Global variables to store the OpenThread module context */ +static otInstance *openthread_instance; +static struct k_mutex openthread_lock; +static struct k_work_q openthread_work_q; +static struct k_work openthread_work; +static sys_slist_t openthread_state_change_cbs; + +K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE); + +k_tid_t openthread_thread_id_get(void) +{ + return (k_tid_t)&openthread_work_q.thread; +} + +static int ncp_hdlc_send(const uint8_t *buf, uint16_t len) +{ + otError err; + + err = otPlatUartSend(buf, len); + if (err != OT_ERROR_NONE) { + return 0; + } + + return len; +} + +static void openthread_process(struct k_work *work) +{ + ARG_UNUSED(work); + + openthread_mutex_lock(); + + while (otTaskletsArePending(openthread_instance)) { + otTaskletsProcess(openthread_instance); + } + + otSysProcessDrivers(openthread_instance); + + openthread_mutex_unlock(); +} + +static void ot_joiner_start_handler(otError error, void *context) +{ + ARG_UNUSED(context); + + switch (error) { + case OT_ERROR_NONE: + LOG_INF("Join success"); + error = otThreadSetEnabled(openthread_instance, true); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to start the OpenThread network [%d]", error); + } + + break; + default: + LOG_ERR("Join failed [%d]", error); + break; + } +} + +static void ot_state_changed_handler(uint32_t flags, void *context) +{ + ARG_UNUSED(context); + + struct openthread_state_changed_callback *entry, *next; + + bool is_up = otIp6IsEnabled(openthread_instance); + + LOG_INF("State changed! Flags: 0x%08" PRIx32 " Current role: %s Ip6: %s", flags, + otThreadDeviceRoleToString(otThreadGetDeviceRole(openthread_instance)), + (is_up ? "up" : "down")); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&openthread_state_change_cbs, entry, next, node) { + if (entry->openthread_state_changed_cb != NULL) { + entry->openthread_state_changed_cb(flags, openthread_instance, + entry->user_data); + } + } +} + +int openthread_state_change_callback_register(struct openthread_state_changed_callback *cb) +{ + CHECKIF(cb == NULL || cb->openthread_state_changed_cb == NULL) { + return -EINVAL; + } + + openthread_mutex_lock(); + sys_slist_append(&openthread_state_change_cbs, &cb->node); + openthread_mutex_unlock(); + + return 0; +} + +void otTaskletsSignalPending(otInstance *instance) +{ + ARG_UNUSED(instance); + + k_work_submit_to_queue(&openthread_work_q, &openthread_work); +} + +void otSysEventSignalPending(void) +{ + otTaskletsSignalPending(NULL); +} + +int openthread_state_change_callback_unregister(struct openthread_state_changed_callback *cb) +{ + bool removed; + + CHECKIF(cb == NULL) { + return -EINVAL; + } + + openthread_mutex_lock(); + removed = sys_slist_find_and_remove(&openthread_state_change_cbs, &cb->node); + openthread_mutex_unlock(); + + if (!removed) { + return -EALREADY; + } + + return 0; +} + +struct otInstance *openthread_get_default_instance(void) +{ + __ASSERT(openthread_instance, "OT instance is not initialized"); + return openthread_instance; +} + +bool openthread_init(openthread_receive_cb rx_handler, void *context) +{ + struct k_work_queue_config q_cfg = { + .name = "openthread", + .no_yield = true, + }; + otError err = OT_ERROR_NONE; + + /* Prevent multiple initializations */ + if (openthread_instance) { + return true; + } + + k_mutex_init(&openthread_lock); + k_work_init(&openthread_work, openthread_process); + + openthread_mutex_lock(); + + otSysInit(0, NULL); + openthread_instance = otInstanceInitSingle(); + + __ASSERT(openthread_instance, "OT instance initialization failed"); + + if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) { + platformShellInit(openthread_instance); + } + + if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { + err = otPlatUartEnable(); + if (err != OT_ERROR_NONE) { + LOG_ERR("Failed to enable UART: [%d]", err); + } + + otNcpHdlcInit(openthread_instance, ncp_hdlc_send); + } else { + otIp6SetReceiveFilterEnabled(openthread_instance, true); + + __ASSERT(rx_handler != NULL, "Receive callback is not set"); + otIp6SetReceiveCallback(openthread_instance, rx_handler, context); + +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) + + otIp4Cidr nat64_cidr; + + if (otIp4CidrFromString(CONFIG_OPENTHREAD_NAT64_CIDR, &nat64_cidr) == + OT_ERROR_NONE) { + if (otNat64SetIp4Cidr(openthread_instance, &nat64_cidr) != OT_ERROR_NONE) { + LOG_ERR("Incorrect NAT64 CIDR"); + return false; + } + } else { + LOG_ERR("Failed to parse NAT64 CIDR"); + return false; + } + + otNat64SetReceiveIp4Callback(openthread_instance, rx_handler, context); + +#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ + + sys_slist_init(&openthread_state_change_cbs); + err = otSetStateChangedCallback(openthread_instance, &ot_state_changed_handler, + NULL); + if (err != OT_ERROR_NONE) { + LOG_ERR("Could not set state changed callback: %d", err); + return false; + } + } + + openthread_mutex_unlock(); + + /* Start work queue for the OpenThread module */ + k_work_queue_start(&openthread_work_q, ot_stack_area, K_KERNEL_STACK_SIZEOF(ot_stack_area), + OT_PRIORITY, &q_cfg); + + (void)k_work_submit_to_queue(&openthread_work_q, &openthread_work); + + return true; +} + +int openthread_run(void) +{ + openthread_mutex_lock(); + otError error = OT_ERROR_NONE; + + if (otDatasetIsCommissioned(openthread_instance)) { + /* OpenThread already has dataset stored - skip the + * configuration. + */ + LOG_DBG("OpenThread already commissioned."); + } else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) { + /* No dataset - initiate network join procedure. */ + LOG_DBG("Starting OpenThread join procedure."); + + error = otJoinerStart(openthread_instance, OT_JOINER_PSKD, NULL, + ZEPHYR_PACKAGE_NAME, OT_PLATFORM_INFO, PACKAGE_VERSION, NULL, + &ot_joiner_start_handler, NULL); + + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to start joiner [%d]", error); + } + + goto exit; + } else { + /* No dataset - load the default configuration. */ + LOG_DBG("Loading OpenThread default configuration."); + + otExtendedPanId xpanid; + otNetworkKey networkKey; + + error = otThreadSetNetworkName(openthread_instance, OT_NETWORK_NAME); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to set %s [%d]", "network name", error); + goto exit; + } + + error = otLinkSetChannel(openthread_instance, OT_CHANNEL); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to set %s [%d]", "channel", error); + goto exit; + } + + error = otLinkSetPanId(openthread_instance, OT_PANID); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to set %s [%d]", "PAN ID", error); + goto exit; + } + + net_bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID); + error = otThreadSetExtendedPanId(openthread_instance, &xpanid); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to set %s [%d]", "ext PAN ID", error); + goto exit; + } + + if (strlen(OT_NETWORKKEY)) { + net_bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE, + (char *)OT_NETWORKKEY); + error = otThreadSetNetworkKey(openthread_instance, &networkKey); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to set %s [%d]", "network key", error); + goto exit; + } + } + } + + LOG_INF("Network name: %s", otThreadGetNetworkName(openthread_instance)); + + /* Start the network. */ + error = otThreadSetEnabled(openthread_instance, true); + if (error != OT_ERROR_NONE) { + LOG_ERR("Failed to start the OpenThread network [%d]", error); + } + +exit: + + openthread_mutex_unlock(); + + return error == OT_ERROR_NONE ? 0 : -EIO; +} + +int openthread_stop(void) +{ + otError error; + + if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { + return 0; + } + + openthread_mutex_lock(); + + error = otThreadSetEnabled(openthread_instance, false); + if (error == OT_ERROR_INVALID_STATE) { + LOG_DBG("Openthread interface was not up [%d]", error); + } + + openthread_mutex_unlock(); + + return 0; +} + +void openthread_mutex_lock(void) +{ + (void)k_mutex_lock(&openthread_lock, K_FOREVER); +} + +int openthread_mutex_try_lock(void) +{ + return k_mutex_lock(&openthread_lock, K_NO_WAIT); +} + +void openthread_mutex_unlock(void) +{ + (void)k_mutex_unlock(&openthread_lock); +} diff --git a/modules/openthread/platform/shell.c b/modules/openthread/platform/shell.c index 22f11d31f3eb..a500848b5627 100644 --- a/modules/openthread/platform/shell.c +++ b/modules/openthread/platform/shell.c @@ -15,6 +15,7 @@ #include #include "platform-zephyr.h" +#include "openthread.h" #define OT_SHELL_BUFFER_SIZE CONFIG_SHELL_CMD_BUFF_SIZE @@ -74,9 +75,9 @@ static int ot_cmd(const struct shell *sh, size_t argc, char *argv[]) shell_p = sh; - openthread_api_mutex_lock(openthread_get_default_context()); + openthread_mutex_lock(); otCliInputLine(rx_buffer); - openthread_api_mutex_unlock(openthread_get_default_context()); + openthread_mutex_unlock(); return 0; } diff --git a/subsys/net/l2/openthread/CMakeLists.txt b/subsys/net/l2/openthread/CMakeLists.txt index 553adcb0d318..b301b2b5265e 100644 --- a/subsys/net/l2/openthread/CMakeLists.txt +++ b/subsys/net/l2/openthread/CMakeLists.txt @@ -2,7 +2,7 @@ zephyr_library_named(subsys__net__ip__l2__openthread) zephyr_library_include_directories(. - ${ZEPHYR_BASE}/modules/openthread/platform + ${ZEPHYR_BASE}/modules/openthread ${ZEPHYR_BASE}/subsys/net/ip) zephyr_library_compile_definitions_ifdef( CONFIG_NEWLIB_LIBC __LINUX_ERRNO_EXTENSIONS__ diff --git a/subsys/net/l2/openthread/openthread.c b/subsys/net/l2/openthread/openthread.c index f3553e138742..89781b83a913 100644 --- a/subsys/net/l2/openthread/openthread.c +++ b/subsys/net/l2/openthread/openthread.c @@ -19,111 +19,22 @@ LOG_MODULE_REGISTER(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL); #include #include #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include +#include #include "openthread_utils.h" -#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) -#include -#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ - -#define PKT_IS_IPv4(_p) ((NET_IPV6_HDR(_p)->vtc & 0xf0) == 0x40) - -#define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE) - -#if defined(CONFIG_OPENTHREAD_THREAD_PREEMPTIVE) -#define OT_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY) -#else -#define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY) -#endif - -#if defined(CONFIG_OPENTHREAD_NETWORK_NAME) -#define OT_NETWORK_NAME CONFIG_OPENTHREAD_NETWORK_NAME -#else -#define OT_NETWORK_NAME "" -#endif - -#if defined(CONFIG_OPENTHREAD_CHANNEL) -#define OT_CHANNEL CONFIG_OPENTHREAD_CHANNEL -#else -#define OT_CHANNEL 0 -#endif - -#if defined(CONFIG_OPENTHREAD_PANID) -#define OT_PANID CONFIG_OPENTHREAD_PANID -#else -#define OT_PANID 0 -#endif - -#if defined(CONFIG_OPENTHREAD_XPANID) -#define OT_XPANID CONFIG_OPENTHREAD_XPANID -#else -#define OT_XPANID "" -#endif - -#if defined(CONFIG_OPENTHREAD_NETWORKKEY) -#define OT_NETWORKKEY CONFIG_OPENTHREAD_NETWORKKEY -#else -#define OT_NETWORKKEY "" -#endif - -#if defined(CONFIG_OPENTHREAD_JOINER_PSKD) -#define OT_JOINER_PSKD CONFIG_OPENTHREAD_JOINER_PSKD -#else -#define OT_JOINER_PSKD "" -#endif - -#if defined(CONFIG_OPENTHREAD_PLATFORM_INFO) -#define OT_PLATFORM_INFO CONFIG_OPENTHREAD_PLATFORM_INFO -#else -#define OT_PLATFORM_INFO "" -#endif - -#if defined(CONFIG_OPENTHREAD_POLL_PERIOD) -#define OT_POLL_PERIOD CONFIG_OPENTHREAD_POLL_PERIOD -#else -#define OT_POLL_PERIOD 0 -#endif - -#define ZEPHYR_PACKAGE_NAME "Zephyr" -#define PACKAGE_VERSION KERNEL_VERSION_STRING - -extern void platformShellInit(otInstance *aInstance); - -K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE); - static struct net_linkaddr *ll_addr; -static otStateChangedCallback state_changed_cb; -k_tid_t openthread_thread_id_get(void) -{ - struct openthread_context *ot_context = openthread_get_default_context(); - - return ot_context ? (k_tid_t)&ot_context->work_q.thread : 0; -} +#define PKT_IS_IPv4(_p) ((NET_IPV6_HDR(_p)->vtc & 0xf0) == 0x40) #ifdef CONFIG_NET_MGMT_EVENT static struct net_mgmt_event_callback ip6_addr_cb; -static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb, - uint32_t mgmt_event, struct net_if *iface) +static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, + struct net_if *iface) { if (net_if_l2(iface) != &NET_L2_GET_NAME(OPENTHREAD)) { return; @@ -148,18 +59,6 @@ static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb, } #endif /* CONFIG_NET_MGMT_EVENT */ -static int ncp_hdlc_send(const uint8_t *buf, uint16_t len) -{ - otError err; - - err = otPlatUartSend(buf, len); - if (err != OT_ERROR_NONE) { - return 0; - } - - return len; -} - #ifndef CONFIG_HDLC_RCP_IF void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64) { @@ -169,32 +68,14 @@ void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64) } #endif /* CONFIG_HDLC_RCP_IF */ -void otTaskletsSignalPending(otInstance *instance) -{ - struct openthread_context *ot_context = openthread_get_default_context(); - - if (ot_context) { - k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work); - } -} - -void otSysEventSignalPending(void) -{ - otTaskletsSignalPending(NULL); -} - -static void ot_state_changed_handler(uint32_t flags, void *context) +static void ot_l2_state_changed_handler(uint32_t flags, struct otInstance *instance, + void *user_data) { + struct openthread_context *ot_context = user_data; struct openthread_state_changed_cb *entry, *next; - struct openthread_context *ot_context = context; - - NET_INFO("State changed! Flags: 0x%08" PRIx32 " Current role: %s", - flags, - otThreadDeviceRoleToString(otThreadGetDeviceRole(ot_context->instance)) - ); if (flags & OT_CHANGED_THREAD_ROLE) { - switch (otThreadGetDeviceRole(ot_context->instance)) { + switch (otThreadGetDeviceRole(instance)) { case OT_DEVICE_ROLE_CHILD: case OT_DEVICE_ROLE_ROUTER: case OT_DEVICE_ROLE_LEADER: @@ -210,38 +91,34 @@ static void ot_state_changed_handler(uint32_t flags, void *context) } if (flags & OT_CHANGED_IP6_ADDRESS_REMOVED) { - NET_DBG("Ipv6 address removed"); rm_ipv6_addr_from_zephyr(ot_context); + NET_DBG("Ipv6 address removed"); } if (flags & OT_CHANGED_IP6_ADDRESS_ADDED) { - NET_DBG("Ipv6 address added"); add_ipv6_addr_to_zephyr(ot_context); + NET_DBG("Ipv6 address added"); } if (flags & OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED) { - NET_DBG("Ipv6 multicast address removed"); rm_ipv6_maddr_from_zephyr(ot_context); + NET_DBG("Ipv6 multicast address removed"); } if (flags & OT_CHANGED_IP6_MULTICAST_SUBSCRIBED) { - NET_DBG("Ipv6 multicast address added"); add_ipv6_maddr_to_zephyr(ot_context); + NET_DBG("Ipv6 multicast address added"); } #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) if (flags & OT_CHANGED_NAT64_TRANSLATOR_STATE) { NET_DBG("Nat64 translator state changed to %x", - otNat64GetTranslatorState(ot_context->instance)); + otNat64GetTranslatorState(instance)); } #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ - if (state_changed_cb) { - state_changed_cb(flags, context); - } - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ot_context->state_change_cbs, entry, next, node) { if (entry->state_changed_cb != NULL) { entry->state_changed_cb(flags, ot_context, entry->user_data); @@ -251,15 +128,14 @@ static void ot_state_changed_handler(uint32_t flags, void *context) static void ot_receive_handler(otMessage *aMessage, void *context) { - struct openthread_context *ot_context = context; + struct openthread_context *ot_context = (struct openthread_context *)context; uint16_t offset = 0U; uint16_t read_len; struct net_pkt *pkt; struct net_buf *pkt_buf; - pkt = net_pkt_rx_alloc_with_buffer(ot_context->iface, - otMessageGetLength(aMessage), + pkt = net_pkt_rx_alloc_with_buffer(ot_context->iface, otMessageGetLength(aMessage), AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { NET_ERR("Failed to reserve net pkt"); @@ -269,8 +145,8 @@ static void ot_receive_handler(otMessage *aMessage, void *context) pkt_buf = pkt->buffer; while (1) { - read_len = otMessageRead(aMessage, offset, pkt_buf->data, - net_buf_tailroom(pkt_buf)); + read_len = + otMessageRead(aMessage, offset, pkt_buf->data, net_buf_tailroom(pkt_buf)); if (!read_len) { break; } @@ -322,41 +198,6 @@ static void ot_receive_handler(otMessage *aMessage, void *context) otMessageFree(aMessage); } -static void ot_joiner_start_handler(otError error, void *context) -{ - struct openthread_context *ot_context = context; - - switch (error) { - case OT_ERROR_NONE: - NET_INFO("Join success"); - error = otThreadSetEnabled(ot_context->instance, true); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to start the OpenThread network [%d]", error); - } - - break; - default: - NET_ERR("Join failed [%d]", error); - break; - } -} - -static void openthread_process(struct k_work *work) -{ - struct openthread_context *ot_context - = CONTAINER_OF(work, struct openthread_context, api_work); - - openthread_api_mutex_lock(ot_context); - - while (otTaskletsArePending(ot_context->instance)) { - otTaskletsProcess(ot_context->instance); - } - - otSysProcessDrivers(ot_context->instance); - - openthread_api_mutex_unlock(ot_context); -} - static bool is_ipv6_frag(struct net_pkt *pkt) { NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr); @@ -420,248 +261,54 @@ int openthread_send(struct net_if *iface, struct net_pkt *pkt) return len; } -int openthread_start(struct openthread_context *ot_context) +static int openthread_l2_init(struct net_if *iface) { - otInstance *ot_instance = ot_context->instance; - otError error = OT_ERROR_NONE; - - openthread_api_mutex_lock(ot_context); - - NET_INFO("OpenThread version: %s", otGetVersionString()); - - if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { - NET_DBG("OpenThread co-processor."); - goto exit; - } - - error = otIp6SetEnabled(ot_context->instance, true); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "IPv6 support", error); - goto exit; - } - - /* Sleepy End Device specific configuration. */ - if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) { - otLinkModeConfig ot_mode = otThreadGetLinkMode(ot_instance); - - /* A SED should always attach the network as a SED to indicate - * increased buffer requirement to a parent. - */ - ot_mode.mRxOnWhenIdle = false; - - error = otThreadSetLinkMode(ot_context->instance, ot_mode); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "link mode", error); - goto exit; - } - - error = otLinkSetPollPeriod(ot_context->instance, OT_POLL_PERIOD); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "poll period", error); - goto exit; - } - } - - /* Configure Child Supervision and MLE Child timeouts. */ - otChildSupervisionSetInterval(ot_context->instance, - CONFIG_OPENTHREAD_CHILD_SUPERVISION_INTERVAL); - otChildSupervisionSetCheckTimeout(ot_context->instance, - CONFIG_OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT); - otThreadSetChildTimeout(ot_context->instance, CONFIG_OPENTHREAD_MLE_CHILD_TIMEOUT); - - if (otDatasetIsCommissioned(ot_instance)) { - /* OpenThread already has dataset stored - skip the - * configuration. - */ - NET_DBG("OpenThread already commissioned."); - } else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) { - /* No dataset - initiate network join procedure. */ - NET_DBG("Starting OpenThread join procedure."); - - error = otJoinerStart(ot_instance, OT_JOINER_PSKD, NULL, ZEPHYR_PACKAGE_NAME, - OT_PLATFORM_INFO, PACKAGE_VERSION, NULL, - &ot_joiner_start_handler, ot_context); - - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to start joiner [%d]", error); - } - - goto exit; - } else { - /* No dataset - load the default configuration. */ - NET_DBG("Loading OpenThread default configuration."); - - otExtendedPanId xpanid; - otNetworkKey networkKey; - - error = otThreadSetNetworkName(ot_instance, OT_NETWORK_NAME); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "network name", error); - goto exit; - } - - error = otLinkSetChannel(ot_instance, OT_CHANNEL); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "channel", error); - goto exit; - } - - error = otLinkSetPanId(ot_instance, OT_PANID); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "PAN ID", error); - goto exit; - } - - net_bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID); - error = otThreadSetExtendedPanId(ot_instance, &xpanid); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "ext PAN ID", error); - goto exit; - } - - if (strlen(OT_NETWORKKEY)) { - net_bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE, - (char *)OT_NETWORKKEY); - error = otThreadSetNetworkKey(ot_instance, &networkKey); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to set %s [%d]", "network key", error); - goto exit; - } - } - } - - NET_INFO("Network name: %s", - otThreadGetNetworkName(ot_instance)); - - /* Start the network. */ - error = otThreadSetEnabled(ot_instance, true); - if (error != OT_ERROR_NONE) { - NET_ERR("Failed to start the OpenThread network [%d]", error); - } - -exit: - - openthread_api_mutex_unlock(ot_context); - - return error == OT_ERROR_NONE ? 0 : -EIO; -} - -int openthread_stop(struct openthread_context *ot_context) -{ - otError error; - - if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { - return 0; - } - - openthread_api_mutex_lock(ot_context); - - error = otThreadSetEnabled(ot_context->instance, false); - if (error == OT_ERROR_INVALID_STATE) { - NET_DBG("Openthread interface was not up [%d]", error); - } - - openthread_api_mutex_unlock(ot_context); - - return 0; -} - -static int openthread_init(struct net_if *iface) -{ - struct openthread_context *ot_context = net_if_l2_data(iface); - struct k_work_queue_config q_cfg = { - .name = "openthread", - .no_yield = true, + struct openthread_context *ot_l2_context = net_if_l2_data(iface); + struct openthread_state_changed_callback ot_l2_state_changed_cb = { + .openthread_state_changed_cb = ot_l2_state_changed_handler, + .user_data = (void *)ot_l2_context, }; - otError err = OT_ERROR_NONE; - - NET_DBG("openthread_init"); - - k_mutex_init(&ot_context->api_lock); - k_work_init(&ot_context->api_work, openthread_process); ll_addr = net_if_get_link_addr(iface); - openthread_api_mutex_lock(ot_context); - - otSysInit(0, NULL); - - ot_context->instance = otInstanceInitSingle(); - ot_context->iface = iface; - - __ASSERT(ot_context->instance, "OT instance is NULL"); - - if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) { - platformShellInit(ot_context->instance); - } - - if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { - err = otPlatUartEnable(); - if (err != OT_ERROR_NONE) { - NET_ERR("Failed to enable UART: [%d]", err); - } - - otNcpHdlcInit(ot_context->instance, ncp_hdlc_send); - } else { - otIp6SetReceiveFilterEnabled(ot_context->instance, true); - otIp6SetReceiveCallback(ot_context->instance, - ot_receive_handler, ot_context); - -#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) - - otIp4Cidr nat64_cidr; - - if (otIp4CidrFromString(CONFIG_OPENTHREAD_NAT64_CIDR, &nat64_cidr) == - OT_ERROR_NONE) { - if (otNat64SetIp4Cidr(openthread_get_default_instance(), &nat64_cidr) != - OT_ERROR_NONE) { - NET_ERR("Incorrect NAT64 CIDR"); - } - } else { - NET_ERR("Failed to parse NAT64 CIDR"); - } - otNat64SetReceiveIp4Callback(ot_context->instance, ot_receive_handler, ot_context); + /* Check whether the OpenThread and its instance were properly initialized by the OpenThread + * module + */ + __ASSERT(openthread_init(ot_receive_handler, (void *)ot_l2_context), + "OT instance is not initialized"); -#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ + ot_l2_context->iface = iface; - sys_slist_init(&ot_context->state_change_cbs); - err = otSetStateChangedCallback(ot_context->instance, - &ot_state_changed_handler, - ot_context); - if (err != OT_ERROR_NONE) { - NET_ERR("Could not set state changed callback: %d", err); - } + openthread_mutex_lock(); - net_mgmt_init_event_callback( - &ip6_addr_cb, ipv6_addr_event_handler, - NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_MADDR_ADD); + if (!IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { + net_mgmt_init_event_callback(&ip6_addr_cb, ipv6_addr_event_handler, + NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_MADDR_ADD); net_mgmt_add_event_callback(&ip6_addr_cb); - net_if_dormant_on(iface); } - openthread_api_mutex_unlock(ot_context); - - k_work_queue_start(&ot_context->work_q, ot_stack_area, - K_KERNEL_STACK_SIZEOF(ot_stack_area), - OT_PRIORITY, &q_cfg); + /* To keep backward compatibility use the additional state change callback list from the ot + * l2 context and register the callback to the openthread module. + */ + sys_slist_init(&ot_l2_context->state_change_cbs); + openthread_state_change_callback_register(&ot_l2_state_changed_cb); - (void)k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work); + openthread_mutex_unlock(); - return (err == OT_ERROR_NONE) ? 0 : -EIO; + return 0; } void ieee802154_init(struct net_if *iface) { if (IS_ENABLED(CONFIG_IEEE802154_NET_IF_NO_AUTO_START)) { - LOG_DBG("Interface auto start disabled."); net_if_flag_set(iface, NET_IF_NO_AUTO_START); } net_if_flag_set(iface, NET_IF_IPV6_NO_ND); net_if_flag_set(iface, NET_IF_IPV6_NO_MLD); - openthread_init(iface); + openthread_l2_init(iface); } static enum net_l2_flags openthread_flags(struct net_if *iface) @@ -674,20 +321,15 @@ static enum net_l2_flags openthread_flags(struct net_if *iface) static int openthread_enable(struct net_if *iface, bool state) { - struct openthread_context *ot_context = net_if_l2_data(iface); - - NET_DBG("iface %p %s", iface, state ? "up" : "down"); - if (state) { if (IS_ENABLED(CONFIG_OPENTHREAD_MANUAL_START)) { - NET_DBG("OpenThread manual start."); return 0; } - return openthread_start(ot_context); + return openthread_run(); } - return openthread_stop(ot_context); + return openthread_stop(); } struct openthread_context *openthread_get_default_context(void) @@ -711,14 +353,44 @@ struct openthread_context *openthread_get_default_context(void) return ot_context; } -struct otInstance *openthread_get_default_instance(void) +/* Keep deprecated functions and forward them to the OpenThread platform module */ +int openthread_start(struct openthread_context *ot_context) +{ + ARG_UNUSED(ot_context); + + return openthread_run(); +} + +void openthread_api_mutex_lock(struct openthread_context *ot_context) +{ + /* The mutex is managed internally by the OpenThread module */ + ARG_UNUSED(ot_context); + + openthread_mutex_lock(); +} + +int openthread_api_mutex_try_lock(struct openthread_context *ot_context) +{ + /* The mutex is managed internally by the OpenThread module */ + ARG_UNUSED(ot_context); + + return openthread_mutex_try_lock(); +} + +void openthread_api_mutex_unlock(struct openthread_context *ot_context) { - struct openthread_context *ot_context = - openthread_get_default_context(); + /* The mutex is managed internally by the OpenThread module */ + ARG_UNUSED(ot_context); - return ot_context ? ot_context->instance : NULL; + openthread_mutex_unlock(); } +/* Keep deprecated state change callback registration functions to keep backward compatibility. + * The callbacks that are registered using these functions are run by the OpenThread module + * as one of the platform callback. However, they will be not supported in the future after + * deprecation period, so it is recommended to switch to + * openthread_state_change_callback_register() instead. + */ int openthread_state_changed_cb_register(struct openthread_context *ot_context, struct openthread_state_changed_cb *cb) { @@ -726,9 +398,9 @@ int openthread_state_changed_cb_register(struct openthread_context *ot_context, return -EINVAL; } - openthread_api_mutex_lock(ot_context); + openthread_mutex_lock(); sys_slist_append(&ot_context->state_change_cbs, &cb->node); - openthread_api_mutex_unlock(ot_context); + openthread_mutex_unlock(); return 0; } @@ -742,9 +414,9 @@ int openthread_state_changed_cb_unregister(struct openthread_context *ot_context return -EINVAL; } - openthread_api_mutex_lock(ot_context); + openthread_mutex_lock(); removed = sys_slist_find_and_remove(&ot_context->state_change_cbs, &cb->node); - openthread_api_mutex_unlock(ot_context); + openthread_mutex_unlock(); if (!removed) { return -EALREADY; @@ -753,25 +425,4 @@ int openthread_state_changed_cb_unregister(struct openthread_context *ot_context return 0; } -void openthread_set_state_changed_cb(otStateChangedCallback cb) -{ - state_changed_cb = cb; -} - -void openthread_api_mutex_lock(struct openthread_context *ot_context) -{ - (void)k_mutex_lock(&ot_context->api_lock, K_FOREVER); -} - -int openthread_api_mutex_try_lock(struct openthread_context *ot_context) -{ - return k_mutex_lock(&ot_context->api_lock, K_NO_WAIT); -} - -void openthread_api_mutex_unlock(struct openthread_context *ot_context) -{ - (void)k_mutex_unlock(&ot_context->api_lock); -} - -NET_L2_INIT(OPENTHREAD_L2, openthread_recv, openthread_send, openthread_enable, - openthread_flags); +NET_L2_INIT(OPENTHREAD_L2, openthread_recv, openthread_send, openthread_enable, openthread_flags); diff --git a/subsys/net/l2/openthread/openthread_utils.c b/subsys/net/l2/openthread/openthread_utils.c index f9b518725b7b..28b8f9f7fe66 100644 --- a/subsys/net/l2/openthread/openthread_utils.c +++ b/subsys/net/l2/openthread/openthread_utils.c @@ -31,7 +31,7 @@ static bool is_mesh_local(struct openthread_context *context, const uint8_t *address) { const otMeshLocalPrefix *ml_prefix = - otThreadGetMeshLocalPrefix(context->instance); + otThreadGetMeshLocalPrefix(openthread_get_default_instance()); return (memcmp(address, ml_prefix->m8, sizeof(ml_prefix->m8)) == 0); } @@ -106,7 +106,7 @@ void add_ipv6_addr_to_zephyr(struct openthread_context *context) const otNetifAddress *address; struct net_if_addr *if_addr; - for (address = otIp6GetUnicastAddresses(context->instance); + for (address = otIp6GetUnicastAddresses(openthread_get_default_instance()); address; address = address->mNext) { if (address->mRloc || is_anycast_locator(address)) { @@ -215,9 +215,9 @@ void add_ipv6_addr_to_ot(struct openthread_context *context, return; } - openthread_api_mutex_lock(context); - error = otIp6AddUnicastAddress(context->instance, &addr); - openthread_api_mutex_unlock(context); + openthread_mutex_lock(); + error = otIp6AddUnicastAddress(openthread_get_default_instance(), &addr); + openthread_mutex_unlock(); if (error != OT_ERROR_NONE) { NET_ERR("Failed to add IPv6 unicast address %s [%d]", @@ -235,9 +235,9 @@ void add_ipv6_maddr_to_ot(struct openthread_context *context, memcpy(&addr, addr6, sizeof(addr)); - openthread_api_mutex_lock(context); - error = otIp6SubscribeMulticastAddress(context->instance, &addr); - openthread_api_mutex_unlock(context); + openthread_mutex_lock(); + error = otIp6SubscribeMulticastAddress(openthread_get_default_instance(), &addr); + openthread_mutex_unlock(); if (error != OT_ERROR_NONE) { NET_ERR("Failed to add IPv6 multicast address %s [%d]", @@ -252,7 +252,7 @@ void add_ipv6_maddr_to_zephyr(struct openthread_context *context) const otNetifMulticastAddress *maddress; struct net_if_mcast_addr *zmaddr; - for (maddress = otIp6GetMulticastAddresses(context->instance); + for (maddress = otIp6GetMulticastAddresses(openthread_get_default_instance()); maddress; maddress = maddress->mNext) { if (net_if_ipv6_maddr_lookup( (struct in6_addr *)(&maddress->mAddress), @@ -306,7 +306,7 @@ void rm_ipv6_addr_from_zephyr(struct openthread_context *context) continue; } - for (address = otIp6GetUnicastAddresses(context->instance); + for (address = otIp6GetUnicastAddresses(openthread_get_default_instance()); address; address = address->mNext) { ot_addr = (struct in6_addr *)(&address->mAddress); @@ -354,7 +354,7 @@ void rm_ipv6_maddr_from_zephyr(struct openthread_context *context) continue; } - for (maddress = otIp6GetMulticastAddresses(context->instance); + for (maddress = otIp6GetMulticastAddresses(openthread_get_default_instance()); maddress; maddress = maddress->mNext) { ot_addr = (struct in6_addr *)(&maddress->mAddress);