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