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 /* Notify user with NULL data pointer */
59 if (data->callback) {
60 data->callback(dev, data->user_data, 0, NULL);
61 }
62
63 /* Clear semaphore rx_semid interrupt status and masked status */
64 ll_hsem_clearflag_cicr(HSEM, mask_semid);
65 }
66
stm32_hsem_mailbox_irq_config_func(const struct device * dev)67 static void stm32_hsem_mailbox_irq_config_func(const struct device *dev)
68 {
69 ARG_UNUSED(dev);
70
71 IRQ_CONNECT(DT_INST_IRQN(0),
72 DT_INST_IRQ(0, priority),
73 stm32_hsem_mailbox_ipm_rx_isr, DEVICE_DT_INST_GET(0), 0);
74
75 irq_enable(DT_INST_IRQN(0));
76 }
77
stm32_hsem_mailbox_ipm_send(const struct device * dev,int wait,uint32_t id,const void * buff,int size)78 int stm32_hsem_mailbox_ipm_send(const struct device *dev, int wait, uint32_t id,
79 const void *buff, int size)
80 {
81 struct stm32_hsem_mailbox_data *data = dev->data;
82
83 ARG_UNUSED(wait);
84 ARG_UNUSED(buff);
85
86 if (size) {
87 LOG_WRN("stm32 HSEM not support data transfer");
88 return -EMSGSIZE;
89 }
90
91 if (id) {
92 LOG_WRN("stm32 HSEM only support a single instance of mailbox");
93 return -EINVAL;
94 }
95
96 /* Lock the semaphore tx_semid */
97 z_stm32_hsem_lock(data->tx_semid, HSEM_LOCK_DEFAULT_RETRY);
98
99 /**
100 * Release the semaphore tx_semid.
101 * This will trigger a HSEMx interrupt on another CPU.
102 */
103 z_stm32_hsem_unlock(data->tx_semid);
104
105 return 0;
106 }
107
stm32_hsem_mailbox_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)108 void stm32_hsem_mailbox_ipm_register_callback(const struct device *dev,
109 ipm_callback_t cb,
110 void *user_data)
111 {
112 struct stm32_hsem_mailbox_data *data = dev->data;
113
114 data->callback = cb;
115 data->user_data = user_data;
116 }
117
stm32_hsem_mailbox_ipm_max_data_size_get(const struct device * dev)118 int stm32_hsem_mailbox_ipm_max_data_size_get(const struct device *dev)
119 {
120 ARG_UNUSED(dev);
121
122 /* stm32 HSEM not support data transfer */
123 return 0;
124 }
125
stm32_hsem_mailbox_ipm_max_id_val_get(const struct device * dev)126 uint32_t stm32_hsem_mailbox_ipm_max_id_val_get(const struct device *dev)
127 {
128 ARG_UNUSED(dev);
129
130 /* stm32 HSEM only support a single instance of mailbox */
131 return 0;
132 }
133
stm32_hsem_mailbox_ipm_set_enabled(const struct device * dev,int enable)134 int stm32_hsem_mailbox_ipm_set_enabled(const struct device *dev, int enable)
135 {
136 struct stm32_hsem_mailbox_data *data = dev->data;
137 uint32_t mask_semid = (1U << data->rx_semid);
138
139 if (enable) {
140 /* Clear semaphore rx_semid interrupt status and masked status */
141 ll_hsem_clearflag_cicr(HSEM, mask_semid);
142 /* Enable semaphore rx_semid on HESMx interrupt */
143 ll_hsem_enableit_cier(HSEM, mask_semid);
144 } else {
145 /* Disable semaphore rx_semid on HSEMx interrupt */
146 ll_hsem_disableit_cier(HSEM, mask_semid);
147 }
148
149 return 0;
150 }
151
stm32_hsem_mailbox_init(const struct device * dev)152 static int stm32_hsem_mailbox_init(const struct device *dev)
153 {
154 struct stm32_hsem_mailbox_data *data = dev->data;
155 const struct stm32_hsem_mailbox_config *cfg = dev->config;
156 const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
157
158 /* Config transfer semaphore */
159 switch (CONFIG_IPM_STM32_HSEM_CPU) {
160 case HSEM_CPU1:
161 if (!device_is_ready(clk)) {
162 LOG_ERR("clock control device not ready");
163 return -ENODEV;
164 }
165
166 /* Enable clock */
167 if (clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken) != 0) {
168 LOG_WRN("Failed to enable clock");
169 return -EIO;
170 }
171
172 data->tx_semid = CFG_HW_IPM_CPU2_SEMID;
173 data->rx_semid = CFG_HW_IPM_CPU1_SEMID;
174 break;
175 case HSEM_CPU2:
176 data->tx_semid = CFG_HW_IPM_CPU1_SEMID;
177 data->rx_semid = CFG_HW_IPM_CPU2_SEMID;
178 break;
179 }
180
181 cfg->irq_config_func(dev);
182
183 return 0;
184 }
185
186 static const struct ipm_driver_api stm32_hsem_mailbox_ipm_dirver_api = {
187 .send = stm32_hsem_mailbox_ipm_send,
188 .register_callback = stm32_hsem_mailbox_ipm_register_callback,
189 .max_data_size_get = stm32_hsem_mailbox_ipm_max_data_size_get,
190 .max_id_val_get = stm32_hsem_mailbox_ipm_max_id_val_get,
191 .set_enabled = stm32_hsem_mailbox_ipm_set_enabled,
192 };
193
194 static const struct stm32_hsem_mailbox_config stm32_hsem_mailbox_0_config = {
195 .irq_config_func = stm32_hsem_mailbox_irq_config_func,
196 .pclken = {
197 .bus = DT_INST_CLOCKS_CELL(0, bus),
198 .enr = DT_INST_CLOCKS_CELL(0, bits)
199 },
200 };
201
202
203 /*
204 * STM32 HSEM has its own LL_HSEM(low-level HSEM) API provided by the hal_stm32 module.
205 * The ipm_stm32_hsem driver only picks up two semaphore IDs from stm32_hsem.h to simulate
206 * a virtual mailbox device. So there will have only one instance.
207 */
208 #define IPM_STM32_HSEM_INIT(inst) \
209 BUILD_ASSERT((inst) == 0, \
210 "multiple instances not supported"); \
211 DEVICE_DT_INST_DEFINE(0, \
212 &stm32_hsem_mailbox_init, \
213 NULL, \
214 &stm32_hsem_mailbox_0_data, \
215 &stm32_hsem_mailbox_0_config, \
216 POST_KERNEL, \
217 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
218 &stm32_hsem_mailbox_ipm_dirver_api); \
219
220 DT_INST_FOREACH_STATUS_OKAY(IPM_STM32_HSEM_INIT)
221