xref: /openbmc/linux/drivers/firmware/arm_scmi/shmem.c (revision 59172b212ec0dbb97ceb5671d912e6e61fa802d5)
15c8a47a5SViresh Kumar // SPDX-License-Identifier: GPL-2.0
25c8a47a5SViresh Kumar /*
35c8a47a5SViresh Kumar  * For transport using shared mem structure.
45c8a47a5SViresh Kumar  *
55c8a47a5SViresh Kumar  * Copyright (C) 2019 ARM Ltd.
65c8a47a5SViresh Kumar  */
75c8a47a5SViresh Kumar 
8*59172b21SCristian Marussi #include <linux/ktime.h>
95c8a47a5SViresh Kumar #include <linux/io.h>
105c8a47a5SViresh Kumar #include <linux/processor.h>
115c8a47a5SViresh Kumar #include <linux/types.h>
125c8a47a5SViresh Kumar 
13*59172b21SCristian Marussi #include <asm-generic/bug.h>
14*59172b21SCristian Marussi 
155c8a47a5SViresh Kumar #include "common.h"
165c8a47a5SViresh Kumar 
175c8a47a5SViresh Kumar /*
185c8a47a5SViresh Kumar  * SCMI specification requires all parameters, message headers, return
195c8a47a5SViresh Kumar  * arguments or any protocol data to be expressed in little endian
205c8a47a5SViresh Kumar  * format only.
215c8a47a5SViresh Kumar  */
225c8a47a5SViresh Kumar struct scmi_shared_mem {
235c8a47a5SViresh Kumar 	__le32 reserved;
245c8a47a5SViresh Kumar 	__le32 channel_status;
255c8a47a5SViresh Kumar #define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR	BIT(1)
265c8a47a5SViresh Kumar #define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE	BIT(0)
275c8a47a5SViresh Kumar 	__le32 reserved1[2];
285c8a47a5SViresh Kumar 	__le32 flags;
295c8a47a5SViresh Kumar #define SCMI_SHMEM_FLAG_INTR_ENABLED	BIT(0)
305c8a47a5SViresh Kumar 	__le32 length;
315c8a47a5SViresh Kumar 	__le32 msg_header;
324ddfb4afSGustavo A. R. Silva 	u8 msg_payload[];
335c8a47a5SViresh Kumar };
345c8a47a5SViresh Kumar 
355c8a47a5SViresh Kumar void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
36*59172b21SCristian Marussi 		      struct scmi_xfer *xfer, struct scmi_chan_info *cinfo)
375c8a47a5SViresh Kumar {
38*59172b21SCristian Marussi 	ktime_t stop;
39*59172b21SCristian Marussi 
405c8a47a5SViresh Kumar 	/*
415c8a47a5SViresh Kumar 	 * Ideally channel must be free by now unless OS timeout last
425c8a47a5SViresh Kumar 	 * request and platform continued to process the same, wait
435c8a47a5SViresh Kumar 	 * until it releases the shared memory, otherwise we may endup
44*59172b21SCristian Marussi 	 * overwriting its response with new message payload or vice-versa.
45*59172b21SCristian Marussi 	 * Giving up anyway after twice the expected channel timeout so as
46*59172b21SCristian Marussi 	 * not to bail-out on intermittent issues where the platform is
47*59172b21SCristian Marussi 	 * occasionally a bit slower to answer.
48*59172b21SCristian Marussi 	 *
49*59172b21SCristian Marussi 	 * Note that after a timeout is detected we bail-out and carry on but
50*59172b21SCristian Marussi 	 * the transport functionality is probably permanently compromised:
51*59172b21SCristian Marussi 	 * this is just to ease debugging and avoid complete hangs on boot
52*59172b21SCristian Marussi 	 * due to a misbehaving SCMI firmware.
535c8a47a5SViresh Kumar 	 */
54*59172b21SCristian Marussi 	stop = ktime_add_ms(ktime_get(), 2 * cinfo->rx_timeout_ms);
55*59172b21SCristian Marussi 	spin_until_cond((ioread32(&shmem->channel_status) &
56*59172b21SCristian Marussi 			 SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ||
57*59172b21SCristian Marussi 			 ktime_after(ktime_get(), stop));
58*59172b21SCristian Marussi 	if (!(ioread32(&shmem->channel_status) &
59*59172b21SCristian Marussi 	      SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
60*59172b21SCristian Marussi 		WARN_ON_ONCE(1);
61*59172b21SCristian Marussi 		dev_err(cinfo->dev,
62*59172b21SCristian Marussi 			"Timeout waiting for a free TX channel !\n");
63*59172b21SCristian Marussi 		return;
64*59172b21SCristian Marussi 	}
65*59172b21SCristian Marussi 
665c8a47a5SViresh Kumar 	/* Mark channel busy + clear error */
675c8a47a5SViresh Kumar 	iowrite32(0x0, &shmem->channel_status);
685c8a47a5SViresh Kumar 	iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
695c8a47a5SViresh Kumar 		  &shmem->flags);
705c8a47a5SViresh Kumar 	iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length);
715c8a47a5SViresh Kumar 	iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header);
725c8a47a5SViresh Kumar 	if (xfer->tx.buf)
735c8a47a5SViresh Kumar 		memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len);
745c8a47a5SViresh Kumar }
755c8a47a5SViresh Kumar 
765c8a47a5SViresh Kumar u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
775c8a47a5SViresh Kumar {
785c8a47a5SViresh Kumar 	return ioread32(&shmem->msg_header);
795c8a47a5SViresh Kumar }
805c8a47a5SViresh Kumar 
815c8a47a5SViresh Kumar void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
825c8a47a5SViresh Kumar 			  struct scmi_xfer *xfer)
835c8a47a5SViresh Kumar {
845c8a47a5SViresh Kumar 	xfer->hdr.status = ioread32(shmem->msg_payload);
855c8a47a5SViresh Kumar 	/* Skip the length of header and status in shmem area i.e 8 bytes */
865c8a47a5SViresh Kumar 	xfer->rx.len = min_t(size_t, xfer->rx.len,
875c8a47a5SViresh Kumar 			     ioread32(&shmem->length) - 8);
885c8a47a5SViresh Kumar 
895c8a47a5SViresh Kumar 	/* Take a copy to the rx buffer.. */
905c8a47a5SViresh Kumar 	memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
915c8a47a5SViresh Kumar }
925c8a47a5SViresh Kumar 
93d5141f37SCristian Marussi void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
94d5141f37SCristian Marussi 			      size_t max_len, struct scmi_xfer *xfer)
95d5141f37SCristian Marussi {
96d5141f37SCristian Marussi 	/* Skip only the length of header in shmem area i.e 4 bytes */
97d5141f37SCristian Marussi 	xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4);
98d5141f37SCristian Marussi 
99d5141f37SCristian Marussi 	/* Take a copy to the rx buffer.. */
100d5141f37SCristian Marussi 	memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len);
101d5141f37SCristian Marussi }
102d5141f37SCristian Marussi 
10387dff4e6SCristian Marussi void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
104d5141f37SCristian Marussi {
105d5141f37SCristian Marussi 	iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status);
106d5141f37SCristian Marussi }
107d5141f37SCristian Marussi 
1085c8a47a5SViresh Kumar bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
1095c8a47a5SViresh Kumar 		     struct scmi_xfer *xfer)
1105c8a47a5SViresh Kumar {
1115c8a47a5SViresh Kumar 	u16 xfer_id;
1125c8a47a5SViresh Kumar 
1135c8a47a5SViresh Kumar 	xfer_id = MSG_XTRACT_TOKEN(ioread32(&shmem->msg_header));
1145c8a47a5SViresh Kumar 
1155c8a47a5SViresh Kumar 	if (xfer->hdr.seq != xfer_id)
1165c8a47a5SViresh Kumar 		return false;
1175c8a47a5SViresh Kumar 
1185c8a47a5SViresh Kumar 	return ioread32(&shmem->channel_status) &
1195c8a47a5SViresh Kumar 		(SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
1205c8a47a5SViresh Kumar 		 SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
1215c8a47a5SViresh Kumar }
122