1 /*
2  * Copyright (c) 2023 AMD-Xilinx Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT xlnx_zynqmp_ipi_mailbox
8 
9 #include "ipm_xlnx_ipi.h"
10 
11 #include <errno.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/ipm.h>
14 #include <zephyr/irq.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(ipm_xlnx_ipi, CONFIG_IPM_LOG_LEVEL);
18 
19 #define XLNX_IPI_MAX_BUF_SIZE_BYTES 32
20 
21 struct xlnx_ipi_data {
22 	size_t len;
23 	void *user_data;
24 	uint8_t data[];
25 };
26 
27 struct xlnx_ipi_reg_info {
28 	uint32_t ipi_ch_bit;
29 };
30 
31 static const struct xlnx_ipi_reg_info xlnx_ipi_reg_info_zynqmp[] = {
32 	{.ipi_ch_bit = IPI_CH0_BIT},  /* IPI CH ID 0 - Default APU  */
33 	{.ipi_ch_bit = IPI_CH1_BIT},  /* IPI CH ID 1 - Default RPU0 */
34 	{.ipi_ch_bit = IPI_CH2_BIT},  /* IPI CH ID 2 - Default RPU1 */
35 	{.ipi_ch_bit = IPI_CH3_BIT},  /* IPI CH ID 3 - Default PMU0 */
36 	{.ipi_ch_bit = IPI_CH4_BIT},  /* IPI CH ID 4 - Default PMU1 */
37 	{.ipi_ch_bit = IPI_CH5_BIT},  /* IPI CH ID 5 - Default PMU2 */
38 	{.ipi_ch_bit = IPI_CH6_BIT},  /* IPI CH ID 6 - Default PMU3 */
39 	{.ipi_ch_bit = IPI_CH7_BIT},  /* IPI CH ID 7 - Default PL0 */
40 	{.ipi_ch_bit = IPI_CH8_BIT},  /* IPI CH ID 8 - Default PL1 */
41 	{.ipi_ch_bit = IPI_CH9_BIT},  /* IPI CH ID 9 - Default PL2 */
42 	{.ipi_ch_bit = IPI_CH10_BIT}, /* IPI CH ID 10 - Default PL3 */
43 };
44 
45 struct xlnx_ipi_config {
46 	uint32_t ipi_ch_bit;
47 	uint32_t host_ipi_reg;
48 	int (*xlnx_ipi_config_func)(const struct device *dev);
49 	const struct device **cdev_list;
50 	int num_cdev;
51 };
52 
53 struct xlnx_ipi_child_data {
54 	bool enabled;
55 	ipm_callback_t ipm_callback;
56 	void *user_data;
57 };
58 
59 struct xlnx_ipi_child_config {
60 	const char *node_id;
61 	uint32_t local_request_region;
62 	uint32_t local_response_region;
63 	uint32_t remote_request_region;
64 	uint32_t remote_response_region;
65 	uint32_t host_ipi_reg;
66 	uint32_t remote_ipi_id;
67 	uint32_t remote_ipi_ch_bit;
68 };
69 
xlnx_mailbox_rx_isr(const struct device * dev)70 static void xlnx_mailbox_rx_isr(const struct device *dev)
71 {
72 	const struct xlnx_ipi_config *config;
73 	const struct device **cdev_list;
74 	const struct xlnx_ipi_child_config *cdev_conf;
75 	const struct xlnx_ipi_child_data *cdev_data;
76 	uint8_t ipi_buf[XLNX_IPI_MAX_BUF_SIZE_BYTES + sizeof(struct xlnx_ipi_data)];
77 	int num_cdev;
78 	struct xlnx_ipi_data *msg;
79 	const struct device *cdev;
80 	uint32_t remote_ipi_ch_bit;
81 	int i, j;
82 
83 	config = dev->config;
84 	cdev_list = config->cdev_list;
85 	num_cdev = config->num_cdev;
86 	msg = (struct xlnx_ipi_data *)ipi_buf;
87 	for (i = 0; i < num_cdev; i++) {
88 		cdev = cdev_list[i];
89 		cdev_conf = cdev->config;
90 		cdev_data = cdev->data;
91 
92 		if (!cdev_data->enabled) {
93 			continue;
94 		}
95 
96 		remote_ipi_ch_bit = cdev_conf->remote_ipi_ch_bit;
97 		if (!sys_test_bit(config->host_ipi_reg + IPI_ISR, remote_ipi_ch_bit)) {
98 			continue;
99 		}
100 
101 		msg->len = XLNX_IPI_MAX_BUF_SIZE_BYTES;
102 		msg->user_data = cdev_data->user_data;
103 		for (j = 0; j < XLNX_IPI_MAX_BUF_SIZE_BYTES; j++) {
104 			msg->data[j] = sys_read8(cdev_conf->remote_request_region + j);
105 		}
106 		if (cdev_data->ipm_callback) {
107 			cdev_data->ipm_callback(cdev, cdev_data->user_data,
108 						cdev_conf->remote_ipi_id, msg);
109 		}
110 		sys_set_bit(config->host_ipi_reg + IPI_ISR, remote_ipi_ch_bit);
111 	}
112 }
113 
xlnx_ipi_send(const struct device * ipmdev,int wait,uint32_t id,const void * data,int size)114 static int xlnx_ipi_send(const struct device *ipmdev, int wait, uint32_t id, const void *data,
115 			 int size)
116 {
117 	const uint8_t *msg = (uint8_t *)data;
118 	const struct xlnx_ipi_child_config *config = ipmdev->config;
119 	unsigned int key;
120 	int i, obs_bit;
121 
122 	ARG_UNUSED(id);
123 
124 	if (size > XLNX_IPI_MAX_BUF_SIZE_BYTES) {
125 		return -EMSGSIZE;
126 	}
127 
128 	key = irq_lock();
129 	if (msg) {
130 		/* Write buffer to send data */
131 		for (i = 0; i < size; i++) {
132 			sys_write8(msg[i], config->local_request_region + i);
133 		}
134 	}
135 	irq_unlock(key);
136 
137 	sys_set_bit(config->host_ipi_reg + IPI_TRIG, config->remote_ipi_ch_bit);
138 
139 	obs_bit = 0;
140 	do {
141 		obs_bit = sys_test_bit(config->host_ipi_reg + IPI_OBS, config->remote_ipi_ch_bit);
142 	} while (obs_bit && wait);
143 
144 	return 0;
145 }
146 
xlnx_ipi_register_callback(const struct device * port,ipm_callback_t cb,void * user_data)147 static void xlnx_ipi_register_callback(const struct device *port, ipm_callback_t cb,
148 				       void *user_data)
149 {
150 	struct xlnx_ipi_child_data *data = port->data;
151 
152 	data->ipm_callback = cb;
153 	data->user_data = user_data;
154 }
155 
xlnx_ipi_max_data_size_get(const struct device * ipmdev)156 static int xlnx_ipi_max_data_size_get(const struct device *ipmdev)
157 {
158 	return XLNX_IPI_MAX_BUF_SIZE_BYTES;
159 }
160 
xlnx_ipi_max_id_val_get(const struct device * ipmdev)161 static uint32_t xlnx_ipi_max_id_val_get(const struct device *ipmdev)
162 {
163 	return UINT32_MAX;
164 }
165 
xlnx_ipi_set_enabled(const struct device * ipmdev,int enable)166 static int xlnx_ipi_set_enabled(const struct device *ipmdev, int enable)
167 {
168 	const struct xlnx_ipi_child_config *config = ipmdev->config;
169 	struct xlnx_ipi_child_data *data = ipmdev->data;
170 
171 	if (enable) {
172 		sys_set_bit(config->host_ipi_reg + IPI_IER, config->remote_ipi_ch_bit);
173 	} else {
174 		sys_set_bit(config->host_ipi_reg + IPI_IDR, config->remote_ipi_ch_bit);
175 	}
176 
177 	/* If IPI channel bit in IPI Mask Register is not set, then interrupt is enabled */
178 	if (!sys_test_bit(config->host_ipi_reg + IPI_IMR, config->remote_ipi_ch_bit)) {
179 		data->enabled = enable;
180 		return 0;
181 	}
182 
183 	return -EINVAL;
184 }
185 
xlnx_ipi_init(const struct device * dev)186 static int xlnx_ipi_init(const struct device *dev)
187 {
188 	const struct xlnx_ipi_config *conf = dev->config;
189 
190 	/* disable all the interrupts */
191 	sys_write32(0xFFFFFFFF, conf->host_ipi_reg + IPI_IDR);
192 
193 	/* clear status of any previous interrupts */
194 	sys_write32(0xFFFFFFFF, conf->host_ipi_reg + IPI_ISR);
195 
196 	conf->xlnx_ipi_config_func(dev);
197 
198 	return 0;
199 }
200 
201 static struct ipm_driver_api xlnx_ipi_api = {
202 	.send = xlnx_ipi_send,
203 	.register_callback = xlnx_ipi_register_callback,
204 	.max_data_size_get = xlnx_ipi_max_data_size_get,
205 	.max_id_val_get = xlnx_ipi_max_id_val_get,
206 	.set_enabled = xlnx_ipi_set_enabled,
207 };
208 
209 #define GET_CHILD_DEV(node_id) DEVICE_DT_GET(node_id),
210 
211 #define XLNX_IPI_CHILD(ch_node)                                                                    \
212 	struct xlnx_ipi_child_data xlnx_ipi_child_data##ch_node = {                                \
213 		.enabled = false,                                                                  \
214 		.ipm_callback = NULL,                                                              \
215 	};                                                                                         \
216 	struct xlnx_ipi_child_config xlnx_ipi_child_config##ch_node = {                            \
217 		.local_request_region = DT_REG_ADDR_BY_NAME(ch_node, local_request_region),        \
218 		.local_response_region = DT_REG_ADDR_BY_NAME(ch_node, local_response_region),      \
219 		.remote_request_region = DT_REG_ADDR_BY_NAME(ch_node, remote_request_region),      \
220 		.remote_response_region = DT_REG_ADDR_BY_NAME(ch_node, remote_response_region),    \
221 		.remote_ipi_id = DT_PROP(ch_node, remote_ipi_id),                                  \
222 		.remote_ipi_ch_bit =                                                               \
223 			xlnx_ipi_reg_info_zynqmp[DT_PROP(ch_node, remote_ipi_id)].ipi_ch_bit,      \
224 		.host_ipi_reg = DT_REG_ADDR_BY_NAME(DT_PARENT(ch_node), host_ipi_reg),             \
225 	};                                                                                         \
226 	DEVICE_DT_DEFINE(ch_node, NULL, NULL, &xlnx_ipi_child_data##ch_node,			   \
227 			 &xlnx_ipi_child_config##ch_node, POST_KERNEL,                             \
228 			 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &xlnx_ipi_api);
229 
230 #define XLNX_IPI(inst)                                                                             \
231 	DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, XLNX_IPI_CHILD);                                   \
232 	static const struct device *cdev##inst[] = {                                               \
233 		DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, GET_CHILD_DEV)};                           \
234 	static int xlnx_ipi_config_func##inst(const struct device *dev);                           \
235 	struct xlnx_ipi_config xlnx_ipi_config##inst = {                                           \
236 		.host_ipi_reg = DT_INST_REG_ADDR_BY_NAME(inst, host_ipi_reg),                      \
237 		.xlnx_ipi_config_func = xlnx_ipi_config_func##inst,                                \
238 		.cdev_list = cdev##inst,                                                           \
239 		.num_cdev = ARRAY_SIZE(cdev##inst),                                                \
240 	};                                                                                         \
241 	DEVICE_DT_INST_DEFINE(inst, &xlnx_ipi_init, NULL, NULL, /* data */                         \
242 			      &xlnx_ipi_config##inst,           /* conf */                         \
243 			      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);             \
244 	static int xlnx_ipi_config_func##inst(const struct device *dev)                            \
245 	{                                                                                          \
246 		IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), xlnx_mailbox_rx_isr,  \
247 			    DEVICE_DT_INST_GET(inst), 0);                                          \
248 		irq_enable(DT_INST_IRQN(inst));                                                    \
249 		LOG_DBG("irq %d is enabled: %s\n", DT_INST_IRQN(inst),                             \
250 			irq_is_enabled(DT_INST_IRQN(inst)) ? "true" : "false");                    \
251 		return 0;                                                                          \
252 	}
253 
254 DT_INST_FOREACH_STATUS_OKAY(XLNX_IPI)
255