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 
125 		k_busy_wait(1);
126 
127 		if ((wait != -1) && (wait > 0)) {
128 			/* lock could not be held this time, return */
129 			wait--;
130 			if (wait == 0)  {
131 				irq_unlock(key);
132 
133 				return -ETIMEDOUT;
134 			}
135 		}
136 	}
137 
138 	/* Only the lower 16bits of id are used */
139 	dev_data->control->dest_cpu_msg_id[dev_data->other_core_id] = (uint16_t)(id & 0xFFFF);
140 
141 	/* data copied, set the id and, generate interrupt in the remote core */
142 	if (dev_data->this_core_id == 0) {
143 		memcpy(dev_data->shm.app_cpu_shm, data, size);
144 		atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
145 		LOG_DBG("Generating interrupt on remote CPU 1 from CPU 0");
146 #if defined(CONFIG_SOC_SERIES_ESP32)
147 		DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, DPORT_CPU_INTR_FROM_CPU_1);
148 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
149 		WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_1_REG, SYSTEM_CPU_INTR_FROM_CPU_1);
150 #endif
151 
152 	} else {
153 		memcpy(dev_data->shm.pro_cpu_shm, data, size);
154 		atomic_set(&dev_data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
155 		LOG_DBG("Generating interrupt on remote CPU 0 from CPU 1");
156 #if defined(CONFIG_SOC_SERIES_ESP32)
157 		DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0);
158 #elif defined(CONFIG_SOC_SERIES_ESP32S3)
159 		WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, SYSTEM_CPU_INTR_FROM_CPU_0);
160 #endif
161 	}
162 
163 	irq_unlock(key);
164 
165 	return 0;
166 }
167 
esp32_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)168 static void esp32_ipm_register_callback(const struct device *dev,
169 				       ipm_callback_t cb,
170 				       void *user_data)
171 {
172 	struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
173 
174 	uint32_t key = irq_lock();
175 
176 	data->cb = cb;
177 	data->user_data = user_data;
178 
179 	irq_unlock(key);
180 }
181 
esp32_ipm_max_data_size_get(const struct device * dev)182 static int esp32_ipm_max_data_size_get(const struct device *dev)
183 {
184 	struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
185 
186 	return data->shm_size;
187 }
188 
esp_32_ipm_max_id_val_get(const struct device * dev)189 static uint32_t esp_32_ipm_max_id_val_get(const struct device *dev)
190 {
191 	ARG_UNUSED(dev);
192 	return 0xFFFF;
193 }
194 
esp_32_ipm_set_enabled(const struct device * dev,int enable)195 static int esp_32_ipm_set_enabled(const struct device *dev, int enable)
196 {
197 	/* The esp32 IPM is always enabled
198 	 * but rpmsg backend needs IPM set enabled to be
199 	 * implemented so just return success here
200 	 */
201 
202 	ARG_UNUSED(dev);
203 	ARG_UNUSED(enable);
204 
205 	return 0;
206 }
207 
208 
esp32_ipm_init(const struct device * dev)209 static int esp32_ipm_init(const struct device *dev)
210 {
211 	struct esp32_ipm_data *data = (struct esp32_ipm_data *)dev->data;
212 	struct esp32_ipm_config *cfg = (struct esp32_ipm_config *)dev->config;
213 	int ret;
214 
215 	data->this_core_id = esp_core_id();
216 	data->other_core_id = (data->this_core_id  == 0) ? 1 : 0;
217 
218 	LOG_DBG("Size of IPM shared memory: %d", data->shm_size);
219 	LOG_DBG("Address of PRO_CPU IPM shared memory: %p", (void *)data->shm.pro_cpu_shm);
220 	LOG_DBG("Address of APP_CPU IPM shared memory: %p", (void *)data->shm.app_cpu_shm);
221 	LOG_DBG("Address of IPM control structure: %p", (void *)data->control);
222 
223 	/* pro_cpu is responsible to initialize the lock of shared memory */
224 	if (data->this_core_id == 0) {
225 		ret = esp_intr_alloc(cfg->irq_source_pro_cpu,
226 				ESP_PRIO_TO_FLAGS(cfg->irq_priority_pro_cpu) |
227 				ESP_INT_FLAGS_CHECK(cfg->irq_flags_pro_cpu) |
228 					ESP_INTR_FLAG_IRAM,
229 				(intr_handler_t)esp32_ipm_isr,
230 				(void *)dev,
231 				NULL);
232 
233 		if (ret != 0) {
234 			LOG_ERR("could not allocate interrupt (err %d)", ret);
235 			return ret;
236 		}
237 
238 		atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
239 	} else {
240 		/* app_cpu wait for initialization from pro_cpu, then takes it,
241 		 * after that releases
242 		 */
243 		ret = esp_intr_alloc(cfg->irq_source_app_cpu,
244 				ESP_PRIO_TO_FLAGS(cfg->irq_priority_app_cpu) |
245 				ESP_INT_FLAGS_CHECK(cfg->irq_flags_app_cpu) |
246 					ESP_INTR_FLAG_IRAM,
247 				(intr_handler_t)esp32_ipm_isr,
248 				(void *)dev,
249 				NULL);
250 
251 		if (ret != 0) {
252 			LOG_ERR("could not allocate interrupt (err %d)", ret);
253 			return ret;
254 		}
255 
256 		LOG_DBG("Waiting CPU0 to sync");
257 		while (!atomic_cas(&data->control->lock,
258 			ESP32_IPM_LOCK_FREE_VAL, data->this_core_id)) {
259 			;
260 		}
261 
262 		atomic_set(&data->control->lock, ESP32_IPM_LOCK_FREE_VAL);
263 
264 		LOG_DBG("Synchronization done");
265 
266 	}
267 
268 	return 0;
269 }
270 
271 static DEVICE_API(ipm, esp32_ipm_driver_api) = {
272 	.send = esp32_ipm_send,
273 	.register_callback = esp32_ipm_register_callback,
274 	.max_data_size_get = esp32_ipm_max_data_size_get,
275 	.max_id_val_get = esp_32_ipm_max_id_val_get,
276 	.set_enabled = esp_32_ipm_set_enabled
277 };
278 
279 #define ESP32_IPM_SHM_SIZE_BY_IDX(idx)		\
280 	DT_INST_PROP(idx, shared_memory_size)	\
281 
282 #define ESP32_IPM_SHM_ADDR_BY_IDX(idx)		\
283 	DT_REG_ADDR(DT_PHANDLE(DT_DRV_INST(idx), shared_memory))	\
284 
285 #define ESP32_IPM_INIT(idx)			\
286 										\
287 static struct esp32_ipm_config esp32_ipm_device_cfg_##idx = {		\
288 	.irq_source_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, irq),			\
289 	.irq_priority_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, priority),	\
290 	.irq_flags_pro_cpu = DT_INST_IRQ_BY_IDX(idx, 0, flags),			\
291 	.irq_source_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, irq),			\
292 	.irq_priority_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, priority),	\
293 	.irq_flags_app_cpu = DT_INST_IRQ_BY_IDX(idx, 1, flags),			\
294 };	\
295 	\
296 static struct esp32_ipm_data esp32_ipm_device_data_##idx = {	\
297 	.shm_size = ESP32_IPM_SHM_SIZE_BY_IDX(idx),		\
298 	.shm.pro_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx),		\
299 	.shm.app_cpu_shm = (uint8_t *)ESP32_IPM_SHM_ADDR_BY_IDX(idx) +	\
300 					ESP32_IPM_SHM_SIZE_BY_IDX(idx)/2,	\
301 	.control = (struct esp32_ipm_control *)DT_INST_REG_ADDR(idx),	\
302 };	\
303 	\
304 DEVICE_DT_INST_DEFINE(idx, &esp32_ipm_init, NULL,	\
305 		    &esp32_ipm_device_data_##idx, &esp32_ipm_device_cfg_##idx,	\
306 		    PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,	\
307 		    &esp32_ipm_driver_api);	\
308 
309 DT_INST_FOREACH_STATUS_OKAY(ESP32_IPM_INIT);
310