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