Skip to content

Commit 6fdce17

Browse files
57300hakonfam
authored andcommitted
[nrf fromtree] drivers: firmware: Add support for IRONside calls
IRONside calls are remote procedure calls which comprise the runtime interface of Nordic IRONside SE. They are realized using a simple IPC mechanism. A local domain (client) issues requests to the server by exchanging data in shared memory, which is divided into evenly sized buffers. The client selects a buffer, writes a request into it, and sends it to the server. The server processes that request and writes a response into the same buffer before returning it to the client. This patch adds the initial client-side implementation on top of MBOX. It features cache management and a blocking alloc/dispatch/release API for synchronous, zero-copy transfers. A new devicetree binding is added to support this implementation. It is patterned after the `zephyr,ipc-*` bindings, where each node associates a pair of mailboxes and a shared memory region. Signed-off-by: Grzegorz Swiderski <[email protected]> (cherry-picked from commit 47df9ec)
1 parent d32cb3c commit 6fdce17

File tree

7 files changed

+277
-0
lines changed

7 files changed

+277
-0
lines changed

drivers/firmware/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# SPDX-License-Identifier: Apache-2.0
22

3+
# zephyr-keep-sorted-start
4+
add_subdirectory(nrf_ironside)
35
add_subdirectory_ifdef(CONFIG_ARM_SCMI scmi)
6+
# zephyr-keep-sorted-stop

drivers/firmware/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ config ARM_SCMI
1010
Enable support for ARM's System Configuration and Management
1111
Interface (SCMI).
1212

13+
# zephyr-keep-sorted-start
14+
source "drivers/firmware/nrf_ironside/Kconfig"
1315
source "drivers/firmware/scmi/Kconfig"
16+
# zephyr-keep-sorted-stop
1417

1518
endmenu
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
6+
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CALL call.c)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config NRF_IRONSIDE_CALL
5+
bool
6+
depends on DT_HAS_NORDIC_IRONSIDE_CALL_ENABLED
7+
depends on SOC_NRF54H20_IRON
8+
select EVENTS
9+
select MBOX
10+
help
11+
This is selected by features that require support for IRONside calls.
12+
13+
if NRF_IRONSIDE_CALL
14+
15+
config NRF_IRONSIDE_CALL_INIT_PRIORITY
16+
int "IRONside calls' initialization priority"
17+
default 41
18+
help
19+
Initialization priority of IRONside calls. It must be below MBOX_INIT_PRIORITY,
20+
but higher than the priority of any feature that selects NRF_IRONSIDE_CALL.
21+
22+
endif # NRF_IRONSIDE_CALL
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <zephyr/cache.h>
7+
#include <zephyr/device.h>
8+
#include <zephyr/drivers/firmware/nrf_ironside/call.h>
9+
#include <zephyr/drivers/mbox.h>
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/sys/barrier.h>
12+
#include <zephyr/sys/math_extras.h>
13+
#include <zephyr/sys/util.h>
14+
15+
#define DT_DRV_COMPAT nordic_ironside_call
16+
17+
#define SHM_NODE DT_INST_PHANDLE(0, memory_region)
18+
#define NUM_BUFS (DT_REG_SIZE(SHM_NODE) / sizeof(struct ironside_call_buf))
19+
#define ALL_BUF_BITS BIT_MASK(NUM_BUFS)
20+
21+
/* Note: this area is already zero-initialized at reset time. */
22+
static struct ironside_call_buf *const bufs = (void *)DT_REG_ADDR(SHM_NODE);
23+
24+
#if defined(CONFIG_DCACHE_LINE_SIZE)
25+
BUILD_ASSERT((DT_REG_ADDR(SHM_NODE) % CONFIG_DCACHE_LINE_SIZE) == 0);
26+
BUILD_ASSERT((sizeof(struct ironside_call_buf) % CONFIG_DCACHE_LINE_SIZE) == 0);
27+
#endif
28+
29+
static const struct mbox_dt_spec mbox_rx = MBOX_DT_SPEC_INST_GET(0, rx);
30+
static const struct mbox_dt_spec mbox_tx = MBOX_DT_SPEC_INST_GET(0, tx);
31+
32+
K_EVENT_DEFINE(alloc_evts);
33+
K_EVENT_DEFINE(rsp_evts);
34+
35+
static void ironside_call_rsp(const struct device *dev, mbox_channel_id_t channel_id,
36+
void *user_data, struct mbox_msg *data)
37+
{
38+
ARG_UNUSED(dev);
39+
ARG_UNUSED(channel_id);
40+
ARG_UNUSED(user_data);
41+
ARG_UNUSED(data);
42+
43+
struct ironside_call_buf *buf;
44+
uint32_t rsp_buf_bits = 0;
45+
46+
/* Check which buffers are not being dispatched currently. Those must
47+
* not be cache-invalidated, in case they're used in thread context.
48+
*
49+
* This value will remain valid as long as ironside_call_rsp is never
50+
* preempted by ironside_call_dispatch; the former runs in MBOX ISR,
51+
* while the latter shall not run in ISR (because of k_event_wait).
52+
*/
53+
const uint32_t skip_buf_bits = k_event_test(&rsp_evts, ALL_BUF_BITS);
54+
55+
for (int i = 0; i < NUM_BUFS; i++) {
56+
if (skip_buf_bits & BIT(i)) {
57+
continue;
58+
}
59+
60+
buf = &bufs[i];
61+
62+
sys_cache_data_invd_range(buf, sizeof(*buf));
63+
barrier_dmem_fence_full();
64+
65+
if (buf->status != IRONSIDE_CALL_STATUS_IDLE &&
66+
buf->status != IRONSIDE_CALL_STATUS_REQ) {
67+
rsp_buf_bits |= BIT(i);
68+
}
69+
}
70+
k_event_post(&rsp_evts, rsp_buf_bits);
71+
}
72+
73+
static int ironside_call_init(const struct device *dev)
74+
{
75+
ARG_UNUSED(dev);
76+
77+
int err;
78+
79+
k_event_set(&alloc_evts, ALL_BUF_BITS);
80+
k_event_set(&rsp_evts, ALL_BUF_BITS);
81+
82+
err = mbox_register_callback_dt(&mbox_rx, ironside_call_rsp, NULL);
83+
__ASSERT_NO_MSG(err == 0);
84+
85+
err = mbox_set_enabled_dt(&mbox_rx, 1);
86+
__ASSERT_NO_MSG(err == 0);
87+
88+
return 0;
89+
}
90+
91+
DEVICE_DT_INST_DEFINE(0, ironside_call_init, NULL, NULL, NULL, POST_KERNEL,
92+
CONFIG_NRF_IRONSIDE_CALL_INIT_PRIORITY, NULL);
93+
94+
struct ironside_call_buf *ironside_call_alloc(void)
95+
{
96+
uint32_t avail_buf_bits;
97+
uint32_t alloc_buf_bit;
98+
99+
do {
100+
avail_buf_bits = k_event_wait(&alloc_evts, ALL_BUF_BITS, false, K_FOREVER);
101+
102+
/* Try allocating the first available block.
103+
* If it's claimed by another thread, go back and wait for another block.
104+
*/
105+
alloc_buf_bit = LSB_GET(avail_buf_bits);
106+
} while (k_event_clear(&alloc_evts, alloc_buf_bit) == 0);
107+
108+
return &bufs[u32_count_trailing_zeros(alloc_buf_bit)];
109+
}
110+
111+
void ironside_call_dispatch(struct ironside_call_buf *buf)
112+
{
113+
const uint32_t buf_bit = BIT(buf - bufs);
114+
int err;
115+
116+
buf->status = IRONSIDE_CALL_STATUS_REQ;
117+
barrier_dmem_fence_full();
118+
119+
sys_cache_data_flush_range(buf, sizeof(*buf));
120+
121+
k_event_clear(&rsp_evts, buf_bit);
122+
123+
err = mbox_send_dt(&mbox_tx, NULL);
124+
__ASSERT_NO_MSG(err == 0);
125+
126+
k_event_wait(&rsp_evts, buf_bit, false, K_FOREVER);
127+
}
128+
129+
void ironside_call_release(struct ironside_call_buf *buf)
130+
{
131+
const uint32_t buf_bit = BIT(buf - bufs);
132+
133+
buf->status = IRONSIDE_CALL_STATUS_IDLE;
134+
barrier_dmem_fence_full();
135+
136+
sys_cache_data_flush_range(buf, sizeof(*buf));
137+
138+
k_event_post(&alloc_evts, buf_bit);
139+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: IPC configuration for Nordic IRONside calls
5+
6+
compatible: "nordic,ironside-call"
7+
8+
include: base.yaml
9+
10+
properties:
11+
memory-region:
12+
type: phandle
13+
required: true
14+
description: phandle to shared memory region
15+
16+
mboxes:
17+
required: true
18+
description: MBOX channel specifiers (TX and RX are required)
19+
20+
mbox-names:
21+
required: true
22+
description: MBOX channel names (must be called "tx" and "rx")
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_
7+
#define ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_
8+
9+
#include <stdint.h>
10+
11+
/** @brief Maximum number of arguments to an IRONside call.
12+
*
13+
* This is chosen so that the containing message buffer size is minimal but
14+
* cache line aligned.
15+
*/
16+
#define NRF_IRONSIDE_CALL_NUM_ARGS 7
17+
18+
/** @brief Message buffer. */
19+
struct ironside_call_buf {
20+
/** Status code. This is set by the API. */
21+
uint16_t status;
22+
/** Operation identifier. This is set by the user. */
23+
uint16_t id;
24+
/** Operation arguments. These are set by the user. */
25+
uint32_t args[NRF_IRONSIDE_CALL_NUM_ARGS];
26+
};
27+
28+
/**
29+
* @name Message buffer status codes.
30+
* @{
31+
*/
32+
33+
/** Buffer is idle and available for allocation. */
34+
#define IRONSIDE_CALL_STATUS_IDLE 0
35+
/** Request was processed successfully by the server. */
36+
#define IRONSIDE_CALL_STATUS_RSP_SUCCESS 1
37+
/** Request status code is unknown. */
38+
#define IRONSIDE_CALL_STATUS_RSP_ERR_UNKNOWN_STATUS 2
39+
/** Request status code is no longer supported. */
40+
#define IRONSIDE_CALL_STATUS_RSP_ERR_EXPIRED_STATUS 3
41+
/** Operation identifier is unknown. */
42+
#define IRONSIDE_CALL_STATUS_RSP_ERR_UNKNOWN_ID 4
43+
/** Operation identifier is no longer supported. */
44+
#define IRONSIDE_CALL_STATUS_RSP_ERR_EXPIRED_ID 5
45+
/** Buffer contains a request from the client. */
46+
#define IRONSIDE_CALL_STATUS_REQ 6
47+
48+
/**
49+
* @}
50+
*/
51+
52+
/**
53+
* @brief Allocate memory for an IRONside call.
54+
*
55+
* This function will block when no buffers are available, until one is
56+
* released by another thread on the client side.
57+
*
58+
* @return Pointer to the allocated buffer.
59+
*/
60+
struct ironside_call_buf *ironside_call_alloc(void);
61+
62+
/**
63+
* @brief Dispatch an IRONside call.
64+
*
65+
* This function will block until a response is received from the server.
66+
*
67+
* @param buf Buffer returned by ironside_call_alloc(). It should be populated
68+
* with request data before calling this function. Upon returning,
69+
* this data will have been replaced by response data.
70+
*/
71+
void ironside_call_dispatch(struct ironside_call_buf *buf);
72+
73+
/**
74+
* @brief Release an IRONside call buffer.
75+
*
76+
* This function must be called after processing the response.
77+
*
78+
* @param buf Buffer used to perform the call.
79+
*/
80+
void ironside_call_release(struct ironside_call_buf *buf);
81+
82+
#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_CALL_H_ */

0 commit comments

Comments
 (0)