1 /*
2 * Copyright (c) 2024 Felipe Neves.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT espressif_mbox_esp32
8 #include "soc/dport_reg.h"
9 #include "soc/gpio_periph.h"
10
11 #include <stdint.h>
12 #include <string.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/mbox.h>
16 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
17 #include <soc.h>
18 #include <zephyr/sys/atomic.h>
19
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(mbox_esp32, CONFIG_MBOX_LOG_LEVEL);
22
23 #define ESP32_MBOX_LOCK_FREE_VAL 0xB33FFFFF
24 #define ESP32_MBOX_NOOP_VAL 0xFF
25
26 __packed struct esp32_mbox_control {
27 uint16_t dest_cpu_msg_id[2];
28 atomic_t lock;
29 };
30
31 struct esp32_mbox_memory {
32 volatile uint8_t *pro_cpu_shm;
33 volatile uint8_t *app_cpu_shm;
34 };
35
36 struct esp32_mbox_config {
37 int irq_source_pro_cpu;
38 int irq_priority_pro_cpu;
39 int irq_flags_pro_cpu;
40 int irq_source_app_cpu;
41 int irq_priority_app_cpu;
42 int irq_flags_app_cpu;
43 };
44
45 struct esp32_mbox_data {
46 mbox_callback_t cb;
47 void *user_data;
48 uint32_t this_core_id;
49 uint32_t other_core_id;
50 uint32_t shm_size;
51 struct esp32_mbox_memory shm;
52 struct esp32_mbox_control *control;
53 };
54
esp32_mbox_isr(const struct device * dev)55 IRAM_ATTR static void esp32_mbox_isr(const struct device *dev)
56 {
57 struct esp32_mbox_data *dev_data = (struct esp32_mbox_data *)dev->data;
58 struct mbox_msg msg;
59 uint32_t core_id = dev_data->this_core_id;
60
61 /* clear interrupt flag */
62 if (core_id == 0) {
63 #if defined(CONFIG_SOC_SERIES_ESP32)
64 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0);
65 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
66 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 0);
67 #endif
68 } else {
69 #if defined(CONFIG_SOC_SERIES_ESP32)
70 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0);
71 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
72 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, 0);
73 #endif
74 }
75
76 /* first of all take the ownership of the shared memory */
77 while (!atomic_cas(&dev_data->control->lock, ESP32_MBOX_LOCK_FREE_VAL,
78 dev_data->this_core_id))
79 ;
80
81 if (dev_data->cb) {
82 /* For ESP32 soft mbox driver, the message parameter of the callback holds
83 * the portion of shared memory that belongs to the current core ID.
84 */
85 msg.data = (dev_data->this_core_id == 0) ? (const void *)dev_data->shm.pro_cpu_shm
86 : (const void *)dev_data->shm.app_cpu_shm;
87 msg.size = dev_data->shm_size;
88
89 dev_data->cb(dev, dev_data->other_core_id, dev_data->user_data, &msg);
90 }
91
92 /* unlock the shared memory */
93 atomic_set(&dev_data->control->lock, ESP32_MBOX_LOCK_FREE_VAL);
94 }
95
esp32_mbox_send(const struct device * dev,mbox_channel_id_t channel,const struct mbox_msg * msg)96 static int esp32_mbox_send(const struct device *dev, mbox_channel_id_t channel,
97 const struct mbox_msg *msg)
98 {
99 ARG_UNUSED(msg);
100
101 struct esp32_mbox_data *dev_data = (struct esp32_mbox_data *)dev->data;
102
103 if (channel > 0xFFFF) {
104 LOG_ERR("Invalid channel");
105 return -EINVAL;
106 }
107
108 uint32_t key = irq_lock();
109
110 /* try to lock the shared memory */
111 while (!atomic_cas(&dev_data->control->lock, ESP32_MBOX_LOCK_FREE_VAL,
112 dev_data->this_core_id)) {
113 k_msleep(1);
114 }
115
116 /* Only the lower 16bits of id are used */
117 dev_data->control->dest_cpu_msg_id[dev_data->other_core_id] = (uint16_t)(channel & 0xFFFF);
118
119 /* Generate interrupt in the remote core */
120 if (dev_data->this_core_id == 0) {
121 atomic_set(&dev_data->control->lock, ESP32_MBOX_LOCK_FREE_VAL);
122 LOG_DBG("Generating interrupt on remote CPU 1 from CPU 0");
123 #if defined(CONFIG_SOC_SERIES_ESP32)
124 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
125 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
126 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, SYSTEM_CPU_INTR_FROM_CPU_1);
127 #endif
128
129 } else {
130 atomic_set(&dev_data->control->lock, ESP32_MBOX_LOCK_FREE_VAL);
131 LOG_DBG("Generating interrupt on remote CPU 0 from CPU 1");
132 #if defined(CONFIG_SOC_SERIES_ESP32)
133 DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
134 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
135 WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, SYSTEM_CPU_INTR_FROM_CPU_0);
136 #endif
137 }
138
139 irq_unlock(key);
140
141 return 0;
142 }
143
esp32_mbox_register_callback(const struct device * dev,mbox_channel_id_t channel,mbox_callback_t cb,void * user_data)144 static int esp32_mbox_register_callback(const struct device *dev, mbox_channel_id_t channel,
145 mbox_callback_t cb, void *user_data)
146 {
147 ARG_UNUSED(channel);
148
149 struct esp32_mbox_data *data = (struct esp32_mbox_data *)dev->data;
150
151 if (!cb) {
152 LOG_ERR("Must provide callback");
153 return -EINVAL;
154 }
155
156 uint32_t key = irq_lock();
157
158 data->cb = cb;
159 data->user_data = user_data;
160
161 irq_unlock(key);
162
163 return 0;
164 }
165
esp32_mbox_mtu_get(const struct device * dev)166 static int esp32_mbox_mtu_get(const struct device *dev)
167 {
168 struct esp32_mbox_data *data = (struct esp32_mbox_data *)dev->data;
169
170 return data->shm_size;
171 }
172
esp32_mbox_max_channels_get(const struct device * dev)173 static uint32_t esp32_mbox_max_channels_get(const struct device *dev)
174 {
175 ARG_UNUSED(dev);
176 return 1;
177 }
178
esp32_mbox_set_enabled(const struct device * dev,mbox_channel_id_t channel,bool enable)179 static int esp32_mbox_set_enabled(const struct device *dev, mbox_channel_id_t channel, bool enable)
180 {
181 /* The esp32 MBOX is always enabled
182 * but rpmsg backend needs MBOX set enabled to be
183 * implemented so just return success here
184 */
185
186 ARG_UNUSED(dev);
187 ARG_UNUSED(enable);
188 ARG_UNUSED(channel);
189
190 return 0;
191 }
192
esp32_mbox_init(const struct device * dev)193 static int esp32_mbox_init(const struct device *dev)
194 {
195 struct esp32_mbox_data *data = (struct esp32_mbox_data *)dev->data;
196 struct esp32_mbox_config *cfg = (struct esp32_mbox_config *)dev->config;
197 int ret;
198
199 data->this_core_id = esp_core_id();
200 data->other_core_id = (data->this_core_id == 0) ? 1 : 0;
201
202 LOG_DBG("Size of MBOX shared memory: %d", data->shm_size);
203 LOG_DBG("Address of PRO_CPU MBOX shared memory: %p", data->shm.pro_cpu_shm);
204 LOG_DBG("Address of APP_CPU MBOX shared memory: %p", data->shm.app_cpu_shm);
205 LOG_DBG("Address of MBOX control structure: %p", data->control);
206
207 /* pro_cpu is responsible to initialize the lock of shared memory */
208 if (data->this_core_id == 0) {
209 ret = esp_intr_alloc(cfg->irq_source_pro_cpu,
210 ESP_PRIO_TO_FLAGS(cfg->irq_priority_pro_cpu) |
211 ESP_INT_FLAGS_CHECK(cfg->irq_flags_pro_cpu) |
212 ESP_INTR_FLAG_IRAM,
213 (intr_handler_t)esp32_mbox_isr, (void *)dev, NULL);
214 atomic_set(&data->control->lock, ESP32_MBOX_LOCK_FREE_VAL);
215 } else {
216 /* app_cpu wait for initialization from pro_cpu, then takes it,
217 * after that releases
218 */
219 ret = esp_intr_alloc(cfg->irq_source_app_cpu,
220 ESP_PRIO_TO_FLAGS(cfg->irq_priority_app_cpu) |
221 ESP_INT_FLAGS_CHECK(cfg->irq_flags_app_cpu) |
222 ESP_INTR_FLAG_IRAM,
223 (intr_handler_t)esp32_mbox_isr, (void *)dev, NULL);
224
225 LOG_DBG("Waiting CPU0 to sync");
226 while (!atomic_cas(&data->control->lock, ESP32_MBOX_LOCK_FREE_VAL,
227 data->this_core_id))
228 ;
229
230 atomic_set(&data->control->lock, ESP32_MBOX_LOCK_FREE_VAL);
231
232 LOG_DBG("Synchronization done");
233 }
234
235 return ret;
236 }
237
238 static DEVICE_API(mbox, esp32_mbox_driver_api) = {
239 .send = esp32_mbox_send,
240 .register_callback = esp32_mbox_register_callback,
241 .mtu_get = esp32_mbox_mtu_get,
242 .max_channels_get = esp32_mbox_max_channels_get,
243 .set_enabled = esp32_mbox_set_enabled,
244 };
245
246 #define ESP32_MBOX_SHM_SIZE_BY_IDX(idx) DT_INST_PROP(idx, shared_memory_size)
247
248 #define ESP32_MBOX_SHM_ADDR_BY_IDX(idx) DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(idx), shared_memory))
249
250 #define ESP32_MBOX_INIT(idx) \
251 static struct esp32_mbox_config esp32_mbox_device_cfg_##idx = { \
252 .irq_source_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, irq), \
253 .irq_priority_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, priority), \
254 .irq_flags_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, flags), \
255 .irq_source_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, irq), \
256 .irq_priority_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, priority), \
257 .irq_flags_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, flags), \
258 }; \
259 static struct esp32_mbox_data esp32_mbox_device_data_##idx = { \
260 .shm_size = ESP32_MBOX_SHM_SIZE_BY_IDX(idx), \
261 .shm.pro_cpu_shm = (uint8_t *)ESP32_MBOX_SHM_ADDR_BY_IDX(idx), \
262 .shm.app_cpu_shm = (uint8_t *)ESP32_MBOX_SHM_ADDR_BY_IDX(idx) + \
263 ESP32_MBOX_SHM_SIZE_BY_IDX(idx) / 2, \
264 .control = (struct esp32_mbox_control *)DT_INST_REG_ADDR(idx), \
265 }; \
266 DEVICE_DT_INST_DEFINE(idx, &esp32_mbox_init, NULL, &esp32_mbox_device_data_##idx, \
267 &esp32_mbox_device_cfg_##idx, PRE_KERNEL_2, \
268 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &esp32_mbox_driver_api);
269
270 DT_INST_FOREACH_STATUS_OKAY(ESP32_MBOX_INIT);
271