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