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