1 /*
2  * Copyright (c) 2024 Celina Sophie Kalus <hello@celinakalus.de>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/device.h>
7 #include <zephyr/drivers/clock_control.h>
8 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
9 #include <zephyr/drivers/mbox.h>
10 #include <zephyr/irq.h>
11 #include <zephyr/logging/log.h>
12 
13 #include "stm32_hsem.h"
14 
15 LOG_MODULE_REGISTER(mbox_stm32_hsem_ipc, CONFIG_MBOX_LOG_LEVEL);
16 
17 #define DT_DRV_COMPAT st_mbox_stm32_hsem
18 
19 #define HSEM_CPU1                   1
20 #define HSEM_CPU2                   2
21 
22 #if DT_NODE_EXISTS(DT_NODELABEL(cpu0))
23 #define HSEM_CPU_ID HSEM_CPU1
24 #elif DT_NODE_EXISTS(DT_NODELABEL(cpu1))
25 #define HSEM_CPU_ID HSEM_CPU2
26 #else
27 #error "Neither cpu0 nor cpu1 defined!"
28 #endif
29 
30 #if HSEM_CPU_ID == HSEM_CPU1
31 #define MBOX_TX_HSEM_ID CFG_HW_IPM_CPU2_SEMID
32 #define MBOX_RX_HSEM_ID CFG_HW_IPM_CPU1_SEMID
33 #else /* HSEM_CPU2 */
34 #define MBOX_TX_HSEM_ID CFG_HW_IPM_CPU1_SEMID
35 #define MBOX_RX_HSEM_ID CFG_HW_IPM_CPU2_SEMID
36 #endif /* HSEM_CPU_ID */
37 
38 #define MAX_CHANNELS 2
39 
40 struct mbox_stm32_hsem_data {
41 	const struct device *dev;
42 	mbox_callback_t cb;
43 	void *user_data;
44 };
45 
46 static struct mbox_stm32_hsem_data stm32_hsem_mbox_data;
47 
48 static struct mbox_stm32_hsem_conf {
49 	struct stm32_pclken pclken;
50 } stm32_hsem_mbox_conf = {
51 	.pclken = {
52 		.bus = DT_INST_CLOCKS_CELL(0, bus),
53 		.enr = DT_INST_CLOCKS_CELL(0, bits)
54 	},
55 };
56 
stm32_hsem_enable_rx_interrupt(void)57 static inline void stm32_hsem_enable_rx_interrupt(void)
58 {
59 	const uint32_t mask_hsem_id = BIT(MBOX_RX_HSEM_ID);
60 
61 #if HSEM_CPU_ID == HSEM_CPU1
62 	LL_HSEM_EnableIT_C1IER(HSEM, mask_hsem_id);
63 #else /* HSEM_CPU2 */
64 	LL_HSEM_EnableIT_C2IER(HSEM, mask_hsem_id);
65 #endif /* HSEM_CPU_ID */
66 }
67 
stm32_hsem_disable_rx_interrupt(void)68 static inline void stm32_hsem_disable_rx_interrupt(void)
69 {
70 	const uint32_t mask_hsem_id = BIT(MBOX_RX_HSEM_ID);
71 
72 #if HSEM_CPU_ID == HSEM_CPU1
73 	LL_HSEM_DisableIT_C1IER(HSEM, mask_hsem_id);
74 #else /* HSEM_CPU2 */
75 	LL_HSEM_DisableIT_C2IER(HSEM, mask_hsem_id);
76 #endif /* HSEM_CPU_ID */
77 }
78 
stm32_hsem_clear_rx_interrupt(void)79 static inline void stm32_hsem_clear_rx_interrupt(void)
80 {
81 	const uint32_t mask_hsem_id = BIT(MBOX_RX_HSEM_ID);
82 
83 #if HSEM_CPU_ID == HSEM_CPU1
84 	LL_HSEM_ClearFlag_C1ICR(HSEM, mask_hsem_id);
85 #else /* HSEM_CPU2 */
86 	LL_HSEM_ClearFlag_C2ICR(HSEM, mask_hsem_id);
87 #endif /* HSEM_CPU_ID */
88 }
89 
stm32_hsem_is_rx_interrupt_active(void)90 static inline uint32_t stm32_hsem_is_rx_interrupt_active(void)
91 {
92 	const uint32_t mask_hsem_id = BIT(MBOX_RX_HSEM_ID);
93 
94 #if HSEM_CPU_ID == HSEM_CPU1
95 	return LL_HSEM_IsActiveFlag_C1ISR(HSEM, mask_hsem_id);
96 #else /* HSEM_CPU2 */
97 	return LL_HSEM_IsActiveFlag_C2ISR(HSEM, mask_hsem_id);
98 #endif /* HSEM_CPU_ID */
99 }
100 
is_rx_channel_valid(const struct device * dev,uint32_t ch)101 static inline bool is_rx_channel_valid(const struct device *dev, uint32_t ch)
102 {
103 	/* Only support one RX channel */
104 	return (ch == MBOX_RX_HSEM_ID);
105 }
106 
is_tx_channel_valid(const struct device * dev,uint32_t ch)107 static inline bool is_tx_channel_valid(const struct device *dev, uint32_t ch)
108 {
109 	/* Only support one TX channel */
110 	return (ch == MBOX_TX_HSEM_ID);
111 }
112 
mbox_dispatcher(const struct device * dev)113 static void mbox_dispatcher(const struct device *dev)
114 {
115 	struct mbox_stm32_hsem_data *data = dev->data;
116 
117 	/* Check semaphore rx_semid interrupt status */
118 	if (!stm32_hsem_is_rx_interrupt_active()) {
119 		return;
120 	}
121 
122 	if (data->cb != NULL) {
123 		data->cb(dev, MBOX_RX_HSEM_ID, data->user_data, NULL);
124 	}
125 
126 	/* Clear semaphore rx_semid interrupt status and masked status */
127 	stm32_hsem_clear_rx_interrupt();
128 }
129 
mbox_stm32_hsem_send(const struct device * dev,uint32_t channel,const struct mbox_msg * msg)130 static int mbox_stm32_hsem_send(const struct device *dev, uint32_t channel,
131 			 const struct mbox_msg *msg)
132 {
133 	if (msg) {
134 		LOG_ERR("Sending data not supported.");
135 		return -EINVAL;
136 	}
137 
138 	if (!is_tx_channel_valid(dev, channel)) {
139 		return -EINVAL;
140 	}
141 
142 	/*
143 	 * Locking and unlocking the hardware semaphore
144 	 * causes an interrupt on the receiving side.
145 	 */
146 	z_stm32_hsem_lock(MBOX_TX_HSEM_ID, HSEM_LOCK_DEFAULT_RETRY);
147 	z_stm32_hsem_unlock(MBOX_TX_HSEM_ID);
148 
149 	return 0;
150 }
151 
mbox_stm32_hsem_register_callback(const struct device * dev,uint32_t channel,mbox_callback_t cb,void * user_data)152 static int mbox_stm32_hsem_register_callback(const struct device *dev, uint32_t channel,
153 				      mbox_callback_t cb, void *user_data)
154 {
155 	struct mbox_stm32_hsem_data *data = dev->data;
156 
157 	if (!(is_rx_channel_valid(dev, channel))) {
158 		return -EINVAL;
159 	}
160 
161 	data->cb = cb;
162 	data->user_data = user_data;
163 
164 	return 0;
165 }
166 
mbox_stm32_hsem_mtu_get(const struct device * dev)167 static int mbox_stm32_hsem_mtu_get(const struct device *dev)
168 {
169 	ARG_UNUSED(dev);
170 
171 	/* We only support signalling */
172 	return 0;
173 }
174 
mbox_stm32_hsem_max_channels_get(const struct device * dev)175 static uint32_t mbox_stm32_hsem_max_channels_get(const struct device *dev)
176 {
177 	ARG_UNUSED(dev);
178 
179 	/* Only two channels supported, one RX and one TX */
180 	return MAX_CHANNELS;
181 }
182 
mbox_stm32_hsem_set_enabled(const struct device * dev,uint32_t channel,bool enable)183 static int mbox_stm32_hsem_set_enabled(const struct device *dev, uint32_t channel, bool enable)
184 {
185 	if (!is_rx_channel_valid(dev, channel)) {
186 		return -EINVAL;
187 	}
188 
189 	if (enable) {
190 		stm32_hsem_clear_rx_interrupt();
191 		stm32_hsem_enable_rx_interrupt();
192 	} else {
193 		stm32_hsem_disable_rx_interrupt();
194 	}
195 
196 	return 0;
197 }
198 
199 #if HSEM_CPU_ID == HSEM_CPU1
mbox_stm32_clock_init(const struct device * dev)200 static int mbox_stm32_clock_init(const struct device *dev)
201 {
202 	const struct mbox_stm32_hsem_conf *cfg = dev->config;
203 	const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
204 
205 	if (!device_is_ready(clk)) {
206 		LOG_ERR("Clock control device not ready.");
207 		return -ENODEV;
208 	}
209 
210 	if (clock_control_on(clk, (clock_control_subsys_t *)&cfg->pclken) != 0) {
211 		LOG_WRN("Failed to enable clock.");
212 		return -EIO;
213 	}
214 
215 	return 0;
216 }
217 #endif /* HSEM_CPU_ID */
218 
mbox_stm32_hsem_init(const struct device * dev)219 static int mbox_stm32_hsem_init(const struct device *dev)
220 {
221 	struct mbox_stm32_hsem_data *data = dev->data;
222 	int ret = 0;
223 
224 	data->dev = dev;
225 
226 #if HSEM_CPU_ID == HSEM_CPU1
227 	ret = mbox_stm32_clock_init(dev);
228 
229 	if (ret != 0) {
230 		return ret;
231 	}
232 #endif /* HSEM_CPU_ID */
233 
234 	/* Configure interrupt service routine */
235 	IRQ_CONNECT(DT_INST_IRQN(0),
236 		    DT_INST_IRQ(0, priority),
237 		    mbox_dispatcher, DEVICE_DT_INST_GET(0), 0);
238 
239 	irq_enable(DT_INST_IRQN(0));
240 
241 	return ret;
242 }
243 
244 static DEVICE_API(mbox, mbox_stm32_hsem_driver_api) = {
245 	.send = mbox_stm32_hsem_send,
246 	.register_callback = mbox_stm32_hsem_register_callback,
247 	.mtu_get = mbox_stm32_hsem_mtu_get,
248 	.max_channels_get = mbox_stm32_hsem_max_channels_get,
249 	.set_enabled = mbox_stm32_hsem_set_enabled,
250 };
251 
252 DEVICE_DT_INST_DEFINE(
253 	0,
254 	mbox_stm32_hsem_init,
255 	NULL,
256 	&stm32_hsem_mbox_data,
257 	&stm32_hsem_mbox_conf,
258 	POST_KERNEL,
259 	CONFIG_MBOX_INIT_PRIORITY,
260 	&mbox_stm32_hsem_driver_api);
261