git: cbcfdff05678 - main - scmi: Fix SCMI mailbox polling mechanism

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Thu, 11 Apr 2024 09:59:24 UTC
The branch main has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=cbcfdff05678b143436f81e09f21b3c727efd805

commit cbcfdff05678b143436f81e09f21b3c727efd805
Author:     Cristian Marussi <cristian.marussi@arm.com>
AuthorDate: 2023-12-07 07:06:10 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2024-04-11 09:58:56 +0000

    scmi: Fix SCMI mailbox polling mechanism
    
    When the system is cold, the SCMI stack processes commands in polling
    mode with the current polling mechanism being a check of the status
    register in the mailbox controller to see if there is any pending
    doorbell request.
    
    Anyway, the completion interrupt is optional by the SCMI specification
    and a system could have been simply designed without it: for this
    reason polling on the mailbox controller status registers is not going
    to work in all situations.
    
    Moreover even alternative SCMI transports based on shared memory, like
    SMC, will not have at all a mailbox controller to poll for.
    
    On the other side, the associated SCMI Shared Memory Transport defines
    dedicated channel flags and status bits that can be used by the agent to
    explicitly request a polling-based transaction, even if the completion
    interrupt was available, and to check afterwards when the platform has
    completed its processing on the outstanding command.
    
    Use SCMI/SMT specific mechanism to process transactions in polling mode.
    
    Reviewed by:    andrew
    Tested on:      Arm Morello Board
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D43042
---
 sys/dev/firmware/arm/scmi.c         |  2 +-
 sys/dev/firmware/arm/scmi_mailbox.c |  3 ++-
 sys/dev/firmware/arm/scmi_shmem.c   | 18 ++++++++++++++++--
 sys/dev/firmware/arm/scmi_shmem.h   |  4 +++-
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/sys/dev/firmware/arm/scmi.c b/sys/dev/firmware/arm/scmi.c
index 8428d17cecec..ee41ad4383c0 100644
--- a/sys/dev/firmware/arm/scmi.c
+++ b/sys/dev/firmware/arm/scmi.c
@@ -85,7 +85,7 @@ scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
 	req->msg_header |= SCMI_MSG_TYPE_CMD << SCMI_HDR_MESSAGE_TYPE_S;
 	req->msg_header |= req->protocol_id << SCMI_HDR_PROTOCOL_ID_S;
 
-	ret = scmi_shmem_prepare_msg(sc->a2p_dev, req);
+	ret = scmi_shmem_prepare_msg(sc->a2p_dev, req, cold);
 	if (ret != 0)
 		return (ret);
 
diff --git a/sys/dev/firmware/arm/scmi_mailbox.c b/sys/dev/firmware/arm/scmi_mailbox.c
index bebdc7348b98..c7f4fda4d5c4 100644
--- a/sys/dev/firmware/arm/scmi_mailbox.c
+++ b/sys/dev/firmware/arm/scmi_mailbox.c
@@ -49,6 +49,7 @@
 
 #include "scmi.h"
 #include "scmi_protocols.h"
+#include "scmi_shmem.h"
 
 struct scmi_mailbox_softc {
 	struct scmi_softc	base;
@@ -91,7 +92,7 @@ scmi_mailbox_xfer_msg(device_t dev)
 
 	do {
 		if (cold) {
-			if (arm_doorbell_get(sc->db))
+			if (scmi_shmem_poll_msg(sc->base.a2p_dev))
 				break;
 			DELAY(10000);
 		} else {
diff --git a/sys/dev/firmware/arm/scmi_shmem.c b/sys/dev/firmware/arm/scmi_shmem.c
index efb6b77c6e4b..066f28777cb7 100644
--- a/sys/dev/firmware/arm/scmi_shmem.c
+++ b/sys/dev/firmware/arm/scmi_shmem.c
@@ -168,7 +168,7 @@ scmi_shmem_get(device_t dev, phandle_t node, int index)
 }
 
 int
-scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req)
+scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req, bool polling)
 {
 	struct scmi_smt_header hdr = {};
 	uint32_t channel_status;
@@ -185,7 +185,10 @@ scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req)
 	hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
 	hdr.msg_header = htole32(req->msg_header);
 	hdr.length = htole32(sizeof(req->msg_header) + req->in_size);
-	hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
+	if (!polling)
+		hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
+	else
+		hdr.flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED;
 
 	/* Write header */
 	scmi_shmem_write(dev, 0, &hdr, SMT_SIZE_HEADER);
@@ -239,6 +242,17 @@ scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len)
 	return (0);
 }
 
+bool scmi_shmem_poll_msg(device_t dev)
+{
+	uint32_t status;
+
+	scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &status,
+	    SMT_SIZE_CHAN_STATUS);
+
+	return (status & (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
+	    SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE));
+}
+
 static device_method_t shmem_methods[] = {
 	DEVMETHOD(device_probe,		shmem_probe),
 	DEVMETHOD(device_attach,	shmem_attach),
diff --git a/sys/dev/firmware/arm/scmi_shmem.h b/sys/dev/firmware/arm/scmi_shmem.h
index ce82a3c90d87..d46493dc0342 100644
--- a/sys/dev/firmware/arm/scmi_shmem.h
+++ b/sys/dev/firmware/arm/scmi_shmem.h
@@ -63,7 +63,9 @@ struct scmi_smt_header {
 struct scmi_req;
 
 device_t	scmi_shmem_get(device_t sdev, phandle_t node, int index);
-int		scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req);
+int		scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req,
+    bool polling);
+bool scmi_shmem_poll_msg(device_t);
 int scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header);
 int scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len);