1 /*
2 * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT espressif_esp32_ipm
8 #include "soc/dport_reg.h"
9 #include "soc/gpio_periph.h"
10
11 #include <stdint.h>
12 #include <string.h>
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/ipm.h>
15 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
16 #include <soc.h>
17 #include <zephyr/sys/atomic.h>
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(ipm_esp32, CONFIG_IPM_LOG_LEVEL);
21
22 #define ESP32_IPM_LOCK_FREE_VAL 0xB33FFFFF
23 #define ESP32_IPM_NOOP_VAL 0xFF
24
25 __packed struct esp32_ipm_control {
26 uint16_t dest_cpu_msg_id[2];
27 atomic_val_t lock;
28 };
29
30 struct esp32_ipm_memory {
31 uint8_t *pro_cpu_shm;
32 uint8_t *app_cpu_shm;
33 };
34
35 struct esp32_ipm_config {
36 int irq_source_pro_cpu;
37 int irq_priority_pro_cpu;
38 int irq_flags_pro_cpu;
39 int irq_source_app_cpu;
40 int irq_priority_app_cpu;
41 int irq_flags_app_cpu;
42 };
43
44 struct esp32_ipm_data {
45 ipm_callback_t cb;
46 void *user_data;
47 uint32_t this_core_id;
48 uint32_t other_core_id;
49 uint32_t shm_size;
50 struct esp32_ipm_memory shm;
51 struct esp32_ipm_control *control;
52 };
53
esp32_ipm_isr(const struct device * dev)54 IRAM_ATTR static void esp32_ipm_isr(const struct device *dev)
55 {
56 struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
57 uint32_t core_id = dev_data->this_core_id;
58
59 /* clear interrupt flag */
60 if (core_id == 0) {
61 #if defined(CONFIG_SOC_SERIES_ESP32)
62 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);
63 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
64 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 0);
65 #endif
66 } else {
67 #if defined(CONFIG_SOC_SERIES_ESP32)
68 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
69 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
70 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, 0);
71 #endif
72 }
73
74 /* first of all take the own of the shared memory */
75 while (!atomic_cas(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL,
76 dev_data->this_core_id)) {
77 ;
78 }
79
80 if (dev_data->cb) {
81
82 volatile void *shm = dev_data->shm.pro_cpu_shm;
83
84 if (core_id != 0) {
85 shm = dev_data->shm.app_cpu_shm;
86 }
87
88 dev_data->cb(dev,
89 dev_data->user_data,
90 dev_data->control->dest_cpu_msg_id[core_id],
91 shm);
92 }
93
94 /* unlock the shared memory */
95 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
96 }
97
esp32_ipm_send(const struct device * dev,int wait,uint32_t id,const void * data,int size)98 static int esp32_ipm_send(const struct device *dev, int wait, uint32_t id,
99 const void *data, int size)
100 {
101 struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
102
103 if (size > 0 && data == NULL) {
104 LOG_ERR("Invalid data source");
105 return -EINVAL;
106 }
107
108 if (id > 0xFFFF) {
109 LOG_ERR("Invalid message ID format");
110 return -EINVAL;
111 }
112
113 if (dev_data->shm_size < size) {
114 LOG_ERR("Not enough memory in IPM channel");
115 return -ENOMEM;
116 }
117
118 uint32_t key = irq_lock();
119
120 /* try to lock the shared memory */
121 while (!atomic_cas(&dev_data->control->lock,
122 ESP32_IPM_LOCK_FREE_VAL,
123 dev_data->this_core_id)) {
124 /* Do not wait for availability */
125 if (wait == 0) {
126 irq_unlock(key);
127
128 return -EBUSY;
129 }
130
131 k_busy_wait(1);
132 }
133
134 /* Only the lower 16bits of id are used */
135 dev_data->control->dest_cpu_msg_id[dev_data->other_core_id] = (uint16_t)(id & 0xFFFF);
136
137 /* data copied, set the id and, generate interrupt in the remote core */
138 if (dev_data->this_core_id == 0) {
139 memcpy(dev_data->shm.app_cpu_shm, data, size);
140 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
141 LOG_DBG("Generating interrupt on remote CPU 1 from CPU 0");
142 #if defined(CONFIG_SOC_SERIES_ESP32)
143 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
144 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
145 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, SYSTEM_CPU_INTR_FROM_CPU_1);
146 #endif
147
148 } else {
149 memcpy(dev_data->shm.pro_cpu_shm, data, size);
150 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
151 LOG_DBG("Generating interrupt on remote CPU 0 from CPU 1");
152 #if defined(CONFIG_SOC_SERIES_ESP32)
153 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
154 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
155 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, SYSTEM_CPU_INTR_FROM_CPU_0);
156 #endif
157 }
158
159 irq_unlock(key);
160
161 return 0;
162 }
163
esp32_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)164 static void esp32_ipm_register_callback(const struct device *dev,
165 ipm_callback_t cb,
166 void *user_data)
167 {
168 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
169
170 uint32_t key = irq_lock();
171
172 data->cb = cb;
173 data->user_data = user_data;
174
175 irq_unlock(key);
176 }
177
esp32_ipm_max_data_size_get(const struct device * dev)178 static int esp32_ipm_max_data_size_get(const struct device *dev)
179 {
180 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
181
182 return data->shm_size;
183 }
184
esp_32_ipm_max_id_val_get(const struct device * dev)185 static uint32_t esp_32_ipm_max_id_val_get(const struct device *dev)
186 {
187 ARG_UNUSED(dev);
188 return 0xFFFF;
189 }
190
esp_32_ipm_set_enabled(const struct device * dev,int enable)191 static int esp_32_ipm_set_enabled(const struct device *dev, int enable)
192 {
193 /* The esp32 IPM is always enabled
194 * but rpmsg backend needs IPM set enabled to be
195 * implemented so just return success here
196 */
197
198 ARG_UNUSED(dev);
199 ARG_UNUSED(enable);
200
201 return 0;
202 }
203
204
esp32_ipm_init(const struct device * dev)205 static int esp32_ipm_init(const struct device *dev)
206 {
207 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
208 struct esp32_ipm_config *cfg = (struct esp32_ipm_config *)dev->config;
209 int ret;
210
211 data->this_core_id = esp_core_id();
212 data->other_core_id = (data->this_core_id == 0) ? 1 : 0;
213
214 LOG_DBG("Size of IPM shared memory: %d", data->shm_size);
215 LOG_DBG("Address of PRO_CPU IPM shared memory: %p", (void *)data->shm.pro_cpu_shm);
216 LOG_DBG("Address of APP_CPU IPM shared memory: %p", (void *)data->shm.app_cpu_shm);
217 LOG_DBG("Address of IPM control structure: %p", (void *)data->control);
218
219 /* pro_cpu is responsible to initialize the lock of shared memory */
220 if (data->this_core_id == 0) {
221 ret = esp_intr_alloc(cfg->irq_source_pro_cpu,
222 ESP_PRIO_TO_FLAGS(cfg->irq_priority_pro_cpu) |
223 ESP_INT_FLAGS_CHECK(cfg->irq_flags_pro_cpu) |
224 ESP_INTR_FLAG_IRAM,
225 (intr_handler_t)esp32_ipm_isr,
226 (void *)dev,
227 NULL);
228
229 if (ret != 0) {
230 LOG_ERR("could not allocate interrupt (err %d)", ret);
231 return ret;
232 }
233
234 atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
235 } else {
236 /* app_cpu wait for initialization from pro_cpu, then takes it,
237 * after that releases
238 */
239 ret = esp_intr_alloc(cfg->irq_source_app_cpu,
240 ESP_PRIO_TO_FLAGS(cfg->irq_priority_app_cpu) |
241 ESP_INT_FLAGS_CHECK(cfg->irq_flags_app_cpu) |
242 ESP_INTR_FLAG_IRAM,
243 (intr_handler_t)esp32_ipm_isr,
244 (void *)dev,
245 NULL);
246
247 if (ret != 0) {
248 LOG_ERR("could not allocate interrupt (err %d)", ret);
249 return ret;
250 }
251
252 LOG_DBG("Waiting CPU0 to sync");
253 while (!atomic_cas(&data->control->lock,
254 ESP32_IPM_LOCK_FREE_VAL, data->this_core_id)) {
255 ;
256 }
257
258 atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
259
260 LOG_DBG("Synchronization done");
261
262 }
263
264 return 0;
265 }
266
267 static DEVICE_API(ipm, esp32_ipm_driver_api) = {
268 .send = esp32_ipm_send,
269 .register_callback = esp32_ipm_register_callback,
270 .max_data_size_get = esp32_ipm_max_data_size_get,
271 .max_id_val_get = esp_32_ipm_max_id_val_get,
272 .set_enabled = esp_32_ipm_set_enabled
273 };
274
275 #define ESP32_IPM_SHM_SIZE_BY_IDX(idx) \
276 DT_INST_PROP(idx, shared_memory_size) \
277
278 #define ESP32_IPM_SHM_ADDR_BY_IDX(idx) \
279 DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(idx), shared_memory)) \
280
281 #define ESP32_IPM_INIT(idx) \
282 \
283 static struct esp32_ipm_config esp32_ipm_device_cfg_##idx = { \
284 .irq_source_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, irq), \
285 .irq_priority_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, priority), \
286 .irq_flags_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, flags), \
287 .irq_source_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, irq), \
288 .irq_priority_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, priority), \
289 .irq_flags_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, flags), \
290 }; \
291 \
292 static struct esp32_ipm_data esp32_ipm_device_data_##idx = { \
293 .shm_size = ESP32_IPM_SHM_SIZE_BY_IDX(idx), \
294 .shm.pro_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx), \
295 .shm.app_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx) + \
296 ESP32_IPM_SHM_SIZE_BY_IDX(idx)/2, \
297 .control = (struct esp32_ipm_control *)DT_INST_REG_ADDR(idx), \
298 }; \
299 \
300 DEVICE_DT_INST_DEFINE(idx, &esp32_ipm_init, NULL, \
301 &esp32_ipm_device_data_##idx, &esp32_ipm_device_cfg_##idx, \
302 PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
303 &esp32_ipm_driver_api); \
304
305 DT_INST_FOREACH_STATUS_OKAY(ESP32_IPM_INIT);
306