1 /*
2  * Copyright (c) 2019, Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nordic_nrf_ipc
8 
9 #include <string.h>
10 #include <drivers/ipm.h>
11 #include <nrfx_ipc.h>
12 #include "ipm_nrfx_ipc.h"
13 
14 #define LOG_LEVEL CONFIG_IPM_LOG_LEVEL
15 #include <logging/log.h>
16 LOG_MODULE_REGISTER(ipm_nrfx_ipc);
17 
18 struct ipm_nrf_data {
19 	ipm_callback_t callback;
20 	void *user_data;
21 };
22 
23 static struct ipm_nrf_data nrfx_ipm_data;
24 
25 static void gipm_init(void);
26 static void gipm_send(uint32_t id);
27 
28 #if IS_ENABLED(CONFIG_IPM_NRF_SINGLE_INSTANCE)
29 
nrfx_ipc_handler(uint32_t event_mask,void * p_context)30 static void nrfx_ipc_handler(uint32_t event_mask, void *p_context)
31 {
32 	if (nrfx_ipm_data.callback) {
33 		while (event_mask) {
34 			uint8_t event_idx = __CLZ(__RBIT(event_mask));
35 
36 			__ASSERT(event_idx < NRFX_IPC_ID_MAX_VALUE,
37 				 "Illegal event_idx: %d", event_idx);
38 			event_mask &= ~BIT(event_idx);
39 			nrfx_ipm_data.callback(DEVICE_DT_INST_GET(0),
40 					       nrfx_ipm_data.user_data,
41 					       event_idx,
42 					       NULL);
43 		}
44 	}
45 }
46 
ipm_nrf_send(const struct device * dev,int wait,uint32_t id,const void * data,int size)47 static int ipm_nrf_send(const struct device *dev, int wait, uint32_t id,
48 			const void *data, int size)
49 {
50 	if (id > NRFX_IPC_ID_MAX_VALUE) {
51 		return -EINVAL;
52 	}
53 
54 	if (size > 0) {
55 		LOG_WRN("nRF driver does not support sending data over IPM");
56 	}
57 
58 	gipm_send(id);
59 	return 0;
60 }
61 
ipm_nrf_max_data_size_get(const struct device * dev)62 static int ipm_nrf_max_data_size_get(const struct device *dev)
63 {
64 	ARG_UNUSED(dev);
65 
66 	return 0;
67 }
68 
ipm_nrf_max_id_val_get(const struct device * dev)69 static uint32_t ipm_nrf_max_id_val_get(const struct device *dev)
70 {
71 	ARG_UNUSED(dev);
72 
73 	return NRFX_IPC_ID_MAX_VALUE;
74 }
75 
ipm_nrf_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)76 static void ipm_nrf_register_callback(const struct device *dev,
77 				      ipm_callback_t cb,
78 				      void *user_data)
79 {
80 	nrfx_ipm_data.callback = cb;
81 	nrfx_ipm_data.user_data = user_data;
82 }
83 
ipm_nrf_set_enabled(const struct device * dev,int enable)84 static int ipm_nrf_set_enabled(const struct device *dev, int enable)
85 {
86 	/* Enable configured channels */
87 	if (enable) {
88 		irq_enable(DT_INST_IRQN(0));
89 		nrfx_ipc_receive_event_group_enable((uint32_t)IPC_EVENT_BITS);
90 	} else {
91 		irq_disable(DT_INST_IRQN(0));
92 		nrfx_ipc_receive_event_group_disable((uint32_t)IPC_EVENT_BITS);
93 	}
94 	return 0;
95 }
96 
ipm_nrf_init(const struct device * dev)97 static int ipm_nrf_init(const struct device *dev)
98 {
99 	gipm_init();
100 	return 0;
101 }
102 
103 static const struct ipm_driver_api ipm_nrf_driver_api = {
104 	.send = ipm_nrf_send,
105 	.register_callback = ipm_nrf_register_callback,
106 	.max_data_size_get = ipm_nrf_max_data_size_get,
107 	.max_id_val_get = ipm_nrf_max_id_val_get,
108 	.set_enabled = ipm_nrf_set_enabled
109 };
110 
111 DEVICE_DT_INST_DEFINE(0, ipm_nrf_init, NULL, NULL, NULL,
112 		    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
113 		    &ipm_nrf_driver_api);
114 
115 #else
116 
117 struct vipm_nrf_data {
118 	ipm_callback_t callback[NRFX_IPC_ID_MAX_VALUE];
119 	void *user_data[NRFX_IPC_ID_MAX_VALUE];
120 	const struct device *ipm_device[NRFX_IPC_ID_MAX_VALUE];
121 	bool ipm_init;
122 };
123 
124 static struct vipm_nrf_data nrfx_vipm_data;
125 
vipm_dispatcher(uint32_t event_mask,void * p_context)126 static void vipm_dispatcher(uint32_t event_mask, void *p_context)
127 {
128 	while (event_mask) {
129 		uint8_t event_idx = __CLZ(__RBIT(event_mask));
130 
131 		__ASSERT(event_idx < NRFX_IPC_ID_MAX_VALUE,
132 			 "Illegal event_idx: %d", event_idx);
133 		event_mask &= ~BIT(event_idx);
134 		if (nrfx_vipm_data.callback[event_idx] != NULL) {
135 			nrfx_vipm_data.callback[event_idx]
136 				(nrfx_vipm_data.ipm_device[event_idx],
137 				 nrfx_vipm_data.user_data[event_idx],
138 				 0,
139 				 NULL);
140 		}
141 	}
142 }
143 
vipm_nrf_max_data_size_get(const struct device * dev)144 static int vipm_nrf_max_data_size_get(const struct device *dev)
145 {
146 	return ipm_max_data_size_get(dev);
147 }
148 
vipm_nrf_max_id_val_get(const struct device * dev)149 static uint32_t vipm_nrf_max_id_val_get(const struct device *dev)
150 {
151 	ARG_UNUSED(dev);
152 
153 	return 0;
154 }
155 
vipm_nrf_init(const struct device * dev)156 static int vipm_nrf_init(const struct device *dev)
157 {
158 	if (!nrfx_vipm_data.ipm_init) {
159 		gipm_init();
160 		nrfx_vipm_data.ipm_init = true;
161 	}
162 	return 0;
163 }
164 
165 #define VIPM_DEVICE_1(_idx)						\
166 static int vipm_nrf_##_idx##_send(const struct device *dev, int wait,	\
167 				  uint32_t id, const void *data, int size)	\
168 {									\
169 	if (!IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_TX)) {		\
170 		LOG_ERR("IPM_" #_idx " is RX message channel");		\
171 		return -EINVAL;						\
172 	}								\
173 									\
174 	if (id > NRFX_IPC_ID_MAX_VALUE) {				\
175 		return -EINVAL;						\
176 	}								\
177 									\
178 	if (id != 0) {							\
179 		LOG_WRN("Passing message ID to IPM with"		\
180 			"predefined message ID");			\
181 	}								\
182 									\
183 	if (size > 0) {							\
184 		LOG_WRN("nRF driver does not support"			\
185 			"sending data over IPM");			\
186 	}								\
187 									\
188 	gipm_send(_idx);						\
189 	return 0;							\
190 }									\
191 									\
192 static void vipm_nrf_##_idx##_register_callback(const struct device *dev, \
193 						ipm_callback_t cb,	\
194 						void *user_data)	\
195 {									\
196 	if (IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_RX)) {		\
197 		nrfx_vipm_data.callback[_idx] = cb;			\
198 		nrfx_vipm_data.user_data[_idx] = user_data;		\
199 		nrfx_vipm_data.ipm_device[_idx] = dev;			\
200 	} else {							\
201 		LOG_WRN("Trying to register a callback"			\
202 			"for TX channel IPM_" #_idx);			\
203 	}								\
204 }									\
205 									\
206 static int vipm_nrf_##_idx##_set_enabled(const struct device *dev, int enable)\
207 {									\
208 	if (!IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_RX)) {		\
209 		LOG_ERR("IPM_" #_idx " is TX message channel");		\
210 		return -EINVAL;						\
211 	} else if (enable) {						\
212 		irq_enable(DT_INST_IRQN(0));		\
213 		nrfx_ipc_receive_event_enable(_idx);			\
214 	} else if (!enable) {						\
215 		nrfx_ipc_receive_event_disable(_idx);			\
216 	}								\
217 	return 0;							\
218 }									\
219 									\
220 static const struct ipm_driver_api vipm_nrf_##_idx##_driver_api = {	\
221 	.send = vipm_nrf_##_idx##_send,					\
222 	.register_callback = vipm_nrf_##_idx##_register_callback,	\
223 	.max_data_size_get = vipm_nrf_max_data_size_get,		\
224 	.max_id_val_get = vipm_nrf_max_id_val_get,			\
225 	.set_enabled = vipm_nrf_##_idx##_set_enabled			\
226 };									\
227 									\
228 DEVICE_DEFINE(vipm_nrf_##_idx, "IPM_"#_idx,				\
229 		    vipm_nrf_init, NULL, NULL, NULL,			\
230 		    PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,	\
231 		    &vipm_nrf_##_idx##_driver_api)
232 
233 #define VIPM_DEVICE(_idx, _)						\
234 	IF_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_ENABLE, (VIPM_DEVICE_1(_idx);))
235 
236 UTIL_LISTIFY(NRFX_IPC_ID_MAX_VALUE, VIPM_DEVICE, _);
237 
238 #endif
239 
gipm_init(void)240 static void gipm_init(void)
241 {
242 	/* Init IPC */
243 #if IS_ENABLED(CONFIG_IPM_NRF_SINGLE_INSTANCE)
244 	nrfx_ipc_init(0, nrfx_ipc_handler, (void *)&nrfx_ipm_data);
245 #else
246 	nrfx_ipc_init(0, vipm_dispatcher, (void *)&nrfx_ipm_data);
247 #endif
248 	IRQ_CONNECT(DT_INST_IRQN(0),
249 		    DT_INST_IRQ(0, priority),
250 		    nrfx_isr, nrfx_ipc_irq_handler, 0);
251 
252 	/* Set up signals and channels */
253 	nrfx_ipc_config_load(&ipc_cfg);
254 }
255 
gipm_send(uint32_t id)256 static void gipm_send(uint32_t id)
257 {
258 	nrfx_ipc_signal(id);
259 }
260