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