diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-ipc_conf_iron.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-ipc_conf_iron.dtsi new file mode 100644 index 00000000000..a44db40538d --- /dev/null +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-ipc_conf_iron.dtsi @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This file is to be merged with the original ipc_conf.dtsi in the future. */ + +/ { + ipc { + /delete-node/ ipc-1-2; + /delete-node/ ipc-1-3; + + cpusec_cpuapp_ipc: ipc-1-2 { + compatible = "nordic,ironside-call"; + memory-region = <&cpusec_cpuapp_ipc_shm>; + mboxes = <&cpusec_bellboard 12>, + <&cpuapp_bellboard 0>; + status = "disabled"; + }; + + cpusec_cpurad_ipc: ipc-1-3 { + compatible = "nordic,ironside-call"; + memory-region = <&cpusec_cpurad_ipc_shm>; + mboxes = <&cpusec_bellboard 18>, + <&cpurad_bellboard 0>; + status = "disabled"; + }; + }; +}; diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map_iron.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map_iron.dtsi new file mode 100644 index 00000000000..2977e83758b --- /dev/null +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map_iron.dtsi @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This file is to be merged with the original memory_map.dtsi in the future. + * The following nodes will be replaced: + */ +/delete-node/ &cpuapp_cpusec_ipc_shm; +/delete-node/ &cpuapp_cpusys_ipc_shm; +/delete-node/ &cpurad_cpusec_ipc_shm; +/delete-node/ &cpurad_cpusys_ipc_shm; +/delete-node/ &cpusec_cpuapp_ipc_shm; +/delete-node/ &cpusec_cpurad_ipc_shm; +/delete-node/ &cpusys_cpuapp_ipc_shm; +/delete-node/ &cpusys_cpurad_ipc_shm; +/delete-node/ &cpuapp_rw_partitions; +/delete-node/ &cpuapp_rx_partitions; +/delete-node/ &cpurad_rx_partitions; + +/ { + reserved-memory { + cpuapp_cpusys_ipc_shm: memory@2f88f600 { + reg = <0x2f88f600 0x80>; + }; + + cpusys_cpuapp_ipc_shm: memory@2f88f680 { + reg = <0x2f88f680 0x80>; + }; + + cpurad_cpusys_ipc_shm: memory@2f88f700 { + reg = <0x2f88f700 0x80>; + }; + + cpusys_cpurad_ipc_shm: memory@2f88f780 { + reg = <0x2f88f780 0x80>; + }; + + cpusec_cpurad_ipc_shm: memory@2f88f800 { + reg = <0x2f88f800 0x80>; + }; + + cpurad_ironside_se_event_report: memory@2f88f880 { + reg = <0x2f88f880 0x100>; + }; + + cpurad_ironside_se_boot_report: memory@2f88f980 { + reg = <0x2f88f980 0x200>; + }; + + cpusec_cpuapp_ipc_shm: memory@2f88fb80 { + reg = <0x2f88fb80 0x80>; + }; + + cpuapp_ironside_se_event_report: memory@2f88fc00 { + reg = <0x2f88fc00 0x100>; + }; + + cpuapp_ironside_se_boot_report: memory@2f88fd00 { + reg = <0x2f88fd00 0x200>; + }; + }; +}; + +&mram1x { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + cpuapp_boot_partition: partition@2c000 { + reg = <0x2c000 DT_SIZE_K(64)>; + }; + + cpuapp_slot0_partition: partition@3c000 { + reg = <0x3c000 DT_SIZE_K(336)>; + }; + + cpurad_slot0_partition: partition@90000 { + reg = <0x90000 DT_SIZE_K(336)>; + }; + + cpuppr_code_partition: partition@e4000 { + reg = <0xe4000 DT_SIZE_K(64)>; + }; + + cpuflpr_code_partition: partition@f4000 { + reg = <0xf4000 DT_SIZE_K(48)>; + }; + + cpuapp_slot1_partition: partition@100000 { + reg = <0x100000 DT_SIZE_K(336)>; + }; + + cpurad_slot1_partition: partition@154000 { + reg = <0x154000 DT_SIZE_K(336)>; + }; + + storage_partition: partition@1a8000 { + reg = <0x1a8000 DT_SIZE_K(40)>; + }; + }; +}; diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_iron.dts b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_iron.dts index 47fffbeb4fc..dbd20dafc85 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_iron.dts +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_iron.dts @@ -5,19 +5,10 @@ */ #include "nrf54h20dk_nrf54h20_cpuapp.dts" +#include "nrf54h20dk_nrf54h20-ipc_conf_iron.dtsi" +#include "nrf54h20dk_nrf54h20-memory_map_iron.dtsi" -/delete-node/&cpurad_rx_partitions; -/delete-node/&cpuapp_rx_partitions; - -/* Update the location of cpusys IPC shared memory */ -/delete-node/&cpuapp_cpusys_ipc_shm; -/delete-node/&cpusys_cpuapp_ipc_shm; -/delete-node/&cpurad_cpusys_ipc_shm; -/delete-node/&cpusys_cpurad_ipc_shm; - -/* This is not yet an exhaustive memory map, and contain only a minimum required to boot - * the application core. - */ +/delete-node/ &cpusec_cpurad_ipc; / { chosen { @@ -26,62 +17,19 @@ }; }; -&mram1x { - cpuapp_rx_partitions: cpuapp-rx-partitions { - compatible = "nordic,owned-partitions", "fixed-partitions"; - nordic,access = ; - #address-cells = <1>; - #size-cells = <1>; - - boot_partition: partition@2c000 { - label = "mcuboot"; - reg = <0x2c000 DT_SIZE_K(64)>; - }; - - slot0_partition: partition@3c000 { - label = "image-0"; - reg = <0x3c000 DT_SIZE_K(336)>; - }; - - slot1_partition: partition@90000 { - label = "image-1"; - reg = <0x90000 DT_SIZE_K(336)>; - }; - - cpuppr_code_partition: partition@e4000 { - reg = <0xe4000 DT_SIZE_K(64)>; - }; - - cpuflpr_code_partition: partition@f4000 { - reg = <0xf4000 DT_SIZE_K(48)>; - }; - }; +&cpusec_cpuapp_ipc { + mbox-names = "tx", "rx"; + status = "okay"; }; -/ { - reserved-memory { - sysctrl_rom_report: memory@2f88ff00 { - reg = <0x2f88ff00 0x100>; - }; - - cpuapp_ironside_se_boot_report: memory@2f88fd00 { - reg = <0x2f88fd00 0x200>; - }; - - cpusys_cpurad_ipc_shm: memory@2f88f780 { - reg = <0x2f88f780 0x80>; - }; - - cpurad_cpusys_ipc_shm: memory@2f88f700 { - reg = <0x2f88f700 0x80>; - }; +boot_partition: &cpuapp_boot_partition { + label = "mcuboot"; +}; - cpusys_cpuapp_ipc_shm: memory@2f88f680 { - reg = <0x2f88f680 0x80>; - }; +slot0_partition: &cpuapp_slot0_partition { + label = "image-0"; +}; - cpuapp_cpusys_ipc_shm: memory@2f88f600 { - reg = <0x2f88f600 0x80>; - }; - }; +slot1_partition: &cpuapp_slot1_partition { + label = "image-1"; }; diff --git a/drivers/firmware/CMakeLists.txt b/drivers/firmware/CMakeLists.txt index 1222e9388ea..167d21addb5 100644 --- a/drivers/firmware/CMakeLists.txt +++ b/drivers/firmware/CMakeLists.txt @@ -1,3 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 +# zephyr-keep-sorted-start +add_subdirectory(nrf_ironside) add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi) +# zephyr-keep-sorted-stop diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 41576061237..3e3afe9ffdd 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -10,6 +10,9 @@ config ARM_SCMI Enable support for ARM's System Configuration and Management Interface (SCMI). +# zephyr-keep-sorted-start +source "drivers/firmware/nrf_ironside/Kconfig" source "drivers/firmware/scmi/Kconfig" +# zephyr-keep-sorted-stop endmenu diff --git a/drivers/firmware/nrf_ironside/CMakeLists.txt b/drivers/firmware/nrf_ironside/CMakeLists.txt new file mode 100644 index 00000000000..90320c62df5 --- /dev/null +++ b/drivers/firmware/nrf_ironside/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CALL call.c) diff --git a/drivers/firmware/nrf_ironside/Kconfig b/drivers/firmware/nrf_ironside/Kconfig new file mode 100644 index 00000000000..adfdf97f648 --- /dev/null +++ b/drivers/firmware/nrf_ironside/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config NRF_IRONSIDE_CALL + bool + depends on DT_HAS_NORDIC_IRONSIDE_CALL_ENABLED + depends on SOC_NRF54H20_IRON + select EVENTS + select MBOX + help + This is selected by features that require support for IRONside calls. + +if NRF_IRONSIDE_CALL + +config NRF_IRONSIDE_CALL_INIT_PRIORITY + int "IRONside calls' initialization priority" + default 41 + help + Initialization priority of IRONside calls. It must be below MBOX_INIT_PRIORITY, + but higher than the priority of any feature that selects NRF_IRONSIDE_CALL. + +endif # NRF_IRONSIDE_CALL diff --git a/drivers/firmware/nrf_ironside/call.c b/drivers/firmware/nrf_ironside/call.c new file mode 100644 index 00000000000..8d77119828c --- /dev/null +++ b/drivers/firmware/nrf_ironside/call.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT nordic_ironside_call + +#define SHM_NODE DT_INST_PHANDLE(0, memory_region) +#define NUM_BUFS (DT_REG_SIZE(SHM_NODE) / sizeof(struct ironside_call_buf)) +#define ALL_BUF_BITS BIT_MASK(NUM_BUFS) + +/* Note: this area is already zero-initialized at reset time. */ +static struct ironside_call_buf *const bufs = (void *)DT_REG_ADDR(SHM_NODE); + +#if defined(CONFIG_DCACHE_LINE_SIZE) +BUILD_ASSERT((DT_REG_ADDR(SHM_NODE) % CONFIG_DCACHE_LINE_SIZE) == 0); +BUILD_ASSERT((sizeof(struct ironside_call_buf) % CONFIG_DCACHE_LINE_SIZE) == 0); +#endif + +static const struct mbox_dt_spec mbox_rx = MBOX_DT_SPEC_INST_GET(0, rx); +static const struct mbox_dt_spec mbox_tx = MBOX_DT_SPEC_INST_GET(0, tx); + +K_EVENT_DEFINE(alloc_evts); +K_EVENT_DEFINE(rsp_evts); + +static void ironside_call_rsp(const struct device *dev, mbox_channel_id_t channel_id, + void *user_data, struct mbox_msg *data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(channel_id); + ARG_UNUSED(user_data); + ARG_UNUSED(data); + + struct ironside_call_buf *buf; + uint32_t rsp_buf_bits = 0; + + /* Check which buffers are not being dispatched currently. Those must + * not be cache-invalidated, in case they're used in thread context. + * + * This value will remain valid as long as ironside_call_rsp is never + * preempted by ironside_call_dispatch; the former runs in MBOX ISR, + * while the latter shall not run in ISR (because of k_event_wait). + */ + const uint32_t skip_buf_bits = k_event_test(&rsp_evts, ALL_BUF_BITS); + + for (int i = 0; i < NUM_BUFS; i++) { + if (skip_buf_bits & BIT(i)) { + continue; + } + + buf = &bufs[i]; + + sys_cache_data_invd_range(buf, sizeof(*buf)); + barrier_dmem_fence_full(); + + if (buf->status != IRONSIDE_CALL_STATUS_IDLE && + buf->status != IRONSIDE_CALL_STATUS_REQ) { + rsp_buf_bits |= BIT(i); + } + } + k_event_post(&rsp_evts, rsp_buf_bits); +} + +static int ironside_call_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + int err; + + k_event_set(&alloc_evts, ALL_BUF_BITS); + k_event_set(&rsp_evts, ALL_BUF_BITS); + + err = mbox_register_callback_dt(&mbox_rx, ironside_call_rsp, NULL); + __ASSERT_NO_MSG(err == 0); + + err = mbox_set_enabled_dt(&mbox_rx, 1); + __ASSERT_NO_MSG(err == 0); + + return 0; +} + +DEVICE_DT_INST_DEFINE(0, ironside_call_init, NULL, NULL, NULL, POST_KERNEL, + CONFIG_NRF_IRONSIDE_CALL_INIT_PRIORITY, NULL); + +struct ironside_call_buf *ironside_call_alloc(void) +{ + uint32_t avail_buf_bits; + uint32_t alloc_buf_bit; + + do { + avail_buf_bits = k_event_wait(&alloc_evts, ALL_BUF_BITS, false, K_FOREVER); + + /* Try allocating the first available block. + * If it's claimed by another thread, go back and wait for another block. + */ + alloc_buf_bit = LSB_GET(avail_buf_bits); + } while (k_event_clear(&alloc_evts, alloc_buf_bit) == 0); + + return &bufs[u32_count_trailing_zeros(alloc_buf_bit)]; +} + +void ironside_call_dispatch(struct ironside_call_buf *buf) +{ + const uint32_t buf_bit = BIT(buf - bufs); + int err; + + buf->status = IRONSIDE_CALL_STATUS_REQ; + barrier_dmem_fence_full(); + + sys_cache_data_flush_range(buf, sizeof(*buf)); + + k_event_clear(&rsp_evts, buf_bit); + + err = mbox_send_dt(&mbox_tx, NULL); + __ASSERT_NO_MSG(err == 0); + + k_event_wait(&rsp_evts, buf_bit, false, K_FOREVER); +} + +void ironside_call_release(struct ironside_call_buf *buf) +{ + const uint32_t buf_bit = BIT(buf - bufs); + + buf->status = IRONSIDE_CALL_STATUS_IDLE; + barrier_dmem_fence_full(); + + sys_cache_data_flush_range(buf, sizeof(*buf)); + + k_event_post(&alloc_evts, buf_bit); +} diff --git a/dts/bindings/firmware/nordic,ironside-call.yaml b/dts/bindings/firmware/nordic,ironside-call.yaml new file mode 100644 index 00000000000..14f39cb0b74 --- /dev/null +++ b/dts/bindings/firmware/nordic,ironside-call.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: IPC configuration for Nordic IRONside calls + +compatible: "nordic,ironside-call" + +include: base.yaml + +properties: + memory-region: + type: phandle + required: true + description: phandle to shared memory region + + mboxes: + required: true + description: MBOX channel specifiers (TX and RX are required) + + mbox-names: + required: true + description: MBOX channel names (must be called "tx" and "rx") diff --git a/include/zephyr/drivers/firmware/nrf_ironside/call.h b/include/zephyr/drivers/firmware/nrf_ironside/call.h new file mode 100644 index 00000000000..178b9371cdb --- /dev/null +++ b/include/zephyr/drivers/firmware/nrf_ironside/call.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_ +#define ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_ + +#include + +/** @brief Maximum number of arguments to an IRONside call. + * + * This is chosen so that the containing message buffer size is minimal but + * cache line aligned. + */ +#define NRF_IRONSIDE_CALL_NUM_ARGS 7 + +/** @brief Message buffer. */ +struct ironside_call_buf { + /** Status code. This is set by the API. */ + uint16_t status; + /** Operation identifier. This is set by the user. */ + uint16_t id; + /** Operation arguments. These are set by the user. */ + uint32_t args[NRF_IRONSIDE_CALL_NUM_ARGS]; +}; + +/** + * @name Message buffer status codes. + * @{ + */ + +/** Buffer is idle and available for allocation. */ +#define IRONSIDE_CALL_STATUS_IDLE 0 +/** Request was processed successfully by the server. */ +#define IRONSIDE_CALL_STATUS_RSP_SUCCESS 1 +/** Request status code is unknown. */ +#define IRONSIDE_CALL_STATUS_RSP_ERR_UNKNOWN_STATUS 2 +/** Request status code is no longer supported. */ +#define IRONSIDE_CALL_STATUS_RSP_ERR_EXPIRED_STATUS 3 +/** Operation identifier is unknown. */ +#define IRONSIDE_CALL_STATUS_RSP_ERR_UNKNOWN_ID 4 +/** Operation identifier is no longer supported. */ +#define IRONSIDE_CALL_STATUS_RSP_ERR_EXPIRED_ID 5 +/** Buffer contains a request from the client. */ +#define IRONSIDE_CALL_STATUS_REQ 6 + +/** + * @} + */ + +/** + * @brief Allocate memory for an IRONside call. + * + * This function will block when no buffers are available, until one is + * released by another thread on the client side. + * + * @return Pointer to the allocated buffer. + */ +struct ironside_call_buf *ironside_call_alloc(void); + +/** + * @brief Dispatch an IRONside call. + * + * This function will block until a response is received from the server. + * + * @param buf Buffer returned by ironside_call_alloc(). It should be populated + * with request data before calling this function. Upon returning, + * this data will have been replaced by response data. + */ +void ironside_call_dispatch(struct ironside_call_buf *buf); + +/** + * @brief Release an IRONside call buffer. + * + * This function must be called after processing the response. + * + * @param buf Buffer used to perform the call. + */ +void ironside_call_release(struct ironside_call_buf *buf); + +#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_ */