git: 403ca28c28bd - main - scmi: Add new SCMI interfaces for init and message processing

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

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

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

    scmi: Add new SCMI interfaces for init and message processing
    
    Introduce a couple of new SCMI interface methods to allow centralized
    initialization of transport-specific features and a couple of methods
    to handle message reception from the SCMI core.
    
    Move SCMI SMT related calls out of the core common SCMI code into the
    transport specific layers Mailbox/SMC.
    
    Make SCMI Mailbox/SMC transports use the new interface methods for
    initialization and message reception.
    
    Reviewed by:    andrew
    Tested on:      Arm Morello Board
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D43044
---
 sys/dev/firmware/arm/scmi.c         |  42 +++---------
 sys/dev/firmware/arm/scmi.h         |   1 -
 sys/dev/firmware/arm/scmi_if.m      |  23 +++++++
 sys/dev/firmware/arm/scmi_mailbox.c | 127 +++++++++++++++++++++++++-----------
 sys/dev/firmware/arm/scmi_shmem.c   |   2 +-
 sys/dev/firmware/arm/scmi_smc.c     |  78 +++++++++++++++++++---
 6 files changed, 192 insertions(+), 81 deletions(-)

diff --git a/sys/dev/firmware/arm/scmi.c b/sys/dev/firmware/arm/scmi.c
index a797e52d74b1..5a69dc1daa7b 100644
--- a/sys/dev/firmware/arm/scmi.c
+++ b/sys/dev/firmware/arm/scmi.c
@@ -43,11 +43,8 @@
 #include <dev/fdt/fdt_common.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
-#include "dev/mailbox/arm/arm_doorbell.h"
-
 #include "scmi.h"
 #include "scmi_protocols.h"
-#include "scmi_shmem.h"
 
 #define	SCMI_HDR_TOKEN_S		18
 #define SCMI_HDR_TOKEN_BF		(0x3fff)
@@ -75,7 +72,6 @@
 static int
 scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
 {
-	uint32_t reply_header;
 	int ret;
 
 	SCMI_ASSERT_LOCKED(sc);
@@ -85,29 +81,12 @@ 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, cold);
-	if (ret != 0)
-		return (ret);
-
-	ret = SCMI_XFER_MSG(sc->dev);
-	if (ret != 0)
-		goto out;
-
-	/* Read header. */
-	ret = scmi_shmem_read_msg_header(sc->a2p_dev, &reply_header);
-	if (ret != 0)
-		goto out;
-
-	if (reply_header != req->msg_header) {
-		ret = EPROTO;
-		goto out;
-	}
+	ret = SCMI_XFER_MSG(sc->dev, req);
+	if (ret == 0)
+		ret = SCMI_COLLECT_REPLY(sc->dev, req);
 
-	ret = scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf,
-	    req->out_size);
-
-out:
-	scmi_shmem_tx_complete(sc->a2p_dev);
+	if (ret == 0  || ret != EBUSY)
+		SCMI_TX_COMPLETE(sc->dev, NULL);
 
 	return (ret);
 }
@@ -141,16 +120,14 @@ scmi_attach(device_t dev)
 	if (node == -1)
 		return (ENXIO);
 
-	sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P);
-	if (sc->a2p_dev == NULL) {
-		device_printf(dev, "A2P shmem dev not found.\n");
-		return (ENXIO);
-	}
-
 	mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF);
 
 	simplebus_init(dev, node);
 
+	error = SCMI_TRANSPORT_INIT(dev);
+	if (error != 0)
+		return (error);
+
 	/*
 	 * Allow devices to identify.
 	 */
@@ -171,6 +148,7 @@ static int
 scmi_detach(device_t dev)
 {
 
+	SCMI_TRANSPORT_CLEANUP(dev);
 	return (0);
 }
 
diff --git a/sys/dev/firmware/arm/scmi.h b/sys/dev/firmware/arm/scmi.h
index bebebff50429..2647db9d9e90 100644
--- a/sys/dev/firmware/arm/scmi.h
+++ b/sys/dev/firmware/arm/scmi.h
@@ -51,7 +51,6 @@ enum scmi_chan {
 struct scmi_softc {
 	struct simplebus_softc	simplebus_sc;
 	device_t		dev;
-	device_t		a2p_dev;
 	struct mtx		mtx;
 };
 
diff --git a/sys/dev/firmware/arm/scmi_if.m b/sys/dev/firmware/arm/scmi_if.m
index 524cf0fb0d66..5cacf99edc47 100644
--- a/sys/dev/firmware/arm/scmi_if.m
+++ b/sys/dev/firmware/arm/scmi_if.m
@@ -27,6 +27,29 @@
 
 INTERFACE scmi;
 
+HEADER {
+	struct scmi_req;
+};
+
+METHOD int transport_init {
+	device_t dev;
+};
+
+METHOD void transport_cleanup {
+	device_t dev;
+};
+
 METHOD int xfer_msg {
 	device_t dev;
+	struct scmi_req *req;
+};
+
+METHOD int collect_reply {
+	device_t dev;
+	struct scmi_req *req;
+};
+
+METHOD void tx_complete {
+	device_t dev;
+	void *chan;
 };
diff --git a/sys/dev/firmware/arm/scmi_mailbox.c b/sys/dev/firmware/arm/scmi_mailbox.c
index c7f4fda4d5c4..fa2b196478cd 100644
--- a/sys/dev/firmware/arm/scmi_mailbox.c
+++ b/sys/dev/firmware/arm/scmi_mailbox.c
@@ -53,10 +53,19 @@
 
 struct scmi_mailbox_softc {
 	struct scmi_softc	base;
+	device_t		a2p_dev;
 	struct arm_doorbell	*db;
 	int			req_done;
 };
 
+static int	scmi_mailbox_transport_init(device_t);
+static void	scmi_mailbox_transport_cleanup(device_t);
+static int	scmi_mailbox_xfer_msg(device_t, struct scmi_req *);
+static int	scmi_mailbox_collect_reply(device_t, struct scmi_req *);
+static void	scmi_mailbox_tx_complete(device_t, void *);
+
+static int	scmi_mailbox_probe(device_t);
+
 static void
 scmi_mailbox_callback(void *arg)
 {
@@ -73,16 +82,64 @@ scmi_mailbox_callback(void *arg)
 }
 
 static int
-scmi_mailbox_xfer_msg(device_t dev)
+scmi_mailbox_transport_init(device_t dev)
+{
+	struct scmi_mailbox_softc *sc;
+	phandle_t node;
+
+	sc = device_get_softc(dev);
+
+	node = ofw_bus_get_node(dev);
+	if (node == -1)
+		return (ENXIO);
+	/*
+	 * TODO
+	 * - Support P2A shmem + IRQ/doorbell
+	 * - Support other mailbox devices
+	 */
+	sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P);
+	if (sc->a2p_dev == NULL) {
+		device_printf(dev, "A2P shmem dev not found.\n");
+		return (ENXIO);
+	}
+
+	/* TODO: Fix ofw_get...mbox doorbell names NOT required in Linux DT */
+	sc->db = arm_doorbell_ofw_get(dev, "tx");
+	if (sc->db == NULL) {
+		device_printf(dev, "Doorbell device not found.\n");
+		return (ENXIO);
+	}
+
+	arm_doorbell_set_handler(sc->db, scmi_mailbox_callback, sc);
+
+	return (0);
+}
+
+static void
+scmi_mailbox_transport_cleanup(device_t dev)
+{
+	struct scmi_mailbox_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	arm_doorbell_set_handler(sc->db, NULL, NULL);
+}
+
+static int
+scmi_mailbox_xfer_msg(device_t dev, struct scmi_req *req)
 {
 	struct scmi_mailbox_softc *sc;
-	int timeout;
+	int ret, timeout;
 
 	sc = device_get_softc(dev);
 	SCMI_ASSERT_LOCKED(&sc->base);
 
 	sc->req_done = 0;
 
+	ret = scmi_shmem_prepare_msg(sc->a2p_dev, req, cold);
+	if (ret != 0)
+		return (ret);
+
 	/* Interrupt SCP firmware. */
 	arm_doorbell_set(sc->db);
 
@@ -92,7 +149,7 @@ scmi_mailbox_xfer_msg(device_t dev)
 
 	do {
 		if (cold) {
-			if (scmi_shmem_poll_msg(sc->base.a2p_dev))
+			if (scmi_shmem_poll_msg(sc->a2p_dev))
 				break;
 			DELAY(10000);
 		} else {
@@ -103,7 +160,7 @@ scmi_mailbox_xfer_msg(device_t dev)
 	} while (timeout--);
 
 	if (timeout <= 0)
-		return (-1);
+		return (ETIMEDOUT);
 
 	dprintf("%s: got reply, timeout %d\n", __func__, timeout);
 
@@ -111,63 +168,59 @@ scmi_mailbox_xfer_msg(device_t dev)
 }
 
 static int
-scmi_mailbox_probe(device_t dev)
+scmi_mailbox_collect_reply(device_t dev, struct scmi_req *req)
 {
+	struct scmi_mailbox_softc *sc;
+	uint32_t reply_header;
+	int ret;
 
-	if (!ofw_bus_is_compatible(dev, "arm,scmi"))
-		return (ENXIO);
+	sc = device_get_softc(dev);
 
-	if (!ofw_bus_status_okay(dev))
-		return (ENXIO);
+	/* Read header. */
+	ret = scmi_shmem_read_msg_header(sc->a2p_dev, &reply_header);
+	if (ret != 0)
+		return (ret);
 
-	device_set_desc(dev, "ARM SCMI interface driver");
+	if (reply_header != req->msg_header)
+		return (EPROTO);
 
-	return (BUS_PROBE_DEFAULT);
+	return (scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf,
+	    req->out_size));
 }
 
-static int
-scmi_mailbox_attach(device_t dev)
+static void
+scmi_mailbox_tx_complete(device_t dev, void *chan)
 {
 	struct scmi_mailbox_softc *sc;
-	int ret;
 
 	sc = device_get_softc(dev);
-
-	/* TODO: Support other mailbox devices */
-	sc->db = arm_doorbell_ofw_get(dev, "tx");
-	if (sc->db == NULL) {
-		device_printf(dev, "Doorbell device not found.\n");
-		return (ENXIO);
-	}
-
-	arm_doorbell_set_handler(sc->db, scmi_mailbox_callback, sc);
-
-	ret = scmi_attach(dev);
-	if (ret != 0)
-		arm_doorbell_set_handler(sc->db, NULL, NULL);
-
-	return (ret);
+	scmi_shmem_tx_complete(sc->a2p_dev);
 }
 
 static int
-scmi_mailbox_detach(device_t dev)
+scmi_mailbox_probe(device_t dev)
 {
-	struct scmi_mailbox_softc *sc;
 
-	sc = device_get_softc(dev);
+	if (!ofw_bus_is_compatible(dev, "arm,scmi"))
+		return (ENXIO);
 
-	arm_doorbell_set_handler(sc->db, NULL, NULL);
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
 
-	return (0);
+	device_set_desc(dev, "ARM SCMI interface driver");
+
+	return (BUS_PROBE_DEFAULT);
 }
 
 static device_method_t scmi_mailbox_methods[] = {
 	DEVMETHOD(device_probe,		scmi_mailbox_probe),
-	DEVMETHOD(device_attach,	scmi_mailbox_attach),
-	DEVMETHOD(device_detach,	scmi_mailbox_detach),
 
 	/* SCMI interface */
-	DEVMETHOD(scmi_xfer_msg,	scmi_mailbox_xfer_msg),
+	DEVMETHOD(scmi_transport_init,		scmi_mailbox_transport_init),
+	DEVMETHOD(scmi_transport_cleanup,	scmi_mailbox_transport_cleanup),
+	DEVMETHOD(scmi_xfer_msg,		scmi_mailbox_xfer_msg),
+	DEVMETHOD(scmi_collect_reply,		scmi_mailbox_collect_reply),
+	DEVMETHOD(scmi_tx_complete,		scmi_mailbox_tx_complete),
 
 	DEVMETHOD_END
 };
diff --git a/sys/dev/firmware/arm/scmi_shmem.c b/sys/dev/firmware/arm/scmi_shmem.c
index 5fb41af05246..7cb2db48f9fe 100644
--- a/sys/dev/firmware/arm/scmi_shmem.c
+++ b/sys/dev/firmware/arm/scmi_shmem.c
@@ -210,7 +210,7 @@ scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req, bool polling)
 	if ((channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) {
 		scmi_shmem_release_channel(sc);
 		device_printf(dev, "Shmem channel busy. Abort !.\n");
-		return (1);
+		return (EBUSY);
 	}
 
 	/* Update header */
diff --git a/sys/dev/firmware/arm/scmi_smc.c b/sys/dev/firmware/arm/scmi_smc.c
index ff5f4a2d1491..e238e8024068 100644
--- a/sys/dev/firmware/arm/scmi_smc.c
+++ b/sys/dev/firmware/arm/scmi_smc.c
@@ -48,25 +48,88 @@
 
 #include "scmi.h"
 #include "scmi_protocols.h"
+#include "scmi_shmem.h"
 
 struct scmi_smc_softc {
 	struct scmi_softc	base;
 	uint32_t		smc_id;
+	device_t		a2p_dev;
 };
 
+static int	scmi_smc_transport_init(device_t);
+static int	scmi_smc_xfer_msg(device_t, struct scmi_req *);
+static int	scmi_smc_collect_reply(device_t, struct scmi_req *);
+static void	scmi_smc_tx_complete(device_t, void *);
+
+static int	scmi_smc_probe(device_t);
+static int	scmi_smc_attach(device_t);
+
 static int
-scmi_smc_xfer_msg(device_t dev)
+scmi_smc_transport_init(device_t dev)
 {
 	struct scmi_smc_softc *sc;
+	phandle_t node;
+	ssize_t len;
+
+	sc = device_get_softc(dev);
+
+	node = ofw_bus_get_node(dev);
+	len = OF_getencprop(node, "arm,smc-id", &sc->smc_id,
+	    sizeof(sc->smc_id));
+	if (len <= 0) {
+		device_printf(dev, "No SMC ID found\n");
+		return (EINVAL);
+	}
+
+	device_printf(dev, "smc id %x\n", sc->smc_id);
+
+	sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P);
+	if (sc->a2p_dev == NULL) {
+		device_printf(dev, "A2P shmem dev not found.\n");
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+static int
+scmi_smc_xfer_msg(device_t dev, struct scmi_req *req)
+{
+	struct scmi_smc_softc *sc;
+	int ret;
 
 	sc = device_get_softc(dev);
 	SCMI_ASSERT_LOCKED(&sc->base);
 
+	ret = scmi_shmem_prepare_msg(sc->a2p_dev, req, cold);
+	if (ret != 0)
+		return (ret);
+
 	arm_smccc_smc(sc->smc_id, 0, 0, 0, 0, 0, 0, 0, NULL);
 
 	return (0);
 }
 
+static int
+scmi_smc_collect_reply(device_t dev, struct scmi_req *req)
+{
+	struct scmi_smc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	return (scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf,
+	    req->out_size));
+}
+
+static void
+scmi_smc_tx_complete(device_t dev, void *chan)
+{
+	struct scmi_smc_softc *sc;
+
+	sc = device_get_softc(dev);
+	scmi_shmem_tx_complete(sc->a2p_dev);
+}
+
 static int
 scmi_smc_probe(device_t dev)
 {
@@ -104,20 +167,15 @@ scmi_smc_attach(device_t dev)
 	return (scmi_attach(dev));
 }
 
-static int
-scmi_smc_detach(device_t dev)
-{
-
-	return (0);
-}
-
 static device_method_t scmi_smc_methods[] = {
 	DEVMETHOD(device_probe,		scmi_smc_probe),
 	DEVMETHOD(device_attach,	scmi_smc_attach),
-	DEVMETHOD(device_detach,	scmi_smc_detach),
 
 	/* SCMI interface */
-	DEVMETHOD(scmi_xfer_msg,	scmi_smc_xfer_msg),
+	DEVMETHOD(scmi_transport_init,		scmi_smc_transport_init),
+	DEVMETHOD(scmi_xfer_msg,		scmi_smc_xfer_msg),
+	DEVMETHOD(scmi_collect_reply,		scmi_smc_collect_reply),
+	DEVMETHOD(scmi_tx_complete,		scmi_smc_tx_complete),
 
 	DEVMETHOD_END
 };