SCMI Comms: Initial implementation of partition

Adds a new SCMI comms partition that can subscribe to system power
state notifications from SCP.

Signed-off-by: Jamie Fox <jamie.fox@arm.com>
Change-Id: Iaf99eed0f238e45f2f96b5f89d0d9e9b915a9275
diff --git a/docs/partitions/index.rst b/docs/partitions/index.rst
index 87e658e..48c835d 100644
--- a/docs/partitions/index.rst
+++ b/docs/partitions/index.rst
@@ -11,6 +11,7 @@
   DMA-350 ARoT <dma350_unpriv_partition/dma350_unpriv_partition>
   External Trusted Secure Storage <external_trusted_secure_storage/external_trusted_secure_storage>
   Measured Boot <measured_boot_integration_guide>
+  SCMI Comms <scmi_comms>
 
 --------------
 
diff --git a/docs/partitions/partitions.rst b/docs/partitions/partitions.rst
index 502e2cd..92d55ba 100644
--- a/docs/partitions/partitions.rst
+++ b/docs/partitions/partitions.rst
@@ -62,9 +62,9 @@
 - Gabor Toth `gabor.toth@arm.com <gabor.toth@arm.com>`_
 - Mark Horvath `mark.horvath@arm.com <mark.horvath@arm.com>`_
 
-####
+****
 ADAC
-####
+****
 
 ADAC partition for authenticated debug and access control for RSE platform.
 
@@ -83,6 +83,17 @@
 
 - Maulik Patel `Maulik.Patel@arm.com <Maulik.Patel@arm.com>`_
 
+**********
+SCMI Comms
+**********
+
+A partition that can subscribe to SCMI system power state notifications from
+SCP.
+
+**Maintainers**
+
+- Jamie Fox `jamie.fox@arm.com <jamie.fox@arm.com>`_
+
 ---------------------------
 
 *Copyright (c) 2021-2024, Arm Limited. All rights reserved.*
diff --git a/docs/partitions/scmi_comms.rst b/docs/partitions/scmi_comms.rst
new file mode 100644
index 0000000..15fa036
--- /dev/null
+++ b/docs/partitions/scmi_comms.rst
@@ -0,0 +1,80 @@
+####################
+SCMI Comms Partition
+####################
+
+The SCMI Comms partition provides a minimal implementation of the SCMI [1]_
+protocol for the purpose of subscribing to system power state notifications from
+SCP.
+
+It currently supports only the shared memory based transport protocol.
+
+***********************
+Supported message types
+***********************
+
+The partition supports the System power management protocol [1]_. It can send
+the following message types:
+
+- SYSTEM_POWER_STATE_NOTIFY
+
+It can receive the following message types:
+
+- SYSTEM_POWER_STATE_SET
+- SYSTEM_POWER_STATE_NOTIFIER
+
+**************
+Code structure
+**************
+
+Partition source files:
+
+- ``scmi_comms.c``: Implements the core SCMI message handling.
+- ``scmi_comms.h``: Common definitions used within the partition.
+- ``scmi_hal.h``: Hardware abstraction layer that must be implemented by the
+  platform to support the SCMI Comms partition.
+- ``scmi_protocol.h``: Defines values from the SCMI spec.
+
+Build options for out of tree build
+===================================
+
+- ``TFM_PARTITION_SCMI_COMMS``: To build the SCMI Comms secure partition its
+  value should be ``ON``. By default, it is switched ``OFF``.
+
+- ``TFM_EXTRA_MANIFEST_LIST_FILES``: ``<tf-m-extras-repo>/partitions/scmi/scmi_comms_manifest_list.yaml``
+
+- ``TFM_EXTRA_PARTITION_PATHS``: ``<tf-m-extras-repo>/partitions/scmi``
+
+****************
+Platform porting
+****************
+
+To use the SCMI Comms partition, a platform must supply an interface library
+called ``scmi_hal`` for the partition to link against. The library must contain
+implementations of all of the functions declared in ``scmi_hal.h``. It must also
+contain a header called ``scmi_hal_defs.h``, with the following definitions:
+
+- ``SCP_SHARED_MEMORY_BASE``: The base address of a memory area shared between
+  SCP and the CPU running TF-M, which is used to pass messages via the SCMI
+  shared memory transport protocol.
+- ``SCP_SHARED_MEMORY_SIZE``: The size of the SCP shared memory area. The
+  maximum SCMI message length that can be transported is the size of this area
+  minus the ``24`` bytes used by the transport protocol.
+
+Additionally, the platform must define ``SCP_DOORBELL_IRQ`` to be the IRQ number
+triggered by the SCP doorbell in its ``config_tfm_target.h`` header. It must
+also implement that IRQ's handler function to route the request to the SCMI
+comms partition (see
+:doc:`TF-M Secure IRQ integration guide<TF-M:integration_guide/tfm_secure_irq_integration_guide>`
+for more details).
+
+**********
+References
+**********
+
+.. [1] `Arm System Control and Management Interface (SCMI) <https://developer.arm.com/documentation/den0056/latest/>`_
+
+--------------
+
+*SPDX-License-Identifier: BSD-3-Clause*
+
+*SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors*
diff --git a/partitions/scmi/CMakeLists.txt b/partitions/scmi/CMakeLists.txt
new file mode 100644
index 0000000..cf19d96
--- /dev/null
+++ b/partitions/scmi/CMakeLists.txt
@@ -0,0 +1,61 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+#
+#-------------------------------------------------------------------------------
+
+if (NOT TFM_PARTITION_SCMI_COMMS)
+    return()
+endif()
+
+cmake_minimum_required(VERSION 3.21)
+cmake_policy(SET CMP0079 NEW)
+
+# The name of the target is required to be of the pattern
+# tfm_app_rot_partition_x or tfm_psa_rot_partition_x, as it affects how the
+# linker script will lay the partition in memory.
+add_library(tfm_psa_rot_partition_scmi_comms STATIC)
+
+target_sources(tfm_psa_rot_partition_scmi_comms
+    PRIVATE
+        scmi_comms.c
+)
+
+# Add the source files generated by parse tools when building.
+# The intermedia file defines the partition stack.
+target_sources(tfm_psa_rot_partition_scmi_comms
+    PRIVATE
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/scmi/auto_generated/intermedia_scmi_comms.c
+)
+
+# The load info file includes the static data of the partition.
+target_sources(tfm_partitions
+    INTERFACE
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/scmi/auto_generated/load_info_scmi_comms.c
+)
+
+target_include_directories(tfm_psa_rot_partition_scmi_comms
+    PRIVATE
+        ${CMAKE_CURRENT_SOURCE_DIR}
+    PUBLIC
+        ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/scmi
+)
+
+target_link_libraries(tfm_psa_rot_partition_scmi_comms
+    PRIVATE
+        tfm_sprt
+        scmi_hal
+        platform_s
+)
+
+############################ Partition Defs ####################################
+
+target_link_libraries(tfm_partitions
+    INTERFACE
+        tfm_psa_rot_partition_scmi_comms
+)
+
+target_compile_definitions(tfm_config
+    INTERFACE
+        TFM_PARTITION_SCMI_COMMS
+)
diff --git a/partitions/scmi/scmi_comms.c b/partitions/scmi/scmi_comms.c
new file mode 100644
index 0000000..796482c
--- /dev/null
+++ b/partitions/scmi/scmi_comms.c
@@ -0,0 +1,396 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+ *
+ */
+
+#include "psa/service.h"
+#include "psa_manifest/scmi_comms.h"
+#include "scmi_comms.h"
+#include "scmi_hal.h"
+#include "scmi_protocol.h"
+#include "tfm_sp_log.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#define SCMI_MESSAGE_HEADER_MESSAGE_ID_POS   0
+#define SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK \
+    (UINT32_C(0xFF) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS)
+
+#define SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS 8
+#define SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK \
+    (UINT32_C(0x3) << SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS)
+
+#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS  10
+#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK \
+    (UINT32_C(0xFF) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS)
+
+#define SCMI_MESSAGE_HEADER_TOKEN_POS        18
+#define SCMI_MESSAGE_HEADER_TOKEN_MASK \
+    (UINT32_C(0x3FF) << SCMI_MESSAGE_HEADER_TOKEN_POS)
+
+static uint32_t scmi_message_header(uint8_t message_id, uint8_t message_type,
+                                    uint8_t protocol_id, uint8_t token)
+{
+    return (((uint32_t)message_id << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS) &
+            SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) |
+           (((uint32_t)message_type << SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS) &
+            SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK) |
+           (((uint32_t)protocol_id << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS) &
+            SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) |
+           (((uint32_t)token << SCMI_MESSAGE_HEADER_TOKEN_POS) &
+            SCMI_MESSAGE_HEADER_TOKEN_MASK);
+}
+
+#define TRANSPORT_BUFFER_STATUS_FREE_POS  0
+#define TRANSPORT_BUFFER_STATUS_FREE_MASK \
+    (UINT32_C(0x1) << TRANSPORT_BUFFER_STATUS_FREE_POS)
+
+#define TRANSPORT_BUFFER_STATUS_ERROR_POS 1
+#define TRANSPORT_BUFFER_STATUS_ERROR_MASK \
+    (UINT32_C(0x1) << TRANSPORT_BUFFER_STATUS_ERROR_POS)
+
+#define TRANSPORT_BUFFER_FLAGS_INTERRUPT_POS 0
+#define TRANSPORT_BUFFER_FLAGS_INTERRUPT_MASK \
+    (UINT32_C(0x1) << TRANSPORT_BUFFER_FLAGS_INTERRUPT_POS)
+
+#define TRANSPORT_BUFFER_MAX_LENGTH \
+    (SCP_SHARED_MEMORY_SIZE - offsetof(struct transport_buffer_t, message_header))
+
+/**
+ * \brief Shared memory area layout used for sending & receiving messages
+ */
+struct transport_buffer_t {
+    uint32_t reserved0; /**< Reserved, must be zero */
+    volatile uint32_t status; /**< Channel status */
+    uint64_t reserved1; /**< Implementation defined field */
+    uint32_t flags; /**< Channel flags */
+    volatile uint32_t length; /**< Length in bytes of the message header and payload */
+    uint32_t message_header; /**< Message header */
+    uint32_t message_payload[]; /**< Message payload */
+};
+
+/**
+ * \brief Structure representing an SCMI message.
+ */
+struct scmi_message_t {
+    uint32_t header;
+    uint32_t payload[(TRANSPORT_BUFFER_MAX_LENGTH - sizeof(uint32_t)) / sizeof(uint32_t)];
+    uint32_t payload_len;
+};
+
+static struct transport_buffer_t *const shared_memory =
+    (struct transport_buffer_t *)SCP_SHARED_MEMORY_BASE;
+
+/**
+ * \brief Initialize the SCMI transport layer.
+ *
+ * \return Error value as defined by scmi_comms_err_t.
+ */
+static scmi_comms_err_t transport_init(void)
+{
+    scmi_comms_err_t err;
+
+    err = scmi_hal_doorbell_init();
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    err = scmi_hal_shared_memory_init();
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    shared_memory->flags = 0;
+    shared_memory->length = 0;
+    shared_memory->status = TRANSPORT_BUFFER_STATUS_FREE_MASK;
+
+    return SCMI_COMMS_SUCCESS;
+}
+
+/**
+ * \brief Read a message from the shared memory to the local buffer.
+ *
+ * \param[out] msg  SCMI message
+ *
+ * \return Error value as defined by scmi_comms_err_t.
+ */
+static scmi_comms_err_t transport_receive(struct scmi_message_t *msg)
+{
+    scmi_comms_err_t err = scmi_hal_doorbell_clear();
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    uint32_t length = shared_memory->length;
+
+    if ((length < sizeof(shared_memory->message_header)) ||
+        (length > TRANSPORT_BUFFER_MAX_LENGTH)) {
+        return SCMI_COMMS_INVALID_ARGUMENT;
+    }
+
+    memcpy(msg, &shared_memory->message_header, length);
+    msg->payload_len = length - sizeof(msg->header);
+
+    return SCMI_COMMS_SUCCESS;
+}
+
+/**
+ * \brief Write a response from the local buffer to the shared memory and signal
+ *        completion.
+ *
+ * \param[in] msg  SCMI message
+ */
+static void transport_respond(const struct scmi_message_t *msg)
+{
+    /* Populate shared memory area */
+    memcpy(shared_memory->message_payload, msg->payload, msg->payload_len);
+    shared_memory->length = msg->payload_len + sizeof(msg->header);
+
+    /* Mark channel as free */
+    shared_memory->status |= TRANSPORT_BUFFER_STATUS_FREE_MASK;
+
+    /* TODO: Issue completion interrupt */
+}
+
+/**
+ * \brief Write a message from the local buffer to the shared memory and wait
+ *        for a response.
+ *
+ * \param[in] msg  SCMI message
+ *
+ * \return Error value as defined by scmi_comms_err_t.
+ */
+static int32_t transport_send(const struct scmi_message_t *msg)
+{
+    int32_t err;
+    uint32_t length = msg->payload_len + sizeof(msg->header);
+
+    if (length > TRANSPORT_BUFFER_MAX_LENGTH) {
+        return SCMI_COMMS_INVALID_ARGUMENT;
+    }
+
+    /* Wait for channel to be free */
+    /* TODO: Timeout */
+    while (!(shared_memory->status & TRANSPORT_BUFFER_STATUS_FREE_MASK));
+
+    /* Populate shared memory area */
+    memcpy(&shared_memory->message_header, msg, length);
+    shared_memory->length = length;
+
+    /* Mark channel as busy */
+    shared_memory->status &= ~TRANSPORT_BUFFER_STATUS_FREE_MASK;
+
+    /* Ring doorbell */
+    err = scmi_hal_doorbell_ring();
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    /* Wait until channel is free */
+    /* TODO: Wait for completion interrupt */
+    while (!(shared_memory->status & TRANSPORT_BUFFER_STATUS_FREE_MASK));
+
+    return SCMI_COMMS_SUCCESS;
+}
+
+/**
+ * \brief Create an SCMI response containing only status.
+ *
+ * \param[out] msg     SCMI message
+ * \param[in]  status  SCMI status
+ */
+static void scmi_response_status(struct scmi_message_t *msg, int32_t status)
+{
+    msg->payload[0] = status;
+    msg->payload_len = sizeof(msg->payload[0]);
+}
+
+/**
+ * \brief Create an SCMI system power state notify message.
+ *
+ * \param[out] msg  SCMI message
+ */
+static void scmi_message_sys_power_state_notify(struct scmi_message_t *msg)
+{
+    msg->header =
+        scmi_message_header(SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFY,
+                            SCMI_MESSAGE_TYPE_COMMAND,
+                            SCMI_PROTOCOL_ID_SYS_POWER_STATE,
+                            0);
+
+    assert(sizeof(struct scmi_sys_power_state_notify_t) <= sizeof(msg->payload));
+
+    memcpy(msg->payload,
+           &(struct scmi_sys_power_state_notify_t) { .notify_enable = 1 },
+           sizeof(struct scmi_sys_power_state_notify_t));
+
+    msg->payload_len = sizeof(struct scmi_sys_power_state_notify_t);
+}
+
+/**
+ * \brief Handle an SCMI system power state set message.
+ *
+ * \param[in,out] msg  SCMI message
+ */
+static void scmi_handle_sys_power_state_set(struct scmi_message_t *msg)
+{
+    if (msg->payload_len != sizeof(struct scmi_sys_power_state_set_t)) {
+        scmi_response_status(msg, SCMI_STATUS_PROTOCOL_ERROR);
+        return;
+    }
+
+    struct scmi_sys_power_state_set_t *pwr_set =
+        (struct scmi_sys_power_state_set_t *)msg->payload;
+
+    int32_t status = scmi_hal_sys_power_state(0, pwr_set->flags, pwr_set->system_state);
+
+    scmi_response_status(msg, status);
+}
+
+/**
+ * \brief Handle an SCMI system power state notification.
+ *
+ * \param[in,out] msg  SCMI message
+ */
+static void scmi_handle_sys_power_state_notifier(struct scmi_message_t *msg)
+{
+    if (msg->payload_len != sizeof(struct scmi_sys_power_state_notifier_t)) {
+        /* No return values for notifications */
+        msg->payload_len = 0;
+        return;
+    }
+
+    struct scmi_sys_power_state_notifier_t *pwr_not =
+        (struct scmi_sys_power_state_notifier_t *)msg->payload;
+
+    scmi_hal_sys_power_state(pwr_not->agent_id, pwr_not->flags, pwr_not->system_state);
+
+    /* No return values for notifications */
+    msg->payload_len = 0;
+}
+
+/**
+ * \brief Handle a received SCMI message.
+ *
+ * \param[in,out] msg  SCMI message
+ */
+static void scmi_handle_message(struct scmi_message_t *msg)
+{
+    uint8_t message_id = (msg->header & SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK)
+                         >> SCMI_MESSAGE_HEADER_MESSAGE_ID_POS;
+    uint8_t message_type = (msg->header & SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK)
+                           >> SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS;
+    uint8_t protocol_id = (msg->header & SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK)
+                          >> SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS;
+
+    if (protocol_id == SCMI_PROTOCOL_ID_SYS_POWER_STATE) {
+        if (message_type == SCMI_MESSAGE_TYPE_COMMAND &&
+            message_id == SCMI_MESSAGE_ID_SYS_POWER_STATE_SET) {
+            scmi_handle_sys_power_state_set(msg);
+            return;
+        } else if (message_type == SCMI_MESSAGE_TYPE_NOTIFICATION &&
+                   message_id == SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFIER) {
+            scmi_handle_sys_power_state_notifier(msg);
+            return;
+        }
+    }
+
+    /* Any command that is sent with an unknown protocol_id or message_id must
+     * be responded to with a return value of NOT_SUPPORTED as the status code.
+     */
+    scmi_response_status(msg, SCMI_STATUS_NOT_SUPPORTED);
+}
+
+/**
+ * \brief Handle a received SCMI response.
+ *
+ * \param[in] msg  SCMI message
+ *
+ * \return SCMI status of the response.
+ */
+static int32_t scmi_handle_response(const struct scmi_message_t *msg)
+{
+    /* Only simple status responses are currently supported */
+    if (msg->payload_len != sizeof(int32_t)) {
+        return SCMI_STATUS_PROTOCOL_ERROR;
+    }
+    return (int32_t)msg->payload[0];
+}
+
+/**
+ * \brief Subscribe to system power state notifications.
+ *
+ * \param[in,out] msg  SCMI message
+ *
+ * \return Error value as defined by scmi_comms_err_t.
+ */
+static scmi_comms_err_t scmi_comms_notification_subscribe(struct scmi_message_t *msg)
+{
+    scmi_comms_err_t err;
+
+    scmi_message_sys_power_state_notify(msg);
+
+    err = transport_send(msg);
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    err = transport_receive(msg);
+    if (err != SCMI_COMMS_SUCCESS) {
+        return err;
+    }
+
+    return (scmi_handle_response(msg) == SCMI_STATUS_SUCCESS) ?
+           SCMI_COMMS_SUCCESS : SCMI_COMMS_GENERIC_ERROR;
+}
+
+void scmi_comms_main(void)
+{
+    scmi_comms_err_t err;
+    struct scmi_message_t agent_buf;
+
+    err = transport_init();
+    if (err != SCMI_COMMS_SUCCESS) {
+        psa_panic();
+    }
+
+    psa_irq_enable(SCP_DOORBELL_SIGNAL);
+
+    /* First wait for SCP to signal that it is ready to receive commands. */
+    (void)psa_wait(SCP_DOORBELL_SIGNAL, PSA_BLOCK);
+
+    err = scmi_hal_doorbell_clear();
+    if (err != SCMI_COMMS_SUCCESS) {
+        psa_panic();
+    }
+
+    psa_eoi(SCP_DOORBELL_SIGNAL);
+
+    /* Subscribe to notifications. If it fails, the agent will still listen for
+     * SCMI commands.
+     */
+    err = scmi_comms_notification_subscribe(&agent_buf);
+    if (err == SCMI_COMMS_SUCCESS) {
+        LOG_INFFMT("SCMI Comms subscribed to power state notifications\r\n");
+    } else {
+        LOG_ERRFMT("SCMI Comms failed to subscribe to power state notifications\r\n");
+    }
+
+    while (1) {
+        (void)psa_wait(SCP_DOORBELL_SIGNAL, PSA_BLOCK);
+
+        err = transport_receive(&agent_buf);
+        if (err == SCMI_COMMS_SUCCESS) {
+            scmi_handle_message(&agent_buf);
+        } else {
+            scmi_response_status(&agent_buf, SCMI_STATUS_PROTOCOL_ERROR);
+        }
+        transport_respond(&agent_buf);
+
+        psa_eoi(SCP_DOORBELL_SIGNAL);
+    }
+}
diff --git a/partitions/scmi/scmi_comms.h b/partitions/scmi/scmi_comms.h
new file mode 100644
index 0000000..ae4bcad
--- /dev/null
+++ b/partitions/scmi/scmi_comms.h
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+ *
+ */
+
+#ifndef __SCMI_COMMS_H__
+#define __SCMI_COMMS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    SCMI_COMMS_SUCCESS = 0,
+    SCMI_COMMS_GENERIC_ERROR,
+    SCMI_COMMS_INVALID_ARGUMENT,
+    SCMI_COMMS_HARDWARE_ERROR,
+} scmi_comms_err_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCMI_COMMS_H__ */
diff --git a/partitions/scmi/scmi_comms.yaml b/partitions/scmi/scmi_comms.yaml
new file mode 100644
index 0000000..b2f106c
--- /dev/null
+++ b/partitions/scmi/scmi_comms.yaml
@@ -0,0 +1,22 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.1,
+  "name": "SCMI_COMMS_PARTITION",
+  "type": "PSA-ROT",
+  "priority": "NORMAL",
+  "model": "IPC",
+  "entry_point": "scmi_comms_main",
+  "stack_size": "0x400",
+  "irqs": [
+    {
+      "source": "SCP_DOORBELL_IRQ",
+      "name": "SCP_DOORBELL",
+      "handling": "SLIH",
+    }
+  ],
+}
diff --git a/partitions/scmi/scmi_comms_manifest_list.yaml b/partitions/scmi/scmi_comms_manifest_list.yaml
new file mode 100644
index 0000000..614f478
--- /dev/null
+++ b/partitions/scmi/scmi_comms_manifest_list.yaml
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+#
+#-------------------------------------------------------------------------------
+
+{
+  "description": "SCMI Comms partition manifest",
+  "type": "manifest_list",
+  "version_major": 0,
+  "version_minor": 1,
+  "manifest_list": [
+    {
+      "description": "SCMI Comms Partition",
+      "manifest": "scmi_comms.yaml",
+      "output_path": "secure_fw/partitions/scmi",
+      "conditional": "TFM_PARTITION_SCMI_COMMS",
+      "version_major": 0,
+      "version_minor": 1,
+      "pid": 279,
+      "linker_pattern": {
+        "library_list": [
+           "*tfm_*partition_scmi_comms.*"
+        ]
+      }
+    }
+  ]
+}
diff --git a/partitions/scmi/scmi_hal.h b/partitions/scmi/scmi_hal.h
new file mode 100644
index 0000000..120be46
--- /dev/null
+++ b/partitions/scmi/scmi_hal.h
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+ *
+ */
+
+#ifndef __SCMI_HAL_H__
+#define __SCMI_HAL_H__
+
+#include "scmi_comms.h"
+#include "scmi_hal_defs.h"
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Initialize the SCMI transport shared memory area.
+ *
+ * \return SCMI_COMMS_SUCCESS on success, SCMI_COMMS_HARDWARE_ERROR on failure.
+ */
+scmi_comms_err_t scmi_hal_shared_memory_init(void);
+
+/**
+ * \brief Initialize the SCMI transport doorbells.
+ *
+ * \return SCMI_COMMS_SUCCESS on success, SCMI_COMMS_HARDWARE_ERROR on failure.
+ */
+scmi_comms_err_t scmi_hal_doorbell_init(void);
+
+/**
+ * \brief Ring the SCMI transport sender doorbell.
+ *
+ * \return SCMI_COMMS_SUCCESS on success, SCMI_COMMS_HARDWARE_ERROR on failure.
+ */
+scmi_comms_err_t scmi_hal_doorbell_ring(void);
+
+/**
+ * \brief Clear the SCMI transport receiver doorbell.
+ *
+ * \return SCMI_COMMS_SUCCESS on success, SCMI_COMMS_HARDWARE_ERROR on failure.
+ */
+scmi_comms_err_t scmi_hal_doorbell_clear(void);
+
+/**
+ * \brief Handle a system power state change.
+ *
+ * \param[in] agent_id      Identifier of the agent that caused the power state change
+ * \param[in] flags         Power state change flags
+ * \param[in] system_state  Power state that is being transitioned to
+ *
+ * \return SCMI status value.
+ */
+int32_t scmi_hal_sys_power_state(uint32_t agent_id, uint32_t flags,
+                                 uint32_t system_state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCMI_HAL_H__ */
diff --git a/partitions/scmi/scmi_protocol.h b/partitions/scmi/scmi_protocol.h
new file mode 100644
index 0000000..bca7a0b
--- /dev/null
+++ b/partitions/scmi/scmi_protocol.h
@@ -0,0 +1,104 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+ *
+ */
+
+#ifndef __SCMI_PROTOCOL_H__
+#define __SCMI_PROTOCOL_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SCMI status definitions
+ */
+#define SCMI_STATUS_SUCCESS            INT32_C(0)
+#define SCMI_STATUS_NOT_SUPPORTED      INT32_C(-1)
+#define SCMI_STATUS_INVALID_PARAMETERS INT32_C(-2)
+#define SCMI_STATUS_DENIED             INT32_C(-3)
+#define SCMI_STATUS_NOT_FOUND          INT32_C(-4)
+#define SCMI_STATUS_OUT_OF_RANGE       INT32_C(-5)
+#define SCMI_STATUS_BUSY               INT32_C(-6)
+#define SCMI_STATUS_COMMS_ERROR        INT32_C(-7)
+#define SCMI_STATUS_GENERIC_ERROR      INT32_C(-8)
+#define SCMI_STATUS_HARDWARE_ERROR     INT32_C(-9)
+#define SCMI_STATUS_PROTOCOL_ERROR     INT32_C(-10)
+
+/**
+ * SCMI message types
+ */
+#define SCMI_MESSAGE_TYPE_COMMAND      UINT8_C(0)
+#define SCMI_MESSAGE_TYPE_NOTIFICATION UINT8_C(3)
+
+/**
+ * SCMI protocol IDs
+ */
+#define SCMI_PROTOCOL_ID_SYS_POWER_STATE UINT8_C(0x12)
+
+/**
+ * SCMI message IDs
+ */
+#define SCMI_MESSAGE_ID_SYS_POWER_STATE_SET      UINT8_C(0x3)
+#define SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFY   UINT8_C(0x5)
+#define SCMI_MESSAGE_ID_SYS_POWER_STATE_NOTIFIER UINT8_C(0x0)
+
+/**
+ * SCMI system power state messages
+ */
+#define SCMI_SYS_POWER_STATE_FLAGS_GRACEFUL_POS 0
+#define SCMI_SYS_POWER_STATE_FLAGS_GRACEFUL_MASK \
+    (UINT32_C(0x1) << SCMI_SYS_POWER_STATE_FLAGS_GRACEFUL_POS)
+
+#define SCMI_SYS_POWER_STATE_SHUTDOWN   UINT32_C(0)
+#define SCMI_SYS_POWER_STATE_COLD_RESET UINT32_C(1)
+#define SCMI_SYS_POWER_STATE_WARM_RESET UINT32_C(2)
+#define SCMI_SYS_POWER_STATE_POWER_UP   UINT32_C(3)
+#define SCMI_SYS_POWER_STATE_SUSPEND    UINT32_C(4)
+
+/**
+ * \brief System power state set message payload.
+ */
+struct scmi_sys_power_state_set_t {
+    uint32_t flags;
+    uint32_t system_state;
+};
+
+/**
+ * \brief System power state set response payload.
+ */
+struct scmi_sys_power_state_set_response_t {
+    int32_t status;
+};
+
+/**
+ * \brief System power state notification subscription message payload.
+ */
+struct scmi_sys_power_state_notify_t {
+    uint32_t notify_enable; /**< Enable scmi_sys_power_state_notifier_t notifications */
+};
+
+/**
+ * \brief System power state notification subscription response payload.
+ */
+struct scmi_sys_power_state_notify_response_t {
+    int32_t status;
+};
+
+/**
+ * \brief System power state notification message payload.
+ */
+struct scmi_sys_power_state_notifier_t {
+    uint32_t agent_id; /**< ID of the agent that caused the power state transition */
+    uint32_t flags;
+    uint32_t system_state;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCMI_PROTOCOL_H__ */