File Index
Files and directories
Sign in
FILE

mailbox-mchp-ipc-sbi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mailbox/mailbox-mchp-ipc-sbi.c C
  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
 31enum {
 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
 39enum 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 */
 52struct 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 */
 65struct 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 */
 79struct 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 */
 95struct mchp_ipc_sbi_msg {
 96	u64 buf_addr;
 97	u16 size;
 98	u8 irq_type;
 99};
100
101struct mchp_ipc_cluster_cfg {
102	void *buf_base;
103	phys_addr_t buf_base_addr;
104	int irq;
105};
106
107struct 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
117static 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
130static 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
143static 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
148static 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
158static 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
169static 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
250static 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
264static 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
328fail_free_buf_msg_rx:
329	kfree(chan_info->msg_buf_rx);
330fail_free_buf_msg_tx:
331	kfree(chan_info->msg_buf_tx);
332fail_free_buf_base_rx:
333	kfree(chan_info->buf_base_rx);
334fail_free_buf_base_tx:
335	kfree(chan_info->buf_base_tx);
336fail:
337	return ret;
338}
339
340static 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
350static 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
356static 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
370static 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		ret = platform_get_irq_byname_optional(pdev, irq_name);
382		if (ret <= 0)
383			continue;
384
385		ipc->cluster_cfg[cpuid].irq = ret;
386		ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[cpuid].irq,
387				       mchp_ipc_cluster_aggr_isr, IRQF_SHARED,
388				       "miv-ihc-irq", ipc);
389		if (ret)
390			return ret;
391
392		ipc->cluster_cfg[cpuid].buf_base = devm_kmalloc(ipc->dev,
393								sizeof(struct mchp_ipc_status),
394								GFP_KERNEL);
395
396		if (!ipc->cluster_cfg[cpuid].buf_base)
397			return -ENOMEM;
398
399		ipc->cluster_cfg[cpuid].buf_base_addr = __pa(ipc->cluster_cfg[cpuid].buf_base);
400
401		irq_found = true;
402	}
403
404	return irq_found;
405}
406
407static int mchp_ipc_probe(struct platform_device *pdev)
408{
409	struct device *dev = &pdev->dev;
410	struct mchp_ipc_mbox_info ipc_info;
411	struct mchp_ipc_sbi_mbox *ipc;
412	struct mchp_ipc_sbi_chan *priv;
413	bool irq_avail = false;
414	int ret;
415	u32 chan_id;
416
417	ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY);
418	if (ret <= 0)
419		return dev_err_probe(dev, -ENODEV, "Microchip SBI extension not detected\n");
420
421	ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
422	if (!ipc)
423		return -ENOMEM;
424
425	platform_set_drvdata(pdev, ipc);
426
427	ipc->buf_base = devm_kmalloc(dev, sizeof(struct mchp_ipc_mbox_info), GFP_KERNEL);
428	if (!ipc->buf_base)
429		return -ENOMEM;
430
431	ipc->buf_base_addr = __pa(ipc->buf_base);
432
433	ret = mchp_ipc_sbi_send(SBI_EXT_IPC_PROBE, ipc->buf_base_addr);
434	if (ret < 0)
435		return dev_err_probe(dev, ret, "could not probe IPC SBI service\n");
436
437	memcpy(&ipc_info, ipc->buf_base, sizeof(struct mchp_ipc_mbox_info));
438	ipc->controller.num_chans = ipc_info.num_channels;
439	ipc->hw_type = ipc_info.hw_type;
440
441	ipc->chans = devm_kcalloc(dev, ipc->controller.num_chans, sizeof(*ipc->chans), GFP_KERNEL);
442	if (!ipc->chans)
443		return -ENOMEM;
444
445	ipc->dev = dev;
446	ipc->controller.txdone_irq = true;
447	ipc->controller.dev = ipc->dev;
448	ipc->controller.ops = &mchp_ipc_ops;
449	ipc->controller.chans = ipc->chans;
450	ipc->controller.of_xlate = mchp_ipc_mbox_xlate;
451
452	for (chan_id = 0; chan_id < ipc->controller.num_chans; chan_id++) {
453		priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
454		if (!priv)
455			return -ENOMEM;
456
457		ipc->chans[chan_id].con_priv = priv;
458		priv->id = chan_id;
459	}
460
461	if (ipc->hw_type == MIV_IHC) {
462		ipc->cluster_cfg = devm_kcalloc(dev, num_online_cpus(),
463						sizeof(struct mchp_ipc_cluster_cfg),
464						GFP_KERNEL);
465		if (!ipc->cluster_cfg)
466			return -ENOMEM;
467
468		if (mchp_ipc_get_cluster_aggr_irq(ipc))
469			irq_avail = true;
470	}
471
472	if (!irq_avail)
473		return dev_err_probe(dev, -ENODEV, "missing interrupt property\n");
474
475	ret = devm_mbox_controller_register(dev, &ipc->controller);
476	if (ret)
477		return dev_err_probe(dev, ret,
478				    "Inter-Processor communication (IPC) registration failed\n");
479
480	return 0;
481}
482
483static const struct of_device_id mchp_ipc_of_match[] = {
484	{.compatible = "microchip,sbi-ipc", },
485	{}
486};
487MODULE_DEVICE_TABLE(of, mchp_ipc_of_match);
488
489static struct platform_driver mchp_ipc_driver = {
490	.driver = {
491		.name = "microchip_ipc",
492		.of_match_table = mchp_ipc_of_match,
493	},
494	.probe = mchp_ipc_probe,
495};
496
497module_platform_driver(mchp_ipc_driver);
498
499MODULE_LICENSE("GPL");
500MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>");
501MODULE_DESCRIPTION("Microchip Inter-Processor Communication (IPC) driver");
~/ Read-only file preview.