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