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