1 /*
2 * Copyright (c) 2021 BrainCo Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT st_stm32_hsem_mailbox
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/drivers/ipm.h>
12 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
13
14 #include "stm32_hsem.h"
15
16 #include <zephyr/logging/log.h>
17 #include <zephyr/irq.h>
18 LOG_MODULE_REGISTER(ipm_stm32_hsem, CONFIG_IPM_LOG_LEVEL);
19
20 #define HSEM_CPU1 1
21 #define HSEM_CPU2 2
22
23 #if CONFIG_IPM_STM32_HSEM_CPU == HSEM_CPU1
24 #define ll_hsem_enableit_cier LL_HSEM_EnableIT_C1IER
25 #define ll_hsem_disableit_cier LL_HSEM_DisableIT_C1IER
26 #define ll_hsem_clearflag_cicr LL_HSEM_ClearFlag_C1ICR
27 #define ll_hsem_isactiveflag_cmisr LL_HSEM_IsActiveFlag_C1MISR
28 #else /* HSEM_CPU2 */
29 #define ll_hsem_enableit_cier LL_HSEM_EnableIT_C2IER
30 #define ll_hsem_disableit_cier LL_HSEM_DisableIT_C2IER
31 #define ll_hsem_clearflag_cicr LL_HSEM_ClearFlag_C2ICR
32 #define ll_hsem_isactiveflag_cmisr LL_HSEM_IsActiveFlag_C2MISR
33 #endif /* CONFIG_IPM_STM32_HSEM_CPU */
34
35 struct stm32_hsem_mailbox_config {
36 void (*irq_config_func)(const struct device *dev);
37 struct stm32_pclken pclken;
38 };
39
40 struct stm32_hsem_mailbox_data {
41 uint32_t tx_semid;
42 uint32_t rx_semid;
43 ipm_callback_t callback;
44 void *user_data;
45 };
46
47 static struct stm32_hsem_mailbox_data stm32_hsem_mailbox_0_data;
48
stm32_hsem_mailbox_ipm_rx_isr(const struct device * dev)49 void stm32_hsem_mailbox_ipm_rx_isr(const struct device *dev)
50 {
51 struct stm32_hsem_mailbox_data *data = dev->data;
52 uint32_t mask_semid = (1U << data->rx_semid);
53
54 /* Check semaphore rx_semid interrupt status */
55 if (!ll_hsem_isactiveflag_cmisr(HSEM, mask_semid)) {
56 return;
57 }
58
59 /* Notify user with NULL data pointer */
60 if (data->callback) {
61 data->callback(dev, data->user_data, 0, NULL);
62 }
63
64 /* Clear semaphore rx_semid interrupt status and masked status */
65 ll_hsem_clearflag_cicr(HSEM, mask_semid);
66 }
67
stm32_hsem_mailbox_irq_config_func(const struct device * dev)68 static void stm32_hsem_mailbox_irq_config_func(const struct device *dev)
69 {
70 ARG_UNUSED(dev);
71
72 IRQ_CONNECT(DT_INST_IRQN(0),
73 DT_INST_IRQ(0, priority),
74 stm32_hsem_mailbox_ipm_rx_isr, DEVICE_DT_INST_GET(0), 0);
75
76 irq_enable(DT_INST_IRQN(0));
77 }
78
stm32_hsem_mailbox_ipm_send(const struct device * dev,int wait,uint32_t id,const void * buff,int size)79 int stm32_hsem_mailbox_ipm_send(const struct device *dev, int wait, uint32_t id,
80 const void *buff, int size)
81 {
82 struct stm32_hsem_mailbox_data *data = dev->data;
83
84 ARG_UNUSED(wait);
85 ARG_UNUSED(buff);
86
87 if (size) {
88 LOG_WRN("stm32 HSEM not support data transfer");
89 return -EMSGSIZE;
90 }
91
92 if (id) {
93 LOG_WRN("stm32 HSEM only support a single instance of mailbox");
94 return -EINVAL;
95 }
96
97 /* Lock the semaphore tx_semid */
98 z_stm32_hsem_lock(data->tx_semid, HSEM_LOCK_DEFAULT_RETRY);
99
100 /**
101 * Release the semaphore tx_semid.
102 * This will trigger a HSEMx interrupt on another CPU.
103 */
104 z_stm32_hsem_unlock(data->tx_semid);
105
106 return 0;
107 }
108
stm32_hsem_mailbox_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)109 void stm32_hsem_mailbox_ipm_register_callback(const struct device *dev,
110 ipm_callback_t cb,
111 void *user_data)
112 {
113 struct stm32_hsem_mailbox_data *data = dev->data;
114
115 data->callback = cb;
116 data->user_data = user_data;
117 }
118
stm32_hsem_mailbox_ipm_max_data_size_get(const struct device * dev)119 int stm32_hsem_mailbox_ipm_max_data_size_get(const struct device *dev)
120 {
121 ARG_UNUSED(dev);
122
123 /* stm32 HSEM not support data transfer */
124 return 0;
125 }
126
stm32_hsem_mailbox_ipm_max_id_val_get(const struct device * dev)127 uint32_t stm32_hsem_mailbox_ipm_max_id_val_get(const struct device *dev)
128 {
129 ARG_UNUSED(dev);
130
131 /* stm32 HSEM only support a single instance of mailbox */
132 return 0;
133 }
134
stm32_hsem_mailbox_ipm_set_enabled(const struct device * dev,int enable)135 int stm32_hsem_mailbox_ipm_set_enabled(const struct device *dev, int enable)
136 {
137 struct stm32_hsem_mailbox_data *data = dev->data;
138 uint32_t mask_semid = (1U << data->rx_semid);
139
140 if (enable) {
141 /* Clear semaphore rx_semid interrupt status and masked status */
142 ll_hsem_clearflag_cicr(HSEM, mask_semid);
143 /* Enable semaphore rx_semid on HESMx interrupt */
144 ll_hsem_enableit_cier(HSEM, mask_semid);
145 } else {
146 /* Disable semaphore rx_semid on HSEMx interrupt */
147 ll_hsem_disableit_cier(HSEM, mask_semid);
148 }
149
150 return 0;
151 }
152
stm32_hsem_mailbox_init(const struct device * dev)153 static int stm32_hsem_mailbox_init(const struct device *dev)
154 {
155 struct stm32_hsem_mailbox_data *data = dev->data;
156 const struct stm32_hsem_mailbox_config *cfg = dev->config;
157 const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
158
159 /* Config transfer semaphore */
160 switch (CONFIG_IPM_STM32_HSEM_CPU) {
161 case HSEM_CPU1:
162 if (!device_is_ready(clk)) {
163 LOG_ERR("clock control device not ready");
164 return -ENODEV;
165 }
166
167 /* Enable clock */
168 if (clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken) != 0) {
169 LOG_WRN("Failed to enable clock");
170 return -EIO;
171 }
172
173 data->tx_semid = CFG_HW_IPM_CPU2_SEMID;
174 data->rx_semid = CFG_HW_IPM_CPU1_SEMID;
175 break;
176 case HSEM_CPU2:
177 data->tx_semid = CFG_HW_IPM_CPU1_SEMID;
178 data->rx_semid = CFG_HW_IPM_CPU2_SEMID;
179 break;
180 }
181
182 cfg->irq_config_func(dev);
183
184 return 0;
185 }
186
187 static DEVICE_API(ipm, stm32_hsem_mailbox_ipm_dirver_api) = {
188 .send = stm32_hsem_mailbox_ipm_send,
189 .register_callback = stm32_hsem_mailbox_ipm_register_callback,
190 .max_data_size_get = stm32_hsem_mailbox_ipm_max_data_size_get,
191 .max_id_val_get = stm32_hsem_mailbox_ipm_max_id_val_get,
192 .set_enabled = stm32_hsem_mailbox_ipm_set_enabled,
193 };
194
195 static const struct stm32_hsem_mailbox_config stm32_hsem_mailbox_0_config = {
196 .irq_config_func = stm32_hsem_mailbox_irq_config_func,
197 .pclken = {
198 .bus = DT_INST_CLOCKS_CELL(0, bus),
199 .enr = DT_INST_CLOCKS_CELL(0, bits)
200 },
201 };
202
203
204 /*
205 * STM32 HSEM has its own LL_HSEM(low-level HSEM) API provided by the hal_stm32 module.
206 * The ipm_stm32_hsem driver only picks up two semaphore IDs from stm32_hsem.h to simulate
207 * a virtual mailbox device. So there will have only one instance.
208 */
209 #define IPM_STM32_HSEM_INIT(inst) \
210 BUILD_ASSERT((inst) == 0, \
211 "multiple instances not supported"); \
212 DEVICE_DT_INST_DEFINE(0, \
213 &stm32_hsem_mailbox_init, \
214 NULL, \
215 &stm32_hsem_mailbox_0_data, \
216 &stm32_hsem_mailbox_0_config, \
217 POST_KERNEL, \
218 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
219 &stm32_hsem_mailbox_ipm_dirver_api); \
220
221 DT_INST_FOREACH_STATUS_OKAY(IPM_STM32_HSEM_INIT)
222