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