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 uint32_t irq_source_pro_cpu;
37 uint32_t irq_source_app_cpu;
38 };
39
40 struct esp32_ipm_data {
41 ipm_callback_t cb;
42 void *user_data;
43 uint32_t this_core_id;
44 uint32_t other_core_id;
45 uint32_t shm_size;
46 struct esp32_ipm_memory shm;
47 struct esp32_ipm_control *control;
48 };
49
esp32_ipm_isr(const struct device * dev)50 IRAM_ATTR static void esp32_ipm_isr(const struct device *dev)
51 {
52 struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
53 uint32_t core_id = dev_data->this_core_id;
54
55 /* clear interrupt flag */
56 if (core_id == 0) {
57 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);
58 } else {
59 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
60 }
61
62 /* first of all take the own of the shared memory */
63 while (!atomic_cas(&dev_data->control->lock,
64 ESP32_IPM_LOCK_FREE_VAL, dev_data->this_core_id))
65 ;
66
67 if (dev_data->cb) {
68
69 volatile void *shm = dev_data->shm.pro_cpu_shm;
70
71 if (core_id != 0) {
72 shm = dev_data->shm.app_cpu_shm;
73 }
74
75 dev_data->cb(dev,
76 dev_data->user_data,
77 dev_data->control->dest_cpu_msg_id[core_id],
78 shm);
79 }
80
81 /* unlock the shared memory */
82 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
83 }
84
esp32_ipm_send(const struct device * dev,int wait,uint32_t id,const void * data,int size)85 static int esp32_ipm_send(const struct device *dev, int wait, uint32_t id,
86 const void *data, int size)
87 {
88 struct esp32_ipm_data *dev_data = (struct esp32_ipm_data *)dev->data;
89
90 if (data == NULL) {
91 LOG_ERR("Invalid data source");
92 return -EINVAL;
93 }
94
95 if (id > 0xFFFF) {
96 LOG_ERR("Invalid message ID format");
97 return -EINVAL;
98 }
99
100 if (dev_data->shm_size < size) {
101 LOG_ERR("Not enough memory in IPM channel");
102 return -ENOMEM;
103 }
104
105 uint32_t key = irq_lock();
106
107 /* try to lock the shared memory */
108 while (!atomic_cas(&dev_data->control->lock,
109 ESP32_IPM_LOCK_FREE_VAL,
110 dev_data->this_core_id)) {
111
112 k_busy_wait(1);
113
114 if ((wait != -1) && (wait > 0)) {
115 /* lock could not be held this time, return */
116 wait--;
117 if (wait == 0) {
118 irq_unlock(key);
119
120 return -ETIMEDOUT;
121 }
122 }
123 }
124
125 /* Only the lower 16bits of id are used */
126 dev_data->control->dest_cpu_msg_id[dev_data->other_core_id] = (uint16_t)(id & 0xFFFF);
127
128 /* data copied, set the id and, generate interrupt in the remote core */
129 if (dev_data->this_core_id == 0) {
130 memcpy(dev_data->shm.app_cpu_shm, data, size);
131 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
132 LOG_DBG("Generating interrupt on remote CPU 1 from CPU 0");
133 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
134 } else {
135 memcpy(dev_data->shm.pro_cpu_shm, data, size);
136 atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
137 LOG_DBG("Generating interrupt on remote CPU 0 from CPU 1");
138 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
139 }
140
141 irq_unlock(key);
142
143 return 0;
144 }
145
esp32_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)146 static void esp32_ipm_register_callback(const struct device *dev,
147 ipm_callback_t cb,
148 void *user_data)
149 {
150 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
151
152 uint32_t key = irq_lock();
153
154 data->cb = cb;
155 data->user_data = user_data;
156
157 irq_unlock(key);
158 }
159
esp32_ipm_max_data_size_get(const struct device * dev)160 static int esp32_ipm_max_data_size_get(const struct device *dev)
161 {
162 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
163
164 return data->shm_size;
165 }
166
esp_32_ipm_max_id_val_get(const struct device * dev)167 static uint32_t esp_32_ipm_max_id_val_get(const struct device *dev)
168 {
169 ARG_UNUSED(dev);
170 return 0xFFFF;
171 }
172
esp_32_ipm_set_enabled(const struct device * dev,int enable)173 static int esp_32_ipm_set_enabled(const struct device *dev, int enable)
174 {
175 /* The esp32 IPM is always enabled
176 * but rpmsg backend needs IPM set enabled to be
177 * implemented so just return success here
178 */
179
180 ARG_UNUSED(dev);
181 ARG_UNUSED(enable);
182
183 return 0;
184 }
185
186
esp32_ipm_init(const struct device * dev)187 static int esp32_ipm_init(const struct device *dev)
188 {
189 struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
190 struct esp32_ipm_config *cfg = (struct esp32_ipm_config *)dev->config;
191
192 data->this_core_id = esp_core_id();
193 data->other_core_id = (data->this_core_id == 0) ? 1 : 0;
194
195 LOG_DBG("Size of IPM shared memory: %d", data->shm_size);
196 LOG_DBG("Address of PRO_CPU IPM shared memory: %p", data->shm.pro_cpu_shm);
197 LOG_DBG("Address of APP_CPU IPM shared memory: %p", data->shm.app_cpu_shm);
198 LOG_DBG("Address of IPM control structure: %p", data->control);
199
200 /* pro_cpu is responsible to initialize the lock of shared memory */
201 if (data->this_core_id == 0) {
202 esp_intr_alloc(cfg->irq_source_pro_cpu,
203 ESP_INTR_FLAG_IRAM,
204 (intr_handler_t)esp32_ipm_isr,
205 (void *)dev,
206 NULL);
207
208 atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
209 } else {
210 /* app_cpu wait for initialization from pro_cpu, then takes it,
211 * after that releases
212 */
213 esp_intr_alloc(cfg->irq_source_app_cpu,
214 ESP_INTR_FLAG_IRAM,
215 (intr_handler_t)esp32_ipm_isr,
216 (void *)dev,
217 NULL);
218
219 LOG_DBG("Waiting CPU0 to sync");
220
221 while (!atomic_cas(&data->control->lock,
222 ESP32_IPM_LOCK_FREE_VAL, data->this_core_id))
223 ;
224
225 atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
226
227 LOG_DBG("Synchronization done");
228
229 }
230
231 return 0;
232 }
233
234 static const struct ipm_driver_api esp32_ipm_driver_api = {
235 .send = esp32_ipm_send,
236 .register_callback = esp32_ipm_register_callback,
237 .max_data_size_get = esp32_ipm_max_data_size_get,
238 .max_id_val_get = esp_32_ipm_max_id_val_get,
239 .set_enabled = esp_32_ipm_set_enabled
240 };
241
242 #define ESP32_IPM_SHM_SIZE_BY_IDX(idx) \
243 DT_INST_PROP(idx, shared_memory_size) \
244
245 #define ESP32_IPM_SHM_ADDR_BY_IDX(idx) \
246 DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(idx), shared_memory)) \
247
248 #define ESP32_IPM_INIT(idx) \
249 \
250 static struct esp32_ipm_config esp32_ipm_device_cfg_##idx = { \
251 .irq_source_pro_cpu = DT_INST_IRQN(idx), \
252 .irq_source_app_cpu = DT_INST_IRQN(idx) + 1, \
253 }; \
254 \
255 static struct esp32_ipm_data esp32_ipm_device_data_##idx = { \
256 .shm_size = ESP32_IPM_SHM_SIZE_BY_IDX(idx), \
257 .shm.pro_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx), \
258 .shm.app_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx) + \
259 ESP32_IPM_SHM_SIZE_BY_IDX(idx)/2, \
260 .control = (struct esp32_ipm_control *)DT_INST_REG_ADDR(idx), \
261 }; \
262 \
263 DEVICE_DT_INST_DEFINE(idx, &esp32_ipm_init, NULL, \
264 &esp32_ipm_device_data_##idx, &esp32_ipm_device_cfg_##idx, \
265 PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
266 &esp32_ipm_driver_api); \
267
268 DT_INST_FOREACH_STATUS_OKAY(ESP32_IPM_INIT);
269