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
@@ -1,188 +1,166 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2022 Ruslan Bukin 
  * Copyright (c) 2023 Arm Ltd
  *
  * This work was supported by Innovate UK project 105694, "Digital Security
  * by Design (DSbD) Technology Platform Prototype".
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 
-#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)
 #define	SCMI_HDR_TOKEN_M		(SCMI_HDR_TOKEN_BF << SCMI_HDR_TOKEN_S)
 
 #define	SCMI_HDR_PROTOCOL_ID_S		10
 #define	SCMI_HDR_PROTOCOL_ID_BF		(0xff)
 #define	SCMI_HDR_PROTOCOL_ID_M		\
     (SCMI_HDR_PROTOCOL_ID_BF << SCMI_HDR_PROTOCOL_ID_S)
 
 #define	SCMI_HDR_MESSAGE_TYPE_S		8
 #define	SCMI_HDR_MESSAGE_TYPE_BF	(0x3)
 #define	SCMI_HDR_MESSAGE_TYPE_M		\
     (SCMI_HDR_MESSAGE_TYPE_BF << SCMI_HDR_MESSAGE_TYPE_S)
 
 #define	SCMI_HDR_MESSAGE_ID_S		0
 #define	SCMI_HDR_MESSAGE_ID_BF		(0xff)
 #define	SCMI_HDR_MESSAGE_ID_M		\
     (SCMI_HDR_MESSAGE_ID_BF << SCMI_HDR_MESSAGE_ID_S)
 
 #define SCMI_MSG_TYPE_CMD	0
 #define SCMI_MSG_TYPE_DRESP	2
 #define SCMI_MSG_TYPE_NOTIF	3
 
 static int
 scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
 {
-	uint32_t reply_header;
 	int ret;
 
 	SCMI_ASSERT_LOCKED(sc);
 
 	req->msg_header = req->message_id << SCMI_HDR_MESSAGE_ID_S;
 	/* TODO: Allocate a token */
 	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);
 }
 
 int
 scmi_request(device_t dev, struct scmi_req *req)
 {
 	struct scmi_softc *sc;
 	int error;
 
 	sc = device_get_softc(dev);
 
 	SCMI_LOCK(sc);
 	error = scmi_request_locked(sc, req);
 	SCMI_UNLOCK(sc);
 
 	return (error);
 }
 
 int
 scmi_attach(device_t dev)
 {
 	struct scmi_softc *sc;
 	phandle_t node;
 	int error;
 
 	sc = device_get_softc(dev);
 	sc->dev = dev;
 
 	node = ofw_bus_get_node(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.
 	 */
 	bus_generic_probe(dev);
 
 	/*
 	 * Now walk the OFW tree and attach top-level devices.
 	 */
 	for (node = OF_child(node); node > 0; node = OF_peer(node))
 		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
 
 	error = bus_generic_attach(dev);
 
 	return (error);
 }
 
 static int
 scmi_detach(device_t dev)
 {
 
+	SCMI_TRANSPORT_CLEANUP(dev);
 	return (0);
 }
 
 static device_method_t scmi_methods[] = {
 	DEVMETHOD(device_attach,	scmi_attach),
 	DEVMETHOD(device_detach,	scmi_detach),
 
 	DEVMETHOD_END
 };
 
 DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc),
     simplebus_driver);
 
 DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0);
 MODULE_VERSION(scmi, 1);
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
@@ -1,73 +1,72 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2022 Ruslan Bukin 
  * Copyright (c) 2023 Arm Ltd
  *
  * This work was supported by Innovate UK project 105694, "Digital Security
  * by Design (DSbD) Technology Platform Prototype".
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #ifndef	_ARM64_SCMI_SCMI_H_
 #define	_ARM64_SCMI_SCMI_H_
 
 #include "scmi_if.h"
 
 #define	SCMI_LOCK(sc)		mtx_lock(&(sc)->mtx)
 #define	SCMI_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
 #define	SCMI_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->mtx, MA_OWNED)
 
 #define dprintf(fmt, ...)
 
 #define SCMI_MSG_HDR_SIZE	(sizeof(uint32_t))
 
 enum scmi_chan {
 	SCMI_CHAN_A2P,
 	SCMI_CHAN_P2A,
 	SCMI_CHAN_MAX
 };
 
 struct scmi_softc {
 	struct simplebus_softc	simplebus_sc;
 	device_t		dev;
-	device_t		a2p_dev;
 	struct mtx		mtx;
 };
 
 struct scmi_req {
 	int protocol_id;
 	int message_id;
 	uint32_t msg_header;
 	const void *in_buf;
 	uint32_t in_size;
 	void *out_buf;
 	uint32_t out_size;
 };
 
 DECLARE_CLASS(scmi_driver);
 
 int scmi_attach(device_t dev);
 int scmi_request(device_t dev, struct scmi_req *req);
 
 #endif /* !_ARM64_SCMI_SCMI_H_ */
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
@@ -1,32 +1,55 @@
 #-
 # SPDX-License-Identifier: BSD-2-Clause
 #
 # Copyright (c) 2023 Arm Ltd
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
 # are met:
 # 1. Redistributions of source code must retain the above copyright
 #    notice unmodified, this list of conditions, and the following
 #    disclaimer.
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
 #
 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
 
 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
@@ -1,179 +1,232 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2022 Ruslan Bukin 
  * Copyright (c) 2023 Arm Ltd
  *
  * This work was supported by Innovate UK project 105694, "Digital Security
  * by Design (DSbD) Technology Platform Prototype".
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 
 #include "dev/mailbox/arm/arm_doorbell.h"
 
 #include "scmi.h"
 #include "scmi_protocols.h"
 #include "scmi_shmem.h"
 
 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)
 {
 	struct scmi_mailbox_softc *sc;
 
 	sc = arg;
 
 	dprintf("%s sc %p\n", __func__, sc);
 
 	SCMI_LOCK(&sc->base);
 	sc->req_done = 1;
 	wakeup(sc);
 	SCMI_UNLOCK(&sc->base);
 }
 
 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);
 
 	timeout = 200;
 
 	dprintf("%s: request\n", __func__);
 
 	do {
 		if (cold) {
-			if (scmi_shmem_poll_msg(sc->base.a2p_dev))
+			if (scmi_shmem_poll_msg(sc->a2p_dev))
 				break;
 			DELAY(10000);
 		} else {
 			msleep(sc, &sc->base.mtx, 0, "scmi", hz / 10);
 			if (sc->req_done)
 				break;
 		}
 	} while (timeout--);
 
 	if (timeout <= 0)
-		return (-1);
+		return (ETIMEDOUT);
 
 	dprintf("%s: got reply, timeout %d\n", __func__, timeout);
 
 	return (0);
 }
 
 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
 };
 
 DEFINE_CLASS_1(scmi_mailbox, scmi_mailbox_driver, scmi_mailbox_methods,
     sizeof(struct scmi_mailbox_softc), scmi_driver);
 
 DRIVER_MODULE(scmi_mailbox, simplebus, scmi_mailbox_driver, 0, 0);
 MODULE_VERSION(scmi_mailbox, 1);
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
@@ -1,309 +1,309 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2022 Ruslan Bukin 
  * Copyright (c) 2023 Arm Ltd
  *
  * This work was supported by Innovate UK project 105694, "Digital Security
  * by Design (DSbD) Technology Platform Prototype".
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
 
 #include 
 #include 
 #include 
 
 #include "mmio_sram_if.h"
 
 #include "scmi_shmem.h"
 #include "scmi.h"
 
 #define INFLIGHT_NONE	0
 #define INFLIGHT_REQ	1
 
 struct shmem_softc {
 	device_t		dev;
 	device_t		parent;
 	int			reg;
 	int			inflight;
 };
 
 static void	scmi_shmem_read(device_t, bus_size_t, void *, bus_size_t);
 static void	scmi_shmem_write(device_t, bus_size_t, const void *,
 				 bus_size_t);
 static void	scmi_shmem_acquire_channel(struct shmem_softc *);
 static void	scmi_shmem_release_channel(struct shmem_softc *);
 
 static int	shmem_probe(device_t);
 static int	shmem_attach(device_t);
 static int	shmem_detach(device_t);
 
 static int
 shmem_probe(device_t dev)
 {
 
 	if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem"))
 		return (ENXIO);
 
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
 	device_set_desc(dev, "ARM SCMI Shared Memory driver");
 
 	return (BUS_PROBE_DEFAULT);
 }
 
 static int
 shmem_attach(device_t dev)
 {
 	struct shmem_softc *sc;
 	phandle_t node;
 	int reg;
 
 	sc = device_get_softc(dev);
 	sc->dev = dev;
 	sc->parent = device_get_parent(dev);
 
 	node = ofw_bus_get_node(dev);
 	if (node == -1)
 		return (ENXIO);
 
 	OF_getencprop(node, "reg", ®, sizeof(reg));
 
 	dprintf("%s: reg %x\n", __func__, reg);
 
 	sc->reg = reg;
 	atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE);
 
 	OF_device_register_xref(OF_xref_from_node(node), dev);
 
 	return (0);
 }
 
 static int
 shmem_detach(device_t dev)
 {
 
 	return (0);
 }
 
 static void
 scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len)
 {
 	struct shmem_softc *sc;
 	uint8_t *addr;
 	int i;
 
 	sc = device_get_softc(dev);
 
 	addr = (uint8_t *)buf;
 
 	for (i = 0; i < len; i++)
 		addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i);
 }
 
 static void
 scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf,
     bus_size_t len)
 {
 	struct shmem_softc *sc;
 	const uint8_t *addr;
 	int i;
 
 	sc = device_get_softc(dev);
 
 	addr = (const uint8_t *)buf;
 
 	for (i = 0; i < len; i++)
 		MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]);
 }
 
 device_t
 scmi_shmem_get(device_t dev, phandle_t node, int index)
 {
 	phandle_t *shmems;
 	device_t shmem_dev;
 	size_t len;
 
 	len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems),
 	    (void **)&shmems);
 	if (len <= 0) {
 		device_printf(dev, "%s: Can't get shmem node.\n", __func__);
 		return (NULL);
 	}
 
 	if (index >= len) {
 		OF_prop_free(shmems);
 		return (NULL);
 	}
 
 	shmem_dev = OF_device_from_xref(shmems[index]);
 	if (shmem_dev == NULL)
 		device_printf(dev, "%s: Can't get shmem device.\n",
 		    __func__);
 
 	OF_prop_free(shmems);
 
 	return (shmem_dev);
 }
 
 static void
 scmi_shmem_acquire_channel(struct shmem_softc *sc)
 {
 
 	 while ((atomic_cmpset_acq_int(&sc->inflight, INFLIGHT_NONE,
 	     INFLIGHT_REQ)) == 0)
 		 DELAY(1000);
 }
 
 static void
 scmi_shmem_release_channel(struct shmem_softc *sc)
 {
 
 	atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE);
 }
 
 int
 scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req, bool polling)
 {
 	struct shmem_softc *sc;
 	struct scmi_smt_header hdr = {};
 	uint32_t channel_status;
 
 	sc = device_get_softc(dev);
 
 	/* Get exclusive write access to channel */
 	scmi_shmem_acquire_channel(sc);
 
 	/* Read channel status */
 	scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &channel_status,
 	    SMT_SIZE_CHAN_STATUS);
 	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 */
 	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);
 	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);
 
 	/* Write request payload if any */
 	if (req->in_size)
 		scmi_shmem_write(dev, SMT_SIZE_HEADER, req->in_buf,
 		    req->in_size);
 
 	return (0);
 }
 
 int
 scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header)
 {
 	uint32_t length, header;
 
 	/* Read and check length. */
 	scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH);
 	if (le32toh(length) < sizeof(header))
 		return (EINVAL);
 
 	/* Read header. */
 	scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, &header,
 	    SMT_SIZE_MSG_HEADER);
 
 	*msg_header = le32toh(header);
 
 	return (0);
 }
 
 int
 scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len)
 {
 	uint32_t length, payld_len;
 
 	/* Read length. */
 	scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH);
 	payld_len = le32toh(length) - SCMI_MSG_HDR_SIZE;
 
 	if (payld_len > buf_len) {
 		device_printf(dev,
 		    "RX payload %dbytes exceeds buflen %dbytes. Truncate.\n",
 		    payld_len, buf_len);
 		payld_len = buf_len;
 	}
 
 	/* Read response payload */
 	scmi_shmem_read(dev, SMT_SIZE_HEADER, buf, payld_len);
 
 	return (0);
 }
 
 void
 scmi_shmem_tx_complete(device_t dev)
 {
 	struct shmem_softc *sc;
 
 	sc = device_get_softc(dev);
 	scmi_shmem_release_channel(sc);
 }
 
 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),
 	DEVMETHOD(device_detach,	shmem_detach),
 	DEVMETHOD_END
 };
 
 DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc),
     simplebus_driver);
 
 EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0,
     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
 MODULE_VERSION(scmi, 1);
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
@@ -1,131 +1,189 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2022 Ruslan Bukin 
  * Copyright (c) 2023 Arm Ltd
  *
  * This work was supported by Innovate UK project 105694, "Digital Security
  * by Design (DSbD) Technology Platform Prototype".
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
 #include 
 #include 
 
 #include 
 
 #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)
 {
 
 	if (!ofw_bus_is_compatible(dev, "arm,scmi-smc"))
 		return (ENXIO);
 
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
 	device_set_desc(dev, "ARM SCMI SCM interface driver");
 
 	return (BUS_PROBE_DEFAULT);
 }
 
 static int
 scmi_smc_attach(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);
 
 	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
 };
 
 DEFINE_CLASS_1(scmi_smc, scmi_smc_driver, scmi_smc_methods,
     sizeof(struct scmi_smc_softc), scmi_driver);
 
 /* Needs to be after the mmio_sram driver */
 EARLY_DRIVER_MODULE(scmi_smc, simplebus, scmi_smc_driver, 0, 0,
     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_LATE);
 MODULE_VERSION(scmi_smc, 1);