xref: /linux/drivers/mailbox/mailbox-mchp-ipc-sbi.c (revision 8bfab832ad6905ba70e69bad87a78f6d90cce64a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Microchip Inter-Processor communication (IPC) driver
4  *
5  * Copyright (c) 2021 - 2024 Microchip Technology Inc. All rights reserved.
6  *
7  * Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com>
8  *
9  */
10 
11 #include <linux/io.h>
12 #include <linux/err.h>
13 #include <linux/smp.h>
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/of_device.h>
18 #include <linux/interrupt.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/platform_device.h>
21 #include <linux/mailbox/mchp-ipc.h>
22 #include <asm/sbi.h>
23 #include <asm/vendorid_list.h>
24 
25 #define IRQ_STATUS_BITS			12
26 #define NUM_CHANS_PER_CLUSTER		5
27 #define IPC_DMA_BIT_MASK		32
28 #define SBI_EXT_MICROCHIP_TECHNOLOGY	(SBI_EXT_VENDOR_START | \
29 					 MICROCHIP_VENDOR_ID)
30 
31 enum {
32 	SBI_EXT_IPC_PROBE = 0x100,
33 	SBI_EXT_IPC_CH_INIT,
34 	SBI_EXT_IPC_SEND,
35 	SBI_EXT_IPC_RECEIVE,
36 	SBI_EXT_IPC_STATUS,
37 };
38 
39 enum ipc_hw {
40 	MIV_IHC,
41 };
42 
43 /**
44  * struct mchp_ipc_mbox_info - IPC probe message format
45  *
46  * @hw_type:		IPC implementation available in the hardware
47  * @num_channels:	number of IPC channels available in the hardware
48  *
49  * Used to retrieve information on the IPC implementation
50  * using the SBI_EXT_IPC_PROBE SBI function id.
51  */
52 struct mchp_ipc_mbox_info {
53 	enum ipc_hw hw_type;
54 	u8 num_channels;
55 };
56 
57 /**
58  * struct mchp_ipc_init - IPC channel init message format
59  *
60  * @max_msg_size:	maxmimum message size in bytes of a given channel
61  *
62  * struct used by the SBI_EXT_IPC_CH_INIT SBI function id to get
63  * the max message size in bytes of the initialized channel.
64  */
65 struct mchp_ipc_init {
66 	u16 max_msg_size;
67 };
68 
69 /**
70  * struct mchp_ipc_status - IPC status message format
71  *
72  * @status:	interrupt status for all channels associated to a cluster
73  * @cluster:	specifies the cluster instance that originated an irq
74  *
75  * struct used by the SBI_EXT_IPC_STATUS SBI function id to get
76  * the message present and message clear interrupt status for all the
77  * channels associated to a cluster.
78  */
79 struct mchp_ipc_status {
80 	u32 status;
81 	u8 cluster;
82 };
83 
84 /**
85  * struct mchp_ipc_sbi_msg - IPC SBI payload message
86  *
87  * @buf_addr:	physical address where the received data should be copied to
88  * @size:	maximum size(in bytes) that can be stored in the buffer pointed to by `buf`
89  * @irq_type:	mask representing the irq types that triggered an irq
90  *
91  * struct used by the SBI_EXT_IPC_SEND/SBI_EXT_IPC_RECEIVE SBI function
92  * ids to send/receive a message from an associated processor using
93  * the IPC.
94  */
95 struct mchp_ipc_sbi_msg {
96 	u64 buf_addr;
97 	u16 size;
98 	u8 irq_type;
99 };
100 
101 struct mchp_ipc_cluster_cfg {
102 	void *buf_base;
103 	phys_addr_t buf_base_addr;
104 	int irq;
105 };
106 
107 struct mchp_ipc_sbi_mbox {
108 	struct device *dev;
109 	struct mbox_chan *chans;
110 	struct mchp_ipc_cluster_cfg *cluster_cfg;
111 	void *buf_base;
112 	unsigned long buf_base_addr;
113 	struct mbox_controller controller;
114 	enum ipc_hw hw_type;
115 };
116 
mchp_ipc_sbi_chan_send(u32 command,u32 channel,unsigned long address)117 static int mchp_ipc_sbi_chan_send(u32 command, u32 channel, unsigned long address)
118 {
119 	struct sbiret ret;
120 
121 	ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, channel,
122 			address, 0, 0, 0, 0);
123 
124 	if (ret.error)
125 		return sbi_err_map_linux_errno(ret.error);
126 	else
127 		return ret.value;
128 }
129 
mchp_ipc_sbi_send(u32 command,unsigned long address)130 static int mchp_ipc_sbi_send(u32 command, unsigned long address)
131 {
132 	struct sbiret ret;
133 
134 	ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, address,
135 			0, 0, 0, 0, 0);
136 
137 	if (ret.error)
138 		return sbi_err_map_linux_errno(ret.error);
139 	else
140 		return ret.value;
141 }
142 
to_mchp_ipc_mbox(struct mbox_controller * mbox)143 static struct mchp_ipc_sbi_mbox *to_mchp_ipc_mbox(struct mbox_controller *mbox)
144 {
145 	return container_of(mbox, struct mchp_ipc_sbi_mbox, controller);
146 }
147 
mchp_ipc_prepare_receive_req(struct mbox_chan * chan)148 static inline void mchp_ipc_prepare_receive_req(struct mbox_chan *chan)
149 {
150 	struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
151 	struct mchp_ipc_sbi_msg request;
152 
153 	request.buf_addr = chan_info->msg_buf_rx_addr;
154 	request.size = chan_info->max_msg_size;
155 	memcpy(chan_info->buf_base_rx, &request, sizeof(struct mchp_ipc_sbi_msg));
156 }
157 
mchp_ipc_process_received_data(struct mbox_chan * chan,struct mchp_ipc_msg * ipc_msg)158 static inline void mchp_ipc_process_received_data(struct mbox_chan *chan,
159 						  struct mchp_ipc_msg *ipc_msg)
160 {
161 	struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
162 	struct mchp_ipc_sbi_msg sbi_msg;
163 
164 	memcpy(&sbi_msg, chan_info->buf_base_rx, sizeof(struct mchp_ipc_sbi_msg));
165 	ipc_msg->buf = (u32 *)chan_info->msg_buf_rx;
166 	ipc_msg->size = sbi_msg.size;
167 }
168 
mchp_ipc_cluster_aggr_isr(int irq,void * data)169 static irqreturn_t mchp_ipc_cluster_aggr_isr(int irq, void *data)
170 {
171 	struct mbox_chan *chan;
172 	struct mchp_ipc_sbi_chan *chan_info;
173 	struct mchp_ipc_sbi_mbox *ipc = (struct mchp_ipc_sbi_mbox *)data;
174 	struct mchp_ipc_msg ipc_msg;
175 	struct mchp_ipc_status status_msg;
176 	int ret;
177 	u32 i, chan_index, chan_id;
178 	bool found = false;
179 
180 	/* Find out the hart that originated the irq */
181 	for_each_online_cpu(i) {
182 		if (irq == ipc->cluster_cfg[i].irq) {
183 			found = true;
184 			break;
185 		}
186 	}
187 
188 	if (unlikely(!found))
189 		return IRQ_NONE;
190 
191 	status_msg.cluster = cpuid_to_hartid_map(i);
192 	memcpy(ipc->cluster_cfg[i].buf_base, &status_msg, sizeof(struct mchp_ipc_status));
193 
194 	ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[i].buf_base_addr);
195 	if (ret < 0) {
196 		dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret);
197 		return IRQ_HANDLED;
198 	}
199 
200 	memcpy(&status_msg, ipc->cluster_cfg[i].buf_base, sizeof(struct mchp_ipc_status));
201 
202 	/*
203 	 * Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify
204 	 * the channel(s) that have a message to be processed/acknowledged.
205 	 * The bits are organized in alternating format, where each pair of bits represents
206 	 * the status of the message present and message clear interrupts for each cluster/hart
207 	 * (from hart 0 to hart 5). Each cluster can have up to 5 fixed channels associated.
208 	 */
209 
210 	for_each_set_bit(i, (unsigned long *)&status_msg.status, IRQ_STATUS_BITS) {
211 		/* Find out the destination hart that triggered the interrupt */
212 		chan_index = i / 2;
213 
214 		/*
215 		 * The IP has no loopback channels, so we need to decrement the index when
216 		 * the target hart has a greater index than our own
217 		 */
218 		if (chan_index >= status_msg.cluster)
219 			chan_index--;
220 
221 		/*
222 		 * Calculate the channel id given the hart and channel index. Channel IDs
223 		 * are unique across all clusters of an IPC, and iterate contiguously
224 		 * across all clusters.
225 		 */
226 		chan_id = status_msg.cluster * (NUM_CHANS_PER_CLUSTER + chan_index);
227 
228 		chan = &ipc->chans[chan_id];
229 		chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
230 
231 		if (i % 2 == 0) {
232 			mchp_ipc_prepare_receive_req(chan);
233 			ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
234 						     chan_info->buf_base_rx_addr);
235 			if (ret < 0)
236 				continue;
237 
238 			mchp_ipc_process_received_data(chan, &ipc_msg);
239 			mbox_chan_received_data(&ipc->chans[chan_id], (void *)&ipc_msg);
240 
241 		} else {
242 			ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
243 						     chan_info->buf_base_rx_addr);
244 			mbox_chan_txdone(&ipc->chans[chan_id], ret);
245 		}
246 	}
247 	return IRQ_HANDLED;
248 }
249 
mchp_ipc_send_data(struct mbox_chan * chan,void * data)250 static int mchp_ipc_send_data(struct mbox_chan *chan, void *data)
251 {
252 	struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
253 	const struct mchp_ipc_msg *msg = data;
254 	struct mchp_ipc_sbi_msg sbi_payload;
255 
256 	memcpy(chan_info->msg_buf_tx, msg->buf, msg->size);
257 	sbi_payload.buf_addr = chan_info->msg_buf_tx_addr;
258 	sbi_payload.size = msg->size;
259 	memcpy(chan_info->buf_base_tx, &sbi_payload, sizeof(sbi_payload));
260 
261 	return mchp_ipc_sbi_chan_send(SBI_EXT_IPC_SEND, chan_info->id, chan_info->buf_base_tx_addr);
262 }
263 
mchp_ipc_startup(struct mbox_chan * chan)264 static int mchp_ipc_startup(struct mbox_chan *chan)
265 {
266 	struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
267 	struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(chan->mbox);
268 	struct mchp_ipc_init ch_init_msg;
269 	int ret;
270 
271 	/*
272 	 * The TX base buffer is used to transmit two types of messages:
273 	 * - struct mchp_ipc_init to initialize the channel
274 	 * - struct mchp_ipc_sbi_msg to transmit user data/payload
275 	 * Ensure the TX buffer size is large enough to accommodate either message type.
276 	 */
277 	size_t max_size = max(sizeof(struct mchp_ipc_init), sizeof(struct mchp_ipc_sbi_msg));
278 
279 	chan_info->buf_base_tx = kmalloc(max_size, GFP_KERNEL);
280 	if (!chan_info->buf_base_tx) {
281 		ret = -ENOMEM;
282 		goto fail;
283 	}
284 
285 	chan_info->buf_base_tx_addr = __pa(chan_info->buf_base_tx);
286 
287 	chan_info->buf_base_rx = kmalloc(max_size, GFP_KERNEL);
288 	if (!chan_info->buf_base_rx) {
289 		ret = -ENOMEM;
290 		goto fail_free_buf_base_tx;
291 	}
292 
293 	chan_info->buf_base_rx_addr = __pa(chan_info->buf_base_rx);
294 
295 	ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_CH_INIT, chan_info->id,
296 				     chan_info->buf_base_tx_addr);
297 	if (ret < 0) {
298 		dev_err(ipc->dev, "channel %u init failed\n", chan_info->id);
299 		goto fail_free_buf_base_rx;
300 	}
301 
302 	memcpy(&ch_init_msg, chan_info->buf_base_tx, sizeof(struct mchp_ipc_init));
303 	chan_info->max_msg_size = ch_init_msg.max_msg_size;
304 
305 	chan_info->msg_buf_tx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
306 	if (!chan_info->msg_buf_tx) {
307 		ret = -ENOMEM;
308 		goto fail_free_buf_base_rx;
309 	}
310 
311 	chan_info->msg_buf_tx_addr = __pa(chan_info->msg_buf_tx);
312 
313 	chan_info->msg_buf_rx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
314 	if (!chan_info->msg_buf_rx) {
315 		ret = -ENOMEM;
316 		goto fail_free_buf_msg_tx;
317 	}
318 
319 	chan_info->msg_buf_rx_addr = __pa(chan_info->msg_buf_rx);
320 
321 	switch (ipc->hw_type) {
322 	case MIV_IHC:
323 		return 0;
324 	default:
325 		goto fail_free_buf_msg_rx;
326 	}
327 
328 fail_free_buf_msg_rx:
329 	kfree(chan_info->msg_buf_rx);
330 fail_free_buf_msg_tx:
331 	kfree(chan_info->msg_buf_tx);
332 fail_free_buf_base_rx:
333 	kfree(chan_info->buf_base_rx);
334 fail_free_buf_base_tx:
335 	kfree(chan_info->buf_base_tx);
336 fail:
337 	return ret;
338 }
339 
mchp_ipc_shutdown(struct mbox_chan * chan)340 static void mchp_ipc_shutdown(struct mbox_chan *chan)
341 {
342 	struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
343 
344 	kfree(chan_info->buf_base_tx);
345 	kfree(chan_info->buf_base_rx);
346 	kfree(chan_info->msg_buf_tx);
347 	kfree(chan_info->msg_buf_rx);
348 }
349 
350 static const struct mbox_chan_ops mchp_ipc_ops = {
351 	.startup = mchp_ipc_startup,
352 	.send_data = mchp_ipc_send_data,
353 	.shutdown = mchp_ipc_shutdown,
354 };
355 
mchp_ipc_mbox_xlate(struct mbox_controller * controller,const struct of_phandle_args * spec)356 static struct mbox_chan *mchp_ipc_mbox_xlate(struct mbox_controller *controller,
357 					     const struct of_phandle_args *spec)
358 {
359 	struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(controller);
360 	unsigned int chan_id = spec->args[0];
361 
362 	if (chan_id >= ipc->controller.num_chans) {
363 		dev_err(ipc->dev, "invalid channel id %d\n", chan_id);
364 		return ERR_PTR(-EINVAL);
365 	}
366 
367 	return &ipc->chans[chan_id];
368 }
369 
mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox * ipc)370 static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
371 {
372 	struct platform_device *pdev = to_platform_device(ipc->dev);
373 	char *irq_name;
374 	int cpuid, ret;
375 	unsigned long hartid;
376 	bool irq_found = false;
377 
378 	for_each_online_cpu(cpuid) {
379 		hartid = cpuid_to_hartid_map(cpuid);
380 		irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
381 		if (!irq_name)
382 			return -ENOMEM;
383 		ret = platform_get_irq_byname_optional(pdev, irq_name);
384 		if (ret <= 0)
385 			continue;
386 
387 		ipc->cluster_cfg[cpuid].irq = ret;
388 		ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[cpuid].irq,
389 				       mchp_ipc_cluster_aggr_isr, IRQF_SHARED,
390 				       "miv-ihc-irq", ipc);
391 		if (ret)
392 			return ret;
393 
394 		ipc->cluster_cfg[cpuid].buf_base = devm_kmalloc(ipc->dev,
395 								sizeof(struct mchp_ipc_status),
396 								GFP_KERNEL);
397 
398 		if (!ipc->cluster_cfg[cpuid].buf_base)
399 			return -ENOMEM;
400 
401 		ipc->cluster_cfg[cpuid].buf_base_addr = __pa(ipc->cluster_cfg[cpuid].buf_base);
402 
403 		irq_found = true;
404 	}
405 
406 	return irq_found;
407 }
408 
mchp_ipc_probe(struct platform_device * pdev)409 static int mchp_ipc_probe(struct platform_device *pdev)
410 {
411 	struct device *dev = &pdev->dev;
412 	struct mchp_ipc_mbox_info ipc_info;
413 	struct mchp_ipc_sbi_mbox *ipc;
414 	struct mchp_ipc_sbi_chan *priv;
415 	bool irq_avail = false;
416 	int ret;
417 	u32 chan_id;
418 
419 	ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY);
420 	if (ret <= 0)
421 		return dev_err_probe(dev, -ENODEV, "Microchip SBI extension not detected\n");
422 
423 	ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
424 	if (!ipc)
425 		return -ENOMEM;
426 
427 	platform_set_drvdata(pdev, ipc);
428 
429 	ipc->buf_base = devm_kmalloc(dev, sizeof(struct mchp_ipc_mbox_info), GFP_KERNEL);
430 	if (!ipc->buf_base)
431 		return -ENOMEM;
432 
433 	ipc->buf_base_addr = __pa(ipc->buf_base);
434 
435 	ret = mchp_ipc_sbi_send(SBI_EXT_IPC_PROBE, ipc->buf_base_addr);
436 	if (ret < 0)
437 		return dev_err_probe(dev, ret, "could not probe IPC SBI service\n");
438 
439 	memcpy(&ipc_info, ipc->buf_base, sizeof(struct mchp_ipc_mbox_info));
440 	ipc->controller.num_chans = ipc_info.num_channels;
441 	ipc->hw_type = ipc_info.hw_type;
442 
443 	ipc->chans = devm_kcalloc(dev, ipc->controller.num_chans, sizeof(*ipc->chans), GFP_KERNEL);
444 	if (!ipc->chans)
445 		return -ENOMEM;
446 
447 	ipc->dev = dev;
448 	ipc->controller.txdone_irq = true;
449 	ipc->controller.dev = ipc->dev;
450 	ipc->controller.ops = &mchp_ipc_ops;
451 	ipc->controller.chans = ipc->chans;
452 	ipc->controller.of_xlate = mchp_ipc_mbox_xlate;
453 
454 	for (chan_id = 0; chan_id < ipc->controller.num_chans; chan_id++) {
455 		priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
456 		if (!priv)
457 			return -ENOMEM;
458 
459 		ipc->chans[chan_id].con_priv = priv;
460 		priv->id = chan_id;
461 	}
462 
463 	if (ipc->hw_type == MIV_IHC) {
464 		ipc->cluster_cfg = devm_kcalloc(dev, num_online_cpus(),
465 						sizeof(struct mchp_ipc_cluster_cfg),
466 						GFP_KERNEL);
467 		if (!ipc->cluster_cfg)
468 			return -ENOMEM;
469 
470 		if (mchp_ipc_get_cluster_aggr_irq(ipc))
471 			irq_avail = true;
472 	}
473 
474 	if (!irq_avail)
475 		return dev_err_probe(dev, -ENODEV, "missing interrupt property\n");
476 
477 	ret = devm_mbox_controller_register(dev, &ipc->controller);
478 	if (ret)
479 		return dev_err_probe(dev, ret,
480 				    "Inter-Processor communication (IPC) registration failed\n");
481 
482 	return 0;
483 }
484 
485 static const struct of_device_id mchp_ipc_of_match[] = {
486 	{.compatible = "microchip,sbi-ipc", },
487 	{}
488 };
489 MODULE_DEVICE_TABLE(of, mchp_ipc_of_match);
490 
491 static struct platform_driver mchp_ipc_driver = {
492 	.driver = {
493 		.name = "microchip_ipc",
494 		.of_match_table = mchp_ipc_of_match,
495 	},
496 	.probe = mchp_ipc_probe,
497 };
498 
499 module_platform_driver(mchp_ipc_driver);
500 
501 MODULE_LICENSE("GPL");
502 MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>");
503 MODULE_DESCRIPTION("Microchip Inter-Processor Communication (IPC) driver");
504