1 /*
2 * Copyright (c) 2025 Renesas Electronics Corporation
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT renesas_rz_mhu_mbox
7
8 #include <stdint.h>
9 #include <string.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/mbox.h>
13 #include <zephyr/logging/log.h>
14
15 #include "r_mhu_ns.h"
16
17 LOG_MODULE_REGISTER(mbox_renesas_rz_mhu, CONFIG_MBOX_LOG_LEVEL);
18
19 /* Global dummy value required for FSP driver implementation */
20 #define MHU_SHM_START_ADDR 0
21 const uint32_t *const __mhu_shmem_start = (uint32_t *)MHU_SHM_START_ADDR;
22
23 /* FSP interrupt handlers. */
24 void mhu_ns_int_isr(void);
25
26 static volatile uint32_t callback_msg;
mhu_ns_callback(mhu_callback_args_t * p_args)27 static void mhu_ns_callback(mhu_callback_args_t *p_args)
28 {
29 callback_msg = p_args->msg;
30 }
31
32 struct mbox_rz_mhu_config {
33 const mhu_api_t *fsp_api;
34 uint16_t mhu_ch_size;
35 /* Number of supported channels */
36 uint32_t num_channels;
37 /* TX channels mask */
38 uint32_t tx_mask;
39 /* RX channels mask */
40 uint32_t rx_mask;
41 };
42
43 struct mbox_rz_mhu_data {
44 const struct device *dev;
45 mhu_ns_instance_ctrl_t *fsp_ctrl;
46 mhu_cfg_t *fsp_cfg;
47 mbox_callback_t cb;
48 void *user_data;
49 uint32_t channel_id;
50 };
51
52 /**
53 * @brief Return true if the channel of the MBOX device is an inbound channel.
54 */
is_rx_channel_valid(const struct device * dev,uint32_t ch)55 static inline bool is_rx_channel_valid(const struct device *dev, uint32_t ch)
56 {
57 const struct mbox_rz_mhu_config *config = dev->config;
58
59 return ((ch < config->num_channels) && (config->rx_mask & BIT(ch)));
60 }
61
62 /**
63 * @brief Return true if the channel of the MBOX device is an outbound channel.
64 */
is_tx_channel_valid(const struct device * dev,uint32_t ch)65 static inline bool is_tx_channel_valid(const struct device *dev, uint32_t ch)
66 {
67 const struct mbox_rz_mhu_config *config = dev->config;
68
69 return ((ch < config->num_channels) && (config->tx_mask & BIT(ch)));
70 }
71
72 /**
73 * Interrupt handler
74 */
mbox_rz_mhu_isr(const struct device * dev)75 static void mbox_rz_mhu_isr(const struct device *dev)
76 {
77 struct mbox_rz_mhu_data *data = dev->data;
78 struct mbox_msg msg;
79
80 mhu_ns_int_isr();
81 if (data->cb && data->fsp_cfg->p_shared_memory) {
82 uint32_t local_msg = callback_msg;
83
84 msg.data = &local_msg;
85
86 /* On the receiving end, the size of the message is always 4 bytes since the FSP MHU
87 * driver requires the message to be of type uint32_t
88 */
89 msg.size = sizeof(local_msg);
90
91 data->cb(dev, data->channel_id, data->user_data, &msg);
92 }
93 }
94
95 /**
96 * @brief Try to send a message over the MBOX device.
97 */
mbox_rz_mhu_send(const struct device * dev,mbox_channel_id_t channel_id,const struct mbox_msg * msg)98 static int mbox_rz_mhu_send(const struct device *dev, mbox_channel_id_t channel_id,
99 const struct mbox_msg *msg)
100 {
101 const struct mbox_rz_mhu_config *config = dev->config;
102 struct mbox_rz_mhu_data *data = dev->data;
103 fsp_err_t fsp_err = FSP_SUCCESS;
104
105 /* FSP driver implementation requires the message to be of type uint32_t */
106 uint32_t message = 0;
107
108 if (!is_tx_channel_valid(dev, channel_id)) {
109 if (!is_rx_channel_valid(dev, channel_id)) {
110 /* Channel is neither RX nor TX */
111 LOG_ERR("Invalid MBOX channel number: %d", channel_id);
112 return -EINVAL;
113 }
114
115 /* Channel is a RX channel, but this function only accepts TX */
116 LOG_ERR("Channel ID %d is a RX channel, but only TX channels are allowed",
117 channel_id);
118 return -ENOSYS;
119 }
120
121 if (msg != NULL) {
122 /* Maximum size allowed is 4 bytes */
123 if (msg->size > config->mhu_ch_size) {
124 LOG_ERR("Size %d is not valid. Maximum size is 4 bytes", msg->size);
125 return -EMSGSIZE;
126 }
127
128 if (msg->data && msg->size) {
129 /* Copy message */
130 memcpy(&message, msg->data, msg->size);
131 } else {
132 /* Clear Message */
133 message = 0;
134 }
135 } else {
136 message = 0;
137 }
138
139 if (data->fsp_cfg->p_shared_memory) {
140
141 #if CONFIG_MBOX_BUSY_WAIT_TIMEOUT_US > 0
142 /* The FSP MHU "msgSend" API continuously polls until the
143 * previous message is consumed before sending a new one. To avoid
144 * blocking indefinitely, we need to check if the remote clears the message
145 * within the allowed time before sending a new one
146 */
147 if (MHU_SEND_TYPE_MSG == data->fsp_ctrl->send_type) {
148 if (data->fsp_ctrl->p_regs->MSG_INT_STSn != 0) {
149 k_busy_wait(CONFIG_MBOX_BUSY_WAIT_TIMEOUT_US);
150 if (data->fsp_ctrl->p_regs->MSG_INT_STSn != 0) {
151 LOG_ERR("Remote is busy");
152 return -EBUSY;
153 }
154 }
155 } else {
156 if (data->fsp_ctrl->p_regs->RSP_INT_STSn != 0) {
157 k_busy_wait(CONFIG_MBOX_BUSY_WAIT_TIMEOUT_US);
158 if (data->fsp_ctrl->p_regs->RSP_INT_STSn != 0) {
159 LOG_ERR("Remote is busy");
160 return -EBUSY;
161 }
162 }
163 }
164 #endif
165
166 /* Send message to shared memory, this will also invoke interrupt on the receiving
167 * core
168 */
169 fsp_err = config->fsp_api->msgSend(data->fsp_ctrl, message);
170 }
171
172 if (fsp_err) {
173 LOG_ERR("Message send failed");
174 return -EIO;
175 }
176
177 return 0;
178 }
179
180 /**
181 * @brief Register a callback function on a channel for incoming messages.
182 */
mbox_rz_mhu_reg_callback(const struct device * dev,mbox_channel_id_t channel_id,mbox_callback_t cb,void * user_data)183 static int mbox_rz_mhu_reg_callback(const struct device *dev, mbox_channel_id_t channel_id,
184 mbox_callback_t cb, void *user_data)
185 {
186 struct mbox_rz_mhu_data *data = dev->data;
187
188 if (!is_rx_channel_valid(dev, channel_id)) {
189 if (!is_tx_channel_valid(dev, channel_id)) {
190 /* Channel is neither RX nor TX */
191 LOG_ERR("Invalid MBOX channel number: %d", channel_id);
192 return -EINVAL;
193 }
194
195 /* Channel is a TX channel, but this function only accepts RX */
196 LOG_ERR("Channel ID %d is a TX channel, but only RX channels are allowed",
197 channel_id);
198 return -ENOSYS;
199 }
200
201 if (!cb) {
202 LOG_ERR("Must provide callback");
203 return -EINVAL;
204 }
205
206 data->cb = cb;
207 data->user_data = user_data;
208 data->channel_id = channel_id;
209
210 return 0;
211 }
212
213 /**
214 * @brief Initialize the module.
215 */
mbox_rz_mhu_init(const struct device * dev)216 static int mbox_rz_mhu_init(const struct device *dev)
217 {
218 const struct mbox_rz_mhu_config *config = dev->config;
219 struct mbox_rz_mhu_data *data = dev->data;
220 fsp_err_t fsp_err = FSP_SUCCESS;
221
222 fsp_err = config->fsp_api->open(data->fsp_ctrl, data->fsp_cfg);
223
224 if (fsp_err) {
225 LOG_ERR("MBOX initialization failed");
226 return -EIO;
227 }
228
229 return 0;
230 }
231
232 /**
233 * @brief Enable (disable) interrupts and callbacks for inbound channels.
234 */
mbox_rz_mhu_set_enabled(const struct device * dev,mbox_channel_id_t channel_id,bool enabled)235 static int mbox_rz_mhu_set_enabled(const struct device *dev, mbox_channel_id_t channel_id,
236 bool enabled)
237 {
238 if (!is_rx_channel_valid(dev, channel_id)) {
239 if (!is_tx_channel_valid(dev, channel_id)) {
240 /* Channel is neither RX nor TX */
241 LOG_ERR("Invalid MBOX channel number: %d", channel_id);
242 return -EINVAL;
243 }
244
245 /* Channel is a TX channel, but this function only accepts RX */
246 LOG_ERR("Channel ID %d is a TX channel, but only RX channels are allowed",
247 channel_id);
248 return -ENOSYS;
249 }
250
251 ARG_UNUSED(enabled);
252 return 0;
253 }
254
255 /**
256 * @brief Return the maximum number of bytes possible in an outbound message.
257 */
mbox_rz_mhu_mtu_get(const struct device * dev)258 static int mbox_rz_mhu_mtu_get(const struct device *dev)
259 {
260 const struct mbox_rz_mhu_config *config = dev->config;
261
262 return config->mhu_ch_size;
263 }
264
265 /**
266 * @brief Return the maximum number of channels.
267 */
mbox_rz_mhu_max_channels_get(const struct device * dev)268 static uint32_t mbox_rz_mhu_max_channels_get(const struct device *dev)
269 {
270 const struct mbox_rz_mhu_config *config = dev->config;
271
272 return config->num_channels;
273 }
274
275 static DEVICE_API(mbox, mbox_rz_mhu_driver_api) = {
276 .send = mbox_rz_mhu_send,
277 .register_callback = mbox_rz_mhu_reg_callback,
278 .mtu_get = mbox_rz_mhu_mtu_get,
279 .max_channels_get = mbox_rz_mhu_max_channels_get,
280 .set_enabled = mbox_rz_mhu_set_enabled,
281 };
282
283 /*
284 * ************************* DRIVER REGISTER SECTION ***************************
285 */
286
287 #define MHU_RZG_IRQ_CONNECT(idx, irq_name, isr) \
288 do { \
289 IRQ_CONNECT(DT_INST_IRQ_BY_NAME(idx, irq_name, irq), \
290 DT_INST_IRQ_BY_NAME(idx, irq_name, priority), isr, \
291 DEVICE_DT_INST_GET(idx), 0); \
292 irq_enable(DT_INST_IRQ_BY_NAME(idx, irq_name, irq)); \
293 } while (0)
294
295 #define MHU_RZG_CONFIG_FUNC(idx) MHU_RZG_IRQ_CONNECT(idx, mhuns, mbox_rz_mhu_isr);
296
297 #define MHU_RZG_INIT(idx) \
298 static mhu_ns_instance_ctrl_t g_mhu_ns##idx##_ctrl; \
299 static mhu_cfg_t g_mhu_ns##idx##_cfg = { \
300 .channel = DT_INST_PROP(idx, channel), \
301 .rx_ipl = DT_INST_IRQ_BY_NAME(idx, mhuns, priority), \
302 .rx_irq = DT_INST_IRQ_BY_NAME(idx, mhuns, irq), \
303 .p_callback = mhu_ns_callback, \
304 .p_context = NULL, \
305 .p_shared_memory = (void *)COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, shared_memory), \
306 (DT_REG_ADDR(DT_INST_PHANDLE(idx, shared_memory))), (NULL)), \
307 }; \
308 static const struct mbox_rz_mhu_config mbox_rz_mhu_config_##idx = { \
309 .fsp_api = &g_mhu_ns_on_mhu_ns, \
310 .mhu_ch_size = 4, \
311 .num_channels = DT_INST_PROP(idx, channels_count), \
312 .tx_mask = DT_INST_PROP(idx, tx_mask), \
313 .rx_mask = DT_INST_PROP(idx, rx_mask), \
314 }; \
315 static struct mbox_rz_mhu_data mbox_rz_mhu_data_##idx = { \
316 .dev = DEVICE_DT_INST_GET(idx), \
317 .fsp_ctrl = &g_mhu_ns##idx##_ctrl, \
318 .fsp_cfg = &g_mhu_ns##idx##_cfg, \
319 }; \
320 static int mbox_rz_mhu_init_##idx(const struct device *dev) \
321 { \
322 MHU_RZG_CONFIG_FUNC(idx) \
323 return mbox_rz_mhu_init(dev); \
324 } \
325 DEVICE_DT_INST_DEFINE(idx, mbox_rz_mhu_init_##idx, NULL, &mbox_rz_mhu_data_##idx, \
326 &mbox_rz_mhu_config_##idx, PRE_KERNEL_1, \
327 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mbox_rz_mhu_driver_api)
328
329 DT_INST_FOREACH_STATUS_OKAY(MHU_RZG_INIT);
330