aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRonald Cron <ronald.cron@arm.com>2018-06-05 09:31:39 +0200
committerRonald Cron <ronald.cron@arm.com>2018-06-08 11:46:47 +0200
commitb151958dbb2f37383f4d9a1f7802c36008d9fef2 (patch)
treefe20ebfb8c10facbfd028edefe601462ae3ee64c
parentfd3027b6fd17a4a33a685adb73f2acfcae9a2ced (diff)
Add support for SGM-775
Co-authored-by: Filipe Rinaldi <filipe.rinaldi@arm.com> Co-authored-by: Paul Beesley <paul.beesley@arm.com> Co-authored-by: Chris Kay <chris.kay@arm.com> Co-authored-by: Elieva Pignat <elieva.pignat@arm.com> Co-authored-by: Pedro Custodio <pedro.krewinkelcustodio@arm.com> Change-Id: Ic7524ad58a7c15d5b055e88a9719b2feee437f1d Signed-off-by: Ronald Cron <ronald.cron@arm.com>
-rw-r--r--module/bootloader/include/mod_bootloader.h100
-rw-r--r--module/bootloader/src/Makefile11
-rw-r--r--module/bootloader/src/mod_bootloader.c162
-rw-r--r--module/clock/include/mod_clock.h477
-rw-r--r--module/clock/src/Makefile11
-rw-r--r--module/clock/src/mod_clock.c460
-rw-r--r--module/css_clock/include/mod_css_clock.h192
-rw-r--r--module/css_clock/src/Makefile11
-rw-r--r--module/css_clock/src/mod_css_clock.c479
-rw-r--r--module/ddr_phy500/include/mod_ddr_phy500.h95
-rw-r--r--module/ddr_phy500/src/Makefile11
-rw-r--r--module/ddr_phy500/src/mod_ddr_phy500.c128
-rw-r--r--module/dmc500/include/mod_dmc500.h571
-rw-r--r--module/dmc500/src/Makefile11
-rw-r--r--module/dmc500/src/mod_dmc500.c390
-rw-r--r--module/dvfs/include/mod_dvfs.h271
-rw-r--r--module/dvfs/src/Makefile15
-rw-r--r--module/dvfs/src/mod_dvfs_domain_api.c265
-rw-r--r--module/dvfs/src/mod_dvfs_domain_api_private.h16
-rw-r--r--module/dvfs/src/mod_dvfs_event.c47
-rw-r--r--module/dvfs/src/mod_dvfs_event_private.h18
-rw-r--r--module/dvfs/src/mod_dvfs_module.c240
-rw-r--r--module/dvfs/src/mod_dvfs_module_private.h40
-rw-r--r--module/dvfs/src/mod_dvfs_private.h16
-rw-r--r--module/dvfs/src/mod_dvfs_util.c71
-rw-r--r--module/dvfs/src/mod_dvfs_util_private.h22
-rw-r--r--module/gtimer/include/mod_gtimer.h58
-rw-r--r--module/gtimer/src/Makefile11
-rw-r--r--module/gtimer/src/gtimer_reg.h90
-rw-r--r--module/gtimer/src/mod_gtimer.c295
-rw-r--r--module/mhu/include/internal/mhu.h29
-rw-r--r--module/mhu/include/mod_mhu.h53
-rw-r--r--module/mhu/src/Makefile11
-rw-r--r--module/mhu/src/mod_mhu.c256
-rw-r--r--module/mock_psu/include/mod_mock_psu.h69
-rw-r--r--module/mock_psu/src/Makefile13
-rw-r--r--module/mock_psu/src/mod_mock_psu_module.c87
-rw-r--r--module/mock_psu/src/mod_mock_psu_module_private.h22
-rw-r--r--module/mock_psu/src/mod_mock_psu_private.h17
-rw-r--r--module/mock_psu/src/mod_mock_psu_psu_driver_api.c71
-rw-r--r--module/mock_psu/src/mod_mock_psu_psu_driver_api_private.h15
-rw-r--r--module/msys_rom/include/mod_msys_rom.h69
-rw-r--r--module/msys_rom/src/Makefile11
-rw-r--r--module/msys_rom/src/mod_msys_rom.c217
-rw-r--r--module/pik_clock/include/mod_pik_clock.h269
-rw-r--r--module/pik_clock/src/Makefile11
-rw-r--r--module/pik_clock/src/mod_pik_clock.c767
-rw-r--r--module/pl011/include/mod_pl011.h55
-rw-r--r--module/pl011/src/Makefile11
-rw-r--r--module/pl011/src/mod_pl011.c244
-rw-r--r--module/pl011/src/pl011.h162
-rw-r--r--module/power_domain/include/mod_power_domain.h922
-rw-r--r--module/power_domain/src/Makefile11
-rw-r--r--module/power_domain/src/mod_power_domain.c1999
-rw-r--r--module/ppu_v0/include/mod_ppu_v0.h63
-rw-r--r--module/ppu_v0/src/Makefile11
-rw-r--r--module/ppu_v0/src/mod_ppu_v0.c316
-rw-r--r--module/ppu_v0/src/ppu_v0.c64
-rw-r--r--module/ppu_v0/src/ppu_v0.h144
-rw-r--r--module/ppu_v1/include/mod_ppu_v1.h166
-rw-r--r--module/ppu_v1/src/Makefile11
-rw-r--r--module/ppu_v1/src/mod_ppu_v1.c991
-rw-r--r--module/ppu_v1/src/ppu_v1.c360
-rw-r--r--module/ppu_v1/src/ppu_v1.h467
-rw-r--r--module/psu/include/mod_psu.h257
-rw-r--r--module/psu/src/Makefile14
-rw-r--r--module/psu/src/mod_psu_device_api.c224
-rw-r--r--module/psu/src/mod_psu_device_api_private.h16
-rw-r--r--module/psu/src/mod_psu_event.c98
-rw-r--r--module/psu/src/mod_psu_event_private.h29
-rw-r--r--module/psu/src/mod_psu_module.c128
-rw-r--r--module/psu/src/mod_psu_module_private.h28
-rw-r--r--module/psu/src/mod_psu_private.h16
-rw-r--r--module/reg_sensor/include/mod_reg_sensor.h38
-rw-r--r--module/reg_sensor/src/Makefile11
-rw-r--r--module/reg_sensor/src/mod_reg_sensor.c101
-rw-r--r--module/scmi/include/internal/mod_scmi.h63
-rw-r--r--module/scmi/include/internal/scmi.h171
-rw-r--r--module/scmi/include/internal/scmi_base.h90
-rw-r--r--module/scmi/include/mod_scmi.h425
-rw-r--r--module/scmi/src/Makefile11
-rw-r--r--module/scmi/src/mod_scmi.c794
-rw-r--r--module/scmi_apcore/doc/scmi_apcore.md153
-rw-r--r--module/scmi_apcore/include/internal/scmi_apcore.h71
-rw-r--r--module/scmi_apcore/include/mod_scmi_apcore.h75
-rw-r--r--module/scmi_apcore/src/Makefile11
-rw-r--r--module/scmi_apcore/src/mod_scmi_apcore.c432
-rw-r--r--module/scmi_clock/include/internal/scmi_clock.h191
-rw-r--r--module/scmi_clock/include/mod_scmi_clock.h111
-rw-r--r--module/scmi_clock/src/Makefile11
-rw-r--r--module/scmi_clock/src/mod_scmi_clock.c763
-rw-r--r--module/scmi_perf/include/internal/scmi_perf.h248
-rw-r--r--module/scmi_perf/include/mod_scmi_perf.h63
-rw-r--r--module/scmi_perf/src/Makefile11
-rw-r--r--module/scmi_perf/src/mod_scmi_perf.c634
-rw-r--r--module/scmi_power_domain/include/internal/scmi_power_domain.h121
-rw-r--r--module/scmi_power_domain/src/Makefile11
-rw-r--r--module/scmi_power_domain/src/mod_scmi_power_domain.c605
-rw-r--r--module/scmi_sensor/include/internal/scmi_sensor.h164
-rw-r--r--module/scmi_sensor/src/Makefile11
-rw-r--r--module/scmi_sensor/src/mod_scmi_sensor.c462
-rw-r--r--module/scmi_system_power/include/internal/scmi_system_power.h86
-rw-r--r--module/scmi_system_power/include/mod_scmi_system_power.h69
-rw-r--r--module/scmi_system_power/src/Makefile11
-rw-r--r--module/scmi_system_power/src/mod_scmi_system_power.c443
-rw-r--r--module/sds/include/mod_sds.h187
-rw-r--r--module/sds/src/Makefile11
-rw-r--r--module/sds/src/mod_sds.c620
-rw-r--r--module/sensor/include/mod_sensor.h234
-rw-r--r--module/sensor/src/Makefile11
-rw-r--r--module/sensor/src/mod_sensor.c182
-rw-r--r--module/smt/include/internal/smt.h41
-rw-r--r--module/smt/include/mod_smt.h123
-rw-r--r--module/smt/src/Makefile11
-rw-r--r--module/smt/src/mod_smt.c563
-rw-r--r--module/system_pll/include/mod_system_pll.h93
-rw-r--r--module/system_pll/src/Makefile11
-rw-r--r--module/system_pll/src/mod_system_pll.c336
-rw-r--r--module/system_power/include/mod_system_power.h126
-rw-r--r--module/system_power/src/Makefile11
-rw-r--r--module/system_power/src/mod_system_power.c365
-rw-r--r--module/timer/include/mod_timer.h294
-rw-r--r--module/timer/src/Makefile11
-rw-r--r--module/timer/src/mod_timer.c653
-rw-r--r--product/sgm775/include/fmw_cmsis.h30
-rw-r--r--product/sgm775/include/sgm775_core.h15
-rw-r--r--product/sgm775/include/sgm775_irq.h178
-rw-r--r--product/sgm775/include/sgm775_mhu.h21
-rw-r--r--product/sgm775/include/sgm775_mmap.h101
-rw-r--r--product/sgm775/include/sgm775_mmap_scp.h19
-rw-r--r--product/sgm775/include/sgm775_pik.h29
-rw-r--r--product/sgm775/include/sgm775_pik_cpu.h145
-rw-r--r--product/sgm775/include/sgm775_pik_debug.h49
-rw-r--r--product/sgm775/include/sgm775_pik_dpu.h51
-rw-r--r--product/sgm775/include/sgm775_pik_gpu.h45
-rw-r--r--product/sgm775/include/sgm775_pik_scp.h63
-rw-r--r--product/sgm775/include/sgm775_pik_system.h69
-rw-r--r--product/sgm775/include/sgm775_pik_vpu.h43
-rw-r--r--product/sgm775/include/sgm775_scmi.h29
-rw-r--r--product/sgm775/include/sgm775_sds.h149
-rw-r--r--product/sgm775/include/sgm775_ssc.h42
-rw-r--r--product/sgm775/include/software_mmap.h128
-rw-r--r--product/sgm775/include/system_clock.h28
-rw-r--r--product/sgm775/include/system_mmap.h61
-rw-r--r--product/sgm775/include/system_mmap_scp.h21
-rw-r--r--product/sgm775/module/sgm775_system/include/mod_sgm775_system.h44
-rw-r--r--product/sgm775/module/sgm775_system/src/Makefile11
-rw-r--r--product/sgm775/module/sgm775_system/src/mod_sgm775_system.c59
-rw-r--r--product/sgm775/product.mk10
-rw-r--r--product/sgm775/scp_ramfw/RTX_Config.h56
-rw-r--r--product/sgm775/scp_ramfw/clock_devices.h26
-rw-r--r--product/sgm775/scp_ramfw/config_clock.c119
-rw-r--r--product/sgm775/scp_ramfw/config_css_clock.c302
-rw-r--r--product/sgm775/scp_ramfw/config_ddr_phy500.c63
-rw-r--r--product/sgm775/scp_ramfw/config_dmc500.c221
-rw-r--r--product/sgm775/scp_ramfw/config_dvfs.c129
-rw-r--r--product/sgm775/scp_ramfw/config_dvfs.h18
-rw-r--r--product/sgm775/scp_ramfw/config_log.c63
-rw-r--r--product/sgm775/scp_ramfw/config_mhu.c54
-rw-r--r--product/sgm775/scp_ramfw/config_mock_psu.c52
-rw-r--r--product/sgm775/scp_ramfw/config_pik_clock.c295
-rw-r--r--product/sgm775/scp_ramfw/config_power_domain.c258
-rw-r--r--product/sgm775/scp_ramfw/config_power_domain.h21
-rw-r--r--product/sgm775/scp_ramfw/config_ppu_v0.c92
-rw-r--r--product/sgm775/scp_ramfw/config_ppu_v0.h22
-rw-r--r--product/sgm775/scp_ramfw/config_ppu_v1.c105
-rw-r--r--product/sgm775/scp_ramfw/config_psu.c58
-rw-r--r--product/sgm775/scp_ramfw/config_scmi.c77
-rw-r--r--product/sgm775/scp_ramfw/config_scmi_apcore.c31
-rw-r--r--product/sgm775/scp_ramfw/config_scmi_clock.c70
-rw-r--r--product/sgm775/scp_ramfw/config_scmi_perf.c44
-rw-r--r--product/sgm775/scp_ramfw/config_scmi_system_power.c19
-rw-r--r--product/sgm775/scp_ramfw/config_sds.c64
-rw-r--r--product/sgm775/scp_ramfw/config_sensor.c74
-rw-r--r--product/sgm775/scp_ramfw/config_smt.c78
-rw-r--r--product/sgm775/scp_ramfw/config_system_pll.c118
-rw-r--r--product/sgm775/scp_ramfw/config_system_power.c62
-rw-r--r--product/sgm775/scp_ramfw/config_timer.c71
-rw-r--r--product/sgm775/scp_ramfw/firmware.mk72
-rw-r--r--product/sgm775/scp_ramfw/fmw_memory.ld.S21
-rw-r--r--product/sgm775/scp_ramfw/rtx_config.c79
-rw-r--r--product/sgm775/scp_romfw/clock_devices.h28
-rw-r--r--product/sgm775/scp_romfw/config_bootloader.c26
-rw-r--r--product/sgm775/scp_romfw/config_clock.c126
-rw-r--r--product/sgm775/scp_romfw/config_css_clock.c134
-rw-r--r--product/sgm775/scp_romfw/config_log.c63
-rw-r--r--product/sgm775/scp_romfw/config_msys_rom.c24
-rw-r--r--product/sgm775/scp_romfw/config_pik_clock.c306
-rw-r--r--product/sgm775/scp_romfw/config_ppu_v0.c48
-rw-r--r--product/sgm775/scp_romfw/config_ppu_v1.c59
-rw-r--r--product/sgm775/scp_romfw/config_sds.c122
-rw-r--r--product/sgm775/scp_romfw/config_system_pll.c91
-rw-r--r--product/sgm775/scp_romfw/config_timer.c68
-rw-r--r--product/sgm775/scp_romfw/firmware.mk38
-rw-r--r--product/sgm775/scp_romfw/fmw_memory.ld.S36
-rw-r--r--product/sgm775/src/sgm775_core.c14
-rwxr-xr-xtools/ci.py6
197 files changed, 29185 insertions, 0 deletions
diff --git a/module/bootloader/include/mod_bootloader.h b/module/bootloader/include/mod_bootloader.h
new file mode 100644
index 00000000..47d22575
--- /dev/null
+++ b/module/bootloader/include/mod_bootloader.h
@@ -0,0 +1,100 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_BOOTLOADER_H
+#define MOD_BOOTLOADER_H
+
+#include <stdint.h>
+#include <fwk_element.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupBoot Bootloader
+ *
+ * \details A service module providing support for loading firmware images.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Module configuration.
+ */
+struct mod_bootloader_config {
+ /*!
+ * Base address of the memory region that the image will be copied from.
+ * Note that this is not the base address of the image to be loaded. This
+ * base address must be combined with an offset value, provided by
+ * application processor firmware via a Shared Data Storage region.
+ */
+ uintptr_t source_base;
+
+ /*!
+ * The number of bytes of storage available at the source location.
+ * This value implicitly limits the maximum size of the image that can be
+ * copied.
+ */
+ size_t source_size;
+
+ /*! Base address of the location that the image will be copied to. */
+ uintptr_t destination_base;
+
+ /*!
+ * The number of bytes of storage available at the destination location.
+ * This value implicitly limits the maximum size of the image that can be
+ * copied.
+ */
+ size_t destination_size;
+
+ /*!
+ * Identifier of the SDS structure containing image metadata, such as the
+ * size of the image and its offset from source_base.
+ */
+ uint32_t sds_struct_id;
+};
+
+/*!
+ * \brief Bootloader interface.
+ */
+struct mod_bootloader_api {
+ /*!
+ * \brief Copy a RAM Firmware image from a source location to a destination
+ * (which is expected to be the SCP SRAM).
+ *
+ * \param config Pointer to an scp_bootloader_config structure containing
+ * settings that control where the image is copied from and to.
+ *
+ * \retval FWK_SUCCESS The RAM Firmware image was copied successfully.
+ * \retval FWK_E_ALIGN The given image offset is not properly aligned.
+ * \retval FWK_E_DATA The image did not pass checksum validation.
+ * \retval FWK_E_PARAM The config structure pointer is invalid.
+ * \retval FWK_E_PARAM One or more config structure fields are invalid.
+ * \retval FWK_E_SIZE The given image size is below the minimum possible
+ * size.
+ * \retval FWK_E_SIZE The image is too large for the destination memory
+ * area.
+ * \retval FWK_E_SIZE The given image offset is beyond the source memory
+ * area.
+ * \retval FWK_E_SIZE The header's size field does not match the given size.
+ * \retval FWK_E_SIZE Part of the image lies outside of the source memory
+ * area.
+ */
+ int (*load_image)(void);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_BOOTLOADER_H */
diff --git a/module/bootloader/src/Makefile b/module/bootloader/src/Makefile
new file mode 100644
index 00000000..effaca48
--- /dev/null
+++ b/module/bootloader/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Bootloader
+BS_LIB_SOURCES := mod_bootloader.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/bootloader/src/mod_bootloader.c b/module/bootloader/src/mod_bootloader.c
new file mode 100644
index 00000000..f2d34113
--- /dev/null
+++ b/module/bootloader/src/mod_bootloader.c
@@ -0,0 +1,162 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_bootloader.h>
+#include <mod_sds.h>
+
+/* Offset within the SDS structure where the valid flag is located. */
+#define BOOTLOADER_STRUCT_VALID_POS 0
+/* Offset within the SDS structure where the image offset is located. */
+#define BOOTLOADER_STRUCT_IMAGE_OFFSET_POS 4
+/* Offset within the SDS structure where the image size is located. */
+#define BOOTLOADER_STRUCT_IMAGE_SIZE_POS 8
+
+#define IMAGE_FLAGS_VALID_MASK 0x1
+
+/* Module context */
+struct bootloader_ctx {
+ const struct mod_bootloader_config *module_config;
+ const struct mod_sds_api *sds_api;
+};
+
+static struct bootloader_ctx module_ctx;
+
+/*
+ * Module API
+ */
+
+static int load_image(void)
+{
+ int status;
+ void *image_base;
+ uint32_t image_flags;
+ uint32_t image_offset;
+ uint32_t image_size;
+
+ if (module_ctx.module_config->source_base == 0)
+ return FWK_E_PARAM;
+ if (module_ctx.module_config->destination_base == 0)
+ return FWK_E_PARAM;
+ if (module_ctx.module_config->source_size == 0)
+ return FWK_E_PARAM;
+ if (module_ctx.module_config->destination_size == 0)
+ return FWK_E_PARAM;
+ if (module_ctx.module_config->sds_struct_id == 0)
+ return FWK_E_PARAM;
+
+ /*
+ * Wait until Trusted Firmware writes the image metadata and sets the
+ * data valid flag.
+ */
+ while (true) {
+ status = module_ctx.sds_api->struct_read(
+ module_ctx.module_config->sds_struct_id,
+ BOOTLOADER_STRUCT_VALID_POS, &image_flags, sizeof(image_flags));
+
+ if (status != FWK_SUCCESS)
+ return status;
+ if (image_flags & IMAGE_FLAGS_VALID_MASK)
+ break;
+ }
+
+ /* The image metadata from Trusted Firmware can now be read and validated */
+ status = module_ctx.sds_api->struct_read(
+ module_ctx.module_config->sds_struct_id,
+ BOOTLOADER_STRUCT_IMAGE_OFFSET_POS, &image_offset,
+ sizeof(image_offset));
+
+ if (status != FWK_SUCCESS)
+ return status;
+ status = module_ctx.sds_api->struct_read(
+ module_ctx.module_config->sds_struct_id,
+ BOOTLOADER_STRUCT_IMAGE_SIZE_POS, &image_size, sizeof(image_size));
+
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (image_size == 0)
+ return FWK_E_SIZE;
+ if ((image_offset % 4) != 0)
+ return FWK_E_ALIGN;
+ if (image_offset > module_ctx.module_config->source_size)
+ return FWK_E_SIZE;
+
+ /* Read the image header now that its base address is known */
+ image_base = (void *)((uint8_t *)module_ctx.module_config->source_base +
+ image_offset);
+
+ memcpy((void *)module_ctx.module_config->destination_base, image_base,
+ image_size);
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_bootloader_api bootloader_api = {
+ .load_image = load_image,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int bootloader_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ /* Store a pointer to the module config within the module context */
+ module_ctx.module_config = data;
+
+ return FWK_SUCCESS;
+}
+
+static int bootloader_bind(fwk_id_t id, unsigned int call_number)
+{
+ int status;
+
+ /* Only the first round of binding is used (round number is zero-indexed) */
+ if (call_number == 1)
+ return FWK_SUCCESS;
+
+ if (fwk_module_is_valid_element_id(id))
+ /* No element-level binding required */
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SDS),
+ FWK_ID_API(FWK_MODULE_IDX_SDS, 0),
+ &module_ctx.sds_api);
+
+ return status;
+}
+
+static int bootloader_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
+ fwk_id_t api_id, const void **api)
+{
+ if (api == NULL)
+ return FWK_E_PARAM;
+
+ if (!fwk_module_is_valid_module_id(id))
+ return FWK_E_PARAM;
+
+ *api = &bootloader_api;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_bootloader = {
+ .name = "Bootloader",
+ .type = FWK_MODULE_TYPE_SERVICE,
+ .api_count = 1,
+ .event_count = 0,
+ .init = bootloader_init,
+ .bind = bootloader_bind,
+ .process_bind_request = bootloader_process_bind_request,
+};
diff --git a/module/clock/include/mod_clock.h b/module/clock/include/mod_clock.h
new file mode 100644
index 00000000..1e3e5dc9
--- /dev/null
+++ b/module/clock/include/mod_clock.h
@@ -0,0 +1,477 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_CLOCK_H
+#define MOD_CLOCK_H
+
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupClock Clock HAL
+ *
+ * \details A Hardware Abstraction Library for configuring clock devices.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Clock states.
+ */
+enum mod_clock_state {
+ /*! The clock is stopped */
+ MOD_CLOCK_STATE_STOPPED,
+
+ /*! The clock is running */
+ MOD_CLOCK_STATE_RUNNING,
+
+ /*! Number of defined clock states */
+ MOD_CLOCK_STATE_COUNT
+};
+
+/*!
+ * \brief Clock notification indices.
+ */
+enum mod_clock_notification_idx {
+ /*! The running state of a clock changed */
+ MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGED,
+
+ /*! The running state of a clock is about to change */
+ MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING,
+
+ /*! Number of defined notifications */
+ MOD_CLOCK_NOTIFICATION_IDX_COUNT
+};
+
+#if BUILD_HAS_MOD_CLOCK
+/*!
+ * \brief Identifier for the \ref MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGED
+ * notification.
+ */
+static const fwk_id_t mod_clock_notification_id_state_changed =
+ FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGED);
+
+/*!
+ * \brief Identifier for the \ref
+ * MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING notification
+ */
+static const fwk_id_t mod_clock_notification_id_state_change_pending =
+ FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING);
+#endif
+
+/*!
+ * \brief Event parameters shared by the
+ * \ref MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGED and
+ * \ref MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING notifications.
+ */
+struct clock_notification_params {
+ /*!
+ * The state that the clock has transitioned to, or is about
+ * to transition to.
+ */
+ enum mod_clock_state new_state;
+};
+
+/*!
+ * \brief Response parameters for the
+ * \ref MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING notification.
+ */
+struct clock_state_change_pending_resp_params {
+ /*!
+ * The status returned by the notified subscriber on processing the
+ * \ref MOD_CLOCK_NOTIFICATION_IDX_STATE_CHANGE_PENDING notification.
+ */
+ int status;
+};
+
+/*!
+ * \brief APIs that the module makes available to entities requesting binding.
+ */
+enum mod_clock_api_type {
+ /*! Clock HAL */
+ MOD_CLOCK_API_TYPE_HAL,
+ MOD_CLOCK_API_COUNT,
+};
+
+/*!
+ * \brief Clock rate types.
+ */
+enum mod_clock_rate_type {
+ /*! The clock has a discrete set of rates that it can attain */
+ MOD_CLOCK_RATE_TYPE_DISCRETE,
+
+ /*! The clock has a continuous range of rates with a constant step */
+ MOD_CLOCK_RATE_TYPE_CONTINUOUS,
+};
+
+/*!
+ * \brief Clock rounding modes.
+ */
+enum mod_clock_round_mode {
+ /*!
+ * Do not perform any rounding. Any rate that is not precise and attainable
+ * will be rejected.
+ */
+ MOD_CLOCK_ROUND_MODE_NONE,
+
+ /*! Round to the closest attainable rate, whether higher or lower */
+ MOD_CLOCK_ROUND_MODE_NEAREST,
+
+ /*! Round to the closest attainable higher rate */
+ MOD_CLOCK_ROUND_MODE_UP,
+
+ /*! Round to the closest attainable lower rate */
+ MOD_CLOCK_ROUND_MODE_DOWN,
+};
+
+/*!
+ * \brief Clock module configuration data.
+ */
+struct mod_clock_config {
+ /*!
+ * \brief Identifier of a notification to subscribe clock devices to in
+ * order to receive notifications of power domain transitions that have
+ * already occurred.
+ *
+ * \note May be \ref FWK_ID_NONE to disable this functionality for all
+ * elements.
+ */
+ const fwk_id_t pd_transition_notification_id;
+
+ /*!
+ * \brief Identifier of a notification to subscribe clock devices to in
+ * order to receive notifications of power domain transitions that are
+ * about to occur.
+ *
+ * \note May be \ref FWK_ID_NONE to disable this functionality for all
+ * elements.
+ */
+ const fwk_id_t pd_pre_transition_notification_id;
+};
+
+/*!
+ * \brief Clock element configuration data.
+ */
+struct mod_clock_dev_config {
+ /*! Reference to the device element within the associated driver module */
+ const fwk_id_t driver_id;
+
+ /*! Reference to the API provided by the device driver module */
+ const fwk_id_t api_id;
+
+ /*!
+ * \brief Reference to the element or module that is the source of the
+ * power domain notification.
+ *
+ * \details If the clock belongs to the always-on (AON) power domain (i.e.
+ * it is always running), or if there are no actions to be performed
+ * when the clock's power domain changes state, then this identifier
+ * must be FWK_ID_NONE. In this case the clock will not be registered
+ * to receive notifications from the power domain module.
+ */
+ fwk_id_t pd_source_id;
+};
+
+/*!
+ * \brief Range of supported rates for a clock.
+ */
+struct mod_clock_range {
+ /*! The type of rates the clock provides (discrete or continuous) */
+ enum mod_clock_rate_type rate_type;
+
+ /*! Minimum rate (in Hz) */
+ uint64_t min;
+
+ /*! Maximum rate (in Hz) */
+ uint64_t max;
+
+ /*!
+ * The number of Hertz by which the rate can be incremented at each step
+ * throughout the clock's range. Valid only when rate_type is equal to
+ * \ref mod_clock_rate_type.MOD_CLOCK_RATE_TYPE_CONTINUOUS, as clocks that
+ * use \ref mod_clock_rate_type.MOD_CLOCK_RATE_TYPE_DISCRETE may not have a
+ * regular step between their rates.
+ */
+ uint64_t step;
+
+ /*! The number of unique rates that the clock can attain */
+ uint64_t rate_count;
+};
+
+/*!
+ * \brief Clock properties exposed via the get_info() API function.
+ *
+ * \details This structure is intended to store clock information that is static
+ * and which does not change during runtime. Dynamic information, such as
+ * the current clock state, are exposed through functions in the clock and
+ * clock driver APIs.
+ */
+struct mod_clock_info {
+ /*! Human-friendly clock name */
+ const char *name;
+
+ /*! Range of supported clock rates */
+ struct mod_clock_range range;
+
+ /*! Number of discrete rates supported */
+ uint64_t rate_count;
+};
+
+/*!
+ * \brief Clock driver interface.
+ */
+struct mod_clock_drv_api {
+ /*! Name of the driver */
+ const char *name;
+
+ /*!
+ * \brief Set a new clock rate by providing a frequency in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate The desired frequency in Hertz.
+ *
+ * \param round_mode The type of rounding to perform, if required, to
+ * achieve the given rate.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_rate)(fwk_id_t clock_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode);
+
+ /*!
+ * \brief Get the current rate of a clock in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate Pointer to storage for the current clock rate in Hertz.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate)(fwk_id_t clock_id, uint64_t *rate);
+
+ /*!
+ * \brief Get a clock rate in Hertz from an index into the clock's range.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate_index The index into the clock's range to get the rate of.
+ *
+ * \param rate Pointer to storage for the rate, in Hertz,
+ * corresponding to the index.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate_from_index)(fwk_id_t clock_id, unsigned int rate_index,
+ uint64_t *rate);
+
+ /*!
+ * \brief Set the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state One of the valid clock states.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_state)(fwk_id_t clock_id, enum mod_clock_state state);
+
+ /*!
+ * \brief Get the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state Pointer to storage for the current clock state.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_state)(fwk_id_t clock_id, enum mod_clock_state *state);
+
+ /*!
+ * \brief Get the range of rates that the clock supports.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state Pointer to storage for the clock range structure.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_range)(fwk_id_t clock_id, struct mod_clock_range *range);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain is
+ * about to change.
+ *
+ * \details This function will be called prior to the change in power
+ * state occurring so that the clock driver implementing this API is
+ * able to perform any required preparatory work beforehand.
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param current_state The current power state that the clock's power
+ * domain will transition away from.
+ *
+ * \param new_state The power state that the clock's power domain will
+ * transition to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_pending_power_transition)(
+ fwk_id_t clock_id,
+ unsigned int current_state,
+ unsigned int new_state);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain
+ * has changed.
+ *
+ * \details This function will be called after the change in power state
+ * has occurred. The driver can take any appropriate actions that are
+ * required to accommodate the new state. The transition can be to a
+ * deeper power state (e.g. ON->OFF) or to a shallower power state
+ * (e.g. OFF->ON).
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state The power state that the clock's power domain transitioned
+ * to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_power_transition)(fwk_id_t clock_id, unsigned int state);
+};
+
+/*!
+ * \brief Clock interface.
+ */
+struct mod_clock_api {
+ /*!
+ * \brief Set a new clock rate by providing a frequency in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate The desired frequency in Hertz.
+ *
+ * \param round_mode The type of rounding to perform, if required, to
+ * achieve the given rate.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_rate)(fwk_id_t clock_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode);
+
+ /*!
+ * \brief Get the current rate of a clock in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate Pointer to storage for the current clock rate in Hertz.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \retval FWK_E_PARAM The rate pointer was NULL.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate)(fwk_id_t clock_id, uint64_t *rate);
+
+ /*!
+ * \brief Get a clock rate in Hertz from an index into the clock's range.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate_index The index into the clock's range to get the rate of.
+ *
+ * \param rate Pointer to storage for the rate, in Hertz,
+ * corresponding to the index.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \retval FWK_E_PARAM The rate pointer was NULL.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate_from_index)(fwk_id_t clock_id, unsigned int rate_index,
+ uint64_t *rate);
+
+ /*!
+ * \brief Set the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state One of the valid clock states.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_state)(fwk_id_t clock_id, enum mod_clock_state state);
+
+ /*!
+ * \brief Get the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state Pointer to storage for the current clock state.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \retval FWK_E_PARAM The state pointer was NULL.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_state)(fwk_id_t clock_id, enum mod_clock_state *state);
+
+ /*!
+ * \brief Get information about a clock's fixed properties.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param info Pointer to storage for the clock device properties.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The clock identifier was invalid.
+ * \retval FWK_E_PARAM The info pointer was NULL.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_info)(fwk_id_t clock_id, struct mod_clock_info *info);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_CLOCK_H */
diff --git a/module/clock/src/Makefile b/module/clock/src/Makefile
new file mode 100644
index 00000000..fd66eff2
--- /dev/null
+++ b/module/clock/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Clock HAL
+BS_LIB_SOURCES := mod_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/clock/src/mod_clock.c b/module/clock/src/mod_clock.c
new file mode 100644
index 00000000..1c05b03b
--- /dev/null
+++ b/module/clock/src/mod_clock.c
@@ -0,0 +1,460 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_thread.h>
+#include <fwk_notification.h>
+#include <mod_clock.h>
+#include <mod_power_domain.h>
+
+/* Device context */
+struct clock_dev_ctx {
+ const struct mod_clock_dev_config *config;
+ struct mod_clock_drv_api *api;
+ unsigned int pd_pre_power_transition_notification_cookie;
+ unsigned int transition_pending_notifications_sent;
+ unsigned int transition_pending_response_status;
+};
+
+/* Module context */
+struct clock_ctx {
+ const struct mod_clock_config *config;
+ struct clock_dev_ctx *dev_ctx_table;
+ unsigned int dev_count;
+};
+
+struct clock_ctx module_ctx;
+
+/*
+ * Module API functions
+ */
+
+static int clock_set_rate(fwk_id_t clock_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ return ctx->api->set_rate(ctx->config->driver_id, rate, round_mode);
+}
+
+static int clock_get_rate(fwk_id_t clock_id, uint64_t *rate)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ return ctx->api->get_rate(ctx->config->driver_id, rate);
+}
+
+static int clock_get_rate_from_index(fwk_id_t clock_id, unsigned int rate_index,
+ uint64_t *rate)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ return ctx->api->get_rate_from_index(ctx->config->driver_id, rate_index,
+ rate);
+}
+
+static int clock_set_state(fwk_id_t clock_id, enum mod_clock_state state)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ return ctx->api->set_state(ctx->config->driver_id, state);
+}
+
+static int clock_get_state(fwk_id_t clock_id, enum mod_clock_state *state)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (state == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ return ctx->api->get_state(ctx->config->driver_id, state);
+}
+
+static int clock_get_info(fwk_id_t clock_id, struct mod_clock_info *info)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (info == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+
+ status = ctx->api->get_range(ctx->config->driver_id, &info->range);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ info->name = fwk_module_get_name(clock_id);
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_api clock_api = {
+ .set_rate = clock_set_rate,
+ .get_rate = clock_get_rate,
+ .get_rate_from_index = clock_get_rate_from_index,
+ .set_state = clock_set_state,
+ .get_state = clock_get_state,
+ .get_info = clock_get_info,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int clock_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ const struct mod_clock_config *config = data;
+
+ module_ctx.dev_count = element_count;
+
+ if (element_count == 0)
+ return FWK_SUCCESS;
+
+ if (config == NULL)
+ return FWK_E_PARAM;
+
+ module_ctx.config = config;
+ module_ctx.dev_ctx_table = fwk_mm_calloc(element_count,
+ sizeof(struct clock_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int clock_dev_init(fwk_id_t element_id, unsigned int sub_element_count,
+ const void *data)
+{
+ struct clock_dev_ctx *ctx;
+ const struct mod_clock_dev_config *dev_config = data;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+ ctx->config = dev_config;
+
+ return FWK_SUCCESS;
+}
+
+static int clock_bind(fwk_id_t id, unsigned int round)
+{
+ struct clock_dev_ctx *ctx;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ /* Only element binding is supported */
+ return FWK_SUCCESS;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(id);
+
+ return fwk_module_bind(ctx->config->driver_id,
+ ctx->config->api_id,
+ &ctx->api);
+
+}
+
+static int clock_start(fwk_id_t id)
+{
+ int status;
+ struct clock_dev_ctx *ctx;
+
+ /* Nothing to be done at the module level */
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(id);
+
+ if (fwk_id_is_type(ctx->config->pd_source_id, FWK_ID_TYPE_NONE))
+ return FWK_SUCCESS;
+
+ if ((ctx->api->process_power_transition != NULL) &&
+ (fwk_id_is_type(
+ module_ctx.config->pd_transition_notification_id,
+ FWK_ID_TYPE_NOTIFICATION))) {
+ status = fwk_notification_subscribe(
+ module_ctx.config->pd_transition_notification_id,
+ ctx->config->pd_source_id,
+ id);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ if ((ctx->api->process_pending_power_transition != NULL) &&
+ (fwk_id_is_type(
+ module_ctx.config->pd_pre_transition_notification_id,
+ FWK_ID_TYPE_NOTIFICATION))) {
+ status = fwk_notification_subscribe(
+ module_ctx.config->pd_pre_transition_notification_id,
+ ctx->config->pd_source_id,
+ id);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int clock_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ if (fwk_id_get_api_idx(api_id) != MOD_CLOCK_API_TYPE_HAL)
+ /* The requested API is not supported. */
+ return FWK_E_ACCESS;
+
+ *api = &clock_api;
+ return FWK_SUCCESS;
+}
+
+static int clock_process_pd_pre_transition_notification(
+ struct clock_dev_ctx *ctx,
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ int status;
+ struct mod_pd_power_state_pre_transition_notification_params *pd_params;
+ struct mod_pd_power_state_pre_transition_notification_resp_params
+ *pd_resp_params;
+ struct clock_notification_params *out_params;
+ struct fwk_event outbound_event = {
+ .response_requested = true,
+ .id = mod_clock_notification_id_state_change_pending,
+ };
+
+ pd_params = (struct mod_pd_power_state_pre_transition_notification_params *)
+ event->params;
+ pd_resp_params =
+ (struct mod_pd_power_state_pre_transition_notification_resp_params *)
+ resp_event->params;
+
+ assert(ctx->api->process_pending_power_transition != NULL);
+ status = ctx->api->process_pending_power_transition(
+ ctx->config->driver_id,
+ pd_params->current_state,
+ pd_params->target_state);
+
+ /*
+ * The response to the notification should initially be the overall result
+ * of the downwards propagation of the state change through the driver(s).
+ */
+ pd_resp_params->status = status;
+
+ if (status != FWK_SUCCESS) {
+ ctx->transition_pending_notifications_sent = 0;
+ return status;
+ }
+
+ ctx->transition_pending_response_status = FWK_SUCCESS;
+ out_params =
+ (struct clock_notification_params *)outbound_event.params;
+
+ /*
+ * For now it is sufficient to assume that a PD ON state implies that
+ * the clock is running and any other state implies that the clock has
+ * stopped. This will likely need to be revisited so that the clock
+ * driver can influence the resulting state that is propagated via the
+ * outgoing notification.
+ */
+ out_params->new_state = (pd_params->target_state == MOD_PD_STATE_ON)
+ ? MOD_CLOCK_STATE_RUNNING
+ : MOD_CLOCK_STATE_STOPPED;
+
+ /* Notify subscribers of the pending clock state change */
+ status = fwk_notification_notify(
+ &outbound_event,
+ &(ctx->transition_pending_notifications_sent));
+
+ if (ctx->transition_pending_notifications_sent > 0) {
+ /* There are one or more subscribers that must respond */
+ resp_event->is_delayed_response = true;
+ ctx->pd_pre_power_transition_notification_cookie = event->cookie;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int clock_process_pd_transition_notification(
+ struct clock_dev_ctx *ctx,
+ const struct fwk_event *event)
+{
+ int status;
+ unsigned int transition_notifications_sent;
+ struct mod_pd_power_state_transition_notification_params *pd_params;
+ struct clock_notification_params* out_params;
+ struct fwk_event outbound_event = {
+ .response_requested = false,
+ .id = mod_clock_notification_id_state_changed,
+ };
+
+ pd_params =
+ (struct mod_pd_power_state_transition_notification_params *)event
+ ->params;
+
+ assert(ctx->api->process_power_transition != NULL);
+ status = ctx->api->process_power_transition(
+ ctx->config->driver_id, pd_params->state);
+
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Notify subscribers of the clock state change */
+ out_params = (struct clock_notification_params *)outbound_event.params;
+ if (pd_params->state == MOD_PD_STATE_ON)
+ out_params->new_state = MOD_CLOCK_STATE_RUNNING;
+ else
+ out_params->new_state = MOD_CLOCK_STATE_STOPPED;
+
+ status = fwk_notification_notify(
+ &outbound_event, &(transition_notifications_sent));
+
+ return FWK_SUCCESS;
+}
+
+static int clock_process_notification_response(
+ struct clock_dev_ctx *ctx,
+ const struct fwk_event *event)
+{
+ struct clock_state_change_pending_resp_params *resp_params;
+ struct mod_pd_power_state_pre_transition_notification_resp_params
+ *pd_resp_params;
+ struct fwk_event pd_response_event = {
+ .id = module_ctx.config->pd_pre_transition_notification_id,
+ .target_id = ctx->config->pd_source_id,
+ .cookie = ctx->pd_pre_power_transition_notification_cookie,
+ .is_notification = true,
+ .is_response = true,
+ .is_delayed_response = true,
+ };
+
+ assert(fwk_id_is_equal(event->id,
+ mod_clock_notification_id_state_change_pending));
+
+ /* At least one notification response must be outstanding */
+ if (ctx->transition_pending_notifications_sent == 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ resp_params =
+ (struct clock_state_change_pending_resp_params *)event->params;
+
+ /*
+ * Change the status used in the response to the power domain notification
+ * if this response has a non-success status code, otherwise leave the
+ * existing value alone. By default the status is FWK_SUCCESS and changing
+ * it to an error status code will veto the power domain state transition
+ * that is pending.
+ */
+ if (resp_params->status != FWK_SUCCESS)
+ ctx->transition_pending_response_status = resp_params->status;
+
+ if ((--(ctx->transition_pending_notifications_sent)) == 0) {
+ /*
+ * If this is the final response then the response to the power domain
+ * notification can be sent.
+ */
+ pd_resp_params =
+ (struct mod_pd_power_state_pre_transition_notification_resp_params
+ *)pd_response_event.params;
+ pd_resp_params->status = ctx->transition_pending_response_status;
+ fwk_thread_put_event(&pd_response_event);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int clock_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ struct clock_dev_ctx *ctx;
+
+ if (!fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT))
+ /* Only elements should be registered for notifications */
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(event->target_id);
+
+ if (event->is_response)
+ return clock_process_notification_response(ctx, event);
+
+ if (fwk_id_is_equal(
+ event->id, module_ctx.config->pd_transition_notification_id))
+ return clock_process_pd_transition_notification(ctx, event);
+ else if (fwk_id_is_equal(event->id,
+ module_ctx.config->pd_pre_transition_notification_id))
+ return clock_process_pd_pre_transition_notification(
+ ctx, event, resp_event);
+ else
+ return FWK_E_HANDLER;
+}
+
+const struct fwk_module module_clock = {
+ .name = "Clock HAL",
+ .type = FWK_MODULE_TYPE_HAL,
+ .api_count = MOD_CLOCK_API_COUNT,
+ .event_count = 0,
+ .notification_count = MOD_CLOCK_NOTIFICATION_IDX_COUNT,
+ .init = clock_init,
+ .element_init = clock_dev_init,
+ .bind = clock_bind,
+ .start = clock_start,
+ .process_bind_request = clock_process_bind_request,
+ .process_notification = clock_process_notification,
+};
diff --git a/module/css_clock/include/mod_css_clock.h b/module/css_clock/include/mod_css_clock.h
new file mode 100644
index 00000000..057362c5
--- /dev/null
+++ b/module/css_clock/include/mod_css_clock.h
@@ -0,0 +1,192 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_CSS_CLOCK_H
+#define MOD_CSS_CLOCK_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_element.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupCSSClock CSS Clock Driver
+ *
+ * \details A driver for Arm Compute Sub-System clock devices.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Type of the clock (indexed vs non-indexed).
+ */
+enum mod_css_clock_type {
+ /*! A lookup table is used to find the settings for a given rate. */
+ MOD_CSS_CLOCK_TYPE_INDEXED,
+ /*! The clock rate is set without looking up settings in a table. */
+ MOD_CSS_CLOCK_TYPE_NON_INDEXED,
+};
+
+/*!
+ * \brief APIs that the module makes available to entities requesting binding.
+ */
+enum mod_css_clock_api_types {
+ /*! Clock HAL */
+ MOD_CSS_CLOCK_API_TYPE_CLOCK,
+ MOD_CSS_CLOCK_API_COUNT,
+};
+
+/*!
+ * \brief Rate lookup table entry.
+ */
+struct mod_css_clock_rate {
+ /*! Rate of the clock in Hertz. */
+ uint64_t rate;
+
+ /*! Rate of the source PLL in Hertz. */
+ uint64_t pll_rate;
+
+ /*! The clock source used to attain the rate. */
+ uint8_t clock_source;
+
+ /*! The clock divider to program. */
+ uint8_t clock_div_type;
+
+ /*! The clock divider used to attain the rate. */
+ uint32_t clock_div;
+
+ /*! The clock modulator numerator setting, if implemented. */
+ uint32_t clock_mod_numerator;
+
+ /*! The clock modulator denominator setting, if implemented. */
+ uint32_t clock_mod_denominator;
+};
+
+/*!
+ * \brief Subsystem clock device configuration.
+ */
+struct mod_css_clock_dev_config {
+ /*! The type of the clock (indexed vs non-indexed). */
+ enum mod_css_clock_type clock_type;
+
+ /*! Clock source used for non-indexed clocks. */
+ uint8_t clock_default_source;
+
+ /*! Clock source used when changing the PLL rate. */
+ uint8_t clock_switching_source;
+
+ /*! Element identifier for the associated PLL. */
+ fwk_id_t pll_id;
+
+ /*! Reference to the API provided by the PLL driver. */
+ fwk_id_t pll_api_id;
+
+ /*! Pointer to the table of clocks that are members of the group. */
+ fwk_id_t const *member_table;
+
+ /*! The number of clocks in the member table. */
+ uint32_t member_count;
+
+ /*! Reference to the API for the clocks that are members of the group. */
+ fwk_id_t member_api_id;
+
+ /*!
+ * The rate, in Hz, that the member clocks run at before any configuration.
+ */
+ uint64_t initial_rate;
+
+ /*! The clock's support for modulation. */
+ bool modulation_supported;
+
+ /*! Pointer to the clock's rate lookup table. */
+ struct mod_css_clock_rate const *rate_table;
+
+ /*! The number of rates in the rate lookup table. */
+ uint32_t rate_count;
+};
+
+/*!
+ * \brief CSS clock control interface.
+ */
+struct mod_css_clock_direct_api {
+ /*! Set the clock device's divider */
+ int (*set_div)(fwk_id_t device_id, uint32_t divider_type,
+ uint32_t divider);
+
+ /*! Set the clock device's source (multi-source clocks only) */
+ int (*set_source)(fwk_id_t device_id, uint8_t source);
+
+ /*! Set the clock device's modulator (multi-source clocks only) */
+ int (*set_mod)(fwk_id_t device_id, uint32_t numerator,
+ uint32_t denominator);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain is
+ * about to change.
+ *
+ * \details This function will be called prior to the change in power
+ * state occurring so that the clock driver implementing this API is
+ * able to perform any required preparatory work beforehand.
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param current_state The current power state that the clock's power
+ * domain will transition away from.
+ *
+ * \param new_state The power state that the clock's power domain will
+ * transition to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_pending_power_transition)(
+ fwk_id_t clock_id,
+ unsigned int current_state,
+ unsigned int new_state);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain
+ * has changed.
+ *
+ * \details This function will be called after the change in power state
+ * has occurred. The driver can take any appropriate actions that are
+ * required to accommodate the new state. The transition can be to a
+ * deeper power state (e.g. ON->OFF) or to a shallower power state
+ * (e.g. OFF->ON).
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state The power state that the clock's power domain transitioned
+ * to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_power_transition)(fwk_id_t clock_id, unsigned int state);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_CSS_CLOCK_H */
diff --git a/module/css_clock/src/Makefile b/module/css_clock/src/Makefile
new file mode 100644
index 00000000..2cc84f99
--- /dev/null
+++ b/module/css_clock/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := CSS Clock Driver
+BS_LIB_SOURCES := mod_css_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/css_clock/src/mod_css_clock.c b/module/css_clock/src/mod_css_clock.c
new file mode 100644
index 00000000..c3300628
--- /dev/null
+++ b/module/css_clock/src/mod_css_clock.c
@@ -0,0 +1,479 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <mod_clock.h>
+#include <mod_css_clock.h>
+#include <mod_power_domain.h>
+
+/* Device context */
+struct css_clock_dev_ctx {
+ bool initialized;
+ uint64_t current_rate;
+ struct mod_clock_drv_api *pll_api;
+ struct mod_css_clock_direct_api *clock_api;
+ const struct mod_css_clock_dev_config *config;
+};
+
+/* Module context */
+struct css_clock_ctx {
+ struct css_clock_dev_ctx *dev_ctx_table;
+ unsigned int dev_count;
+};
+
+static struct css_clock_ctx module_ctx;
+
+/*
+ * Static helper functions
+ */
+
+static int compare_rate_entry(const void *a, const void *b)
+{
+ struct mod_css_clock_rate *key = (struct mod_css_clock_rate *)a;
+ struct mod_css_clock_rate *element = (struct mod_css_clock_rate *)b;
+
+ return (key->rate - element->rate);
+}
+
+static int get_rate_entry(struct css_clock_dev_ctx *ctx, uint64_t target_rate,
+ struct mod_css_clock_rate **entry)
+{
+ struct mod_css_clock_rate *current_rate_entry;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (entry == NULL)
+ return FWK_E_PARAM;
+
+ /* Perform a binary search to find the entry matching the requested rate */
+ current_rate_entry = (struct mod_css_clock_rate *) bsearch(&target_rate,
+ ctx->config->rate_table, ctx->config->rate_count,
+ sizeof(struct mod_css_clock_rate), compare_rate_entry);
+
+ if (current_rate_entry == NULL)
+ return FWK_E_PARAM;
+
+ *entry = current_rate_entry;
+ return FWK_SUCCESS;
+}
+
+static int set_rate_indexed(struct css_clock_dev_ctx *ctx, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ unsigned int i;
+ struct mod_css_clock_rate *rate_entry;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Look up the divider and source settings */
+ status = get_rate_entry(ctx, rate, &rate_entry);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Switch each member clock away from the PLL source */
+ for (i = 0; i < ctx->config->member_count; i++) {
+ status = ctx->clock_api->set_source(ctx->config->member_table[i],
+ ctx->config->clock_switching_source);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = ctx->clock_api->set_div(ctx->config->member_table[i],
+ rate_entry->clock_div_type,
+ rate_entry->clock_div);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (ctx->config->modulation_supported) {
+ status = ctx->clock_api->set_mod(ctx->config->member_table[i],
+ rate_entry->clock_mod_numerator,
+ rate_entry->clock_mod_denominator);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+ }
+
+ /* Change the PLL to the desired rate */
+ status = ctx->pll_api->set_rate(ctx->config->pll_id, rate_entry->pll_rate,
+ MOD_CLOCK_ROUND_MODE_NONE);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Return each member clock back to the PLL source */
+ for (i = 0; i < ctx->config->member_count; i++) {
+ status = ctx->clock_api->set_source(ctx->config->member_table[i],
+ rate_entry->clock_source);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+
+exit:
+ if (status == FWK_SUCCESS)
+ ctx->current_rate = rate;
+ return status;
+}
+
+static int set_rate_non_indexed(struct css_clock_dev_ctx *ctx, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ unsigned int i;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Switch each member clock away from the PLL source */
+ for (i = 0; i < ctx->config->member_count; i++) {
+ status = ctx->clock_api->set_source(ctx->config->member_table[i],
+ ctx->config->clock_switching_source);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+
+ /* Change the PLL to the desired rate */
+ status = ctx->pll_api->set_rate(ctx->config->pll_id, rate, round_mode);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Return each member clock back to the PLL source */
+ for (i = 0; i < ctx->config->member_count; i++) {
+ status = ctx->clock_api->set_source(ctx->config->member_table[i],
+ ctx->config->clock_default_source);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+
+exit:
+ if (status == FWK_SUCCESS)
+ ctx->current_rate = rate;
+ return status;
+}
+
+/*
+ * Module API functions
+ */
+
+static int css_clock_set_rate(fwk_id_t dev_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ struct css_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->clock_type == MOD_CSS_CLOCK_TYPE_INDEXED)
+ return set_rate_indexed(ctx, rate, round_mode);
+ else
+ return set_rate_non_indexed(ctx, rate, round_mode);
+}
+
+static int css_clock_get_rate(fwk_id_t dev_id, uint64_t *rate)
+{
+ int status;
+ struct css_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *rate = ctx->current_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_get_rate_from_index(fwk_id_t dev_id,
+ unsigned int rate_index,
+ uint64_t *rate)
+{
+ int status;
+ struct css_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (rate_index >= ctx->config->rate_count)
+ return FWK_E_PARAM;
+
+ if (ctx->config->clock_type == MOD_CSS_CLOCK_TYPE_INDEXED) {
+ *rate = ctx->config->rate_table[rate_index].rate;
+ return FWK_SUCCESS;
+ } else
+ return FWK_E_SUPPORT;
+}
+
+static int css_clock_set_state(fwk_id_t dev_id, enum mod_clock_state state)
+{
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (state == MOD_CLOCK_STATE_RUNNING)
+ return FWK_SUCCESS; /* CSS clocks are always running */
+
+ /* CSS clocks cannot be turned off */
+ return FWK_E_SUPPORT;
+}
+
+static int css_clock_get_state(fwk_id_t dev_id, enum mod_clock_state *state)
+{
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *state = MOD_CLOCK_STATE_RUNNING;
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_get_range(fwk_id_t dev_id, struct mod_clock_range *range)
+{
+ int status;
+ struct css_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (range == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->clock_type == MOD_CSS_CLOCK_TYPE_INDEXED) {
+ range->rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE;
+ range->min = ctx->config->rate_table[0].rate;
+ range->max = ctx->config->rate_table[ctx->config->rate_count - 1].rate;
+ range->rate_count = ctx->config->rate_count;
+ return FWK_SUCCESS;
+ } else
+ return ctx->pll_api->get_range(ctx->config->pll_id, range);
+}
+
+static int css_clock_power_state_change(
+ fwk_id_t dev_id,
+ unsigned int next_state)
+{
+ int status;
+ unsigned int clock_idx;
+ struct css_clock_dev_ctx *ctx;
+ const struct mod_css_clock_dev_config *dev_config;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ dev_config = ctx->config;
+
+ /* The group's clock driver is not required to handle this transition */
+ if (ctx->clock_api->process_power_transition != NULL) {
+ for (clock_idx = 0; clock_idx < dev_config->member_count; clock_idx++) {
+ /* Allow the member clock's driver to perform any required
+ * processing */
+ status = ctx->clock_api->process_power_transition(
+ dev_config->member_table[clock_idx], next_state);
+
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+ }
+
+ if (next_state == MOD_PD_STATE_ON) {
+ if (ctx->initialized) {
+ /* Restore all clocks in the group to the last frequency */
+ return css_clock_set_rate(dev_id, ctx->current_rate,
+ MOD_CLOCK_ROUND_MODE_NONE);
+ } else {
+ ctx->initialized = true;
+ /* Set all clocks in the group to the initial frequency */
+ return css_clock_set_rate(dev_id, dev_config->initial_rate,
+ MOD_CLOCK_ROUND_MODE_NONE);
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_pending_power_state_change(
+ fwk_id_t dev_id,
+ unsigned int current_state,
+ unsigned int next_state)
+{
+ int status;
+ unsigned int clock_idx;
+ struct css_clock_dev_ctx *ctx;
+ const struct mod_css_clock_dev_config *dev_config;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ dev_config = ctx->config;
+
+ /* The group's clock driver is not required to handle this transition */
+ if (ctx->clock_api->process_pending_power_transition != NULL) {
+ for (clock_idx = 0; clock_idx < dev_config->member_count; clock_idx++) {
+ /* Allow the member clock's driver to perform any required
+ * processing */
+ status = ctx->clock_api->process_pending_power_transition(
+ dev_config->member_table[clock_idx], current_state, next_state);
+
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+ }
+
+ /* Nothing specific to be done in this driver */
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_drv_api api_clock = {
+ .set_rate = css_clock_set_rate,
+ .get_rate = css_clock_get_rate,
+ .get_rate_from_index = css_clock_get_rate_from_index,
+ .set_state = css_clock_set_state,
+ .get_state = css_clock_get_state,
+ .get_range = css_clock_get_range,
+ .process_power_transition = css_clock_power_state_change,
+ .process_pending_power_transition = css_clock_pending_power_state_change,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int css_clock_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ module_ctx.dev_count = element_count;
+
+ if (element_count == 0)
+ return FWK_SUCCESS;
+
+ module_ctx.dev_ctx_table = fwk_mm_calloc(element_count,
+ sizeof(struct css_clock_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_element_init(fwk_id_t element_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ unsigned int i = 0;
+ uint64_t current_rate;
+ uint64_t last_rate = 0;
+ struct css_clock_dev_ctx *ctx;
+ const struct mod_css_clock_dev_config *dev_config = data;
+
+ if (!fwk_module_is_valid_element_id(element_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+
+ if (dev_config->clock_type == MOD_CSS_CLOCK_TYPE_INDEXED) {
+ /* Verify that the rate entries in the lookup table are ordered */
+ while (i < dev_config->rate_count) {
+ current_rate = dev_config->rate_table[i].rate;
+
+ /* The rate entries must be in ascending order */
+ if (current_rate < last_rate)
+ return FWK_E_DATA;
+
+ last_rate = current_rate;
+ i++;
+ }
+ }
+
+ ctx->config = dev_config;
+ ctx->current_rate = ctx->config->initial_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct css_clock_dev_ctx *ctx;
+ const struct mod_css_clock_dev_config *config;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ if (fwk_module_is_valid_module_id(id))
+ /* No module-level binding required */
+ return FWK_SUCCESS;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(id);
+ config = ctx->config;
+
+ /* Ensure that the group has at least one member */
+ if (config->member_count == 0)
+ return FWK_E_DATA;
+
+ /* Bind to the group's common PLL driver */
+ status = fwk_module_bind(config->pll_id, config->pll_api_id,
+ &ctx->pll_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Bind to the API used to control the clocks in the group */
+ status = fwk_module_bind(config->member_table[0],
+ config->member_api_id, &ctx->clock_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+static int css_clock_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id,
+ const void **api)
+{
+ if (fwk_id_get_api_idx(api_id) != MOD_CSS_CLOCK_API_TYPE_CLOCK)
+ /* The requested API is not supported. */
+ return FWK_E_ACCESS;
+
+ *api = &api_clock;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_css_clock = {
+ .name = "Subsystem Clock Driver",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_CSS_CLOCK_API_COUNT,
+ .event_count = 0,
+ .init = css_clock_init,
+ .element_init = css_clock_element_init,
+ .bind = css_clock_bind,
+ .process_bind_request = css_clock_process_bind_request,
+};
diff --git a/module/ddr_phy500/include/mod_ddr_phy500.h b/module/ddr_phy500/include/mod_ddr_phy500.h
new file mode 100644
index 00000000..1bf5e545
--- /dev/null
+++ b/module/ddr_phy500/include/mod_ddr_phy500.h
@@ -0,0 +1,95 @@
+ /*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * DDR-PHY500 driver
+ */
+#ifndef MOD_DDR_PHY500_H
+#define MOD_DDR_PHY500_H
+
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_log.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \addtogroup GroupDDR DDR PHY500 Driver
+ * @{
+ */
+
+/*!
+ * \brief DDR PHY500 register definitions.
+ */
+struct mod_ddr_phy500_reg {
+ /*!
+ * \cond
+ * @{
+ */
+ FWK_RW uint32_t INIT_COMPLETE;
+ FWK_RW uint32_t MEMORY_WIDTH;
+ FWK_RW uint32_t READ_DELAY;
+ FWK_RW uint32_t CAPTURE_MASK;
+ FWK_RW uint32_t CAS_LATENCY;
+ FWK_RW uint32_t T_CTRL_DELAY;
+ FWK_RW uint32_t T_WRLAT;
+ FWK_RW uint32_t T_RDDATA_EN;
+ FWK_RW uint32_t T_RDLAT;
+ FWK_RW uint32_t RESERVED1;
+ FWK_R uint32_t RESERVED2;
+ FWK_R uint32_t LP_REQ;
+ FWK_RW uint32_t LP_ACK;
+ FWK_RW uint32_t RDLVL_REQ;
+ FWK_R uint32_t RDLVL_EN;
+ FWK_RW uint32_t WRLVL_REQ;
+ FWK_RW uint32_t WRLVL_EN;
+ FWK_RW uint32_t MSTR_REQ;
+ FWK_RW uint32_t MSTR_ACK;
+ FWK_RW uint32_t DFI_WR_PREMBL;
+ FWK_R uint8_t RESERVED3[0x820 - 0x50];
+ FWK_RW uint32_t DELAY_SEL;
+ FWK_R uint32_t RESERVED4;
+ FWK_RW uint32_t T_CTRL_UPD_MIN;
+ /*!
+ * \endcond
+ * @}
+ */
+};
+
+/*!
+ * \brief Element configuration.
+ */
+struct mod_ddr_phy500_element_config {
+ /*! Base address of a device configuration register. */
+ uintptr_t ddr;
+};
+
+/*!
+ * \brief DDR PHY500 module configuration.
+ */
+struct mod_ddr_phy500_module_config {
+ /*!
+ * Pointer to a structure containing default values for a subset of the PHY's
+ * configuration registers. These values are common to all PHYs that are
+ * represented by elements in the module's element table.
+ */
+ const struct mod_ddr_phy500_reg *ddr_reg_val;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_DDR_PHY500_H */
diff --git a/module/ddr_phy500/src/Makefile b/module/ddr_phy500/src/Makefile
new file mode 100644
index 00000000..d5f606a7
--- /dev/null
+++ b/module/ddr_phy500/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := DDR PHY500
+BS_LIB_SOURCES += mod_ddr_phy500.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/ddr_phy500/src/mod_ddr_phy500.c b/module/ddr_phy500/src/mod_ddr_phy500.c
new file mode 100644
index 00000000..b3e87fbb
--- /dev/null
+++ b/module/ddr_phy500/src/mod_ddr_phy500.c
@@ -0,0 +1,128 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * DDR-PHY500 driver
+ */
+
+#include <assert.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_ddr_phy500.h>
+#include <mod_dmc500.h>
+
+static struct mod_log_api *log_api;
+
+/*
+ * Functions fulfilling this module's interface
+ */
+static int ddr_phy500_config(fwk_id_t element_id)
+{
+ int status;
+ struct mod_ddr_phy500_reg *ddr;
+ const struct mod_ddr_phy500_module_config *module_config;
+ const struct mod_ddr_phy500_element_config *element_config;
+
+ status = fwk_module_check_call(element_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ module_config = fwk_module_get_data(fwk_module_id_ddr_phy500);
+
+ element_config = fwk_module_get_data(element_id);
+
+ ddr = (struct mod_ddr_phy500_reg *)element_config->ddr;
+
+ status = log_api->log(MOD_LOG_GROUP_INFO,
+ "[DDR] Initializing PHY at 0x%x\n", (uintptr_t) ddr);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ddr->T_CTRL_DELAY = module_config->ddr_reg_val->T_CTRL_DELAY;
+ ddr->READ_DELAY = module_config->ddr_reg_val->READ_DELAY;
+ ddr->T_CTRL_UPD_MIN = module_config->ddr_reg_val->T_CTRL_UPD_MIN;
+ ddr->DELAY_SEL = module_config->ddr_reg_val->DELAY_SEL;
+
+ ddr->CAPTURE_MASK = module_config->ddr_reg_val->CAPTURE_MASK;
+ ddr->T_RDDATA_EN = module_config->ddr_reg_val->T_RDDATA_EN;
+ ddr->T_RDLAT = module_config->ddr_reg_val->T_RDLAT;
+ ddr->T_WRLAT = module_config->ddr_reg_val->T_WRLAT;
+ ddr->DFI_WR_PREMBL = module_config->ddr_reg_val->DFI_WR_PREMBL;
+
+ ddr->LP_ACK = module_config->ddr_reg_val->LP_ACK;
+
+ return FWK_SUCCESS;
+}
+
+static struct mod_dmc500_ddr_phy_api ddr_phy500_api = {
+ .configure = ddr_phy500_config,
+};
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int ddr_phy500_init(fwk_id_t module_id, unsigned int element_count,
+ const void *config)
+{
+ assert(config != NULL);
+
+ return FWK_SUCCESS;
+}
+
+static int ddr_phy500_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ assert(data != NULL);
+
+ return FWK_SUCCESS;
+}
+
+static int ddr_phy500_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ /* Skip the second round (rounds are zero-indexed) */
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ /* Nothing to be done for element-level binding */
+ if (fwk_module_is_valid_element_id(id))
+ return FWK_SUCCESS;
+
+ /* Bind to the log module and get a pointer to its API */
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG), MOD_LOG_API_ID,
+ &log_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int ddr_phy500_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
+ fwk_id_t api_type, const void **api)
+{
+ /* Binding to elements is not permitted. */
+ if (fwk_module_is_valid_element_id(id))
+ return FWK_E_ACCESS;
+
+ *((const void**)api) = &ddr_phy500_api;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_ddr_phy500 = {
+ .name = "DDR-PHY500",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .init = ddr_phy500_init,
+ .element_init = ddr_phy500_element_init,
+ .bind = ddr_phy500_bind,
+ .process_bind_request = ddr_phy500_process_bind_request,
+ .api_count = 1,
+};
diff --git a/module/dmc500/include/mod_dmc500.h b/module/dmc500/include/mod_dmc500.h
new file mode 100644
index 00000000..7f424363
--- /dev/null
+++ b/module/dmc500/include/mod_dmc500.h
@@ -0,0 +1,571 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * DMC-500 module.
+ */
+
+#ifndef MOD_DMC500_H
+#define MOD_DMC500_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_log.h>
+#include <mod_timer.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \addtogroup GroupDMC DMC-500 Driver
+ *
+ * \details Please consult the Arm CoreLink DMC-500 Dynamic Memory Controller
+ * Technical Reference Manual for details on the specific registers that
+ * are programmed here.
+ *
+ * \sa https://developer.arm.com/docs/100132_0000/latest/programmers-model/
+ register-summary
+ * @{
+ */
+
+/*!
+ * \brief DMC-500 register definitions
+ */
+struct mod_dmc500_reg {
+ /*!
+ * \cond
+ * @{
+ */
+ FWK_R uint32_t SI0_SI_STATUS;
+ FWK_R uint32_t SI0_SI_INTERRUPT_STATUS;
+ FWK_R uint32_t SI0_TZ_FAIL_ADDRESS_LOW;
+ FWK_R uint32_t SI0_TZ_FAIL_ADDRESS_HIGH;
+ FWK_R uint32_t SI0_TZ_FAIL_CONTROL;
+ FWK_R uint32_t SI0_TZ_FAIL_ID;
+ FWK_R uint32_t SI0_PMU_REQ_INT_INFO;
+ FWK_RW uint32_t SI0_PMU_REQ_COUNT0;
+ FWK_RW uint32_t SI0_PMU_REQ_COUNT1;
+ FWK_RW uint32_t SI0_PMU_REQ_COUNT2;
+ FWK_RW uint32_t SI0_PMU_REQ_COUNT3;
+ FWK_RW uint32_t SI0_PMU_SCLK_COUNT_COUNT;
+ FWK_RW uint32_t SI0_SI_STATE_CONTROL;
+ FWK_W uint32_t SI0_SI_FLUSH_CONTROL;
+ FWK_RW uint32_t ADDRESS_CONTROL;
+ FWK_RW uint32_t DECODE_CONTROL;
+ FWK_RW uint32_t ADDRESS_MAP;
+ FWK_RW uint32_t RANK_REMAP_CONTROL;
+ FWK_RW uint32_t SI0_SI_INTERRUPT_CONTROL;
+ FWK_W uint32_t SI0_SI_INTERRUPT_CLR;
+ FWK_RW uint32_t TZ_ACTION;
+ FWK_R uint32_t SI0_TZ_REGION_BASE_LOW_0;
+ FWK_R uint32_t SI0_TZ_REGION_BASE_HIGH_0;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_0;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_0;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_0;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_0;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_1;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_1;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_1;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_1;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_1;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_1;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_2;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_2;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_2;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_2;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_2;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_2;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_3;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_3;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_3;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_3;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_3;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_3;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_4;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_4;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_4;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_4;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_4;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_4;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_5;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_5;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_5;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_5;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_5;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_5;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_6;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_6;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_6;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_6;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_6;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_6;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_7;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_7;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_7;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_7;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_7;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_7;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_LOW_8;
+ FWK_RW uint32_t SI0_TZ_REGION_BASE_HIGH_8;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_LOW_8;
+ FWK_RW uint32_t SI0_TZ_REGION_TOP_HIGH_8;
+ FWK_RW uint32_t SI0_TZ_REGION_ATTRIBUTES_8;
+ FWK_RW uint32_t SI0_TZ_REGION_ID_ACCESS_8;
+ FWK_RW uint32_t SI0_PMU_REQ_CONTROL;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MASK_0;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MATCH_0;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MASK_1;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MATCH_1;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MASK_2;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MATCH_2;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MASK_3;
+ FWK_RW uint32_t SI0_PMU_REQ_ATTRIBUTE_MATCH_3;
+ FWK_RW uint32_t SI0_THRESHOLD_CONTROL;
+ uint8_t RESERVED0[0x200 - 0x154];
+ FWK_R uint32_t SI1_SI_STATUS;
+ FWK_R uint32_t SI1_SI_INTERRUPT_STATUS;
+ FWK_R uint32_t SI1_TZ_FAIL_ADDRESS_LOW;
+ FWK_R uint32_t SI1_TZ_FAIL_ADDRESS_HIGH;
+ FWK_R uint32_t SI1_TZ_FAIL_CONTROL;
+ FWK_R uint32_t SI1_TZ_FAIL_ID;
+ FWK_R uint32_t SI1_PMU_REQ_INT_INFO;
+ FWK_RW uint32_t SI1_PMU_REQ_COUNT0;
+ FWK_RW uint32_t SI1_PMU_REQ_COUNT1;
+ FWK_RW uint32_t SI1_PMU_REQ_COUNT2;
+ FWK_RW uint32_t SI1_PMU_REQ_COUNT3;
+ FWK_RW uint32_t SI1_PMU_SCLK_COUNT_COUNT;
+ FWK_RW uint32_t SI1_SI_STATE_CONTROL;
+ FWK_W uint32_t SI1_SI_FLUSH_CONTROL;
+ uint8_t RESERVED1[0x248 - 0x238];
+ FWK_RW uint32_t SI1_SI_INTERRUPT_CONTROL;
+ FWK_W uint32_t SI1_SI_INTERRUPT_CLR;
+ uint32_t RESERVED2;
+ FWK_R uint32_t SI1_TZ_REGION_BASE_LOW_0;
+ FWK_R uint32_t SI1_TZ_REGION_BASE_HIGH_0;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_0;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_0;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_0;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_0;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_1;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_1;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_1;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_1;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_1;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_1;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_2;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_2;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_2;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_2;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_2;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_2;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_3;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_3;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_3;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_3;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_3;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_3;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_4;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_4;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_4;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_4;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_4;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_4;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_5;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_5;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_5;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_5;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_5;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_5;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_6;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_6;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_6;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_6;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_6;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_6;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_7;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_7;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_7;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_7;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_7;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_7;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_LOW_8;
+ FWK_RW uint32_t SI1_TZ_REGION_BASE_HIGH_8;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_LOW_8;
+ FWK_RW uint32_t SI1_TZ_REGION_TOP_HIGH_8;
+ FWK_RW uint32_t SI1_TZ_REGION_ATTRIBUTES_8;
+ FWK_RW uint32_t SI1_TZ_REGION_ID_ACCESS_8;
+ FWK_RW uint32_t SI1_PMU_REQ_CONTROL;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MASK_0;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MATCH_0;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MASK_1;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MATCH_1;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MASK_2;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MATCH_2;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MASK_3;
+ FWK_RW uint32_t SI1_PMU_REQ_ATTRIBUTE_MATCH_3;
+ FWK_RW uint32_t SI1_THRESHOLD_CONTROL;
+ uint8_t RESERVED3[0x400 - 0x354];
+ FWK_R uint32_t DCB_STATUS;
+ FWK_R uint32_t M_INTERRUPT_STATUS;
+ FWK_R uint32_t PMU_DCB_INT_INFO;
+ FWK_W uint32_t DCB_STATE_CONTROL;
+ uint32_t RESERVED4;
+ FWK_RW uint32_t QUEUE_THRESHOLD_CONTROL_31_00;
+ FWK_RW uint32_t QUEUE_THRESHOLD_CONTROL_63_32;
+ uint8_t RESERVED5[0x42C - 0x41C];
+ FWK_RW uint32_t DCB_INTERRUPT_CONTROL;
+ FWK_W uint32_t DCB_INTERRUPT_CLR;
+ FWK_RW uint32_t PMU_DCB_CONTROL;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_0;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_0;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_COUNT_0;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_1;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_1;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_COUNT_1;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_2;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_2;
+ FWK_RW uint32_t PMU_DATA_CONTROL_BLOCK_COUNT_2;
+ FWK_RW uint32_t PMU_TAG_ENTRIES_ATTRIBUTE_MASK;
+ FWK_RW uint32_t PMU_TAG_ENTRIES_ATTRIBUTE_MATCH;
+ FWK_RW uint32_t PMU_TAG_ENTRIES_COUNT;
+ FWK_RW uint32_t PMU_MCLK_COUNT_COUNT;
+ uint32_t RESERVED6;
+ FWK_R uint32_t ERR_RAMECC_FR;
+ uint32_t RESERVED7;
+ FWK_RW uint32_t ERR_RAMECC_CTLR;
+ uint32_t RESERVED8;
+ FWK_RW uint32_t ERR_RAMECC_STATUS;
+ uint32_t RESERVED9;
+ FWK_RW uint32_t ERR_RAMECC_ADDR;
+ FWK_RW uint32_t ERR_RAMECC_ADDR2;
+ FWK_RW uint32_t ERR_RAMECC_MISC0;
+ uint32_t RESERVED10[3];
+ FWK_W uint32_t ERR_RAMECC_INJECT;
+ uint8_t RESERVED11[0x500 - 0x4A4];
+ FWK_R uint32_t QUEUE_STATUS;
+ uint32_t RESERVED12;
+ FWK_R uint32_t PMU_QE_INT_INFO;
+ FWK_RW uint32_t QUEUE_STATE_CONTROL;
+ FWK_RW uint32_t QE_INTERRUPT_CONTROL;
+ FWK_W uint32_t QE_INTERRUPT_CLR;
+ FWK_RW uint32_t RANK_TURNAROUND_CONTROL;
+ FWK_RW uint32_t HIT_TURNAROUND_CONTROL;
+ FWK_RW uint32_t QOS_CLASS_CONTROL;
+ FWK_RW uint32_t ESCALATION_CONTROL;
+ FWK_RW uint32_t QV_CONTROL_31_00;
+ FWK_RW uint32_t QV_CONTROL_63_32;
+ FWK_RW uint32_t RT_CONTROL_31_00;
+ FWK_RW uint32_t RT_CONTROL_63_32;
+ FWK_RW uint32_t TIMEOUT_CONTROL;
+ FWK_RW uint32_t WRITE_PRIORITY_CONTROL_31_00;
+ FWK_RW uint32_t WRITE_PRIORITY_CONTROL_63_32;
+ uint32_t RESERVED13;
+ FWK_RW uint32_t DIR_TURNAROUND_CONTROL;
+ FWK_RW uint32_t HIT_PREDICTION_CONTROL;
+ FWK_RW uint32_t REFRESH_ENABLE;
+ FWK_R uint32_t REFRESH_STATUS;
+ FWK_R uint32_t REFRESH_STATUS_FG;
+ FWK_RW uint32_t REFRESH_PRIORITY;
+ FWK_RW uint32_t MC_UPDATE_CONTROL;
+ FWK_RW uint32_t PHY_UPDATE_CONTROL;
+ FWK_RW uint32_t PHY_MASTER_CONTROL;
+ FWK_RW uint32_t LOW_POWER_CONTROL;
+ FWK_RW uint32_t PMU_QE_CONTROL;
+ FWK_RW uint32_t PMU_QE_MUX;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MASK_0;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MATCH_0;
+ FWK_RW uint32_t PMU_QOS_ENGINE_COUNT_0;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MASK_1;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MATCH_1;
+ FWK_RW uint32_t PMU_QOS_ENGINE_COUNT_1;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MASK_2;
+ FWK_RW uint32_t PMU_QOS_ENGINE_ATTRIBUTE_MATCH_2;
+ FWK_RW uint32_t PMU_QOS_ENGINE_COUNT_2;
+ FWK_RW uint32_t PMU_QUEUED_ENTRIES_ATTRIBUTE_MASK;
+ FWK_RW uint32_t PMU_QUEUED_ENTRIES_ATTRIBUTE_MATCH;
+ FWK_RW uint32_t PMU_QUEUED_ENTRIES_COUNT;
+ uint8_t RESERVED14[0x600 - 0x5A8];
+ FWK_R uint32_t MI_STATUS;
+ FWK_R uint32_t RANKS_READY;
+ FWK_R uint32_t RANKS_RESET;
+ FWK_R uint32_t RANKS_DEEP_POWER_DOWN;
+ FWK_R uint32_t RANKS_SELF_REFRESH;
+ FWK_R uint32_t RANKS_POWERED_DOWN;
+ FWK_R uint32_t RANKS_CLOCK_DISABLED;
+ FWK_R uint32_t PHY_STATUS0;
+ FWK_R uint32_t PHY_STATUS1;
+ uint32_t RESERVED15;
+ FWK_R uint32_t PMU_MI_INT_INFO;
+ uint32_t RESERVED16;
+ FWK_RW uint32_t MI_STATE_CONTROL;
+ FWK_RW uint32_t PHY_CONFIG;
+ FWK_RW uint32_t DIRECT_CMD_SETTINGS;
+ FWK_RW uint32_t DIRECT_CMD;
+ FWK_RW uint32_t DIRECT_CLK_DISABLE;
+ FWK_RW uint32_t DIRECT_ODT;
+ FWK_RW uint32_t DCI_STRB;
+ FWK_RW uint32_t DCI_DATA;
+ FWK_W uint32_t DCI_DATA_CLR;
+ FWK_W uint32_t RANK_STATUS_OVERRIDE;
+ FWK_W uint32_t CLK_STATUS_OVERRIDE;
+ FWK_W uint32_t BANK_STATUS_OVERRIDE;
+ FWK_RW uint32_t MI_INTERRUPT_CONTROL;
+ FWK_W uint32_t MI_INTERRUPT_CLR;
+ FWK_RW uint32_t MEMORY_TYPE;
+ FWK_RW uint32_t FORMAT_CONTROL;
+ FWK_RW uint32_t FEATURE_CONTROL;
+ FWK_RW uint32_t POWER_DOWN_CONTROL;
+ FWK_RW uint32_t REFRESH_CONTROL;
+ FWK_RW uint32_t ODT_WR_CONTROL_31_00;
+ uint32_t RESERVED17;
+ FWK_RW uint32_t ODT_RD_CONTROL_31_00;
+ uint32_t RESERVED18;
+ FWK_RW uint32_t PHY_WRDATA_CS_CONTROL_31_00;
+ uint32_t RESERVED19;
+ FWK_RW uint32_t PHY_RDDATA_CS_CONTROL_31_00;
+ uint32_t RESERVED20;
+ FWK_RW uint32_t PHYUPD_INIT;
+ FWK_RW uint32_t PHY_POWER_CONTROL;
+ uint32_t RESERVED21;
+ FWK_RW uint32_t ODT_TIMING;
+ FWK_RW uint32_t T_REFI;
+ FWK_RW uint32_t T_RFC;
+ FWK_RW uint32_t T_RCD;
+ FWK_RW uint32_t T_RAS;
+ FWK_RW uint32_t T_RP;
+ FWK_RW uint32_t T_RRD;
+ FWK_RW uint32_t T_ACT_WINDOW;
+ FWK_RW uint32_t T_RTR;
+ FWK_RW uint32_t T_RTW;
+ FWK_RW uint32_t T_RTP;
+ FWK_RW uint32_t T_RDPDEN;
+ FWK_RW uint32_t T_WR;
+ FWK_RW uint32_t T_WTR;
+ FWK_RW uint32_t T_WTW;
+ FWK_RW uint32_t T_XTMW;
+ FWK_RW uint32_t T_WRPDEN;
+ FWK_RW uint32_t T_CLOCK_CONTROL;
+ FWK_RW uint32_t T_EP;
+ FWK_RW uint32_t T_XP;
+ FWK_RW uint32_t T_ESR;
+ FWK_RW uint32_t T_XSR;
+ uint32_t RESERVED22;
+ FWK_RW uint32_t T_COMPLETION_CHECKS;
+ FWK_RW uint32_t T_RDDATA_EN;
+ FWK_RW uint32_t T_PHYRDLAT;
+ FWK_RW uint32_t T_PHYWRLAT;
+ FWK_RW uint32_t T_PHY_TRAIN;
+ FWK_R uint32_t ERR_PHY_FR;
+ uint32_t RESERVED23;
+ FWK_RW uint32_t ERR_PHY_CTLR;
+ uint32_t RESERVED24;
+ FWK_RW uint32_t ERR_PHY_STATUS;
+ uint32_t RESERVED25;
+ FWK_RW uint32_t ERR_PHY_ADDR;
+ FWK_RW uint32_t ERR_PHY_ADDR2;
+ FWK_RW uint32_t ERR_PHY_MISC0;
+ uint8_t RESERVED26[0x74C - 0x73C];
+ FWK_W uint32_t ERR_PHY_INJECT;
+ FWK_RW uint32_t PMU_MI_CONTROL;
+ FWK_RW uint32_t PMU_MEMORY_IF_ATTRIBUTE_MASK_0;
+ FWK_RW uint32_t PMU_MEMORY_IF_ATTRIBUTE_MATCH_0;
+ FWK_RW uint32_t PMU_MEMORY_IF_COUNT_0;
+ FWK_RW uint32_t PMU_MEMORY_IF_ATTRIBUTE_MASK_1;
+ FWK_RW uint32_t PMU_MEMORY_IF_ATTRIBUTE_MATCH_1;
+ FWK_RW uint32_t PMU_MEMORY_IF_COUNT_1;
+ FWK_RW uint32_t PMU_BANK_STATES_ATTRIBUTE_MASK;
+ FWK_RW uint32_t PMU_BANK_STATES_ATTRIBUTE_MATCH;
+ FWK_RW uint32_t PMU_BANK_STATES_COUNT;
+ FWK_RW uint32_t PMU_RANK_STATES_ATTRIBUTE_MASK;
+ FWK_RW uint32_t PMU_RANK_STATES_ATTRIBUTE_MATCH;
+ FWK_RW uint32_t PMU_RANK_STATES_COUNT;
+ uint8_t RESERVED27[0xF00 - 0x784];
+ FWK_R uint32_t MEMC_CONFIG;
+ uint32_t RESERVED28[3];
+ FWK_R uint32_t CFG_INTERRUPT_STATUS;
+ FWK_R uint32_t CFG_FAILED_ACCESS_INT_INFO;
+ uint8_t RESERVED29[0xF30 - 0xF18];
+ FWK_RW uint32_t CFG_INTERRUPT_CONTROL;
+ uint32_t RESERVED30;
+ FWK_W uint32_t CFG_INTERRUPT_CLR;
+ uint8_t RESERVED31[0xFC0 - 0xF3C];
+ FWK_RW uint32_t INTEGRATION_TEST_CONTROL;
+ FWK_RW uint32_t INTEGRATION_TEST_OUTPUT;
+ uint32_t RESERVED32[2];
+ FWK_R uint32_t PERIPH_ID_4;
+ uint32_t RESERVED33[3];
+ FWK_R uint32_t PERIPH_ID_0;
+ FWK_R uint32_t PERIPH_ID_1;
+ FWK_R uint32_t PERIPH_ID_2;
+ FWK_R uint32_t PERIPH_ID_3;
+ FWK_R uint32_t COMPONENT_ID_0;
+ FWK_R uint32_t COMPONENT_ID_1;
+ FWK_R uint32_t COMPONENT_ID_2;
+ FWK_R uint32_t COMPONENT_ID_3;
+ /*!
+ * \endcond
+ * @}
+ */
+};
+
+/*!
+ * \brief SI_STATE_CONTROL mask used to prevent request stalling.
+ */
+#define MOD_DMC500_SI_STATE_CONTROL_GO 0
+
+/*!
+ * \brief SI_STATE_CONTROL mask used to enable request stalling.
+ */
+#define MOD_DMC500_SI_STATE_CONTROL_STALL_REQ (1 << 0)
+
+/*!
+ * \brief SI_STATUS mask used to confirm that request stalling is active.
+ */
+#define MOD_DMC500_SI_STATUS_STALL_ACK (1 << 0)
+
+/*!
+ * \brief SI_STATUS mask used to read the empty bit.
+ */
+#define MOD_DMC500_SI_STATUS_EMPTY (1 << 1)
+
+/*!
+ * \brief QUEUE_STATE_CONTROL mask used to prevent request stalling.
+ */
+#define MOD_DMC500_QUEUE_STATE_CONTROL_GO 0
+
+/*!
+ * \brief QUEUE_STATE_CONTROL mask used to enable request stalling.
+ */
+#define MOD_DMC500_QUEUE_STATE_CONTROL_STALL_REQ (1 << 0)
+
+/*!
+ * \brief QUEUE_STATUS mask used to confirm that request stalling is active.
+ */
+#define MOD_DMC500_QUEUE_STATUS_STALL_ACK (1 << 0)
+
+/*!
+ * \brief QUEUE_STATUS mask used to read the empty bit.
+ */
+#define MOD_DMC500_QUEUE_STATUS_EMPTY (1 << 1)
+
+/*!
+ * \brief MI_STATUS mask used to read the idle bit.
+ */
+#define MOD_DMC500_MI_STATUS_IDLE (1 << 0)
+
+/*!
+ * \brief MI_STATUS mask used to read the empty bit.
+ */
+#define MOD_DMC500_MI_STATUS_EMPTY (1 << 1)
+
+/*!
+ * \brief Create the ADDRESS_MAP value.
+ *
+ * \param SHUTTER The address shutter.
+ *
+ * \return The ADDRESS_MAP value.
+ */
+#define ADDRESS_MAP_VAL(SHUTTER) ((1 << 8) | (SHUTTER))
+
+/*!
+ * \brief Create the ADDRESS_CONTROL value.
+ *
+ * \param RANK Number of bits for the rank.
+ * \param BANK Number of bits for the bank.
+ * \param ROW Number of bits for the row.
+ * \param COL Number of bits for the column.
+ *
+ * \return The ADDRESS_CONTROL value.
+ */
+#define ADDRESS_CONTROL_VAL(RANK, BANK, ROW, COL) (((RANK) << 24) | \
+ ((BANK) << 16) | \
+ ((ROW) << 8) | \
+ (COL))
+
+/*!
+ * \brief Create the MEMORY_TYPE value
+ *
+ * \param BANK_GROUP Bank group.
+ * \param WIDTH Memory device width.
+ * \param TYPE Memory type.
+ *
+ * \return The MEMORY_TYPE value.
+ */
+#define MEMORY_TYPE_VAL(BANK_GROUP, WIDTH, TYPE) (((BANK_GROUP) << 16) | \
+ ((WIDTH) << 8) | \
+ (TYPE))
+
+/*!
+ * \brief Create the FORMAT_CONTROL value.
+ *
+ * \param BURST Memory burst.
+ *
+ * \return The FORMAT_CONTROL value.
+ */
+#define FORMAT_CONTROL_VAL(BURST) ((BURST) << 8)
+
+/*!
+ * \brief Element configuration.
+ */
+struct mod_dmc500_element_config {
+ /*! Base address of the DMC-500 device's registers */
+ uintptr_t dmc;
+ /*! Element identifier of the associated DDR PHY-500 device */
+ fwk_id_t ddr_phy_id;
+};
+
+/*!
+ * \brief API of the DDR PHY associate to the DMC
+ */
+struct mod_dmc500_ddr_phy_api {
+ /*!
+ * \brief Configure a DDR PHY500 device
+ *
+ * \param element_id Element identifier corresponding to the device to
+ * configure.
+ *
+ * \retval FWK_SUCCESS if the operation succeed.
+ * \return one of the error code otherwise.
+ */
+ int (*configure)(fwk_id_t element_id);
+};
+
+/*!
+ * \brief DMC-500 module configuration.
+ */
+struct mod_dmc500_module_config {
+ /*!
+ * Element identifier of the timer used for delays when programming the
+ * DMC-500
+ */
+ fwk_id_t timer_id;
+ /*! DDR PHY module ID */
+ fwk_id_t ddr_phy_module_id;
+ /*! DDR PHY API ID */
+ fwk_id_t ddr_phy_api_id;
+ /*! Initial value for the dmc registers */
+ const struct mod_dmc500_reg *reg_val;
+ /*! Pointer to a product-specific function that issues direct commands */
+ void (*direct_ddr_cmd)(struct mod_dmc500_reg *dmc);
+};
+
+/*!
+ * \brief DMC-500 module description.
+ */
+extern const struct fwk_module module_dmc500;
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_DMC500_H */
diff --git a/module/dmc500/src/Makefile b/module/dmc500/src/Makefile
new file mode 100644
index 00000000..fd08ee04
--- /dev/null
+++ b/module/dmc500/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := mod_dmc500
+BS_LIB_SOURCES += mod_dmc500.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/dmc500/src/mod_dmc500.c b/module/dmc500/src/mod_dmc500.c
new file mode 100644
index 00000000..0b68374c
--- /dev/null
+++ b/module/dmc500/src/mod_dmc500.c
@@ -0,0 +1,390 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * DMC-500 driver
+ */
+
+#include <assert.h>
+#include <fwk_errno.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_dmc500.h>
+
+static struct mod_log_api *log_api;
+static struct mod_dmc500_ddr_phy_api *ddr_phy_api;
+static struct mod_timer_api *timer_api;
+
+static int dmc500_config(struct mod_dmc500_reg *dmc, fwk_id_t ddr_phy_id);
+
+/* Framework API */
+static int mod_dmc500_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ return FWK_SUCCESS;
+}
+
+static int mod_dmc500_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ assert(data != NULL);
+
+ return FWK_SUCCESS;
+}
+
+static int mod_dmc500_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ const struct mod_dmc500_module_config *module_config;
+
+ /* Nothing to do in the second round of calls. */
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ /* Nothing to do in case of elements. */
+ if (fwk_module_is_valid_element_id(id))
+ return FWK_SUCCESS;
+
+ module_config = fwk_module_get_data(fwk_module_id_dmc500);
+ assert(module_config != NULL);
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ MOD_LOG_API_ID, &log_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(module_config->ddr_phy_module_id,
+ module_config->ddr_phy_api_id, &ddr_phy_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(module_config->timer_id,
+ FWK_ID_API(FWK_MODULE_IDX_TIMER, 0), &timer_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+static int mod_dmc500_start(fwk_id_t id)
+{
+ const struct mod_dmc500_element_config *element_config;
+ struct mod_dmc500_reg *dmc;
+
+ /* Nothing to start for the module */
+ if (fwk_module_is_valid_module_id(id))
+ return FWK_SUCCESS;
+
+ element_config = fwk_module_get_data(id);
+ dmc = (struct mod_dmc500_reg *)element_config->dmc;
+
+ return dmc500_config(dmc, element_config->ddr_phy_id);
+}
+
+const struct fwk_module module_dmc500 = {
+ .name = "DMC500",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .init = mod_dmc500_init,
+ .element_init = mod_dmc500_element_init,
+ .bind = mod_dmc500_bind,
+ .start = mod_dmc500_start,
+ .api_count = 0,
+ .event_count = 0,
+};
+
+
+static int dmc500_config(struct mod_dmc500_reg *dmc, fwk_id_t ddr_phy_id)
+{
+ int status;
+ uint64_t timeout;
+ uint64_t remaining_ticks;
+ uint64_t counter;
+ const struct mod_dmc500_reg *reg_val;
+ const struct mod_dmc500_module_config *module_config;
+
+ module_config = fwk_module_get_data(fwk_module_id_dmc500);
+ reg_val = module_config->reg_val;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Initialising DMC500 at 0x%x\n", (uintptr_t)dmc);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->ADDRESS_CONTROL = reg_val->ADDRESS_CONTROL;
+ dmc->RANK_REMAP_CONTROL = reg_val->RANK_REMAP_CONTROL;
+ dmc->MEMORY_TYPE = reg_val->MEMORY_TYPE;
+ dmc->FORMAT_CONTROL = reg_val->FORMAT_CONTROL;
+ dmc->DECODE_CONTROL = reg_val->DECODE_CONTROL;
+ dmc->FEATURE_CONTROL = reg_val->FEATURE_CONTROL;
+ dmc->ODT_WR_CONTROL_31_00 = reg_val->ODT_WR_CONTROL_31_00;
+ dmc->ODT_RD_CONTROL_31_00 = reg_val->ODT_RD_CONTROL_31_00;
+ dmc->ODT_TIMING = reg_val->ODT_TIMING;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Setting timing settings\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->T_REFI = reg_val->T_REFI;
+ dmc->T_RFC = reg_val->T_RFC;
+ dmc->T_RDPDEN = reg_val->T_RDPDEN;
+ dmc->T_RCD = reg_val->T_RCD;
+ dmc->T_RAS = reg_val->T_RAS;
+ dmc->T_RP = reg_val->T_RP;
+ dmc->T_RRD = reg_val->T_RRD;
+ dmc->T_ACT_WINDOW = reg_val->T_ACT_WINDOW;
+ dmc->T_RTR = reg_val->T_RTR;
+ dmc->T_RTW = reg_val->T_RTW;
+ dmc->T_RTP = reg_val->T_RTP;
+ dmc->T_WR = reg_val->T_WR;
+ dmc->T_WTR = reg_val->T_WTR;
+ dmc->T_WTW = reg_val->T_WTW;
+ dmc->T_XTMW = reg_val->T_XTMW;
+ dmc->T_CLOCK_CONTROL = reg_val->T_CLOCK_CONTROL;
+ dmc->T_EP = reg_val->T_EP;
+ dmc->T_XP = reg_val->T_XP;
+ dmc->T_ESR = reg_val->T_ESR;
+ dmc->T_XSR = reg_val->T_XSR;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Setting address map\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->ADDRESS_MAP = reg_val->ADDRESS_MAP;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Setting PMU settings\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->SI0_SI_INTERRUPT_CONTROL = reg_val->SI0_SI_INTERRUPT_CONTROL;
+ dmc->SI0_PMU_REQ_CONTROL = reg_val->SI0_PMU_REQ_CONTROL;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MASK_0 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MASK_0;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MATCH_0 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MATCH_0;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MASK_1 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MASK_1;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MATCH_1 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MATCH_1;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MASK_2 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MASK_2;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MATCH_2 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MATCH_2;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MASK_3 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MASK_3;
+ dmc->SI0_PMU_REQ_ATTRIBUTE_MATCH_3 = reg_val->SI0_PMU_REQ_ATTRIBUTE_MATCH_3;
+ dmc->SI0_THRESHOLD_CONTROL = reg_val->SI0_THRESHOLD_CONTROL;
+ dmc->SI1_SI_INTERRUPT_CONTROL = reg_val->SI1_SI_INTERRUPT_CONTROL;
+ dmc->SI1_PMU_REQ_CONTROL = reg_val->SI1_PMU_REQ_CONTROL;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MASK_0 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MASK_0;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MATCH_0 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MATCH_0;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MASK_1 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MASK_1;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MATCH_1 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MATCH_1;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MASK_2 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MASK_2;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MATCH_2 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MATCH_2;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MASK_3 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MASK_3;
+ dmc->SI1_PMU_REQ_ATTRIBUTE_MATCH_3 = reg_val->SI1_PMU_REQ_ATTRIBUTE_MATCH_3;
+ dmc->SI1_THRESHOLD_CONTROL = reg_val->SI1_THRESHOLD_CONTROL;
+ dmc->QUEUE_THRESHOLD_CONTROL_31_00 = reg_val->QUEUE_THRESHOLD_CONTROL_31_00;
+ dmc->QUEUE_THRESHOLD_CONTROL_63_32 = reg_val->QUEUE_THRESHOLD_CONTROL_63_32;
+ dmc->DCB_INTERRUPT_CONTROL = reg_val->DCB_INTERRUPT_CONTROL;
+ dmc->PMU_DCB_CONTROL = reg_val->PMU_DCB_CONTROL;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_0 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_0;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_0 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_0;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_1 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_1;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_1 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_1;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_2 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_2;
+ dmc->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_2 =
+ reg_val->PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_2;
+ dmc->PMU_TAG_ENTRIES_ATTRIBUTE_MASK =
+ reg_val->PMU_TAG_ENTRIES_ATTRIBUTE_MASK;
+ dmc->PMU_TAG_ENTRIES_ATTRIBUTE_MATCH =
+ reg_val->PMU_TAG_ENTRIES_ATTRIBUTE_MATCH;
+ dmc->QE_INTERRUPT_CONTROL = reg_val->QE_INTERRUPT_CONTROL;
+ dmc->RANK_TURNAROUND_CONTROL = reg_val->RANK_TURNAROUND_CONTROL;
+ dmc->HIT_TURNAROUND_CONTROL = reg_val->HIT_TURNAROUND_CONTROL;
+ dmc->QOS_CLASS_CONTROL = reg_val->QOS_CLASS_CONTROL;
+ dmc->ESCALATION_CONTROL = reg_val->ESCALATION_CONTROL;
+ dmc->QV_CONTROL_31_00 = reg_val->QV_CONTROL_31_00;
+ dmc->QV_CONTROL_63_32 = reg_val->QV_CONTROL_63_32;
+ dmc->RT_CONTROL_31_00 = reg_val->RT_CONTROL_31_00;
+ dmc->RT_CONTROL_63_32 = reg_val->RT_CONTROL_63_32;
+ dmc->TIMEOUT_CONTROL = reg_val->TIMEOUT_CONTROL;
+ dmc->WRITE_PRIORITY_CONTROL_31_00 = reg_val->WRITE_PRIORITY_CONTROL_31_00;
+ dmc->WRITE_PRIORITY_CONTROL_63_32 = reg_val->WRITE_PRIORITY_CONTROL_63_32;
+ dmc->DIR_TURNAROUND_CONTROL = reg_val->DIR_TURNAROUND_CONTROL;
+ dmc->HIT_PREDICTION_CONTROL = reg_val->HIT_PREDICTION_CONTROL;
+ dmc->REFRESH_PRIORITY = reg_val->REFRESH_PRIORITY;
+ dmc->MC_UPDATE_CONTROL = reg_val->MC_UPDATE_CONTROL;
+ dmc->PHY_UPDATE_CONTROL = reg_val->PHY_UPDATE_CONTROL;
+ dmc->PHY_MASTER_CONTROL = reg_val->PHY_MASTER_CONTROL;
+ dmc->LOW_POWER_CONTROL = reg_val->LOW_POWER_CONTROL;
+ dmc->PMU_QE_CONTROL = reg_val->PMU_QE_CONTROL;
+ dmc->PMU_QE_MUX = reg_val->PMU_QE_MUX;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MASK_0 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MASK_0;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_0 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_0;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MASK_1 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MASK_1;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_1 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_1;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MASK_2 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MASK_2;
+ dmc->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_2 =
+ reg_val->PMU_QOS_ENGINE_ATTRIBUTE_MATCH_2;
+ dmc->PMU_QUEUED_ENTRIES_ATTRIBUTE_MASK =
+ reg_val->PMU_QUEUED_ENTRIES_ATTRIBUTE_MASK;
+ dmc->PMU_QUEUED_ENTRIES_ATTRIBUTE_MATCH =
+ reg_val->PMU_QUEUED_ENTRIES_ATTRIBUTE_MATCH;
+ dmc->MI_INTERRUPT_CONTROL = reg_val->MI_INTERRUPT_CONTROL;
+ dmc->POWER_DOWN_CONTROL = reg_val->POWER_DOWN_CONTROL;
+ dmc->REFRESH_CONTROL = reg_val->REFRESH_CONTROL;
+ dmc->PMU_MI_CONTROL = reg_val->PMU_MI_CONTROL;
+ dmc->PMU_MEMORY_IF_ATTRIBUTE_MASK_0 =
+ reg_val->PMU_MEMORY_IF_ATTRIBUTE_MASK_0;
+ dmc->PMU_MEMORY_IF_ATTRIBUTE_MATCH_0 =
+ reg_val->PMU_MEMORY_IF_ATTRIBUTE_MATCH_0;
+ dmc->PMU_MEMORY_IF_ATTRIBUTE_MASK_1 =
+ reg_val->PMU_MEMORY_IF_ATTRIBUTE_MASK_1;
+ dmc->PMU_MEMORY_IF_ATTRIBUTE_MATCH_1 =
+ reg_val->PMU_MEMORY_IF_ATTRIBUTE_MATCH_1;
+ dmc->PMU_BANK_STATES_ATTRIBUTE_MASK =
+ reg_val->PMU_BANK_STATES_ATTRIBUTE_MASK;
+ dmc->PMU_BANK_STATES_ATTRIBUTE_MATCH =
+ reg_val->PMU_BANK_STATES_ATTRIBUTE_MATCH;
+ dmc->PMU_RANK_STATES_ATTRIBUTE_MASK =
+ reg_val->PMU_RANK_STATES_ATTRIBUTE_MASK;
+ dmc->PMU_RANK_STATES_ATTRIBUTE_MATCH =
+ reg_val->PMU_RANK_STATES_ATTRIBUTE_MATCH;
+ dmc->CFG_INTERRUPT_CONTROL = reg_val->CFG_INTERRUPT_CONTROL;
+ dmc->T_RDDATA_EN = reg_val->T_RDDATA_EN;
+ dmc->T_PHYRDLAT = reg_val->T_PHYRDLAT;
+ dmc->T_PHYWRLAT = reg_val->T_PHYWRLAT;
+
+ dmc->ERR_RAMECC_CTLR = reg_val->ERR_RAMECC_CTLR;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Setting PHY-related settings\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->PHY_POWER_CONTROL = reg_val->PHY_POWER_CONTROL;
+ dmc->T_PHY_TRAIN = reg_val->T_PHY_TRAIN;
+ dmc->PHYUPD_INIT = reg_val->PHYUPD_INIT;
+
+ dmc->PHY_CONFIG = 0x03000000;
+ dmc->PHY_CONFIG = 0x0600000A;
+ dmc->PHY_CONFIG = 0x01000001;
+
+ status = ddr_phy_api->configure(ddr_phy_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ dmc->PHY_CONFIG = 0x01000001;
+ dmc->PHY_CONFIG = 0x01000000;
+ dmc->PHY_CONFIG = 0x00000003;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Doing direct DDR commands\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ module_config->direct_ddr_cmd(dmc);
+
+ dmc->REFRESH_ENABLE = reg_val->REFRESH_ENABLE;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Setting dmc in READY mode\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = timer_api->time_to_timestamp(module_config->timer_id,
+ 1000 * 1000, &timeout);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = timer_api->get_counter(module_config->timer_id, &counter);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ timeout += counter;
+
+ while ((dmc->MI_STATUS & MOD_DMC500_MI_STATUS_IDLE) !=
+ MOD_DMC500_MI_STATUS_IDLE) {
+ status = timer_api->remaining(module_config->timer_id, timeout,
+ &remaining_ticks);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (remaining_ticks == 0)
+ goto timeout;
+ }
+
+ dmc->MI_STATE_CONTROL = reg_val->MI_STATE_CONTROL;
+ dmc->QUEUE_STATE_CONTROL = reg_val->QUEUE_STATE_CONTROL;
+ dmc->SI0_SI_STATE_CONTROL = reg_val->SI0_SI_STATE_CONTROL;
+ dmc->SI1_SI_STATE_CONTROL = reg_val->SI1_SI_STATE_CONTROL;
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Waiting for Queue stall = 0...\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ while ((dmc->QUEUE_STATUS & MOD_DMC500_QUEUE_STATUS_STALL_ACK) != 0) {
+ status = timer_api->remaining(module_config->timer_id, timeout,
+ &remaining_ticks);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (remaining_ticks == 0)
+ goto timeout;
+ }
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Waiting for SI0 stall = 0...\n");
+ if (status != FWK_SUCCESS)
+ return FWK_SUCCESS;
+
+ while ((dmc->SI0_SI_STATUS & MOD_DMC500_SI_STATUS_STALL_ACK) != 0) {
+ status = timer_api->remaining(module_config->timer_id, timeout,
+ &remaining_ticks);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (remaining_ticks == 0)
+ goto timeout;
+ }
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[DDR] Waiting for SI1 stall = 0...\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ while ((dmc->SI1_SI_STATUS & MOD_DMC500_SI_STATUS_STALL_ACK) != 0) {
+ status = timer_api->remaining(module_config->timer_id, timeout,
+ &remaining_ticks);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (remaining_ticks == 0)
+ goto timeout;
+ }
+
+ status = log_api->log(MOD_LOG_GROUP_DEBUG, "[DDR] DMC init done.\n");
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+
+timeout:
+ status = log_api->log(MOD_LOG_GROUP_ERROR,
+ "[DDR] Timed out in DMC500 init.\n");
+ if (status != FWK_SUCCESS)
+ return status;
+ return FWK_E_TIMEOUT;
+}
diff --git a/module/dvfs/include/mod_dvfs.h b/module/dvfs/include/mod_dvfs.h
new file mode 100644
index 00000000..9b54b087
--- /dev/null
+++ b/module/dvfs/include/mod_dvfs.h
@@ -0,0 +1,271 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_H
+#define MOD_DVFS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \ingroup GroupModules
+ * \defgroup GroupDvfs Dynamic Voltage and Frequency Scaling (DVFS)
+ * \{
+ */
+
+/*!
+ * \defgroup GroupDvfsTypes Types
+ * \{
+ */
+
+/*!
+ * \brief Frequency limits.
+ */
+struct mod_dvfs_frequency_limits {
+ uint64_t minimum; /*!< Minimum permitted rate */
+ uint64_t maximum; /*!< Maximum permitted rate */
+};
+
+/*!
+ * \brief Operating Performance Point (OPP).
+ */
+struct mod_dvfs_opp {
+ uint64_t voltage; /*!< Power supply voltage in millivolts (mV) */
+ uint64_t frequency; /*!< Clock rate in Hertz (Hz) */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupDvfsConfig Configuration
+ * \{
+ */
+
+/*!
+ * \brief Domain configuration.
+ */
+struct mod_dvfs_domain_config {
+ /*!
+ * \brief Power supply identifier.
+ *
+ * \warning This identifier must refer to an element of the \c psu module.
+ */
+ fwk_id_t psu_id;
+
+ /*!
+ * \brief Clock identifier.
+ *
+ * \warning This identifier must refer to an element of the \c clock module.
+ */
+ fwk_id_t clock_id;
+
+ /*! Worst-case transition latency in microseconds */
+ uint16_t latency;
+
+ /*! Sustained operating point index */
+ size_t sustained_idx;
+
+ /*!
+ * \brief Operating points.
+ *
+ * \note The frequencies of these operating points must be in ascending
+ * order.
+ */
+ struct mod_dvfs_opp *opps;
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupDvfsApis APIs
+ * \{
+ */
+
+/*!
+ * \brief Domain API.
+ */
+struct mod_dvfs_domain_api {
+ /*!
+ * \brief Get the current operating point of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp Current operating point.
+ */
+ int (*get_current_opp)(fwk_id_t domain_id, struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get the sustained operating point of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp Sustained operating point.
+ */
+ int (*get_sustained_opp)(
+ fwk_id_t domain_id,
+ struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get an operating point from its index.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param n Index of the operating point to retrieve.
+ * \param [out] opp Requested operating point.
+ */
+ int (*get_nth_opp)(
+ fwk_id_t domain_id,
+ size_t n,
+ struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get the number of operating points of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp_count Number of operating points.
+ */
+ int (*get_opp_count)(fwk_id_t domain_id, size_t *opp_count);
+
+ /*!
+ * \brief Get the worst-case transition latency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] latency Worst-case transition latency.
+ */
+ int (*get_latency)(fwk_id_t domain_id, uint16_t *latency);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param idx Index of the operating point to transition to.
+ */
+ int (*set_frequency)(fwk_id_t domain_id, uint64_t frequency);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \note This function is asynchronous.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param idx Index of the operating point to transition to.
+ */
+ int (*set_frequency_async)(fwk_id_t domain_id, uint64_t frequency);
+
+ /*!
+ * \brief Get the frequency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] limits Current frequency limits.
+ */
+ int (*get_frequency_limits)(
+ fwk_id_t domain_id,
+ struct mod_dvfs_frequency_limits *limits);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param limits Pointer to the new limits.
+ */
+ int (*set_frequency_limits)(
+ fwk_id_t domain_id,
+ const struct mod_dvfs_frequency_limits *limits);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \note This function is asynchronous.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param limits Pointer to the new limits.
+ */
+ int (*set_frequency_limits_async)(
+ fwk_id_t domain_id,
+ const struct mod_dvfs_frequency_limits *limits);
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupDvfsEvents Events
+ * \{
+ */
+
+/*!
+ * \brief <tt>Set operating point</tt> event response parameters.
+ */
+struct mod_dvfs_event_params_set_frequency_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \brief <tt>Set limits</tt> event response parameters.
+ */
+struct mod_dvfs_event_params_set_frequency_limits_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupDvfsIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_dvfs_api_idx {
+ MOD_DVFS_API_IDX_DVFS, /*!< API index for mod_dvfs_api_id_dvfs() */
+ MOD_DVFS_API_IDX_COUNT /*!< Number of defined APIs */
+};
+
+/*! Module API identifier */
+static const fwk_id_t mod_dvfs_api_id_dvfs =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_DVFS);
+
+/*!
+ * \brief Event indices.
+ */
+enum mod_dvfs_event_idx {
+ /*! Event index for mod_dvfs_event_id_set_frequency() */
+ MOD_DVFS_EVENT_IDX_SET_FREQUENCY,
+
+ /*! Event index for mod_dvfs_event_id_set_frequency_limits() */
+ MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS,
+
+ /*! Number of defined events */
+ MOD_DVFS_EVENT_IDX_COUNT
+};
+
+/*! <tt>Set operating point</tt> event identifier */
+static const fwk_id_t mod_dvfs_event_id_set_frequency =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_EVENT_IDX_SET_FREQUENCY);
+
+/*! <tt>Set frequency limits</tt> event identifier */
+static const fwk_id_t mod_dvfs_event_id_set_frequency_limits =
+ FWK_ID_EVENT_INIT(
+ FWK_MODULE_IDX_DVFS,
+ MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS);
+
+/*!
+ * \}
+ */
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_DVFS_H */
diff --git a/module/dvfs/src/Makefile b/module/dvfs/src/Makefile
new file mode 100644
index 00000000..2f62d737
--- /dev/null
+++ b/module/dvfs/src/Makefile
@@ -0,0 +1,15 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := dvfs
+BS_LIB_SOURCES := \
+ mod_dvfs_domain_api.c \
+ mod_dvfs_event.c \
+ mod_dvfs_module.c \
+ mod_dvfs_util.c \
+
+include $(BS_DIR)/lib.mk
diff --git a/module/dvfs/src/mod_dvfs_domain_api.c b/module/dvfs/src/mod_dvfs_domain_api.c
new file mode 100644
index 00000000..4b7d32d5
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_domain_api.c
@@ -0,0 +1,265 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_dvfs_private.h>
+#include <mod_psu.h>
+
+static const struct mod_dvfs_opp *get_opp_for_values(
+ const struct mod_dvfs_domain_ctx *ctx,
+ uint64_t frequency,
+ uint64_t voltage)
+{
+ size_t opp_idx;
+ const struct mod_dvfs_opp *opp;
+
+ /* A value of zero indicates the parameter should be ignored */
+ assert((frequency != 0) || (voltage != 0));
+
+ for (opp_idx = 0; opp_idx < ctx->opp_count; opp_idx++) {
+ opp = &ctx->config->opps[opp_idx];
+
+ /* Only check the frequency if requested */
+ if ((frequency != 0) && (opp->frequency != frequency))
+ continue;
+
+ /* Only check the voltage if requested */
+ if ((voltage != 0) && (opp->voltage != voltage))
+ continue;
+
+ return opp;
+ }
+
+ return NULL;
+}
+
+static bool is_opp_within_limits(
+ const struct mod_dvfs_opp *opp,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ return (opp->frequency >= limits->minimum) &&
+ (opp->frequency <= limits->maximum);
+}
+
+static bool are_limits_valid(
+ const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ if (limits->minimum > limits->maximum)
+ return false;
+
+ if (get_opp_for_values(ctx, limits->minimum, 0) == NULL)
+ return false;
+
+ if (get_opp_for_values(ctx, limits->maximum, 0) == NULL)
+ return false;
+
+ return true;
+}
+
+static const struct mod_dvfs_opp *adjust_opp_for_new_limits(
+ const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *opp,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ uint64_t needle;
+
+ if (opp->frequency < limits->minimum)
+ needle = limits->minimum;
+ else if (opp->frequency > limits->maximum)
+ needle = limits->maximum;
+ else {
+ /* No transition necessary */
+ return opp;
+ }
+
+ return get_opp_for_values(ctx, needle, 0);
+}
+
+static int api_get_current_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
+{
+ int status;
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ assert(opp != NULL);
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ status = __mod_dvfs_get_current_opp(ctx, opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+int api_get_sustained_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ assert(opp != NULL);
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *opp = ctx->config->opps[ctx->config->sustained_idx];
+
+ return FWK_SUCCESS;
+}
+
+int api_get_nth_opp(fwk_id_t domain_id,
+ size_t n,
+ struct mod_dvfs_opp *opp)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ assert(opp != NULL);
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (n >= ctx->opp_count)
+ return FWK_E_PARAM;
+
+ *opp = ctx->config->opps[n];
+
+ return FWK_SUCCESS;
+}
+
+static int api_get_opp_count(fwk_id_t domain_id, size_t *opp_count)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ assert(opp_count != NULL);
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *opp_count = ctx->opp_count;
+
+ return FWK_SUCCESS;
+}
+
+int api_get_latency(fwk_id_t domain_id, uint16_t *latency)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ assert(latency != NULL);
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *latency = ctx->config->latency;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_frequency(fwk_id_t domain_id, uint64_t frequency)
+{
+ int status;
+ const struct mod_dvfs_domain_ctx *ctx;
+ const struct mod_dvfs_opp *new_opp;
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Only accept frequencies that exist in the operating point table */
+ new_opp = get_opp_for_values(ctx, frequency, 0);
+ if (new_opp == NULL)
+ return FWK_E_RANGE;
+
+ if (!is_opp_within_limits(new_opp, &ctx->frequency_limits))
+ return FWK_E_RANGE;
+
+ status = __mod_dvfs_set_opp(ctx, new_opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_frequency_async(fwk_id_t domain_id, uint64_t frequency)
+{
+ return FWK_E_SUPPORT;
+}
+
+int api_get_frequency_limits(
+ fwk_id_t domain_id,
+ struct mod_dvfs_frequency_limits *limits)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *limits = ctx->frequency_limits;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_frequency_limits(
+ fwk_id_t domain_id,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ int status;
+ struct mod_dvfs_domain_ctx *ctx;
+ struct mod_dvfs_opp current_opp;
+ const struct mod_dvfs_opp *new_opp;
+
+ ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (!are_limits_valid(ctx, limits))
+ return FWK_E_PARAM;
+
+ status = __mod_dvfs_get_current_opp(ctx, &current_opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ new_opp = adjust_opp_for_new_limits(ctx, &current_opp, limits);
+ status = __mod_dvfs_set_opp(ctx, new_opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx->frequency_limits = *limits;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_frequency_limits_async(
+ fwk_id_t domain_id,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ return FWK_E_SUPPORT;
+}
+
+const struct mod_dvfs_domain_api __mod_dvfs_domain_api = {
+ .get_current_opp = api_get_current_opp,
+ .get_sustained_opp = api_get_sustained_opp,
+ .get_nth_opp = api_get_nth_opp,
+ .get_opp_count = api_get_opp_count,
+ .get_latency = api_get_latency,
+ .set_frequency = api_set_frequency,
+ .set_frequency_async = api_set_frequency_async,
+ .get_frequency_limits = api_get_frequency_limits,
+ .set_frequency_limits = api_set_frequency_limits,
+ .set_frequency_limits_async = api_set_frequency_limits_async,
+};
diff --git a/module/dvfs/src/mod_dvfs_domain_api_private.h b/module/dvfs/src/mod_dvfs_domain_api_private.h
new file mode 100644
index 00000000..5efd8867
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_domain_api_private.h
@@ -0,0 +1,16 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_DOMAIN_API_PRIVATE_H
+#define MOD_DVFS_DOMAIN_API_PRIVATE_H
+
+#include <mod_dvfs.h>
+
+/* Module API implementation */
+extern const struct mod_dvfs_domain_api __mod_dvfs_domain_api;
+
+#endif /* MOD_DVFS_DOMAIN_API_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_event.c b/module/dvfs/src/mod_dvfs_event.c
new file mode 100644
index 00000000..ff702ef1
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_event.c
@@ -0,0 +1,47 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_macros.h>
+#include <mod_dvfs_private.h>
+
+static int event_set_opp(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ return FWK_E_SUPPORT;
+}
+
+static int event_set_frequency_limits(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ return FWK_E_SUPPORT;
+}
+
+int __mod_dvfs_process_event(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ typedef int (*handler_t)(
+ const struct fwk_event *event,
+ struct fwk_event *response);
+
+ static const handler_t handlers[] = {
+ [MOD_DVFS_EVENT_IDX_SET_FREQUENCY] = event_set_opp,
+ [MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS] = event_set_frequency_limits,
+ };
+
+ handler_t handler;
+
+ /* Ensure we have a handler implemented for this event */
+ handler = handlers[fwk_id_get_event_idx(event->id)];
+ if (handler == NULL)
+ return FWK_E_PARAM;
+
+ /* Delegate event handling to the relevant handler */
+ return handler(event, response);
+}
diff --git a/module/dvfs/src/mod_dvfs_event_private.h b/module/dvfs/src/mod_dvfs_event_private.h
new file mode 100644
index 00000000..b2ff90b0
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_event_private.h
@@ -0,0 +1,18 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_EVENT_PRIVATE_H
+#define MOD_DVFS_EVENT_PRIVATE_H
+
+#include <fwk_event.h>
+
+/* Event handler */
+int __mod_dvfs_process_event(
+ const struct fwk_event *event,
+ struct fwk_event *response);
+
+#endif /* MOD_DVFS_EVENT_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_module.c b/module/dvfs/src/mod_dvfs_module.c
new file mode 100644
index 00000000..2a1eaae5
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_module.c
@@ -0,0 +1,240 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_notification.h>
+#include <mod_clock.h>
+#include <mod_dvfs_private.h>
+#include <mod_power_domain.h>
+#include <mod_psu.h>
+
+static struct mod_dvfs_domain_ctx (*domain_ctx)[];
+
+static int count_opps(const struct mod_dvfs_opp *opps)
+{
+ const struct mod_dvfs_opp *opp = &opps[0];
+
+ while ((opp->voltage != 0) && (opp->frequency != 0))
+ opp++;
+
+ return opp - &opps[0];
+}
+
+static struct mod_dvfs_domain_ctx *get_domain_ctx(fwk_id_t domain_id)
+{
+ unsigned int element_idx = fwk_id_get_element_idx(domain_id);
+
+ return &(*domain_ctx)[element_idx];
+}
+
+static int dvfs_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ domain_ctx = fwk_mm_calloc(
+ element_count,
+ sizeof((*domain_ctx)[0]));
+ if (domain_ctx == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_element_init(
+ fwk_id_t domain_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ struct mod_dvfs_domain_ctx *ctx = get_domain_ctx(domain_id);
+
+ assert(sub_element_count == 0);
+
+ /* Initialize the configuration */
+ ctx->config = data;
+ assert(ctx->config->opps != NULL);
+
+ /* Initialize the context */
+ ctx->opp_count = count_opps(ctx->config->opps);
+ assert(ctx->opp_count > 0);
+
+ /* Frequency limits default to the minimum and maximum available */
+ ctx->frequency_limits = (struct mod_dvfs_frequency_limits) {
+ .minimum = ctx->config->opps[0].frequency,
+ .maximum = ctx->config->opps[ctx->opp_count - 1].frequency,
+ };
+
+ ctx->suspended_opp = ctx->config->opps[ctx->config->sustained_idx];
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_bind_element(fwk_id_t domain_id, unsigned int round)
+{
+ int status;
+ const struct mod_dvfs_domain_ctx *ctx =
+ get_domain_ctx(domain_id);
+
+ /* Only handle the first round */
+ if (round > 0)
+ return FWK_SUCCESS;
+
+ /* Bind to the power supply module */
+ status = fwk_module_bind(
+ ctx->config->psu_id,
+ mod_psu_api_id_psu_device,
+ &ctx->apis.psu);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Bind to the clock module */
+ status = fwk_module_bind(
+ ctx->config->clock_id,
+ FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0),
+ &ctx->apis.clock);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_bind(fwk_id_t id, unsigned int round)
+{
+ /* We only need to handle binding our elements */
+ if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return dvfs_bind_element(id, round);
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_process_bind_request_module(
+ fwk_id_t source_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only expose the module API */
+ if (!fwk_id_is_equal(api_id, mod_dvfs_api_id_dvfs))
+ return FWK_E_PARAM;
+
+ /* We don't do any permissions management */
+ *api = &__mod_dvfs_domain_api;
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only allow binding to the module */
+ if (!fwk_id_is_equal(target_id, fwk_module_id_dvfs))
+ return FWK_E_PARAM;
+
+ return dvfs_process_bind_request_module(source_id, api_id, api);
+}
+
+static int dvfs_start(fwk_id_t id)
+{
+ int status;
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ ctx = get_domain_ctx(id);
+
+ /* Register for clock state notifications */
+ status = fwk_notification_subscribe(
+ mod_clock_notification_id_state_changed,
+ ctx->config->clock_id,
+ id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return fwk_notification_subscribe(
+ mod_clock_notification_id_state_change_pending,
+ ctx->config->clock_id,
+ id);
+}
+
+static int dvfs_notify_system_state_transition_suspend(fwk_id_t domain_id)
+{
+ struct mod_dvfs_domain_ctx *ctx =
+ get_domain_ctx(domain_id);
+
+ return __mod_dvfs_get_current_opp(ctx, &ctx->suspended_opp);
+}
+
+static int dvfs_notify_system_state_transition_resume(fwk_id_t domain_id)
+{
+ const struct mod_dvfs_domain_ctx *ctx =
+ get_domain_ctx(domain_id);
+
+ return __mod_dvfs_set_opp(ctx, &ctx->suspended_opp);
+}
+
+static int dvfs_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ struct clock_notification_params *params;
+ struct clock_state_change_pending_resp_params *resp_params;
+
+ assert(
+ fwk_id_is_equal(
+ event->id,
+ mod_clock_notification_id_state_changed) ||
+ fwk_id_is_equal(
+ event->id,
+ mod_clock_notification_id_state_change_pending));
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
+
+ params = (struct clock_notification_params *)event->params;
+
+ if (fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed)) {
+ if (params->new_state == MOD_CLOCK_STATE_RUNNING)
+ return dvfs_notify_system_state_transition_resume(event->target_id);
+ } else if (params->new_state == MOD_CLOCK_STATE_STOPPED) {
+ /* DVFS has received the pending change notification */
+ resp_params =
+ (struct clock_state_change_pending_resp_params *)resp_event->params;
+ resp_params->status = FWK_SUCCESS;
+
+ return dvfs_notify_system_state_transition_suspend(event->target_id);
+ }
+
+ return FWK_SUCCESS;
+}
+
+struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id)
+{
+ if (fwk_module_check_call(domain_id) != FWK_SUCCESS)
+ return NULL;
+
+ return get_domain_ctx(domain_id);
+}
+
+/* Module description */
+const struct fwk_module module_dvfs = {
+ .name = "DVFS",
+ .type = FWK_MODULE_TYPE_HAL,
+ .init = dvfs_init,
+ .element_init = dvfs_element_init,
+ .bind = dvfs_bind,
+ .process_bind_request = dvfs_process_bind_request,
+ .process_event = __mod_dvfs_process_event,
+ .start = dvfs_start,
+ .process_notification = dvfs_process_notification,
+ .api_count = MOD_DVFS_API_IDX_COUNT,
+ .event_count = MOD_DVFS_EVENT_IDX_COUNT,
+};
diff --git a/module/dvfs/src/mod_dvfs_module_private.h b/module/dvfs/src/mod_dvfs_module_private.h
new file mode 100644
index 00000000..5c441c0c
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_module_private.h
@@ -0,0 +1,40 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_MODULE_PRIVATE_H
+#define MOD_DVFS_MODULE_PRIVATE_H
+
+#include <fwk_id.h>
+#include <mod_clock.h>
+#include <mod_psu.h>
+
+/* Domain context */
+struct mod_dvfs_domain_ctx {
+ /* Domain configuration */
+ const struct mod_dvfs_domain_config *config;
+
+ struct {
+ /* Power supply API */
+ const struct mod_psu_device_api *psu;
+
+ /* Clock API */
+ const struct mod_clock_api *clock;
+ } apis;
+
+ /* Number of operating points */
+ size_t opp_count;
+
+ /* Operating point prior to domain suspension */
+ struct mod_dvfs_opp suspended_opp;
+
+ /* Current operating point limits */
+ struct mod_dvfs_frequency_limits frequency_limits;
+};
+
+struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id);
+
+#endif /* MOD_DVFS_MODULE_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_private.h b/module/dvfs/src/mod_dvfs_private.h
new file mode 100644
index 00000000..1db8b2c8
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_private.h
@@ -0,0 +1,16 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_PRIVATE_H
+#define MOD_DVFS_PRIVATE_H
+
+#include <mod_dvfs_event_private.h>
+#include <mod_dvfs_domain_api_private.h>
+#include <mod_dvfs_module_private.h>
+#include <mod_dvfs_util_private.h>
+
+#endif /* MOD_DVFS_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_util.c b/module/dvfs/src/mod_dvfs_util.c
new file mode 100644
index 00000000..e073a766
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_util.c
@@ -0,0 +1,71 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_module.h>
+#include <mod_dvfs_private.h>
+
+int __mod_dvfs_set_opp(
+ const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *new_opp)
+{
+ int status;
+ struct mod_dvfs_opp current_opp;
+
+ status = __mod_dvfs_get_current_opp(ctx, &current_opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (new_opp->voltage > current_opp.voltage) {
+ /* Raise the voltage before raising the frequency */
+ status = ctx->apis.psu->set_voltage(
+ ctx->config->psu_id,
+ new_opp->voltage);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+ }
+
+ if (new_opp->frequency != current_opp.frequency) {
+ status = ctx->apis.clock->set_rate(
+ ctx->config->clock_id,
+ new_opp->frequency,
+ MOD_CLOCK_ROUND_MODE_NONE);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+ }
+
+ if (new_opp->voltage < current_opp.voltage) {
+ /* Lower the voltage after lowering the frequency */
+ status = ctx->apis.psu->set_voltage(
+ ctx->config->psu_id,
+ new_opp->voltage);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+ }
+
+ return FWK_SUCCESS;
+}
+
+int __mod_dvfs_get_current_opp(
+ const struct mod_dvfs_domain_ctx *ctx,
+ struct mod_dvfs_opp *opp)
+{
+ int status;
+
+ status = ctx->apis.clock->get_rate(
+ ctx->config->clock_id,
+ &opp->frequency);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+
+ status = ctx->apis.psu->get_voltage(
+ ctx->config->psu_id,
+ &opp->voltage);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+
+ return FWK_SUCCESS;
+}
diff --git a/module/dvfs/src/mod_dvfs_util_private.h b/module/dvfs/src/mod_dvfs_util_private.h
new file mode 100644
index 00000000..9b530a20
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs_util_private.h
@@ -0,0 +1,22 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_UTIL_PRIVATE_H
+#define MOD_DVFS_UTIL_PRIVATE_H
+
+#include <mod_dvfs.h>
+#include <mod_dvfs_domain_api_private.h>
+
+int __mod_dvfs_set_opp(
+ const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *new_opp);
+
+int __mod_dvfs_get_current_opp(
+ const struct mod_dvfs_domain_ctx *ctx,
+ struct mod_dvfs_opp *opp);
+
+#endif /* MOD_DVFS_PRIVATE_H */
diff --git a/module/gtimer/include/mod_gtimer.h b/module/gtimer/include/mod_gtimer.h
new file mode 100644
index 00000000..69215e2e
--- /dev/null
+++ b/module/gtimer/include/mod_gtimer.h
@@ -0,0 +1,58 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Generic Timer device driver module and definitions.
+ */
+
+#ifndef MOD_GTIMER_H
+#define MOD_GTIMER_H
+
+#include <stdint.h>
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModuleGtimer Generic Timer Driver
+ *
+ * \details Driver module for the generic timer.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Generic timer device descriptor
+ */
+struct mod_gtimer_dev_config {
+ /*! Address of the device's timer register */
+ uintptr_t hw_timer;
+
+ /*! Address of the device's counter register */
+ uintptr_t hw_counter;
+
+ /*! Address of the device's control register */
+ uintptr_t control;
+
+ /*! The frequency in Hertz that the timer ticks at */
+ const uint32_t frequency;
+
+ /*! Identifier of the clock that this device depends on */
+ fwk_id_t clock_id;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_GTIMER_H */
diff --git a/module/gtimer/src/Makefile b/module/gtimer/src/Makefile
new file mode 100644
index 00000000..39c8d591
--- /dev/null
+++ b/module/gtimer/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Gtimer
+BS_LIB_SOURCES = mod_gtimer.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/gtimer/src/gtimer_reg.h b/module/gtimer/src/gtimer_reg.h
new file mode 100644
index 00000000..29427565
--- /dev/null
+++ b/module/gtimer/src/gtimer_reg.h
@@ -0,0 +1,90 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef GTIMER_REG_H
+#define GTIMER_REG_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief Counter registers (CNTCONTROL)
+ */
+struct cntcontrol_reg {
+ FWK_RW uint32_t CR;
+ FWK_R uint32_t SR;
+ FWK_RW uint32_t CVL;
+ FWK_RW uint32_t CVH;
+ uint32_t RESERVED0[4];
+ FWK_RW uint32_t FID0;
+ uint8_t RESERVED1[0xC0 - 0x24];
+ FWK_RW uint32_t SCR; /* CSS: Synchronization Control Register */
+ FWK_R uint32_t SVL; /* CSS: Synchronized Counter Lower Value Register */
+ FWK_R uint32_t SVU; /* CSS: Synchronized Counter Upper Value Register */
+ uint8_t RESERVED2[0xFD0 - 0xCC];
+ FWK_R uint32_t PID[11];
+};
+
+#define CNTCONTROL_CR_EN UINT32_C(0x00000001)
+#define CNTCONTROL_CR_HDBG UINT32_C(0x00000002)
+#define CNTCONTROL_CR_FCREQ UINT32_C(0x00000100)
+
+#define CNTCONTROL_SCR_ENSYNC UINT32_C(0x00000001)
+#define CNTCONTROL_SCR_ENSYNC_DIRECT UINT32_C(0x00000000)
+#define CNTCONTROL_SCR_ENSYNC_DELAY UINT32_C(0x00000001)
+
+/*!
+ * \brief Counter registers (CNTCTL)
+ */
+struct cntctl_reg {
+ FWK_RW uint32_t FRQ;
+ FWK_RW uint32_t NSAR;
+ FWK_R uint32_t TTIDR;
+ uint8_t RESERVED0[0x40 - 0x0C];
+ FWK_RW uint32_t ACR;
+ uint8_t RESERVED1[0xFD0 - 0x44];
+ FWK_R uint32_t PID[11];
+};
+
+#define CNTCTL_ACR UINT32_C(0x0000003f)
+#define CNTCTL_ACR_RPCT UINT32_C(0x00000001)
+#define CNTCTL_ACR_RVCT UINT32_C(0x00000002)
+#define CNTCTL_ACR_RFRQ UINT32_C(0x00000004)
+#define CNTCTL_ACR_RVOFF UINT32_C(0x00000008)
+#define CNTCTL_ACR_RWVT UINT32_C(0x00000010)
+#define CNTCTL_ACR_RWPT UINT32_C(0x00000020)
+
+/*!
+ * \brief Counter registers (CNTBASE)
+ */
+struct cntbase_reg {
+ FWK_R uint32_t PCTL;
+ FWK_R uint32_t PCTH;
+ FWK_R uint32_t VCTL;
+ FWK_R uint32_t VCTH;
+ FWK_R uint32_t FRQ;
+ FWK_RW uint32_t PL0ACR;
+ FWK_R uint32_t OFFL;
+ FWK_R uint32_t OFFH;
+ FWK_RW uint32_t P_CVALL;
+ FWK_RW uint32_t P_CVALH;
+ FWK_RW uint32_t P_TVAL;
+ FWK_RW uint32_t P_CTL;
+ FWK_RW uint32_t V_CVALL;
+ FWK_RW uint32_t V_CVALH;
+ FWK_RW uint32_t V_TVAL;
+ FWK_RW uint32_t V_CTL;
+ uint8_t RESERVED0[0xFD0 - 0x40];
+ FWK_R uint32_t PID[11];
+};
+
+#define CNTBASE_P_CTL UINT32_C(0x00000007)
+#define CNTBASE_P_CTL_ENABLE UINT32_C(0x00000001)
+#define CNTBASE_P_CTL_IMASK UINT32_C(0x00000002)
+#define CNTBASE_P_CTL_ISTATUS UINT32_C(0x00000004)
+
+#endif /* GTIMER_REG_H */
diff --git a/module/gtimer/src/mod_gtimer.c b/module/gtimer/src/mod_gtimer.c
new file mode 100644
index 00000000..74845169
--- /dev/null
+++ b/module/gtimer/src/mod_gtimer.c
@@ -0,0 +1,295 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_notification.h>
+#include <gtimer_reg.h>
+#include <mod_clock.h>
+#include <mod_gtimer.h>
+#include <mod_power_domain.h>
+#include <mod_timer.h>
+
+#define GTIMER_FREQUENCY_MIN_HZ UINT32_C(1)
+#define GTIMER_FREQUENCY_MAX_HZ UINT32_C(1000000000)
+
+/* Device content */
+struct dev_ctx {
+ struct cntbase_reg *hw_timer;
+ struct cntctl_reg *hw_counter;
+ struct cntcontrol_reg *control;
+ const struct mod_gtimer_dev_config *config;
+};
+
+/* Table of device context structures */
+static struct dev_ctx *ctx_table;
+
+/*
+ * Functions fulfilling the Timer module's driver interface
+ */
+
+static int enable(fwk_id_t dev_id)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ ctx->hw_timer->P_CTL &= ~CNTBASE_P_CTL_IMASK;
+ ctx->hw_timer->P_CTL |= CNTBASE_P_CTL_ENABLE;
+
+ return FWK_SUCCESS;
+}
+
+static int disable(fwk_id_t dev_id)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ ctx->hw_timer->P_CTL |= CNTBASE_P_CTL_IMASK;
+ ctx->hw_timer->P_CTL &= ~CNTBASE_P_CTL_ENABLE;
+
+ return FWK_SUCCESS;
+}
+
+static int set_timer(fwk_id_t dev_id, uint64_t timestamp)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ ctx->hw_timer->P_CVALL = timestamp & 0xFFFFFFFF;
+ ctx->hw_timer->P_CVALH = timestamp >> 32;
+
+ return FWK_SUCCESS;
+}
+
+static int get_timer(fwk_id_t dev_id, uint64_t *timestamp)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ struct {
+ uint32_t low;
+ uint32_t high;
+ } value;
+
+ /* Read 64-bit timer value */
+ value.low = ctx->hw_timer->P_CVALL;
+ value.high = ctx->hw_timer->P_CVALH;
+
+ *timestamp = *(uint64_t *)&value;
+ return FWK_SUCCESS;
+}
+
+static int get_counter(fwk_id_t dev_id, uint64_t *value)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ struct {
+ uint32_t low;
+ uint32_t high;
+ } counter;
+
+ /* Read 64-bit counter value */
+ do {
+ counter.high = ctx->hw_timer->PCTH;
+ counter.low = ctx->hw_timer->PCTL;
+ } while (counter.high != ctx->hw_timer->PCTH);
+
+ *value = *(uint64_t *)&counter;
+ return FWK_SUCCESS;
+}
+
+static int get_frequency(fwk_id_t dev_id, uint32_t *frequency)
+{
+ int status;
+ struct dev_ctx *ctx;
+
+ if (frequency == NULL)
+ return FWK_E_PARAM;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(dev_id);
+
+ *frequency = ctx->config->frequency;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_timer_driver_api module_api = {
+ .name = "Generic Timer Driver",
+ .enable = enable,
+ .disable = disable,
+ .set_timer = set_timer,
+ .get_timer = get_timer,
+ .get_counter = get_counter,
+ .get_frequency = get_frequency,
+};
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int gtimer_init(fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ ctx_table = fwk_mm_alloc(element_count, sizeof(struct dev_ctx));
+
+ if (ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int gtimer_device_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ struct dev_ctx *ctx;
+
+ ctx = ctx_table + fwk_id_get_element_idx(element_id);
+
+ ctx->config = data;
+ if (ctx->config->hw_timer == 0 ||
+ ctx->config->hw_counter == 0 ||
+ ctx->config->control == 0 ||
+ ctx->config->frequency < GTIMER_FREQUENCY_MIN_HZ ||
+ ctx->config->frequency > GTIMER_FREQUENCY_MAX_HZ) {
+
+ return FWK_E_DEVICE;
+ }
+
+ ctx->hw_timer = (struct cntbase_reg *)ctx->config->hw_timer;
+ ctx->hw_counter = (struct cntctl_reg *)ctx->config->hw_counter;
+ ctx->control = (struct cntcontrol_reg *)ctx->config->control;
+
+ disable(element_id);
+
+ ctx->hw_counter->ACR = CNTCTL_ACR_RPCT |
+ CNTCTL_ACR_RVCT |
+ CNTCTL_ACR_RFRQ |
+ CNTCTL_ACR_RVOFF|
+ CNTCTL_ACR_RWPT;
+ ctx->hw_counter->FRQ = ctx->config->frequency;
+
+ return FWK_SUCCESS;
+}
+
+static int gtimer_process_bind_request(fwk_id_t requester_id,
+ fwk_id_t id,
+ fwk_id_t api_type,
+ const void **api)
+{
+ /* No binding to the module */
+ if (fwk_module_is_valid_module_id(id))
+ return FWK_E_ACCESS;
+
+ *api = &module_api;
+
+ return FWK_SUCCESS;
+}
+
+static void gtimer_control_init(struct dev_ctx *ctx)
+{
+ /* Set primary counter update frequency and enable counter. */
+ ctx->control->CR &= ~CNTCONTROL_CR_EN;
+ ctx->control->FID0 = ctx->config->frequency;
+ ctx->control->CR |= CNTCONTROL_CR_FCREQ | CNTCONTROL_CR_EN;
+}
+
+static int gtimer_start(fwk_id_t id)
+{
+ struct dev_ctx *ctx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ ctx = ctx_table + fwk_id_get_element_idx(id);
+
+ if (!fwk_id_is_type(ctx->config->clock_id, FWK_ID_TYPE_NONE)) {
+ /* Register for clock state notifications */
+ return fwk_notification_subscribe(
+ mod_clock_notification_id_state_changed,
+ ctx->config->clock_id,
+ id);
+ } else
+ gtimer_control_init(ctx);
+
+ return FWK_SUCCESS;
+}
+
+static int gtimer_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ struct clock_notification_params *params;
+ struct dev_ctx *ctx;
+
+ assert(fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed));
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
+
+ params = (struct clock_notification_params *)event->params;
+
+ if (params->new_state == MOD_CLOCK_STATE_RUNNING) {
+ ctx = ctx_table + fwk_id_get_element_idx(event->target_id);
+ gtimer_control_init(ctx);
+ }
+
+ return FWK_SUCCESS;
+}
+
+
+/*
+ * Module descriptor
+ */
+const struct fwk_module module_gtimer = {
+ .name = "Generic Timer Driver",
+ .api_count = 1,
+ .event_count = 0,
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .init = gtimer_init,
+ .element_init = gtimer_device_init,
+ .start = gtimer_start,
+ .process_bind_request = gtimer_process_bind_request,
+ .process_notification = gtimer_process_notification,
+};
diff --git a/module/mhu/include/internal/mhu.h b/module/mhu/include/internal/mhu.h
new file mode 100644
index 00000000..b77a027b
--- /dev/null
+++ b/module/mhu/include/internal/mhu.h
@@ -0,0 +1,29 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MHU_H
+#define MHU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+#include <mod_mhu.h>
+
+/*!
+ * \brief MHU Register Definitions
+ */
+struct mhu_reg {
+ /*! Status register */
+ FWK_R uint32_t STAT;
+ uint32_t RESERVED0;
+ /*! Set register (sets the value of STAT) */
+ FWK_W uint32_t SET;
+ uint32_t RESERVED1;
+ /*! Clear register (clears STAT) */
+ FWK_W uint32_t CLEAR;
+};
+
+#endif /* MHU_H */
diff --git a/module/mhu/include/mod_mhu.h b/module/mhu/include/mod_mhu.h
new file mode 100644
index 00000000..feec6596
--- /dev/null
+++ b/module/mhu/include/mod_mhu.h
@@ -0,0 +1,53 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Message Handling Unit (MHU) Device Driver.
+ */
+
+#ifndef MOD_MHU_H
+#define MOD_MHU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupMHU Message Handling Unit (MHU) Driver
+ * @{
+ */
+
+/*!
+ * \brief MHU device
+ *
+ * \details Abstract representation of a bidirectional MHU device that consists
+ * of a single receive interrupt line and a pair of register sets, one for
+ * each direction of communication.
+ */
+struct mod_mhu_device_config {
+ /*! IRQ number of the receive interrupt line */
+ unsigned int irq;
+
+ /*! Base address of the registers of the incoming MHU */
+ uintptr_t in;
+
+ /*! Base address of the registers of the outgoing MHU */
+ uintptr_t out;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_MHU_H */
diff --git a/module/mhu/src/Makefile b/module/mhu/src/Makefile
new file mode 100644
index 00000000..959a1cf6
--- /dev/null
+++ b/module/mhu/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := MHU
+BS_LIB_SOURCES := mod_mhu.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/mhu/src/mod_mhu.c b/module/mhu/src/mod_mhu.c
new file mode 100644
index 00000000..761d719a
--- /dev/null
+++ b/module/mhu/src/mod_mhu.c
@@ -0,0 +1,256 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Message Handling Unit (MHU) Device Driver.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/mhu.h>
+#include <mod_smt.h>
+
+/*
+ * Maximum number of slots per MHU device. The maximum number of slots is 31 and
+ * not 32 because bit [31] in the MHU STAT register is reserved in secure MHUs
+ * for indicating that a non-secure access attempt occurred. This reservation
+ * also applies to non-secure MHUs for consistency, though the bit is unused.
+ */
+#define MHU_SLOT_COUNT_MAX 31
+
+struct mhu_smt_channel {
+ fwk_id_t id;
+ struct mod_smt_driver_input_api *api;
+};
+
+/* MHU device context */
+struct mhu_device_ctx {
+ /* Pointer to the device configuration */
+ const struct mod_mhu_device_config *config;
+
+ /* Number of slots (represented by sub-elements) */
+ unsigned int slot_count;
+
+ /* Mask of slots that are bound to an SMT channel */
+ uint32_t bound_slots;
+
+ /* Table of SMT channels bound to the device */
+ struct mhu_smt_channel *smt_channel_table;
+};
+
+/* MHU context */
+struct mhu_ctx {
+ /* Table of device contexts */
+ struct mhu_device_ctx *device_ctx_table;
+
+ /* Number of devices in the device context table*/
+ unsigned int device_count;
+};
+
+static struct mhu_ctx mhu_ctx;
+
+static void mhu_isr(void)
+{
+ int status;
+ unsigned int interrupt;
+ unsigned int device_idx;
+ struct mhu_device_ctx *device_ctx;
+ struct mhu_reg *reg;
+ unsigned int slot;
+ struct mhu_smt_channel *smt_channel;
+
+ status = fwk_interrupt_get_current(&interrupt);
+ if (status != FWK_SUCCESS)
+ return;
+
+ for (device_idx = 0; device_idx < mhu_ctx.device_count; device_idx++) {
+ device_ctx = &mhu_ctx.device_ctx_table[device_idx];
+ if (device_ctx->config->irq == interrupt)
+ break;
+ }
+
+ if (device_idx >= mhu_ctx.device_count)
+ return;
+
+ reg = (struct mhu_reg *)device_ctx->config->in;
+
+ /* Loop over all the slots */
+ while (reg->STAT != 0) {
+ slot = __builtin_ctz(reg->STAT);
+
+ /*
+ * If the slot is bound to an SMT channel, signal the message to the
+ * SMT channel.
+ */
+ if (device_ctx->bound_slots & (1 << slot)) {
+ smt_channel = &device_ctx->smt_channel_table[slot];
+ smt_channel->api->signal_message(smt_channel->id);
+ }
+
+ /* Acknowledge the interrupt */
+ reg->CLEAR = 1 << slot;
+ }
+}
+
+/*
+ * SMT module driver API
+ */
+
+static int raise_interrupt(fwk_id_t slot_id)
+{
+ int status;
+ struct mhu_device_ctx *device_ctx;
+ unsigned int slot;
+ struct mhu_reg *reg;
+
+ status = fwk_module_check_call(slot_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(slot_id)];
+ slot = fwk_id_get_sub_element_idx(slot_id);
+ reg = (struct mhu_reg *)device_ctx->config->out;
+
+ reg->SET |= (1 << slot);
+
+ return FWK_SUCCESS;
+}
+
+const struct mod_smt_driver_api mhu_mod_smt_driver_api = {
+ .raise_interrupt = raise_interrupt,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int mhu_init(fwk_id_t module_id, unsigned int device_count,
+ const void *unused)
+{
+ if (device_count == 0)
+ return FWK_E_PARAM;
+
+ mhu_ctx.device_ctx_table = fwk_mm_calloc(device_count,
+ sizeof(mhu_ctx.device_ctx_table[0]));
+ if (mhu_ctx.device_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ mhu_ctx.device_count = device_count;
+
+ return FWK_SUCCESS;
+}
+
+static int mhu_device_init(fwk_id_t device_id, unsigned int slot_count,
+ const void *data)
+{
+ struct mod_mhu_device_config *config = (struct mod_mhu_device_config *)data;
+ struct mhu_device_ctx *device_ctx;
+
+ if ((config->in == 0) || (config->out == 0))
+ return FWK_E_PARAM;
+
+ device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(device_id)];
+
+ device_ctx->smt_channel_table = fwk_mm_calloc(slot_count,
+ sizeof(device_ctx->smt_channel_table[0]));
+ if (device_ctx->smt_channel_table == NULL)
+ return FWK_E_NOMEM;
+
+ device_ctx->config = config;
+ device_ctx->slot_count = slot_count;
+
+ return FWK_SUCCESS;
+}
+
+static int mhu_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct mhu_device_ctx *device_ctx;
+ unsigned int slot;
+ struct mhu_smt_channel *smt_channel;
+
+ if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
+ device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(id)];
+
+ for (slot = 0; slot < MHU_SLOT_COUNT_MAX; slot++) {
+ if (!(device_ctx->bound_slots & (1 << slot)))
+ continue;
+
+ smt_channel = &device_ctx->smt_channel_table[slot];
+
+ status = fwk_module_bind(smt_channel->id,
+ FWK_ID_API(FWK_MODULE_IDX_SMT, MOD_SMT_API_IDX_DRIVER_INPUT),
+ &smt_channel->api);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int mhu_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ struct mhu_device_ctx *device_ctx;
+ unsigned int slot;
+
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_SUB_ELEMENT))
+ return FWK_E_ACCESS;
+
+ device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(target_id)];
+ slot = fwk_id_get_sub_element_idx(target_id);
+
+ if (device_ctx->bound_slots & (1 << slot))
+ return FWK_E_ACCESS;
+
+ device_ctx->smt_channel_table[slot].id = source_id;
+ device_ctx->bound_slots |= 1 << slot;
+
+ *api = &mhu_mod_smt_driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int mhu_start(fwk_id_t id)
+{
+ int status;
+ struct mhu_device_ctx *device_ctx;
+
+ if (fwk_id_get_type(id) == FWK_ID_TYPE_MODULE)
+ return FWK_SUCCESS;
+
+ device_ctx = &mhu_ctx.device_ctx_table[fwk_id_get_element_idx(id)];
+
+ if (device_ctx->bound_slots != 0) {
+ status = fwk_interrupt_set_isr(device_ctx->config->irq, &mhu_isr);
+ if (status != FWK_SUCCESS)
+ return status;
+ status = fwk_interrupt_enable(device_ctx->config->irq);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+/* MHU module definition */
+const struct fwk_module module_mhu = {
+ .name = "MHU",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = 1,
+ .init = mhu_init,
+ .element_init = mhu_device_init,
+ .bind = mhu_bind,
+ .start = mhu_start,
+ .process_bind_request = mhu_process_bind_request,
+};
diff --git a/module/mock_psu/include/mod_mock_psu.h b/module/mock_psu/include/mod_mock_psu.h
new file mode 100644
index 00000000..f1d6ae4a
--- /dev/null
+++ b/module/mock_psu/include/mod_mock_psu.h
@@ -0,0 +1,69 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MOCK_PSU_H
+#define MOD_MOCK_PSU_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \ingroup GroupModules
+ * \defgroup GroupMockPsu Mock Power Supply Driver
+ * \{
+ */
+
+/*!
+ * \defgroup GroupMockPsuConfig Configuration
+ * \{
+ */
+
+/*!
+ * \brief Element configuration.
+ */
+struct mod_mock_psu_device_config {
+ /*! Default state of the mock device's supply (enabled or disabled) */
+ bool default_enabled;
+
+ /*! Default voltage, in millivolts (mV), of the device's supply */
+ uint64_t default_voltage;
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupMockPsuIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_mock_psu_api_idx {
+ /*! API index for PSU driver API */
+ MOD_MOCK_PSU_API_IDX_PSU_DRIVER,
+
+ /*! Number of defined APIs */
+ MOD_MOCK_PSU_API_COUNT
+};
+
+/*! Driver API identifier */
+static const fwk_id_t mod_mock_psu_api_id_psu_driver =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_PSU, MOD_MOCK_PSU_API_IDX_PSU_DRIVER);
+
+/*!
+ * \}
+ */
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_MOCK_PSU_H */
diff --git a/module/mock_psu/src/Makefile b/module/mock_psu/src/Makefile
new file mode 100644
index 00000000..5ab1db02
--- /dev/null
+++ b/module/mock_psu/src/Makefile
@@ -0,0 +1,13 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := mock_psu
+BS_LIB_SOURCES := \
+ mod_mock_psu_module.c \
+ mod_mock_psu_psu_driver_api.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/mock_psu/src/mod_mock_psu_module.c b/module/mock_psu/src/mod_mock_psu_module.c
new file mode 100644
index 00000000..c39ef7bd
--- /dev/null
+++ b/module/mock_psu/src/mod_mock_psu_module.c
@@ -0,0 +1,87 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_mock_psu.h>
+#include <mod_mock_psu_private.h>
+
+static struct mod_mock_psu_device_ctx (*device_ctx)[];
+
+static struct mod_mock_psu_device_ctx *get_device_ctx(
+ fwk_id_t device_id)
+{
+ return &(*device_ctx)[fwk_id_get_element_idx(device_id)];
+}
+
+static int mock_psu_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ device_ctx = fwk_mm_calloc(
+ element_count,
+ sizeof((*device_ctx)[0]));
+ if (device_ctx == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int mock_psu_element_init(
+ fwk_id_t device_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ struct mod_mock_psu_device_ctx *ctx;
+ const struct mod_mock_psu_device_config *config = data;
+
+ assert(sub_element_count == 0);
+
+ ctx = get_device_ctx(device_id);
+ ctx->enabled = config->default_enabled;
+ ctx->voltage = config->default_voltage;
+
+ return FWK_SUCCESS;
+}
+
+static int mock_psu_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only accept binds to the elements */
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_PARAM;
+
+ *api = &__mod_mock_psu_psu_driver_api;
+
+ return FWK_SUCCESS;
+}
+
+struct mod_mock_psu_device_ctx *__mod_mock_psu_get_valid_device_ctx(
+ fwk_id_t device_id)
+{
+ if (fwk_module_check_call(device_id) != FWK_SUCCESS)
+ return NULL;
+
+ return get_device_ctx(device_id);
+}
+
+/* Module description */
+const struct fwk_module module_mock_psu = {
+ .name = "MOCK_PSU",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_MOCK_PSU_API_COUNT,
+ .init = mock_psu_init,
+ .element_init = mock_psu_element_init,
+ .process_bind_request = mock_psu_process_bind_request,
+};
diff --git a/module/mock_psu/src/mod_mock_psu_module_private.h b/module/mock_psu/src/mod_mock_psu_module_private.h
new file mode 100644
index 00000000..25f73fc7
--- /dev/null
+++ b/module/mock_psu/src/mod_mock_psu_module_private.h
@@ -0,0 +1,22 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MOCK_PSU_MODULE_PRIVATE_H
+#define MOD_MOCK_PSU_MODULE_PRIVATE_H
+
+#include <stdbool.h>
+#include <fwk_id.h>
+
+struct mod_mock_psu_device_ctx {
+ bool enabled; /* Current enabled state */
+ uint64_t voltage; /* Current voltage (in mV) */
+};
+
+struct mod_mock_psu_device_ctx *__mod_mock_psu_get_valid_device_ctx(
+ fwk_id_t device_id);
+
+#endif /* MOD_MOCK_PSU_MODULE_PRIVATE_H */
diff --git a/module/mock_psu/src/mod_mock_psu_private.h b/module/mock_psu/src/mod_mock_psu_private.h
new file mode 100644
index 00000000..a80a54a8
--- /dev/null
+++ b/module/mock_psu/src/mod_mock_psu_private.h
@@ -0,0 +1,17 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MOCK_PSU_PRIVATE_H
+#define MOD_MOCK_PSU_PRIVATE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <mod_mock_psu_module_private.h>
+#include <mod_mock_psu_psu_driver_api_private.h>
+#include <mod_mock_psu.h>
+
+#endif /* MOD_MOCK_PSU_PRIVATE_H */
diff --git a/module/mock_psu/src/mod_mock_psu_psu_driver_api.c b/module/mock_psu/src/mod_mock_psu_psu_driver_api.c
new file mode 100644
index 00000000..fa510536
--- /dev/null
+++ b/module/mock_psu/src/mod_mock_psu_psu_driver_api.c
@@ -0,0 +1,71 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <mod_mock_psu_private.h>
+
+static int api_set_enabled(fwk_id_t device_id, bool enable)
+{
+ struct mod_mock_psu_device_ctx *ctx;
+
+ ctx = __mod_mock_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ ctx->enabled = enable;
+
+ return FWK_SUCCESS;
+}
+
+static int api_get_enabled(fwk_id_t device_id, bool *enabled)
+{
+ struct mod_mock_psu_device_ctx *ctx;
+
+ ctx = __mod_mock_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *enabled = ctx->enabled;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_voltage(fwk_id_t device_id, uintmax_t voltage)
+{
+ struct mod_mock_psu_device_ctx *ctx;
+
+ ctx = __mod_mock_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ ctx->voltage = voltage;
+
+ return FWK_SUCCESS;
+}
+
+static int api_get_voltage(fwk_id_t device_id, uintmax_t *voltage)
+{
+ struct mod_mock_psu_device_ctx *ctx;
+
+ ctx = __mod_mock_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *voltage = ctx->voltage;
+
+ return FWK_SUCCESS;
+}
+
+const struct mod_psu_driver_api __mod_mock_psu_psu_driver_api = {
+ .set_enabled = api_set_enabled,
+ .get_enabled = api_get_enabled,
+ .set_voltage = api_set_voltage,
+ .get_voltage = api_get_voltage,
+};
diff --git a/module/mock_psu/src/mod_mock_psu_psu_driver_api_private.h b/module/mock_psu/src/mod_mock_psu_psu_driver_api_private.h
new file mode 100644
index 00000000..397b7052
--- /dev/null
+++ b/module/mock_psu/src/mod_mock_psu_psu_driver_api_private.h
@@ -0,0 +1,15 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_MOCK_PSU_PSU_DRIVER_API_PRIVATE_H
+#define MOD_MOCK_PSU_PSU_DRIVER_API_PRIVATE_H
+
+#include <mod_psu.h>
+
+extern const struct mod_psu_driver_api __mod_mock_psu_psu_driver_api;
+
+#endif /* MOD_MOCK_PSU_PSU_DRIVER_API_PRIVATE_H */
diff --git a/module/msys_rom/include/mod_msys_rom.h b/module/msys_rom/include/mod_msys_rom.h
new file mode 100644
index 00000000..82ca90f9
--- /dev/null
+++ b/module/msys_rom/include/mod_msys_rom.h
@@ -0,0 +1,69 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description: Module dedicated to the ROM firmware of the mobile platforms to
+ * initiate the boot of the primary AP core and then to jump to the SCP RAM
+ * firmware.
+ */
+
+#ifndef MOD_MSYS_ROM_H
+#define MOD_MSYS_ROM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_id.h>
+
+/*!
+ * \ingroup GroupMSYSModule
+ * \defgroup GroupMSYSROM ROM Support
+ * @{
+ */
+
+/*!
+ * \brief Module configuration data.
+ */
+struct msys_rom_config {
+ /*! Base address of the Application Processor (AP) context area */
+ const uintptr_t ap_context_base;
+
+ /*! Size of the AP context area */
+ const size_t ap_context_size;
+
+ /*! Base address of the RAM firmware image */
+ const uintptr_t ramfw_base;
+
+ /*! Element ID of the primary cluster PPU */
+ const fwk_id_t id_primary_cluster;
+
+ /*! Element ID of the primary core PPU */
+ const fwk_id_t id_primary_core;
+};
+
+/*!
+ * \brief Notification indices.
+ */
+enum mod_msys_rom_notification_idx {
+ /*! <tt>SYSTOP powered on</tt> notification */
+ MOD_MSYS_ROM_NOTIFICATION_IDX_POWER_SYSTOP,
+
+ /*! Number of notifications defined by the module */
+ MOD_MSYS_ROM_NOTIFICATION_COUNT,
+};
+
+/*! <tt>SYSTOP powered on</tt> notification identifier */
+static const fwk_id_t mod_msys_rom_notification_id_systop =
+ FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_MSYS_ROM,
+ MOD_MSYS_ROM_NOTIFICATION_IDX_POWER_SYSTOP);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_MSYS_ROM_H */
diff --git a/module/msys_rom/src/Makefile b/module/msys_rom/src/Makefile
new file mode 100644
index 00000000..416e8c41
--- /dev/null
+++ b/module/msys_rom/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := msys ROM
+BS_LIB_SOURCES := mod_msys_rom.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/msys_rom/src/mod_msys_rom.c b/module/msys_rom/src/mod_msys_rom.c
new file mode 100644
index 00000000..354f112f
--- /dev/null
+++ b/module/msys_rom/src/mod_msys_rom.c
@@ -0,0 +1,217 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description: Module dedicated to the ROM firmware of the mobile platforms to
+ * initiate the boot of the primary AP core and then to jump to the SCP RAM
+ * firmware.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdnoreturn.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_interrupt.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_notification.h>
+#include <fwk_thread.h>
+#include <mod_bootloader.h>
+#include <mod_log.h>
+#include <mod_msys_rom.h>
+#include <mod_power_domain.h>
+#include <mod_ppu_v1.h>
+
+struct msys_rom_ctx {
+ const struct msys_rom_config *rom_config;
+ struct mod_log_api *log_api;
+ struct ppu_v1_boot_api *ppu_boot_api;
+ struct mod_bootloader_api *bootloader_api;
+ unsigned int notification_count; /* Notifications awaiting a response */
+} ctx;
+
+enum rom_event {
+ ROM_EVENT_RUN,
+ ROM_EVENT_COUNT
+};
+
+/*
+ * This function assumes that the RAM firmware image is located at the beginning
+ * of the SCP SRAM. The reset handler will be at offset 0x4.
+ */
+static noreturn void msys_jump_to_ramfw(void)
+{
+ uintptr_t const *reset_base =
+ (uintptr_t *)(ctx.rom_config->ramfw_base + 0x4);
+ void (*ramfw_reset_handler)(void);
+
+ /*
+ * Disable interrupts for the duration of the ROM firmware to RAM firmware
+ * transition.
+ */
+ fwk_interrupt_global_disable();
+
+ ramfw_reset_handler = (void (*)(void))*reset_base;
+
+ /*
+ * Execute the RAM firmware's reset handler to pass control from ROM
+ * firmware to the RAM firmware.
+ */
+ ramfw_reset_handler();
+
+ while (true)
+ continue;
+}
+
+static int msys_deferred_setup(void)
+{
+ int status;
+
+ /* Initialize the AP context area by zeroing it */
+ memset((void *)ctx.rom_config->ap_context_base,
+ 0,
+ ctx.rom_config->ap_context_size);
+
+ /* Power on the primary cluster and cpu */
+ ctx.ppu_boot_api->power_mode_on(ctx.rom_config->id_primary_cluster);
+ ctx.ppu_boot_api->power_mode_on(ctx.rom_config->id_primary_core);
+
+ ctx.log_api->log(MOD_LOG_GROUP_INFO, "[SYSTEM] Primary CPU powered\n");
+
+ status = ctx.bootloader_api->load_image();
+ if (status != FWK_SUCCESS) {
+ ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SYSTEM] Failed to load RAM firmware image\n");
+ return FWK_E_DATA;
+ }
+
+ msys_jump_to_ramfw();
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int msys_rom_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ ctx.rom_config = data;
+
+ if ((ctx.rom_config->ap_context_base == 0) ||
+ (ctx.rom_config->ap_context_size == 0))
+ return FWK_E_RANGE;
+
+ return FWK_SUCCESS;
+}
+
+static int msys_rom_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ /* Use second round only (round numbering is zero-indexed) */
+ if (round == 1) {
+
+ /* Bind to the log component */
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &ctx.log_api);
+
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Bind to the PPU module */
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_PPU_V1),
+ FWK_ID_API(FWK_MODULE_IDX_PPU_V1,
+ MOD_PPU_V1_API_IDX_BOOT),
+ &ctx.ppu_boot_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Bind to the bootloader module */
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_BOOTLOADER),
+ FWK_ID_API(FWK_MODULE_IDX_BOOTLOADER, 0),
+ &ctx.bootloader_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int msys_rom_start(fwk_id_t id)
+{
+ struct fwk_event event = {
+ .source_id = FWK_ID_MODULE(FWK_MODULE_IDX_MSYS_ROM),
+ .target_id = FWK_ID_MODULE(FWK_MODULE_IDX_MSYS_ROM),
+ .id = FWK_ID_EVENT(FWK_MODULE_IDX_MSYS_ROM, ROM_EVENT_RUN),
+ };
+
+ return fwk_thread_put_event(&event);
+}
+
+static int msys_rom_process_event(const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ int status;
+ struct mod_pd_power_state_transition_notification_params
+ *notification_params;
+ struct fwk_event systop_on_event = {
+ .response_requested = true,
+ .id = mod_msys_rom_notification_id_systop,
+ };
+
+ /* Notify any subscribers of the SYSTOP power domain state change */
+ notification_params =
+ (struct mod_pd_power_state_transition_notification_params *)
+ systop_on_event.params;
+ notification_params->state = MOD_PD_STATE_ON;
+
+ status = fwk_notification_notify(&systop_on_event, &ctx.notification_count);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Complete remaining setup now if there are no subscribers to respond */
+ if (ctx.notification_count == 0)
+ return msys_deferred_setup();
+
+ return FWK_SUCCESS;
+}
+
+static int msys_rom_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ assert(fwk_id_is_equal(event->id, mod_msys_rom_notification_id_systop));
+ assert(event->is_response == true);
+
+ /* At least one notification response must be outstanding */
+ if (ctx.notification_count == 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ /* Complete remaining setup now that all subscribers have responded */
+ if ((--ctx.notification_count) == 0)
+ return msys_deferred_setup();
+
+ return FWK_SUCCESS;
+}
+
+/* Module descriptor */
+const struct fwk_module module_msys_rom = {
+ .name = "MSYS_ROM",
+ .type = FWK_MODULE_TYPE_SERVICE,
+ .event_count = ROM_EVENT_COUNT,
+ .notification_count = MOD_MSYS_ROM_NOTIFICATION_COUNT,
+ .init = msys_rom_init,
+ .bind = msys_rom_bind,
+ .start = msys_rom_start,
+ .process_event = msys_rom_process_event,
+ .process_notification = msys_rom_process_notification,
+};
diff --git a/module/pik_clock/include/mod_pik_clock.h b/module/pik_clock/include/mod_pik_clock.h
new file mode 100644
index 00000000..d03e7b36
--- /dev/null
+++ b/module/pik_clock/include/mod_pik_clock.h
@@ -0,0 +1,269 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PIK_CLOCK_H
+#define MOD_PIK_CLOCK_H
+
+#include <stdint.h>
+#include <fwk_element.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupPIKClock PIK Clock Driver
+ *
+ * \details A driver for clock devices that are part of a PIK.
+ *
+ * @{
+ */
+
+/*!
+ * \brief APIs provided by the driver.
+ */
+enum mod_pik_clock_api_type {
+ /*! An implementation of the Clock HAL module's clock driver API */
+ MOD_PIK_CLOCK_API_TYPE_CLOCK,
+ /*! A low-level API for direct control of CSS clocks */
+ MOD_PIK_CLOCK_API_TYPE_CSS,
+ MOD_PIK_CLOCK_API_COUNT,
+};
+
+/*!
+ * \brief Sub-types of PIK clock.
+ */
+enum mod_pik_clock_type {
+ /*! A clock with a fixed source. Only its divider can be changed. */
+ MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE,
+ /*! A clock with multiple, selectable sources and at least one divider. */
+ MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ /*!
+ * A clock with multiple, selectable sources, at least one divider, and
+ * support for modulation.
+ */
+ MOD_PIK_CLOCK_TYPE_CLUSTER,
+};
+
+/*!
+ * \brief Divider register types.
+ */
+enum mod_pik_clock_msclock_divider {
+ /*! Divider affecting the system PLL clock source. */
+ MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ /*! Divider affecting the private or external PLL clock sources. */
+ MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+};
+
+/*!
+ * \brief Selectable clock sources for multi-source clocks.
+ */
+enum mod_pik_clock_msclock_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to the system PLL clock */
+ MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK = 2,
+ /*! The clock source is set to a private PLL */
+ MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK = 4,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_MSCLOCK_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for V8.2 cluster clocks.
+ */
+enum mod_clusclock_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0 = 2,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1 = 3,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL2 = 4,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL3 = 5,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL4 = 6,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL5 = 7,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL6 = 8,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL7 = 9,
+ /*! The clock source is set to the system PLL clock */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSPLLCLK = 10,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_CLUSCLK_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for GPU clocks.
+ */
+enum mod_pik_clock_gpuclock_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_GPUCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_GPUCLK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to the system PLL clock */
+ MOD_PIK_CLOCK_GPUCLK_SOURCE_SYSPLLCLK = 2,
+ /*! The clock source is set to the dedicated GPU PLL */
+ MOD_PIK_CLOCK_GPUCLK_SOURCE_GPUPLLCLK = 4,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_GPUCLK_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for DPU scaler clocks.
+ */
+enum mod_pik_clock_dpuclock_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_DPUCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_DPUCLK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to the dedicated display PLL */
+ MOD_PIK_CLOCK_DPUCLK_SOURCE_DISPLAYPLLCLK = 2,
+ /*! The clock source is set to a pixel clock PLL */
+ MOD_PIK_CLOCK_DPUCLK_SOURCE_PIXELCLK = 4,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_DPUCLK_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for the DPU AXI clock.
+ */
+enum mod_pik_clock_aclkdpu_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_ACLKDPU_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_ACLKDPU_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to the dedicated display PLL */
+ MOD_PIK_CLOCK_ACLKDPU_SOURCE_DISPLAYPLLCLK = 2,
+ /*! The clock source is set to the system PLL clock */
+ MOD_PIK_CLOCK_ACLKDPU_SOURCE_SYSPLLCLK = 4,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_ACLKDPU_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for the video processor clock.
+ */
+enum mod_pik_clock_vpuclk_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_VPUCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_VPUCLK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to the system PLL clock */
+ MOD_PIK_CLOCK_VPUCLK_SOURCE_SYSPLLCLK = 2,
+ /*! The clock source is set to the dedicated video PLL */
+ MOD_PIK_CLOCK_VPUCLK_SOURCE_VIDEOPLLCLK = 4,
+ /*! Number of valid clock sources */
+ MOD_PIK_CLOCK_VPUCLK_SOURCE_MAX
+};
+
+/*!
+ * \brief Selectable clock sources for interconnect clock.
+ */
+enum mod_pik_clock_intclk_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_INTCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_INTCLK_SOURCE_SYSREFCLK = 1,
+ /*! The clock source is set to a private PLL */
+ MOD_PIK_CLOCK_INTCLK_SOURCE_INTPLL = 2,
+};
+
+/*!
+ * \brief Selectable clock sources for DMC clock.
+ */
+enum mod_pik_clock_dmcclk_source {
+ /*! The clock is gated */
+ MOD_PIK_CLOCK_DMCCLK_SOURCE_GATED = 0,
+ /*! The clock source is set to the system reference clock */
+ MOD_PIK_CLOCK_DMCCLK_SOURCE_REFCLK = 1,
+ /*! The clock source is set to a private PLL */
+ MOD_PIK_CLOCK_DMCCLK_SOURCE_DDRPLL = 2,
+};
+
+/*!
+ * \brief Rate lookup entry.
+ */
+struct mod_pik_clock_rate {
+ /*! Rate in Hertz. */
+ uint64_t rate;
+ /*! Clock source used to obtain the rate (multi-source clocks only). */
+ uint8_t source;
+ /*! The divider register to use (multi-source clocks only). */
+ enum mod_pik_clock_msclock_divider divider_reg;
+ /*! Divider used to obtain the rate. */
+ uint8_t divider;
+};
+
+/*!
+ * \brief Subsystem clock device configuration.
+ */
+struct mod_pik_clock_dev_config {
+ /*! The type of the clock device. */
+ enum mod_pik_clock_type type;
+
+ /*!
+ * \brief Indicates whether the clock is part of a CSS clock group (\c true)
+ * or operating as an independent clock (\c false).
+ *
+ * \details The value determines the API that the clock exposes during
+ * binding. If the clock is part of a group then the \ref
+ * mod_pik_clock_api_type.MOD_PIK_CLOCK_API_TYPE_CSS API is exposed for
+ * direct control via the CSS Clock module, otherwise the \ref
+ * mod_pik_clock_api_type.MOD_PIK_CLOCK_API_TYPE_CLOCK API, defined by
+ * the Clock HAL, is exposed.
+ */
+ bool is_group_member;
+
+ /*! Pointer to the clock's control register. */
+ volatile uint32_t * const control_reg;
+
+ /*! Pointer to the clock's modulator register, if any. */
+ volatile uint32_t * const modulator_reg;
+
+ /*! Pointer to the clock's DIV_SYS divider register, if any. */
+ volatile uint32_t * const divsys_reg;
+
+ /*! Pointer to the clock's DIV_EXT divider register, if any. */
+ volatile uint32_t * const divext_reg;
+
+ /*! Pointer to the clock's rate lookup table. */
+ const struct mod_pik_clock_rate *rate_table;
+
+ /*! The number of rates in the rate lookup table. */
+ uint32_t rate_count;
+
+ /*! The rate, in Hz, to set during module initialization. */
+ uint64_t initial_rate;
+
+ /*!
+ * If \c true, the driver will not attempt to set a default frequency, or to
+ * otherwise configure the PLL during the pre-runtime phase. The PLL is
+ * expected to be initialized later in response to a notification or other
+ * event.
+ */
+ const bool defer_initialization;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_PIK_CLOCK_H */
diff --git a/module/pik_clock/src/Makefile b/module/pik_clock/src/Makefile
new file mode 100644
index 00000000..0437f00d
--- /dev/null
+++ b/module/pik_clock/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := PIK Clock Driver
+BS_LIB_SOURCES := mod_pik_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/pik_clock/src/mod_pik_clock.c b/module/pik_clock/src/mod_pik_clock.c
new file mode 100644
index 00000000..e03788f7
--- /dev/null
+++ b/module/pik_clock/src/mod_pik_clock.c
@@ -0,0 +1,767 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_css_clock.h>
+#include <mod_pik_clock.h>
+#include <mod_power_domain.h>
+
+/*
+ * Masks for single-source clock divider control.
+ */
+#define SSCLK_CONTROL_CLKDIV UINT32_C(0x000000F0)
+#define SSCLK_CONTROL_CRNTCLKDIV UINT32_C(0x0000F000)
+#define SSCLK_CONTROL_ENTRY_DLY UINT32_C(0xFF000000)
+
+/*
+ * Offsets for single-source clock divider control.
+ */
+#define SSCLK_CONTROL_CLKDIV_POS 4
+#define SSCLK_CONTROL_CRNTCLKDIV_POS 12
+#define SSCLK_CONTROL_ENTRY_DLY_POS 24
+
+/*
+ * Masks for multi-source clock divider control (both DIV1 and DIV2).
+ */
+#define MSCLK_DIV_CLKDIV UINT32_C(0x0000001F)
+#define MSCLK_DIV_CRNTCLKDIV UINT32_C(0x001F0000)
+#define MSCLK_DIV_ENTRY_DLY UINT32_C(0xFF000000)
+
+/*
+ * Offsets for multi-source clock divider control (both DIV1 and DIV2).
+ */
+#define MSCLK_DIV_CLKDIV_POS 0
+#define MSCLK_DIV_CRNTCLKDIV_POS 16
+#define MSCLK_DIV_ENTRY_DLY_POS 24
+
+/*
+ * Masks for multi-source clock source selection.
+ */
+#define MSCLK_CONTROL_CLKSEL UINT32_C(0x000000FF)
+#define MSCLK_CONTROL_CLKSEL_GATED UINT32_C(0x00000000)
+#define MSCLK_CONTROL_CLKSEL_SYSREFCLK UINT32_C(0x00000001)
+#define MSCLK_CONTROL_CLKSEL_SYSPLLCLK UINT32_C(0x00000002)
+#define MSCLK_CONTROL_CLKSEL_PRIVPLLCLK UINT32_C(0x00000004)
+#define MSCLK_CONTROL_CRNTCLK UINT32_C(0x0000FF00)
+
+/*
+ * Offsets for multi-source clock source selection.
+ */
+#define MSCLK_CONTROL_CLKSEL_POS 0
+#define MSCLK_CONTROL_CRNTCLK_POS 8
+
+/*
+ * Masks for cluster clock source selection.
+ *
+ * Note: The CLKSEL field mask is shared with the multi-source clock subtype, as
+ * are the masks for the GATED and REFCLK sources.
+ */
+#define CLUSCLK_CONTROL_CLKSEL_PLL0 UINT32_C(0x00000002)
+#define CLUSCLK_CONTROL_CLKSEL_PLL1 UINT32_C(0x00000003)
+#define CLUSCLK_CONTROL_CLKSEL_PLL2 UINT32_C(0x00000004)
+#define CLUSCLK_CONTROL_CLKSEL_PLL3 UINT32_C(0x00000005)
+#define CLUSCLK_CONTROL_CLKSEL_PLL4 UINT32_C(0x00000006)
+#define CLUSCLK_CONTROL_CLKSEL_PLL5 UINT32_C(0x00000007)
+#define CLUSCLK_CONTROL_CLKSEL_PLL6 UINT32_C(0x00000008)
+#define CLUSCLK_CONTROL_CLKSEL_PLL7 UINT32_C(0x00000009)
+#define CLUSCLK_CONTROL_CLKSEL_SYSPLLCLK UINT32_C(0x0000000A)
+
+/*
+ * Masks for cluster clock modulator control.
+ */
+#define CLUSCLK_MOD_DENOMINATOR UINT32_C(0x000000FF)
+#define CLUSCLK_MOD_NUMERATOR UINT32_C(0x0000FF00)
+#define CLUSCLK_MOD_CRNTDENOMINATOR UINT32_C(0x00FF0000)
+#define CLUSCLK_MOD_CRNTNUMERATOR UINT32_C(0xFF000000)
+
+/*
+ * Offsets for cluster clock modulator control.
+ */
+#define CLUSCLK_MOD_DENOMINATOR_POS 0
+#define CLUSCLK_MOD_NUMERATOR_POS 8
+#define CLUSCLK_MOD_CRNTDENOMINATOR_POS 16
+#define CLUSCLK_MOD_CRNTNUMERATOR_POS 24
+
+/* Device context */
+struct pik_clock_dev_ctx {
+ bool initialized;
+ uint64_t current_rate;
+ uint8_t current_source;
+ enum mod_clock_state current_state;
+ const struct mod_pik_clock_dev_config *config;
+};
+
+/* Module context */
+struct pik_clock_ctx {
+ struct pik_clock_dev_ctx *dev_ctx_table;
+ unsigned int dev_count;
+};
+
+static struct pik_clock_ctx module_ctx;
+
+/*
+ * Static helper functions
+ */
+
+static int compare_rate_entry(const void *a, const void *b)
+{
+ struct mod_pik_clock_rate *key = (struct mod_pik_clock_rate *)a;
+ struct mod_pik_clock_rate *element = (struct mod_pik_clock_rate *)b;
+
+ return (key->rate - element->rate);
+}
+
+static int get_rate_entry(struct pik_clock_dev_ctx *ctx, uint64_t target_rate,
+ struct mod_pik_clock_rate **entry)
+{
+ struct mod_pik_clock_rate *current_rate_entry;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (entry == NULL)
+ return FWK_E_PARAM;
+
+ /* Perform a binary search to find the entry matching the requested rate */
+ current_rate_entry = (struct mod_pik_clock_rate *) bsearch(&target_rate,
+ ctx->config->rate_table, ctx->config->rate_count,
+ sizeof(struct mod_pik_clock_rate), compare_rate_entry);
+
+ if (current_rate_entry == NULL)
+ return FWK_E_PARAM;
+
+ *entry = current_rate_entry;
+ return FWK_SUCCESS;
+}
+
+static int ssclock_set_div(struct pik_clock_dev_ctx *ctx, uint32_t divider,
+ bool wait_after_set)
+{
+ uint32_t clkdiv;
+
+ if (divider == 0)
+ return FWK_E_PARAM;
+ if (divider > 16)
+ return FWK_E_PARAM;
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (ctx->config->type != MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE)
+ return FWK_E_PARAM;
+
+ /* The resulting divider is the programmed value plus one */
+ clkdiv = divider - 1;
+
+ /* Set */
+ *ctx->config->control_reg =
+ (*ctx->config->control_reg & ~SSCLK_CONTROL_CLKDIV) |
+ (clkdiv << SSCLK_CONTROL_CLKDIV_POS);
+
+ if (wait_after_set) {
+ while ((*ctx->config->control_reg & SSCLK_CONTROL_CRNTCLKDIV) !=
+ (clkdiv << SSCLK_CONTROL_CRNTCLKDIV_POS))
+ continue;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int msclock_set_div(struct pik_clock_dev_ctx *ctx,
+ enum mod_pik_clock_msclock_divider divider_type,
+ uint32_t divider,
+ bool wait_after_set)
+{
+ volatile uint32_t * divider_reg;
+ uint32_t clkdiv;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (divider == 0)
+ return FWK_E_PARAM;
+ if (divider > 16)
+ return FWK_E_PARAM;
+ if (ctx->config->type == MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE)
+ return FWK_E_PARAM;
+
+ /* The resulting divider is the programmed value plus one */
+ clkdiv = divider - 1;
+
+ if (divider_type == MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS)
+ divider_reg = ctx->config->divsys_reg;
+ else if (divider_type == MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT)
+ divider_reg = ctx->config->divext_reg;
+ else
+ return FWK_E_PARAM;
+
+ /* Set */
+ *divider_reg = (*divider_reg & ~MSCLK_DIV_CLKDIV) |
+ (clkdiv << MSCLK_DIV_CLKDIV_POS);
+
+ if (wait_after_set) {
+ while ((*divider_reg & MSCLK_DIV_CRNTCLKDIV) !=
+ (clkdiv << MSCLK_DIV_CRNTCLKDIV_POS))
+ continue;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int msclock_set_source(struct pik_clock_dev_ctx *ctx,
+ enum mod_pik_clock_msclock_source source,
+ bool wait_after_set)
+{
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (ctx->config->type == MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE)
+ return FWK_E_PARAM;
+
+ /* Set */
+ *ctx->config->control_reg =
+ (*ctx->config->control_reg & ~MSCLK_CONTROL_CLKSEL) |
+ (source << MSCLK_CONTROL_CLKSEL_POS);
+
+ if (wait_after_set) {
+ while ((*ctx->config->control_reg & MSCLK_CONTROL_CRNTCLK) !=
+ ((uint32_t)(source << MSCLK_CONTROL_CRNTCLK_POS)))
+ continue;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int cluster_set_modulator(struct pik_clock_dev_ctx *ctx,
+ uint32_t numerator, uint32_t denominator,
+ bool wait_after_set)
+{
+ uint32_t modulator_setting;
+
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+ if (ctx->config->type != MOD_PIK_CLOCK_TYPE_CLUSTER)
+ return FWK_E_PARAM;
+ if (ctx->config->modulator_reg == NULL)
+ return FWK_E_PARAM;
+ if (denominator > 255)
+ return FWK_E_PARAM;
+ if (denominator == 0)
+ return FWK_E_PARAM;
+ if (numerator > 255)
+ return FWK_E_PARAM;
+
+ modulator_setting = (denominator << CLUSCLK_MOD_DENOMINATOR_POS) |
+ (numerator << CLUSCLK_MOD_NUMERATOR_POS);
+
+
+ *ctx->config->modulator_reg = ((*ctx->config->modulator_reg &
+ ~(CLUSCLK_MOD_DENOMINATOR | CLUSCLK_MOD_NUMERATOR)) |
+ modulator_setting);
+
+ if (wait_after_set) {
+ while ((*ctx->config->modulator_reg & CLUSCLK_MOD_CRNTNUMERATOR) !=
+ (numerator << CLUSCLK_MOD_CRNTNUMERATOR_POS))
+ continue;
+
+ while ((*ctx->config->modulator_reg & CLUSCLK_MOD_CRNTDENOMINATOR) !=
+ (denominator << CLUSCLK_MOD_CRNTDENOMINATOR_POS))
+ continue;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int do_pik_clock_set_rate(fwk_id_t dev_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+ struct mod_pik_clock_rate *rate_entry;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ /* Look up the divider and source settings */
+ status = get_rate_entry(ctx, rate, &rate_entry);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ switch (ctx->config->type) {
+ case MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE:
+ status = ssclock_set_div(ctx, rate_entry->divider, false);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ break;
+ case MOD_PIK_CLOCK_TYPE_CLUSTER:
+ /* Modulator feature not currently used */
+ cluster_set_modulator(ctx, 1, 1, false);
+ /* Intentional fall-through */
+ case MOD_PIK_CLOCK_TYPE_MULTI_SOURCE:
+ if (ctx->current_source == MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED)
+ /* Leave the new rate to be applied when the clock is (re)started */
+ goto exit;
+
+ status = msclock_set_div(ctx, rate_entry->divider_reg,
+ rate_entry->divider, false);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = msclock_set_source(ctx, rate_entry->source, false);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ ctx->current_source = rate_entry->source;
+ break;
+ default:
+ return FWK_E_SUPPORT;
+ }
+
+exit:
+ if (status == FWK_SUCCESS)
+ ctx->current_rate = rate;
+ return status;
+}
+
+/*
+ * Clock driver API functions
+ */
+
+static int pik_clock_set_rate(fwk_id_t dev_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (!ctx->initialized)
+ return FWK_E_INIT;
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ return do_pik_clock_set_rate(dev_id, rate, round_mode);
+}
+
+static int pik_clock_get_rate(fwk_id_t dev_id, uint64_t *rate)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->type == MOD_PIK_CLOCK_TYPE_MULTI_SOURCE &&
+ ctx->current_source == MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED)
+ /* Indicate that the clock is not running */
+ *rate = 0;
+ else
+ *rate = ctx->current_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int pik_clock_get_rate_from_index(fwk_id_t dev_id,
+ unsigned int rate_index,
+ uint64_t *rate)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (rate_index >= ctx->config->rate_count)
+ return FWK_E_PARAM;
+
+ *rate = ctx->config->rate_table[rate_index].rate;
+ return FWK_SUCCESS;
+}
+
+static int pik_clock_set_state(
+ fwk_id_t dev_id,
+ enum mod_clock_state target_state)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+ struct mod_pik_clock_rate *rate_entry;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->type == MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE)
+ /* Cannot gate single-source clocks */
+ return FWK_E_SUPPORT;
+
+ if (!ctx->initialized)
+ return FWK_E_INIT;
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ /*
+ * This state from the device context relates only to the clock state
+ * that is derived from its parent power domain.
+ */
+ return FWK_E_PWRSTATE;
+
+ if (target_state == MOD_CLOCK_STATE_STOPPED) {
+ /* The clock is powered and will be gated. */
+ status = msclock_set_source(ctx, MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED,
+ false);
+ if (status == FWK_SUCCESS)
+ ctx->current_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED;
+
+ return status;
+ } else {
+ /* Look up the divider and source settings */
+ status = get_rate_entry(ctx, ctx->current_rate, &rate_entry);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = msclock_set_source(ctx, rate_entry->source, false);
+ if (status == FWK_SUCCESS)
+ ctx->current_source = rate_entry->source;
+
+ return status;
+ }
+}
+
+static int pik_clock_get_state(fwk_id_t dev_id, enum mod_clock_state *state)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->type == MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE) {
+ /*
+ * Single-source clocks cannot be gated so their running state will be
+ * derived purely from the state of their parent power domain, if any.
+ */
+ *state = ctx->current_state;
+ return FWK_SUCCESS;
+ }
+
+ /*
+ * Multi-source clocks may be stopped due to gating as well as the state of
+ * their parent power domain.
+ */
+ if (ctx->current_source == MOD_PIK_CLOCK_MSCLOCK_SOURCE_GATED)
+ *state = MOD_CLOCK_STATE_STOPPED;
+ else
+ *state = ctx->current_state;
+
+ return FWK_SUCCESS;
+}
+
+static int pik_clock_get_range(fwk_id_t dev_id, struct mod_clock_range *range)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (range == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ range->rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE;
+ range->min = ctx->config->rate_table[0].rate;
+ range->max = ctx->config->rate_table[ctx->config->rate_count - 1].rate;
+ range->rate_count = ctx->config->rate_count;
+
+ return FWK_SUCCESS;
+}
+
+static int pik_clock_power_state_change(
+ fwk_id_t dev_id,
+ unsigned int state)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->config->is_group_member)
+ return FWK_E_ACCESS;
+
+ if (state == MOD_PD_STATE_ON) {
+ if (ctx->initialized)
+ /* Restore the previous rate */
+ return do_pik_clock_set_rate(
+ dev_id, ctx->current_rate, MOD_CLOCK_ROUND_MODE_NONE);
+ else {
+ /* Perform deferred initialization and set the initial rate */
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ ctx->initialized = true;
+ return do_pik_clock_set_rate(
+ dev_id, ctx->config->initial_rate, MOD_CLOCK_ROUND_MODE_NONE);
+ }
+ } else
+ ctx->current_state = MOD_CLOCK_STATE_STOPPED;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_drv_api api_clock = {
+ .set_rate = pik_clock_set_rate,
+ .get_rate = pik_clock_get_rate,
+ .get_rate_from_index = pik_clock_get_rate_from_index,
+ .set_state = pik_clock_set_state,
+ .get_state = pik_clock_get_state,
+ .get_range = pik_clock_get_range,
+ .process_power_transition = pik_clock_power_state_change,
+};
+
+/*
+ * Direct driver API functions
+ */
+
+static int pik_clock_direct_set_div(fwk_id_t clock_id, uint32_t divider_type,
+ uint32_t divider)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+ assert(ctx->config->is_group_member);
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ switch (ctx->config->type) {
+ case MOD_PIK_CLOCK_TYPE_SINGLE_SOURCE:
+ status = ssclock_set_div(ctx, divider, false);
+ break;
+ case MOD_PIK_CLOCK_TYPE_CLUSTER:
+ case MOD_PIK_CLOCK_TYPE_MULTI_SOURCE:
+ status = msclock_set_div(ctx,
+ (enum mod_pik_clock_msclock_divider)divider_type, divider, false);
+ break;
+ default:
+ return FWK_E_SUPPORT;
+ }
+
+ return status;
+}
+
+static int pik_clock_direct_set_source(fwk_id_t clock_id, uint8_t source)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+ assert(ctx->config->is_group_member);
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ return msclock_set_source(ctx, source, false);
+}
+
+static int pik_clock_direct_set_mod(fwk_id_t clock_id, uint32_t numerator,
+ uint32_t denominator)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(clock_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(clock_id);
+ assert(ctx->config->is_group_member);
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ return cluster_set_modulator(ctx, numerator, denominator, false);
+}
+
+static int pik_clock_direct_power_state_change(
+ fwk_id_t dev_id,
+ unsigned int state)
+{
+ int status;
+ struct pik_clock_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (!ctx->config->is_group_member)
+ return FWK_E_ACCESS;
+
+ if (state == MOD_PD_STATE_ON) {
+ if (!ctx->initialized)
+ /* Perform delayed intialization */
+ ctx->initialized = true;
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ } else
+ ctx->current_state = MOD_CLOCK_STATE_STOPPED;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_css_clock_direct_api api_direct = {
+ .set_div = pik_clock_direct_set_div,
+ .set_source = pik_clock_direct_set_source,
+ .set_mod = pik_clock_direct_set_mod,
+ .process_power_transition = pik_clock_direct_power_state_change,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int pik_clock_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ module_ctx.dev_count = element_count;
+
+ if (element_count == 0)
+ return FWK_SUCCESS;
+
+ module_ctx.dev_ctx_table = fwk_mm_calloc(element_count,
+ sizeof(struct pik_clock_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int pik_clock_element_init(fwk_id_t element_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ unsigned int i = 0;
+ uint64_t current_rate;
+ uint64_t last_rate = 0;
+ struct pik_clock_dev_ctx *ctx;
+ const struct mod_pik_clock_dev_config *dev_config = data;
+
+ if (!fwk_module_is_valid_element_id(element_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+
+ /* Verify that the rate entries in the device's lookup table are ordered */
+ while (i < dev_config->rate_count) {
+ current_rate = dev_config->rate_table[i].rate;
+
+ /* The rate entries must be in ascending order */
+ if (current_rate < last_rate)
+ return FWK_E_DATA;
+
+ last_rate = current_rate;
+ i++;
+ }
+
+ ctx->config = dev_config;
+
+ /* Begin with an invalid source */
+ ctx->current_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_MAX;
+
+ if (ctx->config->defer_initialization)
+ return FWK_SUCCESS;
+
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ ctx->initialized = true;
+
+ /*
+ * Clock devices that are members of a clock group must skip initialization
+ * at this time since they will be set to a specific rate by the CSS Clock
+ * driver during the start stage or in response to a notification.
+ */
+ if (ctx->config->is_group_member)
+ return FWK_SUCCESS;
+
+ return do_pik_clock_set_rate(
+ element_id, dev_config->initial_rate, MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int pik_clock_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id,
+ const void **api)
+{
+ struct pik_clock_dev_ctx *ctx;
+
+ /* Only elements can be bound to as the API depends on the element type */
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_ACCESS;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(target_id);
+
+ if (ctx->config->is_group_member) {
+ #if BUILD_HAS_MOD_CSS_CLOCK
+ /* Only the CSS Clock module can bind to group members. */
+ if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_CSS_CLOCK) {
+ *api = &api_direct;
+ return FWK_SUCCESS;
+ } else
+ return FWK_E_ACCESS;
+ #else
+ /* The CSS Clock module is required to support group members. */
+ return FWK_E_SUPPORT;
+ #endif
+ } else
+ *api = &api_clock;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_pik_clock = {
+ .name = "PIK Clock Driver",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_PIK_CLOCK_API_COUNT,
+ .event_count = 0,
+ .init = pik_clock_init,
+ .element_init = pik_clock_element_init,
+ .process_bind_request = pik_clock_process_bind_request,
+};
diff --git a/module/pl011/include/mod_pl011.h b/module/pl011/include/mod_pl011.h
new file mode 100644
index 00000000..6c96bac5
--- /dev/null
+++ b/module/pl011/include/mod_pl011.h
@@ -0,0 +1,55 @@
+
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PL011_H
+#define MOD_PL011_H
+
+#include <stdint.h>
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModulePl011 PL011 Driver
+ *
+ * \brief Arm PL011 device driver, fulfilling the Log module's driver API.
+ *
+ * \details This module implements a device driver for the Primecell® PL011
+ * UART.
+ * @{
+ */
+
+/*!
+ * \brief PL011 device configuration data.
+ */
+struct mod_pl011_device_config {
+ /*! Base address of the device registers */
+ uintptr_t reg_base;
+
+ /*! Baud rate (bits per second) */
+ unsigned int baud_rate_bps;
+
+ /*! Reference clock (Hertz) */
+ uint64_t clock_rate_hz;
+
+ /*! Identifier of the clock that this device depends on */
+ fwk_id_t clock_id;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_PL011_H */
diff --git a/module/pl011/src/Makefile b/module/pl011/src/Makefile
new file mode 100644
index 00000000..d4bec75c
--- /dev/null
+++ b/module/pl011/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := "PL011"
+BS_LIB_SOURCES = mod_pl011.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/pl011/src/mod_pl011.c b/module/pl011/src/mod_pl011.c
new file mode 100644
index 00000000..da0bf939
--- /dev/null
+++ b/module/pl011/src/mod_pl011.c
@@ -0,0 +1,244 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_errno.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_notification.h>
+#include <mod_clock.h>
+#include <mod_log.h>
+#include <mod_pl011.h>
+#include <mod_power_domain.h>
+#include <pl011.h>
+
+static const struct mod_pl011_device_config **device_config_table;
+
+static struct pl011_reg *get_device_reg(fwk_id_t device_id)
+{
+ unsigned int device_idx;
+
+ device_idx = fwk_id_get_element_idx(device_id);
+ return (struct pl011_reg *)device_config_table[device_idx]->reg_base;
+}
+
+/*
+ * For details on the constants and equations used to calculate the baud rate
+ * settings, please consult the PL011 TRM.
+ */
+static int set_baud_rate(unsigned int baud_rate_bps, uint64_t clock_rate_hz,
+ struct pl011_reg *reg)
+{
+ uint32_t divisor_integer;
+ uint32_t divisor_fractional;
+ uint32_t divisor;
+ uint32_t clock_rate_x4;
+
+ assert(reg);
+
+ if (baud_rate_bps == 0)
+ return FWK_E_PARAM;
+
+ if ((clock_rate_hz < PL011_UARTCLK_MIN) ||
+ (clock_rate_hz > PL011_UARTCLK_MAX))
+ return FWK_E_PARAM;
+
+ /* The highest clock x4 should still fit in 32 bits */
+ assert((PL011_UARTCLK_MAX * UINT64_C(4)) < UINT32_MAX);
+
+ /* Ensure baud rate is not higher than the clock can support */
+ clock_rate_x4 = clock_rate_hz * 4;
+ if (baud_rate_bps > clock_rate_x4)
+ return FWK_E_RANGE;
+
+ /* Calculate integer and fractional divisors */
+ divisor = clock_rate_x4 / baud_rate_bps;
+ divisor_integer = divisor / 64;
+ divisor_fractional = divisor % 64;
+
+ /* The integer divisor must fit in 16 bits */
+ if (divisor_integer > 0xffff)
+ return FWK_E_RANGE;
+
+ /* The fractional divisor must fit in 6 bits */
+ if (divisor_fractional > 0x3f)
+ return FWK_E_RANGE;
+
+ /*
+ * When the integer divisor is equals to 0xffff, the fractional divisor can
+ * only be 0.
+ */
+ if ((divisor_integer == 0xffff) && (divisor_fractional != 0))
+ return FWK_E_RANGE;
+
+ /* Set registers */
+ reg->IBRD = divisor_integer;
+ reg->FBRD = divisor_fractional;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Module log driver API
+ */
+
+static int do_putchar(fwk_id_t device_id, char c)
+{
+ int status;
+ struct pl011_reg *reg;
+
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ reg = get_device_reg(device_id);
+
+ while (reg->FR & PL011_FR_TXFF)
+ continue;
+
+ reg->DR = c;
+
+ return FWK_SUCCESS;
+}
+
+static int do_flush(fwk_id_t device_id)
+{
+ int status;
+ struct pl011_reg *reg;
+
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ reg = get_device_reg(device_id);
+
+ while (reg->FR & PL011_FR_BUSY)
+ continue;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_log_driver_api driver_api = {
+ .flush = do_flush,
+ .putchar = do_putchar,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int pl011_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ if (element_count == 0)
+ return FWK_E_DATA;
+
+ /*
+ * Create an array of pointers used to store the configuration data pointer
+ * of each element.
+ */
+ device_config_table = fwk_mm_calloc(element_count,
+ sizeof(*device_config_table));
+ if (device_config_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ struct pl011_reg *reg;
+ const struct mod_pl011_device_config *config = data;
+ int status;
+
+ reg = (struct pl011_reg *)config->reg_base;
+ if (reg == NULL)
+ return FWK_E_DATA;
+
+ status = set_baud_rate(config->baud_rate_bps,
+ config->clock_rate_hz,
+ reg);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /*
+ * Initialize PL011 device
+ */
+ reg->ECR = PL011_ECR_CLR;
+ reg->LCR_H = PL011_LCR_H_WLEN_8BITS |
+ PL011_LCR_H_FEN;
+ reg->CR = PL011_CR_UARTEN |
+ PL011_CR_RXE |
+ PL011_CR_TXE;
+
+ device_config_table[fwk_id_get_element_idx(element_id)] = config;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_process_bind_request(fwk_id_t requester_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ *api = &driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int pl011_start(fwk_id_t id)
+{
+ const struct mod_pl011_device_config *config;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ config = device_config_table[fwk_id_get_element_idx(id)];
+
+ if (fwk_id_is_type(config->clock_id, FWK_ID_TYPE_NONE))
+ return FWK_SUCCESS;
+
+ return fwk_notification_subscribe(
+ mod_clock_notification_id_state_change_pending,
+ config->clock_id,
+ id);
+}
+
+int pl011_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ struct clock_notification_params *params;
+ struct clock_state_change_pending_resp_params *resp_params;
+
+ assert(
+ fwk_id_is_equal(
+ event->id,
+ mod_clock_notification_id_state_change_pending));
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
+
+ resp_params =
+ (struct clock_state_change_pending_resp_params *)resp_event->params;
+ resp_params->status = FWK_SUCCESS;
+
+ params = (struct clock_notification_params *)event->params;
+
+ if (params->new_state == MOD_CLOCK_STATE_STOPPED)
+ return do_flush(event->target_id);
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_pl011 = {
+ .name = "PL011",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = 1,
+ .init = pl011_init,
+ .element_init = pl011_element_init,
+ .start = pl011_start,
+ .process_bind_request = pl011_process_bind_request,
+ .process_notification = pl011_process_notification
+};
diff --git a/module/pl011/src/pl011.h b/module/pl011/src/pl011.h
new file mode 100644
index 00000000..00f6f8e5
--- /dev/null
+++ b/module/pl011/src/pl011.h
@@ -0,0 +1,162 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * PL011 (UART) register definitions
+ */
+
+#ifndef PL011_H
+#define PL011_H
+
+#include <fwk_macros.h>
+
+struct pl011_reg {
+ FWK_RW uint16_t DR;
+ uint16_t RESERVED0;
+ union {
+ FWK_RW uint8_t RSR;
+ FWK_RW uint8_t ECR;
+ };
+ uint8_t RESERVED1[0x18 - 0x05];
+ FWK_R uint16_t FR;
+ uint16_t RESERVED2[3];
+ FWK_RW uint8_t ILPR;
+ uint8_t RESERVED3[3];
+ FWK_RW uint16_t IBRD;
+ uint16_t RESERVED4;
+ FWK_RW uint32_t FBRD;
+ FWK_RW uint16_t LCR_H;
+ uint16_t RESERVED5;
+ FWK_RW uint16_t CR;
+ uint16_t RESERVED6;
+ FWK_RW uint16_t IFLS;
+ uint16_t RESERVED7;
+ FWK_RW uint16_t IMSC;
+ uint16_t RESERVED8;
+ FWK_R uint16_t RIS;
+ uint16_t RESERVED9;
+ FWK_R uint16_t MIS;
+ uint16_t RESERVED10;
+ FWK_W uint16_t ICR;
+ uint16_t RESERVED11;
+ FWK_RW uint16_t DMACR;
+ uint8_t RESERVED12[0xFE0 - 0x4C];
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t CID0;
+ FWK_R uint32_t CID1;
+ FWK_R uint32_t CID2;
+ FWK_R uint32_t CID3;
+};
+
+#define PL011_DR_DATA UINT16_C(0x00FF)
+#define PL011_DR_FE UINT16_C(0x0100)
+#define PL011_DR_PE UINT16_C(0x0200)
+#define PL011_DR_BE UINT16_C(0x0400)
+#define PL011_DR_OE UINT16_C(0x0800)
+
+#define PL011_RSR_FE UINT8_C(0x01)
+#define PL011_RSR_PE UINT8_C(0x02)
+#define PL011_RSR_BE UINT8_C(0x04)
+#define PL011_RSR_OE UINT8_C(0x08)
+#define PL011_ECR_CLR UINT8_C(0xFF)
+
+#define PL011_FR_CTS UINT16_C(0x0001)
+#define PL011_FR_DSR UINT16_C(0x0002)
+#define PL011_FR_DCD UINT16_C(0x0004)
+#define PL011_FR_BUSY UINT16_C(0x0008)
+#define PL011_FR_RXFE UINT16_C(0x0010)
+#define PL011_FR_TXFF UINT16_C(0x0020)
+#define PL011_FR_RXFF UINT16_C(0x0040)
+#define PL011_FR_TXFE UINT16_C(0x0080)
+#define PL011_FR_RI UINT16_C(0x0100)
+
+#define PL011_LCR_H_BRK UINT16_C(0x0001)
+#define PL011_LCR_H_PEN UINT16_C(0x0002)
+#define PL011_LCR_H_EPS UINT16_C(0x0004)
+#define PL011_LCR_H_STP2 UINT16_C(0x0008)
+#define PL011_LCR_H_FEN UINT16_C(0x0010)
+#define PL011_LCR_H_WLEN UINT16_C(0x0060)
+#define PL011_LCR_H_WLEN_5BITS UINT16_C(0x0000)
+#define PL011_LCR_H_WLEN_6BITS UINT16_C(0x0020)
+#define PL011_LCR_H_WLEN_7BITS UINT16_C(0x0040)
+#define PL011_LCR_H_WLEN_8BITS UINT16_C(0x0060)
+#define PL011_LCR_H_SPS UINT16_C(0x0080)
+
+#define PL011_CR_UARTEN UINT16_C(0x0001)
+#define PL011_CR_SIREN UINT16_C(0x0002)
+#define PL011_CR_SIRLP UINT16_C(0x0004)
+#define PL011_CR_LBE UINT16_C(0x0080)
+#define PL011_CR_TXE UINT16_C(0x0100)
+#define PL011_CR_RXE UINT16_C(0x0200)
+#define PL011_CR_DTR UINT16_C(0x0400)
+#define PL011_CR_RTS UINT16_C(0x0800)
+#define PL011_CR_OUT1 UINT16_C(0x1000)
+#define PL011_CR_OUT2 UINT16_C(0x2000)
+#define PL011_CR_RTSEN UINT16_C(0x4000)
+#define PL011_CR_CTSEN UINT16_C(0x8000)
+
+#define PL011_IFLS_TXIFLSEL UINT16_C(0x0007)
+#define PL011_IFLS_RXIFLSEL UINT16_C(0x0038)
+
+#define PL011_IMSC_RIMIM UINT16_C(0x0001)
+#define PL011_IMSC_CTSMIM UINT16_C(0x0002)
+#define PL011_IMSC_DCDMIM UINT16_C(0x0004)
+#define PL011_IMSC_DSRMIM UINT16_C(0x0008)
+#define PL011_IMSC_RXIM UINT16_C(0x0010)
+#define PL011_IMSC_TXIM UINT16_C(0x0020)
+#define PL011_IMSC_RTIM UINT16_C(0x0040)
+#define PL011_IMSC_FEIM UINT16_C(0x0080)
+#define PL011_IMSC_PEIM UINT16_C(0x0100)
+#define PL011_IMSC_BEIM UINT16_C(0x0200)
+#define PL011_IMSC_OEIM UINT16_C(0x0400)
+
+#define PL011_RIS_RIRMIS UINT16_C(0x0001)
+#define PL011_RIS_CTSRMIS UINT16_C(0x0002)
+#define PL011_RIS_DCDRMIS UINT16_C(0x0004)
+#define PL011_RIS_DSRRMIS UINT16_C(0x0008)
+#define PL011_RIS_RXRIS UINT16_C(0x0010)
+#define PL011_RIS_TXRIS UINT16_C(0x0020)
+#define PL011_RIS_RTRIS UINT16_C(0x0040)
+#define PL011_RIS_FERIS UINT16_C(0x0080)
+#define PL011_RIS_PERIS UINT16_C(0x0100)
+#define PL011_RIS_BERIS UINT16_C(0x0200)
+#define PL011_RIS_OERIS UINT16_C(0x0400)
+
+#define PL011_MIS_RIMMIS UINT16_C(0x0001)
+#define PL011_MIS_CTSMMIS UINT16_C(0x0002)
+#define PL011_MIS_DCDMMIS UINT16_C(0x0004)
+#define PL011_MIS_DSRMMIS UINT16_C(0x0008)
+#define PL011_MIS_RXMIS UINT16_C(0x0010)
+#define PL011_MIS_TXMIS UINT16_C(0x0020)
+#define PL011_MIS_RTMIS UINT16_C(0x0040)
+#define PL011_MIS_FEMIS UINT16_C(0x0080)
+#define PL011_MIS_PEMIS UINT16_C(0x0100)
+#define PL011_MIS_BEMIS UINT16_C(0x0200)
+#define PL011_MIS_OEMIS UINT16_C(0x0400)
+
+#define PL011_ICR_RIMIC UINT16_C(0x0001)
+#define PL011_ICR_CTSMIC UINT16_C(0x0002)
+#define PL011_ICR_DCDMIC UINT16_C(0x0004)
+#define PL011_ICR_DSRMIC UINT16_C(0x0008)
+#define PL011_ICR_RXIC UINT16_C(0x0010)
+#define PL011_ICR_TXIC UINT16_C(0x0020)
+#define PL011_ICR_RTIC UINT16_C(0x0040)
+#define PL011_ICR_FEIC UINT16_C(0x0080)
+#define PL011_ICR_PEIC UINT16_C(0x0100)
+#define PL011_ICR_BEIC UINT16_C(0x0200)
+#define PL011_ICR_OEIC UINT16_C(0x0400)
+
+#define PL011_DMACR_RXDMAE UINT16_C(0x0001)
+#define PL011_DMACR_TXDMAE UINT16_C(0x0002)
+#define PL011_DMACR_DMAAONERR UINT16_C(0x0004)
+
+#define PL011_UARTCLK_MIN (1420 * FWK_KHZ)
+#define PL011_UARTCLK_MAX (542720 * FWK_KHZ)
+
+#endif /* PL011 */
diff --git a/module/power_domain/include/mod_power_domain.h b/module/power_domain/include/mod_power_domain.h
new file mode 100644
index 00000000..ed204f83
--- /dev/null
+++ b/module/power_domain/include/mod_power_domain.h
@@ -0,0 +1,922 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Power domain management support.
+ */
+
+#ifndef MOD_POWER_DOMAIN_H
+#define MOD_POWER_DOMAIN_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupPowerDomain Power Domains
+ *
+ * \details Support for querying and setting the power state of power domains
+ * such as CPU, cluster and GPU power domains.
+ *
+ * @{
+ */
+
+/*!
+ * Maximum number of states for a power domain.
+ */
+#define MOD_PD_STATE_COUNT_MAX 16
+
+/*!
+ * \brief Types of power domain.
+ */
+enum mod_pd_type {
+ /*! Processor. */
+ MOD_PD_TYPE_CORE,
+ /*! Processor cluster. */
+ MOD_PD_TYPE_CLUSTER,
+ /*! Generic device. */
+ MOD_PD_TYPE_DEVICE,
+ /*! Debug device. */
+ MOD_PD_TYPE_DEVICE_DEBUG,
+ /*! System. */
+ MOD_PD_TYPE_SYSTEM,
+ /*! Number of power domain types. */
+ MOD_PD_TYPE_COUNT
+};
+
+/*!
+ * \brief Bit width of power domain type within attributes.
+ */
+#define MOD_PD_TYPE_BITS_WIDTH ((sizeof(unsigned int) * CHAR_BIT) - \
+ __builtin_clz(MOD_PD_TYPE_COUNT - 1))
+
+/*!
+ * \brief Attributes of a power domain.
+ */
+struct mod_pd_attributes {
+ /*! Type of the power domain. */
+ enum mod_pd_type pd_type : MOD_PD_TYPE_BITS_WIDTH;
+
+ /*! \internal */
+ unsigned int not_used : ((sizeof(unsigned int) * CHAR_BIT) -
+ MOD_PD_TYPE_BITS_WIDTH);
+};
+
+/*!
+ * \brief Identifiers for the power levels.
+ */
+enum mod_pd_level {
+ /*! Level 0. */
+ MOD_PD_LEVEL_0,
+ /*! Level 1. */
+ MOD_PD_LEVEL_1,
+ /*! Level 2. */
+ MOD_PD_LEVEL_2,
+ /*! Level 3. */
+ MOD_PD_LEVEL_3,
+ /*! Number of power domain levels. */
+ MOD_PD_LEVEL_COUNT
+};
+
+/*!
+ * \brief Number of bits used for each level within a power domain tree
+ * position.
+ */
+#define MOD_PD_TREE_POS_BITS_PER_LEVEL 8
+
+/*!
+ * \brief Shifts for the power level fields within a power domain tree position.
+ */
+enum {
+ /*! Number of bits to shift for the power level 0 field. */
+ MOD_PD_TREE_POS_LEVEL_0_SHIFT =
+ MOD_PD_LEVEL_0 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 1 field. */
+ MOD_PD_TREE_POS_LEVEL_1_SHIFT =
+ MOD_PD_LEVEL_1 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 2 field. */
+ MOD_PD_TREE_POS_LEVEL_2_SHIFT =
+ MOD_PD_LEVEL_2 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 3 field. */
+ MOD_PD_TREE_POS_LEVEL_3_SHIFT =
+ MOD_PD_LEVEL_3 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ MOD_PD_TREE_POS_LEVEL_SHIFT =
+ MOD_PD_LEVEL_COUNT * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+};
+
+/*!
+ * \brief Mask for the power level fields within a power domain tree position.
+ */
+#define MOD_PD_TREE_POS_LEVEL_MASK UINT64_C(0xFF)
+
+/*!
+ * \brief Build the power domain tree position of a power domain.
+ */
+#define MOD_PD_TREE_POS(LEVEL, LEVEL_3, LEVEL_2, LEVEL_1, LEVEL_0) \
+ ((((uint64_t)((LEVEL) & MOD_PD_TREE_POS_LEVEL_MASK)) << \
+ MOD_PD_TREE_POS_LEVEL_SHIFT) | \
+ (((uint64_t)((LEVEL_3) & MOD_PD_TREE_POS_LEVEL_MASK)) << \
+ MOD_PD_TREE_POS_LEVEL_3_SHIFT) | \
+ (((uint64_t)((LEVEL_2) & MOD_PD_TREE_POS_LEVEL_MASK)) << \
+ MOD_PD_TREE_POS_LEVEL_2_SHIFT) | \
+ (((uint64_t)((LEVEL_1) & MOD_PD_TREE_POS_LEVEL_MASK)) << \
+ MOD_PD_TREE_POS_LEVEL_1_SHIFT) | \
+ (((uint64_t)((LEVEL_0) & MOD_PD_TREE_POS_LEVEL_MASK)) << \
+ MOD_PD_TREE_POS_LEVEL_0_SHIFT))
+
+/*!
+ * \brief Representation of the invalid tree position. Used when checking that
+ * power domains are declared in increasing order of their tree position.
+ */
+#define MOD_PD_INVALID_TREE_POS MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0)
+
+/*!
+ * \brief Power domain module configuration.
+ */
+struct mod_power_domain_config {
+ /*!
+ * Identifiers of the modules and elements which are granted access to the
+ * restricted interface of the module.
+ */
+ fwk_id_t *authorized_id_table;
+
+ /*! Number of identifiers in the "authorized_id_table" table. */
+ size_t authorized_id_table_size;
+};
+
+/*!
+ * \brief Configuration data for a power domain.
+ *
+ * \details The configuration data of a power domain describes the power
+ * domain and its relationship with other power domains in the system.
+ * Assumptions about the organisation of the power domains, underlying the
+ * way in which power domains are described here, are:
+ * - Each power domain can be represented as a node in a power domain
+ * topology tree.
+ * - Sibling power domains are mutually exclusive.
+ * - Parent power domains are shared by the children.
+ *
+ * For more information, refer to the "Arm Power State Coordination
+ * Interface Platform Design Document", available here:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_
+ * Coordination_Interface_PDD_v1_1_DEN0022D.pdf
+ */
+struct mod_power_domain_element_config {
+ /*!
+ * \brief Defines the position of the power domain within the power domain
+ * tree.
+ *
+ * \details Each child of a power domain is assigned a number ranging from 0
+ * to 255. Compute the position of a power domain from the position of
+ * its parent 'parent_pos' (the number assigned to the power domain as
+ * a child of its parent is 'child_pos') as follows:
+ *
+ * tree_pos = (parent_pos - (1 << MOD_PD_TREE_POS_LEVEL_SHIFT)) +
+ * (child_pos << (8*pd_level))
+ *
+ * The position of the top-level domain is defined as:
+ * (level of the top-level domain) << MOD_PD_TREE_POS_LEVEL_SHIFT
+ *
+ * If the power domain hierarchy maps to the core hierarchy (based on
+ * MPIDR levels of affinity), derive the position of the core power
+ * domains from the Aff0, Aff1, Aff2 and Aff3 fields of the MPIDR
+ * registers of the cores as follows:
+ *
+ * core power domain position = (Aff3 << MOD_PD_TREE_LEVEL_3_SHIFT) +
+ * (Aff2 << MOD_PD_TREE_LEVEL_2_SHIFT) +
+ * (Aff1 << MOD_PD_TREE_LEVEL_1_SHIFT) +
+ * (Aff0 << MOD_PD_TREE_LEVEL_0_SHIFT)
+ *
+ * In the module configuration data, the power domains have to be in
+ * increasing order of their power domain position. Thus, the power
+ * domains with the lowest power level have to be first and the system
+ * power domain has to be last. This table must contain at least one
+ * element, the system power domain.
+ */
+ uint64_t tree_pos;
+
+ /*!
+ * Identifier of the module or element providing the driver for the power
+ * domain.
+ */
+ fwk_id_t driver_id;
+
+ /*! Identifier of the driver API. */
+ fwk_id_t api_id;
+
+ /*! Attributes of the power domain. */
+ struct mod_pd_attributes attributes;
+
+ /*!
+ * Mask of the allowed power states for the power domain, for each possible
+ * power state of its parent. Table of allowed state masks. The bit 'i' of
+ * the entry 'j' is equal to one if and only if the state 'i' for the power
+ * domain is allowed when its parent is in state 'j'. The number of bits of
+ * each entry of this table has to be greater or equal than
+ * MOD_PD_STATE_COUNT_MAX.
+ */
+ const uint32_t *allowed_state_mask_table;
+
+ /*!
+ * Power state names. The power state names are used only for debug
+ * purposes and this table is optional. If not provided then the default
+ * power state names (OFF, ON, SLEEP, 3, 4, ...) are used for the power
+ * domain states.
+ */
+ const char **state_name_table;
+
+ /*! Size of the table of allowed state masks */
+ size_t allowed_state_mask_table_size;
+
+ /*! Size of the table of allowed state masks */
+ size_t state_name_table_size;
+};
+
+/*!
+ * \brief Types of system shutdown
+ */
+enum mod_pd_system_shutdown {
+ /*! System shutdown */
+ MOD_PD_SYSTEM_SHUTDOWN,
+
+ /*! System cold reset */
+ MOD_PD_SYSTEM_COLD_RESET,
+
+ /*! System warm reset */
+ MOD_PD_SYSTEM_WARM_RESET,
+
+ /*! Sub-system reset */
+ MOD_PD_SYSTEM_SUB_SYSTEM_RESET,
+
+ /*! Forced system shutdown */
+ MOD_PD_SYSTEM_FORCED_SHUTDOWN,
+
+ /*! Number of shutdown types */
+ MOD_PD_SYSTEM_COUNT,
+};
+
+/*!
+ * \brief Identifiers of the power domain states. The other states are defined
+ * by the platform code for more flexibility. The power states defined by
+ * the platform must be ordered from the shallowest to the deepest state.
+ */
+enum mod_pd_state {
+ /*! \c OFF power state */
+ MOD_PD_STATE_OFF,
+
+ /*! \c ON power state */
+ MOD_PD_STATE_ON,
+
+ /*! \c SLEEP power state */
+ MOD_PD_STATE_SLEEP,
+
+ /*! Number of power states */
+ MOD_PD_STATE_COUNT
+};
+
+/*!
+ * \brief Masks for the power domain states.
+ */
+enum mod_pd_state_mask {
+ MOD_PD_STATE_OFF_MASK = 1 << MOD_PD_STATE_OFF,
+ MOD_PD_STATE_ON_MASK = 1 << MOD_PD_STATE_ON,
+ MOD_PD_STATE_SLEEP_MASK = 1 << MOD_PD_STATE_SLEEP,
+};
+
+/*!
+ * \brief Number of bits for each level state in a composite power state.
+ */
+#define MOD_PD_CS_STATE_BITS_PER_LEVEL 4
+
+/*!
+ * \brief Mask for a composite power state.
+ */
+#define MOD_PD_CS_STATE_MASK ((1 << MOD_PD_CS_STATE_BITS_PER_LEVEL) - 1)
+
+/*!
+ * \brief Shifts for the states and child policies fields in a composite
+ * power state.
+ */
+enum {
+ MOD_PD_CS_LEVEL_0_STATE_SHIFT =
+ MOD_PD_LEVEL_0 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_1_STATE_SHIFT =
+ MOD_PD_LEVEL_1 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_2_STATE_SHIFT =
+ MOD_PD_LEVEL_2 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_3_STATE_SHIFT =
+ MOD_PD_LEVEL_3 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_SHIFT =
+ MOD_PD_LEVEL_COUNT * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+
+ MOD_PD_CS_VALID_BITS = (1 <<
+ (MOD_PD_CS_LEVEL_SHIFT + MOD_PD_CS_STATE_BITS_PER_LEVEL)) - 1
+};
+
+/*!
+ * \brief Compute a composite power domain state.
+ */
+#define MOD_PD_COMPOSITE_STATE(HIGHEST_LEVEL, LEVEL_3_STATE, LEVEL_2_STATE, \
+ LEVEL_1_STATE, LEVEL_0_STATE) \
+ (((HIGHEST_LEVEL) << MOD_PD_CS_LEVEL_SHIFT) | \
+ ((LEVEL_3_STATE) << MOD_PD_CS_LEVEL_3_STATE_SHIFT) | \
+ ((LEVEL_2_STATE) << MOD_PD_CS_LEVEL_2_STATE_SHIFT) | \
+ ((LEVEL_1_STATE) << MOD_PD_CS_LEVEL_1_STATE_SHIFT) | \
+ ((LEVEL_0_STATE) << MOD_PD_CS_LEVEL_0_STATE_SHIFT))
+
+/*!
+ * \brief Power domain driver interface.
+ *
+ * \details The interface the power domain module relies on to perform
+ * actions on a power domain. Examples include powering a domain on or off,
+ * resetting the domain and preparing for/reacting to a system shutdown or
+ * reset.
+ */
+struct mod_pd_driver_api {
+ /*!
+ * \brief Set the power domain identified by \p dev_id to the \p state power
+ * state.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>set state</tt> request. The function is called once the module
+ * has checked that the power domain's children and parent are in a
+ * proper state for the power domain to be transitioned to the \p state
+ * power state. This function must not return until the transition has
+ * completed.
+ *
+ * \warning In the case of a <tt>shutdown</tt> request, if provided, \ref
+ * shutdown will be called instead.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Power state the power domain has to be put into.
+ *
+ * \retval FWK_SUCCESS The power state has been successfully set.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \return One of the other specific error codes described by the driver
+ * module.
+ */
+ int (*set_state)(fwk_id_t dev_id, unsigned int state);
+
+ /*!
+ * \brief Get the current power state of the power domain identified by
+ * \p dev_id.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>get state</tt> or <tt>get composite state</tt> request.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Pointer to storage for the power state of the power domain.
+ *
+ * \retval FWK_SUCCESS The power state was successfully returned.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*get_state)(fwk_id_t dev_id, unsigned int *state);
+
+ /*!
+ * \brief Reset the power domain identified by \p dev_id.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>reset</tt> request. The module will ensure that any children of
+ * the power domain are powered off before attempting a reset.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ *
+ * \retval FWK_SUCCESS The power domain was successfully reset.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \return One of the other specific error codes described by the module.
+ */
+ int (*reset)(fwk_id_t dev_id);
+
+ /*!
+ * \brief Check whether a state transition request should be denied.
+ *
+ * \note This function is optional (may be \c NULL) and it is expected that
+ * most drivers will not implement it.
+ *
+ * \details The power domain module calls this function when it wishes to
+ * know whether it should permit or deny a state transition request.
+ * It is intended to provide a device a denial mechanism that can make
+ * judgements based on information unavailable to the power domain
+ * module, such as dependencies between power domains beyond the power
+ * domain tree model.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Power state the power domain is intended to be put into.
+ *
+ * \retval true The transition should be denied.
+ * \retval false The power domain can transition to the power state \p
+ * state.
+ */
+ bool (*deny)(fwk_id_t dev_id, unsigned int state);
+
+ /*!
+ * \brief Prepare the last standing core for a system suspend.
+ *
+ * \details The function prepares the last standing core for entering the
+ * \ref MOD_PD_STATE_OFF state (powered off, no wake-up interrupt) when
+ * it will execute WFI. The function should also ensure that when the
+ * core is powered off a state transition report is sent by means of
+ * the \ref mod_pd_driver_input_api::report_power_state_transition
+ * driver input interface function indicating that the core power
+ * domain state should be updated. This function is mandatory for core
+ * power domains, but is otherwise unused.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ *
+ * \retval FWK_SUCCESS The core has been successfully prepared.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*prepare_core_for_system_suspend)(fwk_id_t dev_id);
+
+ /*!
+ * \brief Shutdown a power domain as part of a system shutdown.
+ *
+ * \note This function is optional (may be \c NULL).
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>shutdown</tt> request. The power domain must be in the
+ * \ref MOD_PD_STATE_OFF state when it returns.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state State the system will be suspended to. The definition
+ * of those states is platform specific.
+ *
+ * \retval FWK_SUCCESS The core has been successfully shutdown.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*shutdown)(fwk_id_t dev_id,
+ enum mod_pd_system_shutdown system_shutdown);
+};
+
+/*!
+ * \brief Power domain module public interface.
+ *
+ * \details The interface the power domain module exposes to the other modules
+ * and their elements without any restriction.
+ */
+
+struct mod_pd_public_api {
+ /*!
+ * \brief Get the type of a power domain.
+ *
+ * \param pd_id Identifier of the power domain that the type is being
+ * queried for.
+ * \param type Pointer to storage for the type of the power domain.
+ *
+ * \retval FWK_SUCCESS The type of the power domain was returned.
+ * \retval FWK_E_STATE The module has not been initialized.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'type' is equal to NULL.
+ */
+ int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type);
+
+ /*!
+ * \brief Get the identifier of the parent of a power domain.
+ *
+ * \note The function returns \ref FWK_ID_NONE in the case of the root power
+ * domain which does not have any parent.
+ *
+ * \param pd_id Identifier of the power domain.
+ * \param parent_pd_id Pointer to storage for the identifier of the parent
+ * power domain.
+ *
+ * \retval FWK_SUCCESS The identifier of the parent power domain was
+ * returned.
+ * \retval FWK_E_STATE The component has not been initialized.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL.
+ */
+ int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id);
+};
+
+/*!
+ * \brief Power domain module restricted interface.
+ *
+ * \details The interface the power domain module exposes to a restricted set
+ * of modules and/or elements. The set of modules and/or elements that are
+ * allowed to access this interface is defined by the module configuration
+ * data.
+ */
+struct mod_pd_restricted_api {
+ /*!
+ * \brief Get the type of a power domain.
+ *
+ * \param pd_id Identifier of the power domain that the type is being
+ * queried for.
+ * \param type Pointer to storage for the type of the power domain.
+ *
+ * \retval FWK_SUCCESS The type of the power domain was returned.
+ * \retval FWK_E_STATE The module has not been initialized.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'type' is equal to NULL.
+ */
+ int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type);
+
+ /*!
+ * \brief Get the identifier of the parent of a power domain.
+ *
+ * \note The function returns \ref FWK_ID_NONE in the case of the root power
+ * domain which does not have any parent.
+ *
+ * \param pd_id Identifier of the power domain.
+ * \param parent_pd_id Pointer to storage for the identifier of the parent
+ * power domain.
+ *
+ * \retval FWK_SUCCESS The identifier of the parent power domain was
+ * returned.
+ * \retval FWK_E_STATE The component has not been initialized.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL.
+ */
+ int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id);
+
+ /*!
+ * \brief Set the state of a power domain.
+ *
+ * \note The function sets the state of the power domain identified by
+ * 'pd_id' synchronously from the point of view of the caller.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ * \param state State of the power domain.
+ *
+ * \retval FWK_SUCCESS The power state was set.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The 'state' is not valid.
+ */
+ int (*set_state)(fwk_id_t pd_id, unsigned int state);
+
+ /*!
+ * \brief Request an asynchronous power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ * \param state State of the power domain.
+ *
+ * \retval FWK_SUCCESS The power state transition request was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_state_async)(fwk_id_t pd_id, bool resp_requested,
+ unsigned int state);
+
+ /*!
+ * \brief Set the state of a power domain and possibly of one or several of
+ * its ancestors.
+ *
+ * \note The function sets the state of the power domain identified by
+ * 'pd_id' and possibly of one or several of its ancestors. When the
+ * function returns the state transition is completed.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param composite_state State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The composite power state transition was completed.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_composite_state)(fwk_id_t pd_id, uint32_t composite_state);
+
+ /*!
+ * \brief Request an asynchronous composite power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \param composite_state State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The composite power state transition was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_composite_state_async)(fwk_id_t pd_id, bool resp_requested,
+ uint32_t composite_state);
+
+ /*!
+ * \brief Get the state of a given power domain.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be
+ * retrieved.
+ * \param state Pointer to storage for the power domain state.
+ *
+ * \retval FWK_SUCCESS The power state was returned.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'state' is equal to NULL.
+ */
+ int (*get_state)(fwk_id_t pd_id, unsigned int *state);
+
+ /*!
+ * \brief Get the composite state of a power domain and its ancestors (if
+ * any) in the power domain tree.
+ *
+ * \note The function gets the composite state of the power domain
+ * identified by 'pd_id' and its ancestors (if any) synchronously from
+ * the point of view of the calling thread.
+ *
+ * \param pd_id Identifier of the power domain whose composite state has to
+ * be retrieved.
+ * \param composite_state Pointer to storage for the power domain composite
+ * state.
+ *
+ * \retval FWK_SUCCESS The composite state was returned.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'composite state' is equal to NULL.
+ */
+ int (*get_composite_state)(fwk_id_t pd_id, unsigned int *composite_state);
+
+ /*!
+ * \brief Reset of a power domain.
+ *
+ * \note The function resets the power domain identified by 'pd_id'. When
+ * the function returns the power domain reset is completed.
+ *
+ * \param pd_id Identifier of the power domain to reset.
+ *
+ * \retval FWK_SUCCESS Power state retrieving request transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*reset)(fwk_id_t pd_id);
+
+ /*!
+ * \brief Suspend the system.
+ *
+ * \note The function initiates the suspension of the system. On call, all
+ * but one core power domain must be in the MOD_PD_STATE_OFF state.
+ * When the function returns the power down of the last standing core
+ * is programmed to occur as soon as the core executes WFI.
+ *
+ * \param state State the system has to be suspended to. The definition
+ * of those states is platform specific.
+ *
+ * \retval FWK_SUCCESS The system suspension has been initiated
+ * successfully.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM Invalid state.
+ * \retval FWK_E_STATE The system is not in the proper state to be
+ * suspended.
+ */
+ int (*system_suspend)(unsigned int state);
+
+ /*!
+ * \brief Shutdown the system.
+ *
+ * \note The function shutdowns the system whatever its current state. If
+ * the shutdown is successful, the function does not return.
+ *
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ */
+ int (*system_shutdown)(enum mod_pd_system_shutdown system_shutdown);
+};
+
+/*!
+ * \brief Power domain module driver input API.
+ *
+ * \details The interface the power domain module exposes to its module drivers
+ * to be able to ask for power state transitions or report power state
+ * transitions following the occurrence of interrupts.
+ */
+struct mod_pd_driver_input_api {
+ /*!
+ * \brief Request an asynchronous power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ * \param state State of the power domain.
+ *
+ * \retval FWK_SUCCESS The power state transition request was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_state_async)(fwk_id_t pd_id, bool resp_requested,
+ unsigned int state);
+
+ /*!
+ * \brief Request an asynchronous composite power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \param composite_state State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The composite power state transition was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_composite_state_async)(fwk_id_t pd_id, bool resp_requested,
+ uint32_t composite_state);
+
+ /*!
+ * \brief Request for a power domain to be reset.
+ *
+ * \note The function queues a reset request. When the function returns the
+ * power domain has not been reset, the reset has just been requested
+ * to the power domain module.
+ *
+ * \param pd_id Identifier of the power domain to reset.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \retval FWK_SUCCESS Reset request transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*reset_async)(fwk_id_t pd_id, bool resp_requested);
+
+ /*!
+ * \brief Report a power domain state transition.
+ *
+ * \warning Failure to call this function on a power state transition may
+ * leave the power domain module with an outdated power state for the
+ * power domain.
+ *
+ * \param pd_id Identifier of the power domain, a power state transition
+ * report is is sent to.
+ *
+ * \retval FWK_SUCCESS Report transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the call
+ * to the API.
+ * \retval FWK_E_NOMEM Failed to allocate a report event.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*report_power_state_transition)(fwk_id_t pd_id, unsigned int state);
+};
+
+/*!
+ * \brief Parameters of a power state pre-transition notification.
+ */
+struct mod_pd_power_state_pre_transition_notification_params {
+ /*! Current power state */
+ unsigned int current_state;
+
+ /*! Target power state */
+ unsigned int target_state;
+};
+
+/*!
+ * \brief Parameters of the response to a power state pre-transition
+ * notification.
+ */
+struct mod_pd_power_state_pre_transition_notification_resp_params {
+ /*!
+ * Status of the transition for the entity being notified.
+ */
+ int status;
+};
+
+/*!
+ * \brief Parameters of a power domain transition notification.
+ */
+struct mod_pd_power_state_transition_notification_params {
+ /*! Power state the power domain has transitioned to */
+ unsigned int state;
+};
+
+/*!
+ * \defgroup GroupPowerDomainIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices
+ */
+enum mod_pd_api_idx {
+ MOD_PD_API_IDX_PUBLIC,
+ MOD_PD_API_IDX_RESTRICTED,
+ MOD_PD_API_IDX_DRIVER_INPUT,
+ MOD_PD_API_IDX_COUNT,
+};
+
+#if BUILD_HAS_MOD_POWER_DOMAIN
+/*! Public API identifier */
+static const fwk_id_t mod_pd_api_id_public =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_POWER_DOMAIN, MOD_PD_API_IDX_PUBLIC);
+
+/*! Restricted API identifier */
+static const fwk_id_t mod_pd_api_id_restricted =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_POWER_DOMAIN, MOD_PD_API_IDX_RESTRICTED);
+
+/*! Driver input API identifier */
+static const fwk_id_t mod_pd_api_id_driver_input =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_POWER_DOMAIN, MOD_PD_API_IDX_DRIVER_INPUT);
+#endif
+
+/*!
+ * \brief Notification indices.
+ */
+enum mod_pd_notification_idx {
+ /*! Power state transition */
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION,
+
+ /*! Power state pre-transition */
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION,
+
+ /*! Number of notifications defined by the power domain module */
+ MOD_PD_NOTIFICATION_COUNT,
+};
+
+/*!
+ * \brief Notification identifiers.
+ */
+#if BUILD_HAS_MOD_POWER_DOMAIN
+/*! Identifier of the power state transition notification */
+static const fwk_id_t mod_pd_notification_id_power_state_transition =
+ FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION);
+
+/*! Identifier of the power state pre-transition notification */
+static const fwk_id_t mod_pd_notification_id_power_state_pre_transition =
+ FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION);
+#endif
+
+/*!
+ * \}
+ */
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_POWER_DOMAIN_H */
diff --git a/module/power_domain/src/Makefile b/module/power_domain/src/Makefile
new file mode 100644
index 00000000..1c4c5fe8
--- /dev/null
+++ b/module/power_domain/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Power domain
+BS_LIB_SOURCES := mod_power_domain.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/power_domain/src/mod_power_domain.c b/module/power_domain/src/mod_power_domain.c
new file mode 100644
index 00000000..cba05ed4
--- /dev/null
+++ b/module/power_domain/src/mod_power_domain.c
@@ -0,0 +1,1999 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Power domain management support.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_thread.h>
+#include <fwk_multi_thread.h>
+#include <fwk_notification.h>
+#include <mod_log.h>
+#include <mod_power_domain.h>
+
+/*
+ * Module and power domain contexts
+ */
+
+struct response_ctx {
+ /* Pending response flag. */
+ bool pending;
+
+ /* Cookie of the event to respond to. */
+ uint32_t cookie;
+};
+
+/* Context for the power state transition notification */
+struct power_state_transition_notification_ctx {
+ /* Number of pending notification responses */
+ unsigned int pending_responses;
+
+ /*
+ * Power state the power domain has transitioned to.
+ */
+ unsigned int state;
+};
+
+/* Context for the power state pre-transition notification */
+struct power_state_pre_transition_notification_ctx {
+ /* Number of pending notification responses */
+ unsigned int pending_responses;
+
+ /* Target power state */
+ unsigned int state;
+
+ /*
+ * Status of the responses received so far. Either FWK_SUCCESS if all the
+ * responses received so far have indicated success, or FWK_E_DEVICE
+ * otherwise.
+ */
+ int response_status;
+
+ /*
+ * Validity flag. Set to true when a notification is sent and reset to
+ * false when the requested state for the power domain is changed.
+ */
+ bool valid;
+};
+
+struct pd_ctx {
+ /* Identifier of the power domain */
+ fwk_id_t id;
+
+ /* Driver interface */
+ struct mod_pd_driver_api *driver_api;
+
+ /* Driver's identifier of the power domain */
+ fwk_id_t driver_id;
+
+ /* Power domain configuration data */
+ const struct mod_power_domain_element_config *config;
+
+ /*
+ * Mask of the valid states. Bit \c n in \ref valid_states_mask is equal
+ * to one if and only if the state \c n is a valid state for the power
+ * domain. The number of bits of this field has to be greater or equal than
+ * MOD_PD_STATE_COUNT_MAX.
+ */
+ uint32_t valid_state_mask;
+
+ /*
+ * Table of allowed state masks. Bit \c n of the entry \c m is equal to
+ * one if and only if the state \c n for the power domain is allowed when
+ * its parent is in state \c m. The number of bits of each entry of this
+ * table has to be greater or equal than MOD_PD_STATE_COUNT_MAX.
+ */
+ const uint32_t *allowed_state_mask_table;
+
+ /* Size of the table of allowed state masks */
+ size_t allowed_state_mask_table_size;
+
+ /* Pointer to the power domain's parent context */
+ struct pd_ctx *parent;
+
+ /*
+ * Pointer to the context of the power domain's first child. This
+ * field is equal to NULL if the power domain does not have any children.
+ */
+ struct pd_ctx *first_child;
+
+ /*
+ * Pointer to the context of the power domain sibling in the chain of the
+ * children power domains of their parent.
+ */
+ struct pd_ctx *sibling;
+
+ /* Requested power state for the power domain */
+ unsigned int requested_state;
+
+ /* Last power state requested to the driver for the power domain */
+ unsigned int state_requested_to_driver;
+
+ /*
+ * Current state of the power domain. When a transition is in progress, the
+ * current state is different from the last requested state.
+ */
+ unsigned int current_state;
+
+ /* Pending response context */
+ struct response_ctx response;
+
+ /* Context for the power state transition notification */
+ struct power_state_transition_notification_ctx
+ power_state_transition_notification_ctx;
+
+ /* Context for the power state pre-transition notification */
+ struct power_state_pre_transition_notification_ctx
+ power_state_pre_transition_notification_ctx;
+};
+
+struct system_suspend_ctx {
+ /* Flag indicating if a system suspend is ongoing (true) or not (false) */
+ bool ongoing;
+
+ /* Last standing core context */
+ struct pd_ctx *last_core_pd;
+
+ /* Suspend state for the system power domain */
+ unsigned int state;
+};
+
+struct mod_pd_ctx {
+ /* Module configuration data */
+ struct mod_power_domain_config *config;
+
+ /* Table of power domain contexts */
+ struct pd_ctx *pd_ctx_table;
+
+ /* Number of power domains */
+ unsigned int pd_count;
+
+ /* Log module API */
+ const struct mod_log_api *log_api;
+
+ /* Context of the system power domain */
+ struct pd_ctx *system_pd_ctx;
+
+ /* System suspend context */
+ struct system_suspend_ctx system_suspend;
+};
+
+/*
+ * Power domain module events
+ */
+
+/* Power module event indexes */
+enum pd_event_idx {
+ PD_EVENT_IDX_SET_STATE,
+ PD_EVENT_IDX_GET_STATE,
+ PD_EVENT_IDX_RESET,
+ PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION,
+ PD_EVENT_IDX_SYSTEM_SUSPEND,
+ PD_EVENT_IDX_SYSTEM_SHUTDOWN,
+ PD_EVENT_COUNT
+};
+
+/* Standard parameter of a response event */
+struct pd_response {
+ /* Status of the event processing */
+ int status;
+};
+
+/*
+ * PD_EVENT_IDX_SET_STATE
+ * Parameters of the set state request event
+ */
+struct pd_set_state_request {
+ /*
+ * The composite state that defines the power state that the power domain,
+ * target of the request, has to be put into and possibly the power states
+ * the ancestors of the power domain have to be put into.
+ */
+ uint32_t composite_state;
+};
+
+/* Parameters of the set state response event */
+struct pd_set_state_response {
+ /* Status of the set state request event processing */
+ int status;
+
+ /*
+ * The composite state that defines the power state that the power domain,
+ * target of the request, had to be put into and possibly the power states
+ * the ancestors of the power domain had to be put into.
+ */
+ uint32_t composite_state;
+};
+
+/*
+ * PD_EVENT_IDX_GET_STATE
+ * Parameters of the get state request event
+ */
+struct pd_get_state_request {
+ /*
+ * Flag indicating if the composite state of the power domain and its
+ * ancestors has to be returned (composite=true) or just the power domain
+ * state (composite=false).
+ */
+ bool composite;
+};
+
+/* Parameters of the get state response event */
+struct pd_get_state_response {
+ /* Status of the get state request event processing */
+ int status;
+
+ /* Copy of the "composite" request parameter */
+ bool composite;
+
+ /*
+ * The power state of the power domain target of the request or the
+ * composite state of the power domain and its ancestors depending on the
+ * value of the "composite" request parameter.
+ */
+ uint32_t state;
+};
+
+/*
+ * PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION
+ * Parameters of the power state transition report event
+ */
+struct pd_power_state_transition_report {
+ /* The new power state of the power domain */
+ uint32_t state;
+};
+
+/*
+ * PD_EVENT_IDX_SYSTEM_SUSPEND
+ * Parameters of the system suspend request event
+ */
+struct pd_system_suspend_request {
+ /* System suspend state, platform specific */
+ unsigned int state;
+};
+
+/*
+ * PD_EVENT_IDX_SYSTEM_SHUTDOWN
+ * Parameters of the system shutdown request event
+ */
+struct pd_system_shutdown_request {
+ /* System shutdown type */
+ enum mod_pd_system_shutdown system_shutdown;
+};
+
+/*
+ * For each power level, shift in a composite state of the state for the power
+ * level.
+ */
+static const unsigned int mod_pd_cs_level_state_shift[MOD_PD_LEVEL_COUNT] = {
+ MOD_PD_CS_LEVEL_0_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_1_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_2_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_3_STATE_SHIFT
+};
+
+/*
+ * Internal variables
+ */
+static struct mod_pd_ctx mod_pd_ctx;
+static const char driver_error_msg[] = "[PD] Driver error %e in %s @%d\n";
+
+static const unsigned int tree_pos_level_shift[MOD_PD_LEVEL_COUNT] = {
+ MOD_PD_TREE_POS_LEVEL_0_SHIFT,
+ MOD_PD_TREE_POS_LEVEL_1_SHIFT,
+ MOD_PD_TREE_POS_LEVEL_2_SHIFT,
+ MOD_PD_TREE_POS_LEVEL_3_SHIFT
+};
+
+static const char * const default_state_name_table[] = {
+ "OFF", "ON", "SLEEP", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15"
+};
+
+/*
+ * Utility functions
+ */
+
+/* Functions related to power domain positions in the power domain tree */
+static bool is_valid_tree_pos(uint64_t tree_pos)
+{
+ return (tree_pos < MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0));
+}
+
+static enum mod_pd_level get_level_from_tree_pos(uint64_t tree_pos)
+{
+ return (enum mod_pd_level)((tree_pos >> MOD_PD_TREE_POS_LEVEL_SHIFT) &
+ MOD_PD_TREE_POS_LEVEL_MASK);
+}
+
+static uint64_t compute_parent_tree_pos_from_tree_pos(uint64_t tree_pos)
+{
+ uint64_t parent_tree_pos;
+ unsigned int level;
+
+ level = get_level_from_tree_pos(tree_pos);
+
+ parent_tree_pos = (tree_pos &
+ (~((((uint64_t)1) << tree_pos_level_shift[level+1])-1)))
+ + (((uint64_t)1) << MOD_PD_TREE_POS_LEVEL_SHIFT);
+
+ return parent_tree_pos;
+}
+
+/*
+ * Get a pointer to the descriptor of a power domain given its position in the
+ * power domain tree.
+ *
+ * \param tree_pos The power domain position in the power domain tree.
+ *
+ * \retval NULL The tree position of the power domain is invalid.
+ * \return Pointer to the descriptor of the power domain.
+ */
+static struct pd_ctx *get_ctx_from_tree_pos(uint64_t tree_pos)
+{
+ unsigned int min_idx = 0;
+ unsigned int max_idx_plus_one = mod_pd_ctx.pd_count;
+ unsigned int middle_idx;
+ struct pd_ctx *pd;
+
+ while (min_idx < max_idx_plus_one) {
+ middle_idx = (min_idx + max_idx_plus_one) / 2;
+ pd = &mod_pd_ctx.pd_ctx_table[middle_idx];
+ if (pd->config->tree_pos == tree_pos)
+ return pd;
+ else {
+ if (pd->config->tree_pos > tree_pos)
+ max_idx_plus_one = middle_idx;
+ else
+ min_idx = middle_idx + 1;
+ }
+ }
+
+ return NULL;
+}
+
+/* State related utility functions */
+static bool is_valid_state(const struct pd_ctx *pd, unsigned int state)
+{
+ return (state < MOD_PD_STATE_COUNT_MAX) &&
+ ((pd->valid_state_mask & (1 << state)) != 0);
+}
+
+static unsigned int normalize_state(unsigned int state)
+{
+ switch (state) {
+ case MOD_PD_STATE_OFF:
+ return (MOD_PD_STATE_COUNT_MAX + 1);
+
+ case MOD_PD_STATE_SLEEP:
+ return MOD_PD_STATE_COUNT_MAX;
+
+ default:
+ return state;
+ }
+}
+
+static bool is_deeper_state(unsigned int state,
+ unsigned int state_to_compare_to)
+{
+ return normalize_state(state) > normalize_state(state_to_compare_to);
+}
+
+static bool is_shallower_state(unsigned int state,
+ unsigned int state_to_compare_to)
+{
+ return normalize_state(state) < normalize_state(state_to_compare_to);
+}
+
+static bool is_allowed_by_child(const struct pd_ctx *child,
+ unsigned int parent_state, unsigned int child_state)
+{
+ if (parent_state >= child->allowed_state_mask_table_size)
+ return false;
+
+ return ((child->allowed_state_mask_table[parent_state]
+ & (1 << child_state)) != 0);
+}
+
+static bool is_allowed_by_children(const struct pd_ctx *pd, unsigned int state)
+{
+ const struct pd_ctx *child;
+
+ for (child = pd->first_child; child != NULL; child = child->sibling) {
+ if (!is_allowed_by_child(child, state, child->requested_state))
+ return false;
+ }
+
+ return true;
+}
+
+static const char *get_state_name(const struct pd_ctx *pd, unsigned int state)
+{
+ static char const unknown_name[] = "Unknown";
+
+ if (state < pd->config->state_name_table_size)
+ return pd->config->state_name_table[state];
+ else if (state < FWK_ARRAY_SIZE(default_state_name_table))
+ return default_state_name_table[state];
+ else
+ return unknown_name;
+}
+
+/* Functions related to a composite state */
+static unsigned int get_level_state_from_composite_state(
+ uint32_t composite_state, enum mod_pd_level level)
+{
+ return (composite_state >> mod_pd_cs_level_state_shift[level])
+ & MOD_PD_CS_STATE_MASK;
+}
+
+static enum mod_pd_level get_highest_level_from_composite_state(
+ uint32_t composite_state)
+{
+ return (enum mod_pd_level)((composite_state >> MOD_PD_CS_LEVEL_SHIFT) &
+ MOD_PD_CS_STATE_MASK);
+}
+
+static bool is_valid_composite_state(struct pd_ctx *target_pd,
+ uint32_t composite_state)
+{
+ enum mod_pd_level level;
+ enum mod_pd_level highest_level;
+ unsigned int state, child_state = MOD_PD_STATE_OFF;
+ struct pd_ctx *pd = target_pd;
+ struct pd_ctx *child = NULL;
+
+ assert(target_pd != NULL);
+
+ if (composite_state & (~MOD_PD_CS_VALID_BITS))
+ goto error;
+
+ level = get_level_from_tree_pos(pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+
+ if ((highest_level < level) ||
+ (highest_level >= MOD_PD_LEVEL_COUNT))
+ goto error;
+
+ for (; level <= highest_level; level++) {
+ if (pd == NULL)
+ goto error;
+
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (!is_valid_state(pd, state))
+ goto error;
+
+ if ((child != NULL) && !is_allowed_by_child(child, state, child_state))
+ goto error;
+
+ child = pd;
+ child_state = state;
+ pd = pd->parent;
+ }
+
+ return true;
+
+error:
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Invalid composite state for %s: 0x%08x\n",
+ fwk_module_get_name(target_pd->id), composite_state);
+ return false;
+}
+
+/*
+ * Determine whether a composite state requires that the transition begins
+ * with the highest or lowest level.
+ *
+ * \param lowest_pd Target of the composite state transition request.
+ * \param uint32_t composite_state Target composite state.
+ * \retval true The power state transition must propagate upwards.
+ * \retval false The power state transition must propagate downwards.
+ */
+static bool is_upwards_transition_propagation(const struct pd_ctx *lowest_pd,
+ uint32_t composite_state)
+{
+ enum mod_pd_level lowest_level, highest_level, level;
+ const struct pd_ctx *pd;
+ unsigned int state;
+
+ lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+
+ for (level = lowest_level, pd = lowest_pd; level <= highest_level;
+ level++, pd = pd->parent) {
+
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (state == pd->requested_state)
+ continue;
+
+ return is_deeper_state(state, pd->requested_state);
+ }
+
+ return false;
+}
+
+/* Sub-routine of 'pd_post_init()', to build the power domain tree */
+static int build_pd_tree(void)
+{
+ unsigned int index;
+ struct pd_ctx *pd;
+ uint64_t tree_pos;
+ uint64_t parent_tree_pos;
+ uint64_t last_parent_tree_pos;
+ struct pd_ctx *parent = NULL;
+ struct pd_ctx *child;
+ struct pd_ctx *prev_sibling;
+
+ last_parent_tree_pos = 0; /* Impossible value for a parent position */
+ for (index = 0; index < mod_pd_ctx.pd_count; index++) {
+ pd = &mod_pd_ctx.pd_ctx_table[index];
+ tree_pos = pd->config->tree_pos;
+ parent_tree_pos = compute_parent_tree_pos_from_tree_pos(tree_pos);
+ if (parent_tree_pos != last_parent_tree_pos) {
+ parent = get_ctx_from_tree_pos(parent_tree_pos);
+ last_parent_tree_pos = parent_tree_pos;
+ }
+ pd->parent = parent;
+
+ if (parent == NULL) {
+ if (index == (mod_pd_ctx.pd_count - 1))
+ break;
+ else
+ return FWK_E_PARAM;
+ }
+
+ /*
+ * Update the list of children of the power domain parent. The children
+ * are in increasing order of their identifier in the chain of children.
+ */
+ child = parent->first_child;
+ prev_sibling = NULL;
+
+ while ((child != NULL) && (child->config->tree_pos < tree_pos)) {
+ prev_sibling = child;
+ child = child->sibling;
+ }
+
+ if (prev_sibling == NULL) {
+ pd->sibling = parent->first_child;
+ parent->first_child = pd;
+ } else {
+ pd->sibling = prev_sibling->sibling;
+ prev_sibling->sibling = pd;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Check whether a transition to a given power state for a power domain is
+ * possible given the current state of its parent and children (if any).
+ *
+ * \param pd Description of the power domain to check the power state transition
+ * for.
+ * \param state Power state.
+ */
+static bool is_allowed_by_parent_and_children(struct pd_ctx *pd,
+ unsigned int state)
+{
+ struct pd_ctx *parent, *child;
+
+ parent = pd->parent;
+ if (parent != NULL) {
+ if (!is_allowed_by_child(pd, parent->current_state, state))
+ return false;
+ }
+
+ child = pd->first_child;
+ while (child != NULL) {
+ if (!is_allowed_by_child(child, state, child->current_state))
+ return false;
+ child = child->sibling;
+ }
+
+ return true;
+}
+
+/*
+ * Check whether a power state pre-transition notification must be sent.
+ *
+ * \param pd Description of the power domain
+ * \param state Power state the power domain has to transit to
+ *
+ * \retval true A power state pre-transition notification must be sent.
+ * \retval false A power state pre-transition notification doesn't have to be
+ * sent.
+ */
+static bool check_power_state_pre_transition_notification(struct pd_ctx *pd,
+ unsigned int state)
+{
+ if (!is_deeper_state(state, pd->state_requested_to_driver))
+ return false;
+
+ if ((state == pd->power_state_pre_transition_notification_ctx.state) &&
+ pd->power_state_pre_transition_notification_ctx.valid) {
+ return (pd->power_state_pre_transition_notification_ctx.response_status
+ != FWK_SUCCESS);
+ }
+
+ return true;
+}
+
+/*
+ * Initiate a power state pre-transition notification if necessary.
+ *
+ * \param pd Description of the power domain to initiate the notification
+ * for.
+ *
+ * \retval true Waiting for notification responses.
+ * \retval false Not waiting for any notification response.
+ */
+static bool initiate_power_state_pre_transition_notification(struct pd_ctx *pd)
+{
+ unsigned int state;
+ struct fwk_event notification_event = {
+ .id = mod_pd_notification_id_power_state_pre_transition,
+ .response_requested = true
+ };
+ struct mod_pd_power_state_pre_transition_notification_params *params;
+
+ state = pd->requested_state;
+ if (!check_power_state_pre_transition_notification(pd, state))
+ return false;
+
+ /*
+ * If still waiting for some responses on the previous power state
+ * pre-transition notification, wait for them before to issue the next one.
+ */
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0)
+ return true;
+
+ params = (struct mod_pd_power_state_pre_transition_notification_params *)
+ notification_event.params;
+ params->current_state = pd->current_state;
+ params->target_state = state;
+ fwk_notification_notify(&notification_event,
+ &pd->power_state_pre_transition_notification_ctx.pending_responses);
+
+ pd->power_state_pre_transition_notification_ctx.state = state;
+ pd->power_state_pre_transition_notification_ctx.response_status =
+ FWK_SUCCESS;
+ pd->power_state_pre_transition_notification_ctx.valid = true;
+
+ return (pd->power_state_pre_transition_notification_ctx.pending_responses
+ != 0);
+}
+
+/*
+ * Initiate the transition to a power state for a power domain.
+ *
+ * \param pd Description of the power domain to initiate the state transition
+ * for.
+ *
+ * \retval FWK_SUCCESS The power state transition was initiated.
+ * \retval FWK_E_DEVICE The power state transition was denied by the driver.
+ * \return One of the other driver-defined error codes.
+ */
+static int initiate_power_state_transition(struct pd_ctx *pd)
+{
+ int status;
+ unsigned int state = pd->requested_state;
+
+ if ((pd->driver_api->deny != NULL) &&
+ pd->driver_api->deny(pd->driver_id, state)) {
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Transition of %s to state <%s>,\n",
+ fwk_module_get_name(pd->id), get_state_name(pd, state));
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "\tdenied by driver.\n");
+ return FWK_E_DEVICE;
+ }
+
+ status = pd->driver_api->set_state(pd->driver_id, state);
+
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[PD] %s: %s->%s, %e\n", fwk_module_get_name(pd->id),
+ get_state_name(pd, pd->state_requested_to_driver),
+ get_state_name(pd, state), status);
+
+ pd->state_requested_to_driver = state;
+
+ return status;
+}
+
+/*
+ * Respond to a request.
+ *
+ * \param pd Description of the power domain in charge of the response
+ * \param status Response status
+ */
+static void respond(struct pd_ctx *pd, int status)
+{
+ struct fwk_event resp_event;
+ const struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&resp_event.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp_event.params);
+
+ if (!pd->response.pending)
+ return;
+
+ status = fwk_thread_get_delayed_response(
+ pd->id, pd->response.cookie, &resp_event);
+ pd->response.pending = false;
+
+ if (status != FWK_SUCCESS)
+ return;
+
+ resp_params->composite_state = req_params->composite_state;
+ resp_params->status = status;
+
+ fwk_thread_put_event(&resp_event);
+}
+
+/*
+ * Process a 'set state' request
+ *
+ * \param lowest_pd Description of the target of the 'set state' request
+ * \param req_params Parameters of the 'set state' request
+ * \param [out] Response event
+ */
+static void process_set_state_request(struct pd_ctx *lowest_pd,
+ struct pd_set_state_request *req_params, struct fwk_event *resp_event)
+{
+ int status;
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)resp_event->params;
+ uint32_t composite_state;
+ bool up;
+ enum mod_pd_level lowest_level, highest_level, level;
+ unsigned int nb_pds;
+ struct pd_ctx *pd;
+ const struct pd_ctx *parent;
+ struct pd_ctx *pd_in_charge_of_response = NULL;
+ unsigned int pd_index;
+ unsigned int state;
+ bool first_power_state_transition_initiated = false;
+
+ /* A set state request cancels any pending system suspend. */
+ mod_pd_ctx.system_suspend.ongoing = false;
+
+ composite_state = req_params->composite_state;
+ up = is_upwards_transition_propagation(lowest_pd, composite_state);
+
+ /*
+ * It has already been tested as part of the composite state validation that
+ * 'highest_level >= lowest_level' and 'highest_level' is lower
+ * than the highest power level.
+ */
+ lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+ nb_pds = highest_level - lowest_level + 1;
+
+ status = FWK_SUCCESS;
+ pd = lowest_pd;
+ for (pd_index = 0; pd_index < nb_pds; pd_index++, pd = pd->parent) {
+ if (up)
+ level = lowest_level + pd_index;
+ else {
+ /*
+ * When walking down the power domain tree, get the context of the
+ * next power domain to process as well as its level.
+ */
+ pd = lowest_pd;
+ for (level = lowest_level;
+ level < (highest_level - pd_index); level++)
+ pd = pd->parent;
+ }
+
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (state == pd->requested_state)
+ continue;
+
+ /*
+ * Check that the requested power state is compatible with the states
+ * currently requested for the parent and children of the power domain.
+ */
+ parent = pd->parent;
+ if ((parent != NULL) &&
+ (!is_allowed_by_child(pd, parent->requested_state, state))) {
+ status = FWK_E_PWRSTATE;
+ break;
+ }
+
+ if (!is_allowed_by_children(pd, state))
+ continue;
+
+ /*
+ * A new valid power state is requested for the power domain. Send any
+ * pending response concerning the previous requested power state.
+ */
+ pd->requested_state = state;
+ pd->power_state_pre_transition_notification_ctx.valid = false;
+ respond(pd, FWK_E_OVERWRITTEN);
+
+ if (pd->state_requested_to_driver == state)
+ continue;
+
+ /*
+ * The driver must be called thus the processing of the set state
+ * request is going to be asynchronous. Assign the responsability of
+ * the response to the request to the power domain. If there is no
+ * need for a driver call for the ancestors or descendants of the power
+ * domain as part of the processing of the requested composite state,
+ * the response to the request will be sent when the transition to the
+ * new requested power state is completed.
+ */
+ pd_in_charge_of_response = pd;
+
+ /*
+ * If a power state transition has already been initiated for an
+ * ancestor or descendant, we don't initiate the power state transition
+ * now. It will be initiated on completion of the transition of one
+ * of its ancestor or descendant.
+ */
+ if (first_power_state_transition_initiated)
+ continue;
+
+ /*
+ * If the parent or a child is not currently in a power state
+ * compatible with the new requested state for the power domain, do not
+ * initiate the transition now as well. It will be initiated when the
+ * parent and the children are in a proper state.
+ */
+ if (!is_allowed_by_parent_and_children(pd, state))
+ continue;
+
+ /*
+ * Defer the power state transition if power state pre-transition
+ * notification responses need to be waited for.
+ */
+ if (initiate_power_state_pre_transition_notification(pd))
+ continue;
+
+ status = initiate_power_state_transition(pd);
+ first_power_state_transition_initiated = (status == FWK_SUCCESS);
+ }
+
+ if (!resp_event->response_requested)
+ return;
+
+ if (pd_in_charge_of_response != NULL) {
+ resp_event->is_delayed_response = true;
+ resp_event->source_id = pd_in_charge_of_response->id;
+ pd_in_charge_of_response->response.pending = true;
+ pd_in_charge_of_response->response.cookie = resp_event->cookie;
+ } else {
+ resp_params->status = status;
+ resp_params->composite_state = composite_state;
+ }
+}
+
+/*
+ * Complete a system suspend
+ *
+ * Following the shutdown of the last standing core put all of its ancestors
+ * in the MOD_PD_STATE_OFF state but the system power domain which is put
+ * into the state that has been asked for.
+ *
+ * target_pd Description of the power domain target of the 'set composite state'
+ * request to suspend the system in the desired state.
+ */
+static int complete_system_suspend(struct pd_ctx *target_pd)
+{
+ enum mod_pd_level level;
+ unsigned int composite_state = 0;
+ struct pd_ctx *pd = target_pd;
+ struct fwk_event resp_event = { };
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp_event.params);
+
+ /*
+ * Traverse the PD tree bottom-up from current power domain to the top
+ * to build the composite state with MOD_PD_STATE_OFF power state for all
+ * levels but the last one.
+ */
+ level = get_level_from_tree_pos(target_pd->config->tree_pos);
+ do {
+ composite_state |= ((pd->parent != NULL) ? MOD_PD_STATE_OFF :
+ mod_pd_ctx.system_suspend.state)
+ << mod_pd_cs_level_state_shift[level++];
+ pd = pd->parent;
+ } while (pd != NULL);
+
+ /*
+ * Finally, we need to update the highest valid level in the composite
+ * state.
+ */
+ composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT;
+
+ process_set_state_request(target_pd,
+ &((struct pd_set_state_request){
+ .composite_state = composite_state,
+ }), &resp_event);
+
+ return resp_params->status;
+}
+
+/*
+ * Process a 'get composite state' request.
+ *
+ * pd Description of the target of the 'get state' request
+ * req_params Parameters of the 'get state' request
+ * resp_params Parameters of the 'get state' request response to be filled in
+ */
+static void process_get_state_request(struct pd_ctx *pd,
+ const struct pd_get_state_request *req_params,
+ struct pd_get_state_response *resp_params)
+{
+ enum mod_pd_level level = get_level_from_tree_pos(pd->config->tree_pos);
+ unsigned int composite_state = 0;
+
+ if (!req_params->composite)
+ resp_params->state = pd->current_state;
+ else {
+ /*
+ * Traverse the PD tree bottom-up from current power domain to the top,
+ * collecting node's states and placing them in the correct position in
+ * the composite state.
+ */
+ do {
+ composite_state |= pd->current_state <<
+ mod_pd_cs_level_state_shift[level++];
+ pd = pd->parent;
+ } while (pd != NULL);
+
+ /*
+ * Finally, we need to update the highest valid level in
+ * the composite state.
+ */
+ composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT;
+
+ resp_params->state = composite_state;
+ }
+
+ resp_params->status = FWK_SUCCESS;
+}
+
+/*
+ * Process a 'reset' request.
+ *
+ * pd Description of the target of the 'reset' request
+ * resp_params Parameters of the 'reset' request response to be filled in
+ */
+static void process_reset_request(struct pd_ctx *pd,
+ struct pd_response *resp_params)
+{
+ int status;
+ struct pd_ctx *child;
+
+ status = FWK_E_PWRSTATE;
+ if (pd->requested_state == MOD_PD_STATE_OFF)
+ goto exit;
+
+ child = pd->first_child;
+ while (child != NULL) {
+ if ((child->requested_state != MOD_PD_STATE_OFF) ||
+ (child->current_state != MOD_PD_STATE_OFF))
+ goto exit;
+ child = child->sibling;
+ }
+
+ status = pd->driver_api->reset(pd->driver_id);
+
+exit:
+ resp_params->status = status;
+}
+
+/*
+ * Process a power state transition report describing a transition to a deeper
+ * state.
+ *
+ * \param pd Target power domain context
+ */
+static void process_power_state_transition_report_deeper_state(
+ struct pd_ctx *pd)
+{
+ struct pd_ctx *parent = pd->parent;
+ unsigned int requested_state = parent->requested_state;
+
+ if (parent == NULL)
+ return;
+
+ if (parent->state_requested_to_driver == requested_state)
+ return;
+
+ if (!is_allowed_by_parent_and_children(parent, requested_state))
+ return;
+
+ if (!initiate_power_state_pre_transition_notification(parent))
+ initiate_power_state_transition(parent);
+}
+
+/*
+ * Process a power state transition report describing a transition to a
+ * shallower state.
+ *
+ * \param pd Target power domain context
+ */
+static void process_power_state_transition_report_shallower_state(
+ struct pd_ctx *pd)
+{
+ struct pd_ctx *child;
+ unsigned int requested_state;
+
+ for (child = pd->first_child; child != NULL; child = child->sibling) {
+ requested_state = child->requested_state;
+ if (child->state_requested_to_driver == requested_state)
+ continue;
+
+ if (!is_allowed_by_parent_and_children(child, requested_state))
+ return;
+
+ if (!initiate_power_state_pre_transition_notification(child))
+ initiate_power_state_transition(child);
+ }
+}
+
+/*
+ * Process a power state transition report
+ *
+ * \param pd Description of the target of the power state transition report
+ * \param report_params Parameters of the power state transition report
+ */
+static void process_power_state_transition_report(struct pd_ctx *pd,
+ const struct pd_power_state_transition_report *report_params)
+{
+ unsigned int new_state = report_params->state;
+ unsigned int previous_state;
+ struct fwk_event notification_event = {
+ .id = mod_pd_notification_id_power_state_transition,
+ .response_requested = true
+ };
+ struct mod_pd_power_state_transition_notification_params *params;
+
+ if (new_state == pd->requested_state)
+ respond(pd, FWK_SUCCESS);
+
+ previous_state = pd->current_state;
+ pd->current_state = new_state;
+
+ if (pd->power_state_transition_notification_ctx.pending_responses == 0) {
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ notification_event.params;
+ params->state = new_state;
+ pd->power_state_transition_notification_ctx.state = new_state;
+ fwk_notification_notify(&notification_event,
+ &pd->power_state_transition_notification_ctx.pending_responses);
+ }
+
+ if ((mod_pd_ctx.system_suspend.ongoing) &&
+ (pd == mod_pd_ctx.system_suspend.last_core_pd)) {
+ mod_pd_ctx.system_suspend.ongoing = false;
+ complete_system_suspend(pd);
+ }
+
+ if (is_deeper_state(new_state, previous_state))
+ process_power_state_transition_report_deeper_state(pd);
+ else if (is_shallower_state(new_state, previous_state))
+ process_power_state_transition_report_shallower_state(pd);
+}
+
+/*
+ * Process a 'system suspend' request
+ *
+ * req_params Parameters of the 'system suspend' request
+ * resp_params Parameters of the 'system suspend' request response to be filled
+ * in
+ */
+static void process_system_suspend_request(
+ const struct pd_system_suspend_request *req_params,
+ struct pd_response *resp_params)
+{
+ int status;
+ unsigned int pd_idx;
+ struct pd_ctx *pd;
+ struct pd_ctx *last_core_pd = NULL;
+ struct pd_ctx *last_cluster_pd = NULL;
+
+ /*
+ * All core related power domains have to be in the MOD_PD_STATE_OFF state
+ * but one core and its ancestors.
+ */
+ for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
+ pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
+ if ((pd->requested_state == MOD_PD_STATE_OFF) &&
+ (pd->current_state == MOD_PD_STATE_OFF))
+ continue;
+
+ if (pd->config->attributes.pd_type == MOD_PD_TYPE_CORE) {
+ if (last_core_pd != NULL) {
+ resp_params->status = FWK_E_STATE;
+ return;
+ }
+ last_core_pd = pd;
+ } else if (pd->config->attributes.pd_type == MOD_PD_TYPE_CLUSTER) {
+ if (last_cluster_pd != NULL) {
+ resp_params->status = FWK_E_STATE;
+ return;
+ }
+ last_cluster_pd = pd;
+ }
+ }
+
+ if (last_core_pd == NULL) {
+ status = complete_system_suspend(
+ (last_cluster_pd != NULL) ? last_cluster_pd :
+ mod_pd_ctx.system_pd_ctx);
+ } else {
+ status = last_core_pd->driver_api->prepare_core_for_system_suspend(
+ last_core_pd->driver_id);
+ if (status == FWK_SUCCESS) {
+ mod_pd_ctx.system_suspend.ongoing = true;
+ mod_pd_ctx.system_suspend.last_core_pd = last_core_pd;
+ mod_pd_ctx.system_suspend.state = req_params->state;
+ last_core_pd->requested_state =
+ last_core_pd->state_requested_to_driver = MOD_PD_STATE_OFF;
+ }
+ }
+
+ resp_params->status = status;
+}
+
+/*
+ * Process a 'system shutdown' request
+ *
+ * req_params Parameters of the 'system shutdown' request
+ * resp_params Parameters of the 'system shutdown' request response to be filled
+ * in
+ */
+static void process_system_shutdown_request(
+ const struct pd_system_shutdown_request *req_params,
+ struct pd_response *resp_params)
+{
+ int status;
+ unsigned int pd_idx;
+ struct pd_ctx *pd;
+ fwk_id_t pd_id;
+
+ for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
+ pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
+ pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, pd_idx);
+
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[PD] Shutting down %s\n", fwk_module_get_name(pd_id));
+
+ if (pd->driver_api->shutdown != NULL) {
+ status = pd->driver_api->shutdown(pd->driver_id,
+ req_params->system_shutdown);
+ } else
+ status = pd->driver_api->set_state(pd->driver_id, MOD_PD_STATE_OFF);
+
+ if (status != FWK_SUCCESS)
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Shutdown of %s returned %e\n",
+ fwk_module_get_name(pd_id), status);
+ else
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_DEBUG,
+ "[PD] %s shutdown\n", fwk_module_get_name(pd_id));
+
+ pd->requested_state =
+ pd->state_requested_to_driver =
+ pd->current_state = MOD_PD_STATE_OFF;
+ }
+
+ resp_params->status = FWK_E_PANIC;
+}
+
+/*
+ * API functions
+ */
+
+/* Functions common to the public and restricted API */
+static int pd_get_domain_type(fwk_id_t pd_id, enum mod_pd_type *type)
+{
+ int status;
+ struct pd_ctx *pd;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (type == NULL)
+ return FWK_E_PARAM;
+
+ if (!fwk_module_is_valid_element_id(pd_id))
+ return FWK_E_PARAM;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ *type = pd->config->attributes.pd_type;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_get_domain_parent_id(fwk_id_t pd_id, fwk_id_t *parent_pd_id)
+{
+ int status;
+ const struct pd_ctx *pd;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (parent_pd_id == NULL)
+ return FWK_E_PARAM;
+
+ if (!fwk_module_is_valid_element_id(pd_id))
+ return FWK_E_PARAM;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ *parent_pd_id = (pd->parent != NULL) ? pd->parent->id : FWK_ID_NONE;
+
+ return FWK_SUCCESS;
+}
+
+/* Functions specific to the restricted API */
+
+static int pd_set_state(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+ struct pd_ctx *pd;
+ enum mod_pd_level level;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_state(pd, state))
+ return FWK_E_PARAM;
+
+ level = get_level_from_tree_pos(pd->config->tree_pos);
+
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE);
+ req_params->composite_state = (level << MOD_PD_CS_LEVEL_SHIFT) |
+ (state << mod_pd_cs_level_state_shift[level]);
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_set_state_async(fwk_id_t pd_id,
+ bool response_requested, unsigned int state)
+{
+ int status;
+ struct pd_ctx *pd;
+ enum mod_pd_level level;
+ struct fwk_event req = { };
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_state(pd, state))
+ return FWK_E_PARAM;
+
+ level = get_level_from_tree_pos(pd->config->tree_pos);
+
+ req.source_id = pd->driver_id;
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE);
+ req.response_requested = response_requested;
+ req_params->composite_state = (level << MOD_PD_CS_LEVEL_SHIFT) |
+ (state << mod_pd_cs_level_state_shift[level]);
+
+ return fwk_thread_put_event(&req);
+}
+
+static int pd_set_composite_state(fwk_id_t pd_id, uint32_t composite_state)
+{
+ int status;
+ struct pd_ctx *pd;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_composite_state(pd, composite_state))
+ return FWK_E_PARAM;
+
+ req.source_id = pd->driver_id;
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE);
+ req_params->composite_state = composite_state;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_set_composite_state_async(fwk_id_t pd_id,
+ bool response_requested,
+ uint32_t composite_state)
+{
+ int status;
+ struct pd_ctx *pd;
+ struct fwk_event req = { };
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_composite_state(pd, composite_state))
+ return FWK_E_PARAM;
+
+ req.source_id = pd->driver_id;
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE);
+ req.response_requested = response_requested;
+ req_params->composite_state = composite_state;
+
+ return fwk_thread_put_event(&req);
+}
+
+static int pd_get_state(fwk_id_t pd_id, unsigned int *state)
+{
+ int status;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_get_state_request *req_params =
+ (struct pd_get_state_request *)(&req.params);
+ struct pd_get_state_response *resp_params =
+ (struct pd_get_state_response *)(&resp.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (state == NULL)
+ return FWK_E_PARAM;
+
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_GET_STATE);
+ req_params->composite = false;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (resp_params->status != FWK_SUCCESS)
+ return resp_params->status;
+
+ *state = resp_params->state;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_get_composite_state(fwk_id_t pd_id, unsigned int *composite_state)
+{
+ int status;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_get_state_request *req_params =
+ (struct pd_get_state_request *)(&req.params);
+ struct pd_get_state_response *resp_params =
+ (struct pd_get_state_response *)(&resp.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (composite_state == NULL)
+ return FWK_E_PARAM;
+
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_GET_STATE);
+ req_params->composite = true;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (resp_params->status != FWK_SUCCESS)
+ return resp_params->status;
+
+ *composite_state = resp_params->state;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_reset(fwk_id_t pd_id)
+{
+ int status;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_RESET);
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_system_suspend(unsigned int state)
+{
+ int status;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_system_suspend_request *req_params =
+ (struct pd_system_suspend_request *)(&req.params);
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ status = fwk_module_check_call(fwk_module_id_power_domain);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ req.target_id = fwk_module_id_power_domain;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN,
+ PD_EVENT_IDX_SYSTEM_SUSPEND);
+ req_params->state = state;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_system_shutdown(enum mod_pd_system_shutdown system_shutdown)
+{
+ int status;
+ struct fwk_event req = { };
+ struct fwk_event resp;
+ struct pd_system_shutdown_request *req_params =
+ (struct pd_system_shutdown_request *)(&req.params);
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ status = fwk_module_check_call(fwk_module_id_power_domain);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ req.target_id = fwk_module_id_power_domain;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN,
+ PD_EVENT_IDX_SYSTEM_SHUTDOWN);
+ req_params->system_shutdown = system_shutdown;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+/* Functions specific to the driver input API */
+
+static int pd_reset_async(fwk_id_t pd_id, bool response_requested)
+{
+ int status;
+ struct fwk_event req = { };
+ struct pd_ctx *pd;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ req.source_id = pd->driver_id;
+ req.target_id = pd_id;
+ req.id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_RESET);
+ req.response_requested = response_requested;
+
+ return fwk_thread_put_event(&req);
+}
+
+static int report_power_state_transition(const struct pd_ctx *pd,
+ unsigned int state)
+{
+ struct fwk_event report;
+ struct pd_power_state_transition_report *report_params =
+ (struct pd_power_state_transition_report *)(&report.params);
+
+ report = (struct fwk_event){
+ .source_id = pd->driver_id,
+ .target_id = pd->id,
+ .id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN,
+ PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION)
+ };
+ report_params->state = state;
+
+ return fwk_thread_put_event(&report);
+}
+
+static int pd_report_power_state_transition(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+ const struct pd_ctx *pd;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ return report_power_state_transition(pd, state);
+}
+
+/* Module APIs */
+
+static const struct mod_pd_public_api pd_public_api = {
+ .get_domain_type = pd_get_domain_type,
+ .get_domain_parent_id = pd_get_domain_parent_id,
+};
+
+static const struct mod_pd_restricted_api pd_restricted_api = {
+ .get_domain_type = pd_get_domain_type,
+ .get_domain_parent_id = pd_get_domain_parent_id,
+
+ .set_state = pd_set_state,
+ .set_state_async = pd_set_state_async,
+ .set_composite_state = pd_set_composite_state,
+ .set_composite_state_async = pd_set_composite_state_async,
+ .get_state = pd_get_state,
+ .get_composite_state = pd_get_composite_state,
+ .reset = pd_reset,
+ .system_suspend = pd_system_suspend,
+ .system_shutdown = pd_system_shutdown
+};
+
+static const struct mod_pd_driver_input_api pd_driver_input_api = {
+ .set_state_async = pd_set_state_async,
+ .set_composite_state_async = pd_set_composite_state_async,
+ .reset_async = pd_reset_async,
+ .report_power_state_transition = pd_report_power_state_transition,
+};
+
+/*
+ * Framework handlers
+ */
+static int pd_init(fwk_id_t module_id, unsigned int dev_count,
+ const void *data)
+{
+ if ((data == NULL) || (dev_count == 0))
+ return FWK_E_PARAM;
+
+ mod_pd_ctx.config = (struct mod_power_domain_config *)data;
+
+ if ((mod_pd_ctx.config->authorized_id_table == NULL) &&
+ (mod_pd_ctx.config->authorized_id_table_size != 0))
+ return FWK_E_PARAM;
+
+ mod_pd_ctx.pd_ctx_table = fwk_mm_calloc(dev_count, sizeof(struct pd_ctx));
+ if (mod_pd_ctx.pd_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ mod_pd_ctx.pd_count = dev_count;
+ mod_pd_ctx.system_pd_ctx = &mod_pd_ctx.pd_ctx_table[dev_count - 1];
+
+ return FWK_SUCCESS;
+}
+
+static int pd_power_domain_init(fwk_id_t pd_id, unsigned int unused,
+ const void *config)
+{
+ static uint64_t max_tree_pos = MOD_PD_INVALID_TREE_POS;
+
+ const struct mod_power_domain_element_config *pd_config =
+ (const struct mod_power_domain_element_config *)config;
+ struct pd_ctx *pd;
+ unsigned int state;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_tree_pos(pd_config->tree_pos))
+ return FWK_E_PARAM;
+
+ if (pd_config->attributes.pd_type >=
+ MOD_PD_TYPE_COUNT)
+ return FWK_E_PARAM;
+
+ /*
+ * Check that the power domains are declared by increasing order of their
+ * tree position.
+ */
+ if ((max_tree_pos != MOD_PD_INVALID_TREE_POS) &&
+ (pd_config->tree_pos <= max_tree_pos))
+ return FWK_E_PARAM;
+ max_tree_pos = pd_config->tree_pos;
+
+ if ((pd_config->allowed_state_mask_table == NULL) ||
+ (pd_config->allowed_state_mask_table_size == 0))
+ return FWK_E_PARAM;
+
+ pd->allowed_state_mask_table = pd_config->allowed_state_mask_table;
+ pd->allowed_state_mask_table_size =
+ pd_config->allowed_state_mask_table_size;
+
+ for (state = 0; state < pd->allowed_state_mask_table_size; state++)
+ pd->valid_state_mask |= pd->allowed_state_mask_table[state];
+
+ pd->id = pd_id;
+ pd->config = pd_config;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_post_init(fwk_id_t module_id)
+{
+ int status;
+
+ status = build_pd_tree();
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct pd_ctx *pd;
+ const struct mod_power_domain_element_config *config;
+ struct mod_pd_driver_api *driver_api;
+
+ /* Nothing to do but during the first round of calls */
+ if (round != 0)
+ return FWK_SUCCESS;
+
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &mod_pd_ctx.log_api);
+ }
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(id)];
+ config = pd->config;
+
+ status = fwk_module_bind(config->driver_id, config->api_id, &driver_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd->driver_id = config->driver_id;
+ if ((driver_api->set_state == NULL) ||
+ (driver_api->get_state == NULL) ||
+ (driver_api->reset == NULL) ||
+ ((config->attributes.pd_type == MOD_PD_TYPE_CORE) &&
+ (driver_api->prepare_core_for_system_suspend == NULL)))
+ return FWK_E_PARAM;
+
+ pd->driver_api = driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_start(fwk_id_t id)
+{
+ int status;
+ int index;
+ struct pd_ctx *pd;
+ unsigned int state;
+
+ /* Nothing to do for elements */
+ if (fwk_module_is_valid_element_id(id))
+ return FWK_SUCCESS;
+
+ for (index = mod_pd_ctx.pd_count - 1; index >= 0; index--) {
+ pd = &mod_pd_ctx.pd_ctx_table[index];
+ pd->requested_state = MOD_PD_STATE_OFF;
+ pd->state_requested_to_driver = MOD_PD_STATE_OFF;
+ pd->current_state = MOD_PD_STATE_OFF;
+
+ /*
+ * If the power domain parent is powered down, don't call the driver
+ * to get the power domain state as the power domain registers may
+ * not be accessible. That way, the drivers don't have to care about
+ * this case.
+ */
+ if ((pd->parent != NULL) &&
+ (pd->parent->requested_state == MOD_PD_STATE_OFF))
+ continue;
+
+ /* Get the current power state of the power domain from its driver. */
+ status = pd->driver_api->get_state(pd->driver_id, &state);
+ if (status != FWK_SUCCESS) {
+ mod_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR, driver_error_msg,
+ status, __func__, __LINE__);
+ } else {
+ pd->requested_state = pd->state_requested_to_driver = state;
+
+ if (state == MOD_PD_STATE_OFF)
+ continue;
+
+ report_power_state_transition(pd, state);
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ struct pd_ctx *pd;
+ unsigned int id_idx;
+
+ switch (fwk_id_get_api_idx(api_id)) {
+ case MOD_PD_API_IDX_PUBLIC:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_ACCESS;
+ *api = &pd_public_api;
+ break;
+
+ case MOD_PD_API_IDX_RESTRICTED:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_ACCESS;
+ if (mod_pd_ctx.config->authorized_id_table_size == 0) {
+ *api = &pd_restricted_api;
+ return FWK_SUCCESS;
+ }
+ for (id_idx = 0;
+ id_idx < mod_pd_ctx.config->authorized_id_table_size;
+ id_idx++) {
+
+ if (fwk_id_is_equal(source_id,
+ mod_pd_ctx.config->authorized_id_table[id_idx])) {
+ *api = &pd_restricted_api;
+ return FWK_SUCCESS;
+ }
+ }
+ return FWK_E_ACCESS;
+
+ case MOD_PD_API_IDX_DRIVER_INPUT:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_ACCESS;
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(target_id)];
+ if (!fwk_id_is_equal(source_id, pd->driver_id))
+ return FWK_E_ACCESS;
+ *api = &pd_driver_input_api;
+ break;
+
+ default:
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_event(const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ struct pd_ctx *pd = NULL;
+
+ if (fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT))
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
+
+ switch (fwk_id_get_event_idx(event->id)) {
+ case PD_EVENT_IDX_SET_STATE:
+ assert(pd != NULL);
+
+ process_set_state_request(pd,
+ (struct pd_set_state_request *)event->params, resp);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_GET_STATE:
+ assert(pd != NULL);
+
+ process_get_state_request(pd,
+ (struct pd_get_state_request *)event->params,
+ (struct pd_get_state_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_RESET:
+ assert(pd != NULL);
+
+ process_reset_request(pd, (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION:
+ assert(pd != NULL);
+
+ process_power_state_transition_report(pd,
+ (struct pd_power_state_transition_report *)event->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_SYSTEM_SUSPEND:
+ process_system_suspend_request(
+ (struct pd_system_suspend_request *)event->params,
+ (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_SYSTEM_SHUTDOWN:
+ process_system_shutdown_request(
+ (struct pd_system_shutdown_request *)event->params,
+ (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ default:
+ mod_pd_ctx.log_api->log(
+ MOD_LOG_GROUP_ERROR,
+ "[PD] Invalid power state request: <%d>.\n",
+ event->id);
+
+ return FWK_E_PARAM;
+ }
+}
+
+static int process_power_state_pre_transition_notification_response(
+ struct pd_ctx *pd,
+ struct mod_pd_power_state_pre_transition_notification_resp_params *params)
+{
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses
+ == 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ if (params->status != FWK_SUCCESS) {
+ pd->power_state_pre_transition_notification_ctx.response_status =
+ FWK_E_DEVICE;
+ }
+
+ pd->power_state_pre_transition_notification_ctx.pending_responses--;
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0)
+ return FWK_SUCCESS;
+
+ if (pd->power_state_pre_transition_notification_ctx.valid == true) {
+ /*
+ * All the notification responses have been received, the requested
+ * state for the power domain has not changed in the
+ * meantime and all the notified entities agreed on the power state
+ * transition, proceed with it.
+ */
+ if (pd->power_state_pre_transition_notification_ctx.response_status ==
+ FWK_SUCCESS)
+ initiate_power_state_transition(pd);
+ } else {
+ /*
+ * All the notification responses have been received but the
+ * requested state for the power domain has changed, start the
+ * processings for the new requested state.
+ */
+ if ((pd->requested_state == pd->state_requested_to_driver) ||
+ (!is_allowed_by_parent_and_children(pd, pd->requested_state)))
+ return FWK_SUCCESS;
+
+ if (!initiate_power_state_pre_transition_notification(pd))
+ initiate_power_state_transition(pd);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int process_power_state_transition_notification_response(
+ struct pd_ctx *pd)
+{
+ struct fwk_event notification_event;
+ struct mod_pd_power_state_transition_notification_params *params;
+
+ if (pd->power_state_transition_notification_ctx.pending_responses == 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ pd->power_state_transition_notification_ctx.pending_responses--;
+ if (pd->power_state_transition_notification_ctx.pending_responses != 0)
+ return FWK_SUCCESS;
+
+ if (pd->power_state_transition_notification_ctx.state == pd->current_state)
+ return FWK_SUCCESS;
+
+ /*
+ * While receiving the responses, the power state of the power domain
+ * has changed. Send a notification for the current power state.
+ */
+ notification_event.id = mod_pd_notification_id_power_state_transition;
+ notification_event.response_requested = true;
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ notification_event.params;
+ params->state = pd->current_state;
+
+ pd->power_state_transition_notification_ctx.state = pd->current_state;
+ fwk_notification_notify(&notification_event,
+ &pd->power_state_transition_notification_ctx.pending_responses);
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_notification(const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ struct pd_ctx *pd;
+
+ /* Only responses are expected. */
+ if (!event->is_response) {
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+
+ if (!fwk_module_is_valid_element_id(event->target_id)) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
+
+ if (fwk_id_is_equal(event->id,
+ mod_pd_notification_id_power_state_transition))
+ return process_power_state_transition_notification_response(pd);
+
+ return process_power_state_pre_transition_notification_response(pd,
+ (struct mod_pd_power_state_pre_transition_notification_resp_params *)
+ event->params);
+}
+
+/* Module definition */
+const struct fwk_module module_power_domain = {
+ .name = "POWER DOMAIN",
+ .type = FWK_MODULE_TYPE_HAL,
+ .api_count = MOD_PD_API_IDX_COUNT,
+ .event_count = PD_EVENT_COUNT,
+ .notification_count = MOD_PD_NOTIFICATION_COUNT,
+ .init = pd_init,
+ .element_init = pd_power_domain_init,
+ .post_init = pd_post_init,
+ .bind = pd_bind,
+ .start = pd_start,
+ .process_bind_request = pd_process_bind_request,
+ .process_event = pd_process_event,
+ .process_notification = pd_process_notification
+};
diff --git a/module/ppu_v0/include/mod_ppu_v0.h b/module/ppu_v0/include/mod_ppu_v0.h
new file mode 100644
index 00000000..d18aa443
--- /dev/null
+++ b/module/ppu_v0/include/mod_ppu_v0.h
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * PPU v0 driver module
+ */
+
+#ifndef MOD_PPU_V0_H
+#define MOD_PPU_V0_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <mod_power_domain.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModulePPUv0 PPUv0 Driver
+ * @{
+ */
+
+/*!
+ * \brief Power domain PPU descriptor.
+ */
+struct mod_ppu_v0 {
+ /*! Base address of the PPU registers */
+ uintptr_t reg_base;
+
+ /*! PPU's IRQ number */
+ unsigned int irq;
+};
+
+/*!
+ * \brief Configuration data of a power domain of the PPU_V0 driver module.
+ */
+struct mod_ppu_v0_pd_config {
+ /*! Power domain type */
+ enum mod_pd_type pd_type;
+
+ /*! PPU descriptor */
+ struct mod_ppu_v0 ppu;
+
+ /*!
+ * Flag indicating if this domain should be powered on during element init.
+ */
+ bool default_power_on;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_PPU_V0_H */
diff --git a/module/ppu_v0/src/Makefile b/module/ppu_v0/src/Makefile
new file mode 100644
index 00000000..c958e926
--- /dev/null
+++ b/module/ppu_v0/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := PPU_V0
+BS_LIB_SOURCES = ppu_v0.c mod_ppu_v0.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/ppu_v0/src/mod_ppu_v0.c b/module/ppu_v0/src/mod_ppu_v0.c
new file mode 100644
index 00000000..1d207d62
--- /dev/null
+++ b/module/ppu_v0/src/mod_ppu_v0.c
@@ -0,0 +1,316 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Power State Management PPU v0 driver.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_log.h>
+#include <mod_power_domain.h>
+#include <mod_ppu_v0.h>
+#include <ppu_v0.h>
+#if BUILD_HAS_MOD_SYSTEM_POWER
+#include <mod_system_power.h>
+#endif
+
+/* Power domain context */
+struct ppu_v0_pd_ctx {
+ /* Power domain configuration data */
+ const struct mod_ppu_v0_pd_config *config;
+
+ /* PPU registers */
+ struct ppu_v0_reg *ppu;
+
+ /* Identifier of the entity bound to the power domain driver API */
+ fwk_id_t bound_id;
+
+ /* Power module driver input API */
+ struct mod_pd_driver_input_api *pd_driver_input_api;
+};
+
+/* Module context */
+struct ppu_v0_ctx {
+ /* Table of the power domain contexts */
+ struct ppu_v0_pd_ctx *pd_ctx_table;
+
+ /* Log API */
+ struct mod_log_api *log_api;
+};
+
+/*
+ * Internal variables
+ */
+
+static struct ppu_v0_ctx ppu_v0_ctx;
+
+#define MODE_UNSUPPORTED ~0U
+static const uint8_t ppu_mode_to_power_state[] = {
+ [PPU_V0_MODE_ON] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V0_MODE_FUNC_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V0_MODE_MEM_OFF] = (uint8_t)MODE_UNSUPPORTED,
+ [PPU_V0_MODE_FULL_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V0_MODE_LOGIC_RET] = (uint8_t)MODE_UNSUPPORTED,
+ [PPU_V0_MODE_MEM_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V0_MODE_OFF] = (uint8_t)MOD_PD_STATE_OFF,
+ [PPU_V0_MODE_WARM_RESET] = (uint8_t)MODE_UNSUPPORTED,
+};
+
+/*
+ * Power domain driver interface
+ */
+
+/* Driver functions not specific to any type of power domain. */
+static int get_state(struct ppu_v0_reg *ppu, unsigned int *state)
+{
+ enum ppu_v0_mode ppu_mode;
+
+ /* Ensure ppu_mode_to_power_state has an entry for each PPU state */
+ static_assert(FWK_ARRAY_SIZE(ppu_mode_to_power_state) == PPU_V0_MODE_COUNT,
+ "[MOD_PPU_V0] ppu_mode_to_power_state size error");
+
+ ppu_v0_get_power_mode(ppu, &ppu_mode);
+ assert(ppu_mode < PPU_V0_MODE_COUNT);
+
+ *state = ppu_mode_to_power_state[ppu_mode];
+ if (*state == MODE_UNSUPPORTED) {
+ ppu_v0_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Unexpected PPU mode (%i).\n", ppu_mode);
+ return FWK_E_DEVICE;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_init(struct ppu_v0_pd_ctx *pd_ctx)
+{
+ ppu_v0_init(pd_ctx->ppu);
+
+ return FWK_SUCCESS;
+}
+
+static int pd_set_state(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+ struct ppu_v0_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ switch (state) {
+ case MOD_PD_STATE_ON:
+ ppu_v0_set_power_mode(pd_ctx->ppu, PPU_V0_MODE_ON);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_ON);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ case MOD_PD_STATE_OFF:
+ ppu_v0_set_power_mode(pd_ctx->ppu, PPU_V0_MODE_OFF);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_OFF);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ default:
+ ppu_v0_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Requested power state (%i) is not supported.\n", state);
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_get_state(fwk_id_t pd_id, unsigned int *state)
+{
+ int status;
+ struct ppu_v0_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ return get_state(pd_ctx->ppu, state);
+}
+
+static int pd_reset(fwk_id_t pd_id)
+{
+ int status;
+ struct ppu_v0_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ /* Model does not support warm reset at the moment. Using OFF instead. */
+ status = ppu_v0_set_power_mode(pd_ctx->ppu, PPU_V0_MODE_OFF);
+ if (status == FWK_SUCCESS)
+ status = ppu_v0_set_power_mode(pd_ctx->ppu, PPU_V0_MODE_ON);
+
+ return status;
+}
+
+static const struct mod_pd_driver_api pd_driver = {
+ .set_state = pd_set_state,
+ .get_state = pd_get_state,
+ .reset = pd_reset,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int ppu_v0_mod_init(fwk_id_t module_id, unsigned int pd_count,
+ const void *unused)
+{
+ ppu_v0_ctx.pd_ctx_table = fwk_mm_calloc(pd_count,
+ sizeof(struct ppu_v0_pd_ctx));
+ if (ppu_v0_ctx.pd_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v0_pd_init(fwk_id_t pd_id, unsigned int unused, const void *data)
+{
+ const struct mod_ppu_v0_pd_config *config = data;
+ struct ppu_v0_pd_ctx *pd_ctx;
+ int status;
+
+ if (config->pd_type >= MOD_PD_TYPE_COUNT)
+ return FWK_E_DATA;
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+ pd_ctx->config = config;
+ pd_ctx->ppu = (struct ppu_v0_reg *)(config->ppu.reg_base);
+ pd_ctx->bound_id = FWK_ID_NONE;
+
+ switch (config->pd_type) {
+ case MOD_PD_TYPE_DEVICE:
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+ case MOD_PD_TYPE_SYSTEM:
+ status = pd_init(pd_ctx);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (config->default_power_on)
+ return ppu_v0_set_power_mode(pd_ctx->ppu, PPU_V0_MODE_ON);
+
+ return FWK_SUCCESS;
+
+ default:
+ return FWK_E_SUPPORT;
+ }
+}
+
+static int ppu_v0_bind(fwk_id_t id, unsigned int round)
+{
+ struct ppu_v0_pd_ctx *pd_ctx;
+
+ /* Nothing to do during the first round of calls where the power module
+ will bind to the power domains of this module. */
+ if (round == 0)
+ return FWK_SUCCESS;
+
+ /* In the case of the module, bind to the log component */
+ if (fwk_module_is_valid_module_id(id)) {
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &ppu_v0_ctx.log_api);
+ }
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(id);
+
+ if (fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE))
+ return FWK_SUCCESS;
+
+ switch (fwk_id_get_module_idx(pd_ctx->bound_id)) {
+ #if BUILD_HAS_MOD_POWER_DOMAIN
+ case FWK_MODULE_IDX_POWER_DOMAIN:
+ return fwk_module_bind(pd_ctx->bound_id,
+ mod_pd_api_id_driver_input,
+ &pd_ctx->pd_driver_input_api);
+ break;
+ #endif
+
+ #if BUILD_HAS_MOD_SYSTEM_POWER
+ case FWK_MODULE_IDX_SYSTEM_POWER:
+ return fwk_module_bind(pd_ctx->bound_id,
+ mod_system_power_api_id_pd_driver_input,
+ &pd_ctx->pd_driver_input_api);
+ break;
+ #endif
+
+ default:
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+}
+
+static int ppu_v0_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t not_used,
+ const void **api)
+{
+ struct ppu_v0_pd_ctx *pd_ctx;
+
+ pd_ctx = ppu_v0_ctx.pd_ctx_table + fwk_id_get_element_idx(target_id);
+
+ switch (pd_ctx->config->pd_type) {
+ case MOD_PD_TYPE_SYSTEM:
+ if (!fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE)) {
+ assert(false);
+ return FWK_E_ACCESS;
+ }
+ /* Fallthrough */
+
+ case MOD_PD_TYPE_DEVICE:
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+ #if BUILD_HAS_MOD_POWER_DOMAIN
+ if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_POWER_DOMAIN) {
+ pd_ctx->bound_id = source_id;
+ *api = &pd_driver;
+ break;
+ }
+ #endif
+ #if BUILD_HAS_MOD_SYSTEM_POWER
+ if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_SYSTEM_POWER) {
+ *api = &pd_driver;
+ break;
+ }
+ #endif
+ assert(false);
+ return FWK_E_ACCESS;
+
+ default:
+ (void)pd_driver;
+ return FWK_E_SUPPORT;
+ }
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_ppu_v0 = {
+ .name = "PPU_V0",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = 1,
+ .init = ppu_v0_mod_init,
+ .element_init = ppu_v0_pd_init,
+ .bind = ppu_v0_bind,
+ .process_bind_request = ppu_v0_process_bind_request,
+};
diff --git a/module/ppu_v0/src/ppu_v0.c b/module/ppu_v0/src/ppu_v0.c
new file mode 100644
index 00000000..cedaf24e
--- /dev/null
+++ b/module/ppu_v0/src/ppu_v0.c
@@ -0,0 +1,64 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <ppu_v0.h>
+#include <fwk_errno.h>
+
+void ppu_v0_init(struct ppu_v0_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ /* Set mode as masked to all input edge interrupts */
+ ppu->IESR = 0;
+
+ /* Mask all interrupts */
+ ppu->IMR = PPU_V0_IMR_MASK;
+
+ /* Acknowledge any interrupt left pending */
+ ppu->ISR = PPU_V0_ISR_MASK;
+}
+
+int ppu_v0_request_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode mode)
+{
+ uint32_t power_policy;
+ assert(ppu != NULL);
+ assert(mode < PPU_V0_MODE_COUNT);
+
+ power_policy = ppu->POWER_POLICY &
+ ~(PPU_V0_PPR_POLICY | PPU_V0_PPR_DYNAMIC_EN);
+ ppu->POWER_POLICY = power_policy | mode;
+
+ return FWK_SUCCESS;
+}
+
+int ppu_v0_set_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode mode)
+{
+ int status;
+ assert(ppu != NULL);
+
+ status = ppu_v0_request_power_mode(ppu, mode);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ while ((ppu->POWER_STATUS & (PPU_V0_PSR_POWSTAT | PPU_V0_PSR_DYNAMIC))
+ != mode)
+ continue;
+
+ return FWK_SUCCESS;
+}
+
+int ppu_v0_get_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode *mode)
+{
+ assert(ppu != NULL);
+ assert(mode != NULL);
+
+ *mode = (enum ppu_v0_mode)(ppu->POWER_STATUS & PPU_V0_PSR_POWSTAT);
+
+ return FWK_SUCCESS;
+}
diff --git a/module/ppu_v0/src/ppu_v0.h b/module/ppu_v0/src/ppu_v0.h
new file mode 100644
index 00000000..b9e034cb
--- /dev/null
+++ b/module/ppu_v0/src/ppu_v0.h
@@ -0,0 +1,144 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PPU_V0_H
+#define PPU_V0_H
+
+/*!
+ * \cond
+ * @{
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_macros.h>
+
+struct ppu_v0_reg {
+ FWK_RW uint32_t POWER_POLICY;
+ FWK_R uint32_t POWER_STATUS;
+ FWK_R uint32_t STATIC_CFG_STATUS;
+ FWK_R uint32_t DEV_IF_IP_CUR_STATUS;
+ FWK_R uint32_t MISC_IP_CUR_STATUS;
+ FWK_R uint32_t STORED_STATUS;
+ FWK_W uint32_t OFF_MEM_RET_UNLOCK;
+ uint32_t RESERVED0;
+ FWK_RW uint32_t POWER_CFG;
+ uint32_t RESERVED1[3];
+ FWK_RW uint32_t IMR;
+ FWK_RW uint32_t ISR;
+ FWK_RW uint32_t IESR;
+ uint32_t RESERVED2[5];
+ FWK_RW uint32_t FUNC_RET_RAM_CFG;
+ FWK_RW uint32_t FULL_RET_RAM_CFG;
+ FWK_RW uint32_t MEM_RET_RAM_CFG;
+ uint32_t RESERVED3;
+ FWK_RW uint32_t MODE_ENTRY_DELAY_TIME_0;
+ FWK_RW uint32_t MODE_ENTRY_DELAY_TIME_1;
+ uint32_t RESERVED4[2];
+ FWK_RW uint32_t DEV_CONTROL_DELAY_CFG_0;
+ FWK_RW uint32_t DEV_CONTROL_DELAY_CFG_1;
+ uint8_t RESERVED5[0xFC8 - 0x78];
+ FWK_R uint32_t IMPLEMENTATION_ID;
+ FWK_R uint32_t ARCHITECTURE_ID;
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t CID0;
+ FWK_R uint32_t CID1;
+ FWK_R uint32_t CID2;
+ FWK_R uint32_t CID3;
+};
+
+enum ppu_v0_mode {
+ PPU_V0_MODE_OFF = 0,
+ PPU_V0_MODE_MEM_RET = 1,
+ PPU_V0_MODE_LOGIC_RET = 2,
+ PPU_V0_MODE_FULL_RET = 3,
+ PPU_V0_MODE_MEM_OFF = 4,
+ PPU_V0_MODE_FUNC_RET = 5,
+ PPU_V0_MODE_ON = 6,
+ PPU_V0_MODE_WARM_RESET = 7,
+ PPU_V0_MODE_COUNT,
+};
+
+/*
+ * Bit definitions for PPR
+ */
+#define PPU_V0_PPR_POLICY UINT32_C(0x00000007)
+#define PPU_V0_PPR_DYNAMIC_EN UINT32_C(0x00000100)
+#define PPU_V0_PPR_EMULATED_EN UINT32_C(0x00000200)
+#define PPU_V0_PPR_OFF_LOCK_EN UINT32_C(0x00001000)
+
+/*
+ * Bit definitions for PSR
+ */
+#define PPU_V0_PSR_EMULATED UINT32_C(0x00000200)
+#define PPU_V0_PSR_DYNAMIC UINT32_C(0x00000100)
+#define PPU_V0_PSR_POWSTAT UINT32_C(0x00000007)
+
+/*
+ * Bit definitions for IMR
+ */
+#define PPU_V0_IMR_MASK UINT32_C(0x010000FF)
+#define PPU_V0_IMR_STA_POLICY_TRN UINT32_C(0x00000001)
+#define PPU_V0_IMR_STA_ACCEPT UINT32_C(0x00000002)
+#define PPU_V0_IMR_STA_DENY UINT32_C(0x00000004)
+#define PPU_V0_IMR_DYN_ACCEPT UINT32_C(0x00000008)
+#define PPU_V0_IMR_DYN_DENY UINT32_C(0x00000010)
+#define PPU_V0_IMR_EMU_ACCEPT UINT32_C(0x00000020)
+#define PPU_V0_IMR_EMU_DENY UINT32_C(0x00000040)
+#define PPU_V0_IMR_UNSPT_POLICY UINT32_C(0x00000080)
+#define PPU_V0_IMR_DYN_POLICY_MIN UINT32_C(0x01000000)
+
+/*
+ * Bit definitions for ISR
+ */
+#define PPU_V0_ISR_MASK UINT32_C(0x01FF01FF)
+#define PPU_V0_ISR_STA_POLICY_TRN UINT32_C(0x00000001)
+#define PPU_V0_ISR_STA_ACCEPT UINT32_C(0x00000002)
+#define PPU_V0_ISR_STA_DENY UINT32_C(0x00000004)
+#define PPU_V0_ISR_DYN_ACCEPT UINT32_C(0x00000008)
+#define PPU_V0_ISR_DYN_DENY UINT32_C(0x00000010)
+#define PPU_V0_ISR_EMU_ACCEPT UINT32_C(0x00000020)
+#define PPU_V0_ISR_EMU_DENY UINT32_C(0x00000040)
+#define PPU_V0_ISR_UNSPT_POLICY UINT32_C(0x00000080)
+#define PPU_V0_ISR_DBGEMUPWRDWN_EDGE UINT32_C(0x00000100)
+#define PPU_V0_ISR_ACTIVE_EDGE UINT32_C(0x00FF0000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE0 UINT32_C(0x00010000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE1 UINT32_C(0x00020000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE2 UINT32_C(0x00040000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE3 UINT32_C(0x00080000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE4 UINT32_C(0x00100000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE5 UINT32_C(0x00200000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE6 UINT32_C(0x00400000)
+#define PPU_V0_ISR_ACTIVE_EDGE_ACTIVE7 UINT32_C(0x00800000)
+#define PPU_V0_ISR_DYN_POLICY_MIN UINT32_C(0x01000000)
+
+/*
+ * Bit definitions for ARCHITECTURE_ID
+ */
+#define PPU_V0_ARCHITECTURE_ID UINT32_C(0x00000000)
+
+/*
+ * Interface
+ */
+void ppu_v0_init(struct ppu_v0_reg *ppu);
+int ppu_v0_request_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode mode);
+int ppu_v0_set_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode mode);
+int ppu_v0_get_power_mode(struct ppu_v0_reg *ppu, enum ppu_v0_mode *mode);
+
+/*!
+ * \endcond
+ * @}
+ */
+
+#endif /* PPU_V0_H */
diff --git a/module/ppu_v1/include/mod_ppu_v1.h b/module/ppu_v1/include/mod_ppu_v1.h
new file mode 100644
index 00000000..6966101e
--- /dev/null
+++ b/module/ppu_v1/include/mod_ppu_v1.h
@@ -0,0 +1,166 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * PPU v1 Driver
+ */
+
+#ifndef MOD_PPU_V1_H
+#define MOD_PPU_V1_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <mod_power_domain.h>
+
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModulePPUv1 PPUv1 Driver
+ * @{
+ */
+
+/*!
+ * \brief Indexes of the interfaces exposed by the module.
+ */
+enum mod_ppu_v1_api_idx {
+ /*! Power domain driver API */
+ MOD_PPU_V1_API_IDX_POWER_DOMAIN_DRIVER,
+ /*! interrupt Service Routine driver API */
+ MOD_PPU_V1_API_IDX_ISR,
+ /*! System boot API */
+ MOD_PPU_V1_API_IDX_BOOT,
+ /*! Number of exposed interfaces */
+ MOD_PPU_V1_API_IDX_COUNT,
+};
+
+/*!
+ * \brief Power domain PPU descriptor.
+ */
+struct mod_ppu_v1 {
+ /*! Base address of the PPU registers */
+ uintptr_t reg_base;
+
+ /*! PPU's IRQ number */
+ unsigned int irq;
+};
+
+/*!
+ * \brief PPU_V1 module configuration
+ */
+struct mod_ppu_v1_config {
+ /*! Identifier of the power domain notification to register elements for */
+ const fwk_id_t pd_notification_id;
+
+ /*!
+ * Identifier of the source module or element that is expected to send power
+ * domain notifications.
+ */
+ fwk_id_t pd_source_id;
+};
+
+/*!
+ * \brief Configuration data of a power domain of the PPU_V1 driver module.
+ */
+struct mod_ppu_v1_pd_config {
+ /*! Power domain type */
+ enum mod_pd_type pd_type;
+
+ /*! PPU descriptor */
+ struct mod_ppu_v1 ppu;
+
+ /*!
+ * In the case of a core power domain, identifier of the cluster power
+ * domain it belongs to. If the power domain is not a core power domain,
+ * the value of this field is undefined.
+ */
+ fwk_id_t cluster_id;
+
+ /*!
+ * Flag indicating if this domain should be powered on during element
+ * init. This flag is only supported for device and system PPUs and should
+ * not be set for any other type.
+ */
+ bool default_power_on;
+
+ /*!
+ * \brief Identifier of an entity wishing to be notified when the PPU
+ * transitions out of the OFF state.
+ *
+ * \note This field may be set to \ref FWK_ID_NONE, in which case no
+ * observer will be set.
+ */
+ fwk_id_t observer_id;
+
+ /*!
+ * \brief Identifier of the power state observer API implemented by
+ * \ref observer_id.
+ */
+ fwk_id_t observer_api;
+
+ /*!
+ * \brief Parameter passed to
+ * \ref mod_ppu_v1_power_state_observer_api::post_ppu_on().
+ */
+ void *post_ppu_on_param;
+};
+
+/*!
+ * \brief PPU_V1 Power State Observer API.
+ *
+ * \details This API should be implemented by any modules that should be
+ * notified when a PPU changes state.
+ */
+struct mod_ppu_v1_power_state_observer_api {
+ /*!
+ * \brief Called after a PPU has turned on.
+ *
+ * \param param Generic configurable parameter.
+ */
+ void (*post_ppu_on)(void *param);
+};
+
+/*!
+ * \brief PPU_V1 module ISR API
+ */
+struct ppu_v1_isr_api {
+ /*!
+ * \brief Handle a power domain PPU interrupt
+ *
+ * \param pd_id Identifier of the power domain
+ */
+ void (*ppu_interrupt_handler)(fwk_id_t pd_id);
+};
+
+/*!
+ * \brief PPU_V1 module boot API
+ */
+struct ppu_v1_boot_api {
+ /*!
+ * \brief Power on a specified power domain
+ *
+ * \param pd_id Identifier of the power domain
+ *
+ * \retval FWK_SUCCESS Operation successful.
+ * \return One of the standard framework error codes.
+ */
+ int (*power_mode_on)(fwk_id_t pd_id);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_PPU_V1_H */
diff --git a/module/ppu_v1/src/Makefile b/module/ppu_v1/src/Makefile
new file mode 100644
index 00000000..8d3a5227
--- /dev/null
+++ b/module/ppu_v1/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := PPU_V1
+BS_LIB_SOURCES = ppu_v1.c mod_ppu_v1.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/ppu_v1/src/mod_ppu_v1.c b/module/ppu_v1/src/mod_ppu_v1.c
new file mode 100644
index 00000000..2f13d72a
--- /dev/null
+++ b/module/ppu_v1/src/mod_ppu_v1.c
@@ -0,0 +1,991 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Power State Management PPU v1 driver.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_notification.h>
+#include <mod_log.h>
+#include <mod_power_domain.h>
+#include <mod_ppu_v1.h>
+#include <ppu_v1.h>
+#if BUILD_HAS_MOD_SYSTEM_POWER
+#include <mod_system_power.h>
+#endif
+
+#define CORE_PER_CLUSTER_COUNT_MAX 8
+
+/* Power domain context */
+struct ppu_v1_pd_ctx {
+ /* Power domain configuration data */
+ const struct mod_ppu_v1_pd_config *config;
+
+ /* PPU registers */
+ struct ppu_v1_reg *ppu;
+
+ /* Identifier of the entity bound to the power domain driver API */
+ fwk_id_t bound_id;
+
+ /* Power module driver input API */
+ struct mod_pd_driver_input_api *pd_driver_input_api;
+
+ /* Context of the parent power domain (used only for core power domains) */
+ struct ppu_v1_pd_ctx *parent_pd_ctx;
+
+ /* Pointer to the power state observer API */
+ const struct mod_ppu_v1_power_state_observer_api *observer_api;
+
+ /* Context data specific to the type of power domain */
+ void *data;
+};
+
+/* Cluster power domain specific context */
+struct ppu_v1_cluster_pd_ctx {
+ /*
+ * Table of pointers to the contexts of the cores being part of the
+ * cluster.
+ */
+ struct ppu_v1_pd_ctx *core_pd_ctx_table[CORE_PER_CLUSTER_COUNT_MAX];
+
+ /* Number of cores */
+ unsigned int core_count;
+};
+
+/* Module context */
+struct ppu_v1_ctx {
+ /* Table of the power domain contexts */
+ struct ppu_v1_pd_ctx *pd_ctx_table;
+
+ /* Number of power domains */
+ size_t pd_ctx_table_size;
+
+ /* Log API */
+ struct mod_log_api *log_api;
+};
+
+/*
+ * Internal variables
+ */
+
+static struct ppu_v1_ctx ppu_v1_ctx;
+
+#define MODE_UNSUPPORTED ~0U
+static const uint8_t ppu_mode_to_power_state[] = {
+ [PPU_V1_MODE_OFF] = (uint8_t)MOD_PD_STATE_OFF,
+ [PPU_V1_MODE_OFF_EMU] = (uint8_t)MOD_PD_STATE_OFF,
+ [PPU_V1_MODE_MEM_RET] = (uint8_t)MOD_PD_STATE_OFF,
+ [PPU_V1_MODE_MEM_RET_EMU] = (uint8_t)MOD_PD_STATE_OFF,
+ [PPU_V1_MODE_LOGIC_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V1_MODE_FULL_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V1_MODE_MEM_OFF] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V1_MODE_FUNC_RET] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V1_MODE_ON] = (uint8_t)MOD_PD_STATE_ON,
+ [PPU_V1_MODE_WARM_RST] = (uint8_t)MODE_UNSUPPORTED,
+ [PPU_V1_MODE_DBG_RECOV] = (uint8_t)MODE_UNSUPPORTED
+};
+
+/*
+ * Functions not specific to any type of power domain
+ */
+
+static int get_state(struct ppu_v1_reg *ppu, unsigned int *state)
+{
+ enum ppu_v1_mode mode;
+
+ /* Ensure ppu_to_pd_state_v1 has an entry for each PPU state */
+ static_assert((FWK_ARRAY_SIZE(ppu_mode_to_power_state) ==
+ PPU_V1_MODE_COUNT), "[PPU_V1] ppu_mode_to_power_state size error");
+
+ mode = ppu_v1_get_power_mode(ppu);
+ assert(mode < PPU_V1_MODE_COUNT);
+
+ *state = ppu_mode_to_power_state[mode];
+
+ if ((*state == MOD_PD_STATE_OFF) && (ppu_v1_is_dynamic_enabled(ppu)))
+ *state = MOD_PD_STATE_SLEEP;
+
+ if (*state == MODE_UNSUPPORTED) {
+ ppu_v1_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PPU_V1] Unexpected PPU mode (%i).\n", mode);
+ return FWK_E_DEVICE;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_pd_set_state(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ switch (state) {
+ case MOD_PD_STATE_ON:
+ ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_ON);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_ON);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ case MOD_PD_STATE_OFF:
+ ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_OFF);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_OFF);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ default:
+ ppu_v1_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PD] Requested power state (%i) is not supported.\n", state);
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_pd_get_state(fwk_id_t pd_id, unsigned int *state)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ return get_state(pd_ctx->ppu, state);
+}
+
+static int ppu_v1_pd_reset(fwk_id_t pd_id)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ /* Model does not support warm reset at the moment. Using OFF instead. */
+ status = ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_OFF);
+ if (status == FWK_SUCCESS)
+ status = ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_ON);
+
+ return status;
+}
+
+static const struct mod_pd_driver_api pd_driver = {
+ .set_state = ppu_v1_pd_set_state,
+ .get_state = ppu_v1_pd_get_state,
+ .reset = ppu_v1_pd_reset,
+};
+
+/*
+ * Functions specific to core power domains
+ */
+static int ppu_v1_core_pd_init(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ int status;
+ struct ppu_v1_reg *ppu = pd_ctx->ppu;
+ unsigned int state;
+
+ ppu_v1_init(ppu);
+
+ status = get_state(ppu, &state);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (state == MOD_PD_STATE_ON) {
+ ppu_v1_interrupt_unmask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+ ppu_v1_dynamic_enable(ppu, PPU_V1_MODE_OFF);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_core_pd_set_state(fwk_id_t core_pd_id, unsigned int state)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+ struct ppu_v1_reg *ppu;
+
+ status = fwk_module_check_call(core_pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(core_pd_id);
+ ppu = pd_ctx->ppu;
+
+ switch (state) {
+ case MOD_PD_STATE_OFF:
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ ppu_v1_interrupt_mask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+ ppu_v1_set_power_mode(ppu, PPU_V1_MODE_OFF);
+ ppu_v1_lock_off_disable(ppu);
+ ppu_v1_off_unlock(ppu);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_OFF);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ case MOD_PD_STATE_ON:
+ ppu_v1_interrupt_unmask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ ppu_v1_set_power_mode(ppu, PPU_V1_MODE_ON);
+ ppu_v1_dynamic_enable(ppu, PPU_V1_MODE_OFF);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_ON);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ case MOD_PD_STATE_SLEEP:
+ /*
+ * If the dynamic transitions have been enabled then the core is
+ * already in the SLEEP power state or will transit to the SLEEP power
+ * state if the appropriate processing is done on AP side. Thus nothing
+ * to do in that case. If the dynamic transitions are not enabled then
+ * this is an OFF to SLEEP transition.
+ */
+ if (!ppu_v1_is_dynamic_enabled(ppu)) {
+ ppu_v1_dynamic_enable(ppu, PPU_V1_MODE_OFF);
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ }
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_SLEEP);
+ assert(status == FWK_SUCCESS);
+ break;
+
+ default:
+ ppu_v1_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PPU_V1] Requested CPU power state (%i) is not supported!\n",
+ state);
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_core_pd_reset(fwk_id_t core_pd_id)
+{
+ int status;
+
+ status = ppu_v1_core_pd_set_state(core_pd_id, MOD_PD_STATE_OFF);
+ if (status == FWK_SUCCESS)
+ status = ppu_v1_core_pd_set_state(core_pd_id, MOD_PD_STATE_ON);
+
+ return status;
+}
+
+static int ppu_v1_core_pd_prepare_for_system_suspend(fwk_id_t core_pd_id)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+ struct ppu_v1_reg *ppu;
+
+ status = fwk_module_check_call(core_pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(core_pd_id);
+ ppu = pd_ctx->ppu;
+
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ ppu_v1_request_power_mode(ppu, PPU_V1_MODE_OFF);
+
+ return FWK_SUCCESS;
+}
+
+static void core_pd_ppu_interrupt_handler(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ int status;
+ struct ppu_v1_reg *ppu;
+
+ ppu = pd_ctx->ppu;
+
+ /* ON request interrupt */
+ if (ppu_v1_is_power_active_edge_interrupt(ppu, PPU_V1_MODE_ON)) {
+ ppu_v1_ack_power_active_edge_interrupt(ppu, PPU_V1_MODE_ON);
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ ppu_v1_interrupt_unmask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_ON);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+ /* Minimum policy reached interrupt */
+ } else if (ppu_v1_is_dyn_policy_min_interrupt(ppu)) {
+ ppu_v1_ack_interrupt(ppu, PPU_V1_ISR_DYN_POLICY_MIN_IRQ);
+ ppu_v1_interrupt_mask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_SLEEP);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+
+ /*
+ * Enable the core PACTIVE ON signal rising edge interrupt then check if
+ * the PACTIVE ON signal is high. If it is high, we may have missed the
+ * transition from low to high. In that case, just disable the interrupt
+ * and acknowledge it in case it is pending. There is no need to send an
+ * update request as one has already been queued.
+ */
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_RISING_EDGE);
+ if (ppu_v1_is_power_devactive_high(ppu, PPU_V1_MODE_ON)) {
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+ ppu_v1_ack_power_active_edge_interrupt(ppu, PPU_V1_MODE_ON);
+ ppu_v1_interrupt_unmask(ppu, PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK);
+ }
+ }
+}
+
+static const struct mod_pd_driver_api core_pd_driver = {
+ .set_state = ppu_v1_core_pd_set_state,
+ .get_state = ppu_v1_pd_get_state,
+ .reset = ppu_v1_core_pd_reset,
+ .prepare_core_for_system_suspend = ppu_v1_core_pd_prepare_for_system_suspend
+};
+
+/*
+ * Functions specific to cluster power domains
+ */
+
+static void unlock_all_cores(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ struct ppu_v1_cluster_pd_ctx *cluster_pd_ctx;
+ struct ppu_v1_reg *cpu_ppu;
+ unsigned int core_idx;
+
+ assert(pd_ctx != NULL);
+
+ cluster_pd_ctx = pd_ctx->data;
+
+ for (core_idx = 0; core_idx < cluster_pd_ctx->core_count; ++core_idx) {
+ cpu_ppu = cluster_pd_ctx->core_pd_ctx_table[core_idx]->ppu;
+ ppu_v1_lock_off_disable(cpu_ppu);
+ ppu_v1_off_unlock(cpu_ppu);
+ }
+}
+
+static bool lock_all_dynamic_cores(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ struct ppu_v1_cluster_pd_ctx *cluster_pd_ctx;
+ struct ppu_v1_reg *cpu_ppu;
+ unsigned int core_idx;
+
+ assert(pd_ctx != NULL);
+
+ cluster_pd_ctx = pd_ctx->data;
+
+ for (core_idx = 0; core_idx < cluster_pd_ctx->core_count; ++core_idx) {
+ cpu_ppu = cluster_pd_ctx->core_pd_ctx_table[core_idx]->ppu;
+
+ if (!ppu_v1_is_dynamic_enabled(cpu_ppu))
+ continue;
+
+ ppu_v1_lock_off_enable(cpu_ppu);
+ while ((!ppu_v1_is_locked(cpu_ppu)) &&
+ (!ppu_v1_is_power_devactive_high(cpu_ppu, PPU_V1_MODE_ON)))
+ continue;
+
+ if (ppu_v1_is_power_devactive_high(cpu_ppu, PPU_V1_MODE_ON))
+ return false;
+ }
+
+ return true;
+}
+
+static bool cluster_off(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ struct ppu_v1_reg *ppu;
+ bool lock_successful;
+
+ assert(pd_ctx != NULL);
+
+ ppu = pd_ctx->ppu;
+
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+
+ lock_successful = lock_all_dynamic_cores(pd_ctx);
+ if (!lock_successful) {
+ unlock_all_cores(pd_ctx);
+ return false;
+ }
+
+ ppu_v1_set_power_mode(ppu, PPU_V1_MODE_OFF);
+ return true;
+}
+
+static void cluster_on(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ int status;
+ struct ppu_v1_reg *ppu;
+
+ assert(pd_ctx != NULL);
+
+ ppu = pd_ctx->ppu;
+
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_MASKED);
+
+ ppu_v1_set_power_mode(ppu, PPU_V1_MODE_ON);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_ON);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+
+ if (pd_ctx->observer_api != NULL)
+ pd_ctx->observer_api->post_ppu_on(pd_ctx->config->post_ppu_on_param);
+
+ unlock_all_cores(pd_ctx);
+}
+
+static int ppu_v1_cluster_pd_init(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ int status;
+ struct ppu_v1_reg *ppu = pd_ctx->ppu;
+ unsigned int state;
+
+ ppu_v1_init(ppu);
+
+ status = get_state(ppu, &state);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* For clusters with operating mode support, enable the dynamic support */
+ if (ppu_v1_get_num_opmode(ppu) > 1)
+ ppu_v1_opmode_dynamic_enable(ppu, PPU_V1_OPMODE_00);
+
+ if (state == MOD_PD_STATE_ON) {
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_cluster_pd_set_state(fwk_id_t cluster_pd_id,
+ unsigned int state)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+ struct ppu_v1_reg *ppu;
+ (void)ppu;
+
+ status = fwk_module_check_call(cluster_pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(cluster_pd_id);
+ ppu = pd_ctx->ppu;
+
+ switch (state) {
+ case MOD_PD_STATE_ON:
+ cluster_on(pd_ctx);
+ #ifdef BUILD_HAS_MULTITHREADING
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE);
+ #endif
+ return FWK_SUCCESS;
+
+ case MOD_PD_STATE_OFF:
+ if (!cluster_off(pd_ctx)) {
+ /* Cluster failed to transition to off */
+ #ifdef BUILD_HAS_MULTITHREADING
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON, PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE);
+ #endif
+ return FWK_E_STATE;
+ }
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_OFF);
+ assert(status == FWK_SUCCESS);
+ return FWK_SUCCESS;
+
+ default:
+ ppu_v1_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[PPU_V1] Requested CPU power state (%i) is not supported!\n",
+ state);
+ return FWK_E_PARAM;
+ }
+}
+
+static void cluster_pd_ppu_interrupt_handler(struct ppu_v1_pd_ctx *pd_ctx)
+{
+ int status;
+ struct ppu_v1_reg *ppu;
+ enum ppu_v1_mode current_mode;
+
+ assert(pd_ctx != NULL);
+
+ ppu = pd_ctx->ppu;
+
+ if (!ppu_v1_is_power_active_edge_interrupt(ppu, PPU_V1_MODE_ON))
+ return; /* Spurious interrupt */
+
+ ppu_v1_ack_power_active_edge_interrupt(ppu, PPU_V1_MODE_ON);
+ current_mode = ppu_v1_get_power_mode(ppu);
+
+ switch (current_mode) {
+ case PPU_V1_MODE_OFF:
+ /* Cluster has to be powered on */
+ cluster_on(pd_ctx);
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON,
+ PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE);
+ return;
+
+ case PPU_V1_MODE_ON:
+ /*
+ * It may be possible to turn off the cluster, check all PACTIVE lines
+ * to make sure it is not just requesting a low power mode.
+ */
+ while (current_mode > 0) {
+ if (ppu_v1_is_power_devactive_high(ppu, current_mode--))
+ return;
+ }
+
+ /* All PACTIVE lines are low, so the cluster can be turned off */
+ if (cluster_off(pd_ctx)) {
+ /* Cluster successfuly transitioned to off */
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON, PPU_V1_EDGE_SENSITIVITY_RISING_EDGE);
+ status = pd_ctx->pd_driver_input_api->report_power_state_transition(
+ pd_ctx->bound_id, MOD_PD_STATE_SLEEP);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+ } else {
+ /* Cluster did not transition to off */
+ ppu_v1_set_input_edge_sensitivity(ppu,
+ PPU_V1_MODE_ON, PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE);
+ }
+ return;
+
+ default:
+ /* Cluster is in an invalid power mode */
+ assert(false);
+ return;
+ }
+}
+
+static const struct mod_pd_driver_api cluster_pd_driver = {
+ .set_state = ppu_v1_cluster_pd_set_state,
+ .get_state = ppu_v1_pd_get_state,
+ .reset = ppu_v1_pd_reset,
+};
+
+static void ppu_interrupt_handler(uintptr_t pd_ctx_param)
+{
+ struct ppu_v1_pd_ctx *pd_ctx = (struct ppu_v1_pd_ctx *)pd_ctx_param;
+
+ assert(pd_ctx != NULL);
+
+ if (pd_ctx->config->pd_type == MOD_PD_TYPE_CORE)
+ core_pd_ppu_interrupt_handler(pd_ctx);
+ else
+ cluster_pd_ppu_interrupt_handler(pd_ctx);
+};
+
+static void ppu_isr_api_interrupt_handler(fwk_id_t pd_id)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return;
+
+ if (!fwk_id_is_type(pd_id, FWK_ID_TYPE_ELEMENT))
+ return;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+ ppu_interrupt_handler((uintptr_t)pd_ctx);
+}
+
+static const struct ppu_v1_isr_api isr_api = {
+ .ppu_interrupt_handler = ppu_isr_api_interrupt_handler,
+};
+
+static int ppu_power_mode_on(fwk_id_t pd_id)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (!fwk_id_is_type(pd_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_PARAM;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+
+ return ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_ON);
+}
+
+static const struct ppu_v1_boot_api boot_api = {
+ .power_mode_on = ppu_power_mode_on,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int ppu_v1_mod_init(fwk_id_t module_id, unsigned int pd_count,
+ const void *unused)
+{
+ ppu_v1_ctx.pd_ctx_table = fwk_mm_calloc(pd_count,
+ sizeof(struct ppu_v1_pd_ctx));
+ if (ppu_v1_ctx.pd_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ ppu_v1_ctx.pd_ctx_table_size = pd_count;
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_pd_init(fwk_id_t pd_id, unsigned int unused, const void *data)
+{
+ const struct mod_ppu_v1_pd_config *config = data;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ if (config->pd_type >= MOD_PD_TYPE_COUNT)
+ return FWK_E_DATA;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id);
+ pd_ctx->config = config;
+ pd_ctx->ppu = (struct ppu_v1_reg *)(config->ppu.reg_base);
+ pd_ctx->bound_id = FWK_ID_NONE;
+
+ if (config->ppu.irq != FWK_INTERRUPT_NONE) {
+ fwk_interrupt_set_isr_param(config->ppu.irq,
+ ppu_interrupt_handler,
+ (uintptr_t)pd_ctx);
+ }
+
+ if (config->pd_type == MOD_PD_TYPE_CLUSTER) {
+ pd_ctx->data = fwk_mm_calloc(1, sizeof(struct ppu_v1_cluster_pd_ctx));
+ if (pd_ctx->data == NULL)
+ return FWK_E_NOMEM;
+ }
+
+ if (config->default_power_on) {
+ switch (config->pd_type) {
+ case MOD_PD_TYPE_DEVICE:
+ /* Fall through */
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+ /* Fall through */
+ case MOD_PD_TYPE_SYSTEM:
+ ppu_v1_init(pd_ctx->ppu);
+ return ppu_v1_set_power_mode(pd_ctx->ppu, PPU_V1_MODE_ON);
+
+ default:
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_post_init(fwk_id_t module_id)
+{
+ unsigned int pd_idx;
+ struct ppu_v1_pd_ctx *pd_ctx, *cluster_pd_ctx;
+ const struct mod_ppu_v1_pd_config *config;
+ fwk_id_t cluster_id;
+ struct ppu_v1_cluster_pd_ctx *cluster_pd_specific_ctx;
+
+ for (pd_idx = 0; pd_idx < ppu_v1_ctx.pd_ctx_table_size; pd_idx++) {
+ pd_ctx = &ppu_v1_ctx.pd_ctx_table[pd_idx];
+ config = pd_ctx->config;
+ if (config->pd_type != MOD_PD_TYPE_CORE)
+ continue;
+
+ cluster_id = config->cluster_id;
+
+ if ((!fwk_module_is_valid_element_id(cluster_id)) ||
+ (fwk_id_get_module_idx(cluster_id) != FWK_MODULE_IDX_PPU_V1))
+ return FWK_E_PARAM;
+
+ cluster_pd_ctx = &ppu_v1_ctx.pd_ctx_table[
+ fwk_id_get_element_idx(cluster_id)];
+ cluster_pd_specific_ctx = cluster_pd_ctx->data;
+
+ if (cluster_pd_specific_ctx->core_count >= CORE_PER_CLUSTER_COUNT_MAX)
+ return FWK_E_NOMEM;
+
+ cluster_pd_specific_ctx->core_pd_ctx_table[
+ cluster_pd_specific_ctx->core_count++] = pd_ctx;
+ pd_ctx->parent_pd_ctx = cluster_pd_ctx;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_bind(fwk_id_t id, unsigned int round)
+{
+ int status = FWK_SUCCESS;
+ struct ppu_v1_pd_ctx *pd_ctx;
+
+ /* Nothing to do during the first round of calls where the power module
+ will bind to the power domains of this module. */
+ if (round == 0)
+ return FWK_SUCCESS;
+
+ /* In the case of the module, bind to the log component */
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &ppu_v1_ctx.log_api);
+ return status;
+ }
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(id);
+
+ if (!fwk_id_is_equal(pd_ctx->config->observer_id, FWK_ID_NONE)) {
+ if (pd_ctx->config->pd_type != MOD_PD_TYPE_CLUSTER) {
+ /* State observation only supported for clusters */
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+
+ status = fwk_module_bind(pd_ctx->config->observer_id,
+ pd_ctx->config->observer_api,
+ &pd_ctx->observer_api);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ if (fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE))
+ return FWK_SUCCESS;
+
+ switch (fwk_id_get_module_idx(pd_ctx->bound_id)) {
+ #if BUILD_HAS_MOD_POWER_DOMAIN
+ case FWK_MODULE_IDX_POWER_DOMAIN:
+ return fwk_module_bind(pd_ctx->bound_id,
+ mod_pd_api_id_driver_input,
+ &pd_ctx->pd_driver_input_api);
+ break;
+ #endif
+
+ #if BUILD_HAS_MOD_SYSTEM_POWER
+ case FWK_MODULE_IDX_SYSTEM_POWER:
+ return fwk_module_bind(pd_ctx->bound_id,
+ mod_system_power_api_id_pd_driver_input,
+ &pd_ctx->pd_driver_input_api);
+ break;
+ #endif
+
+ default:
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+}
+
+static int ppu_v1_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id,
+ const void **api)
+{
+ struct ppu_v1_pd_ctx *pd_ctx;
+ unsigned int api_idx;
+ bool is_power_domain_module = false;
+ bool is_system_power_module = false;
+
+ api_idx = fwk_id_get_api_idx(api_id);
+
+ if (api_idx == MOD_PPU_V1_API_IDX_ISR) {
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_SUPPORT;
+
+ *api = &isr_api;
+ return FWK_SUCCESS;
+ }
+
+ if (api_idx == MOD_PPU_V1_API_IDX_BOOT) {
+ *api = &boot_api;
+ return FWK_SUCCESS;
+ }
+
+ if (api_idx != MOD_PPU_V1_API_IDX_POWER_DOMAIN_DRIVER)
+ return FWK_E_SUPPORT;
+
+ if (!fwk_module_is_valid_element_id(target_id))
+ return FWK_E_PARAM;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(target_id);
+
+ /* Allow multiple binding only for device power domain for now */
+ if ((pd_ctx->config->pd_type != MOD_PD_TYPE_DEVICE) &&
+ (!fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE))) {
+ assert(false);
+ return FWK_E_ACCESS;
+ }
+
+ #if BUILD_HAS_MOD_POWER_DOMAIN
+ is_power_domain_module = (fwk_id_get_module_idx(source_id) ==
+ FWK_MODULE_IDX_POWER_DOMAIN);
+ #endif
+ #if BUILD_HAS_MOD_SYSTEM_POWER
+ is_system_power_module = (fwk_id_get_module_idx(source_id) ==
+ FWK_MODULE_IDX_SYSTEM_POWER);
+ #endif
+
+ switch (pd_ctx->config->pd_type) {
+ case MOD_PD_TYPE_CORE:
+ if (is_power_domain_module) {
+ *api = &core_pd_driver;
+ pd_ctx->bound_id = source_id;
+ return FWK_SUCCESS;
+ }
+ break;
+
+ case MOD_PD_TYPE_CLUSTER:
+ if (is_power_domain_module) {
+ *api = &cluster_pd_driver;
+ pd_ctx->bound_id = source_id;
+ return FWK_SUCCESS;
+ }
+ break;
+
+ case MOD_PD_TYPE_SYSTEM:
+ if (is_power_domain_module || is_system_power_module) {
+ *api = &pd_driver;
+ pd_ctx->bound_id = source_id;
+ return FWK_SUCCESS;
+ }
+ break;
+
+ default:
+ if (is_power_domain_module)
+ pd_ctx->bound_id = source_id;
+ *api = &pd_driver;
+ return FWK_SUCCESS;
+ }
+
+ pd_ctx->bound_id = FWK_ID_NONE;
+ return FWK_E_ACCESS;
+}
+
+static int ppu_v1_start(fwk_id_t id)
+{
+ int status;
+ struct ppu_v1_pd_ctx *pd_ctx;
+ const struct mod_ppu_v1_config *module_config;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(id);
+ module_config = fwk_module_get_data(fwk_id_build_module_id(id));
+ assert(module_config != NULL);
+
+ /* Register for power domain transition notifications */
+ status = fwk_notification_subscribe(
+ module_config->pd_notification_id,
+ module_config->pd_source_id,
+ id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ switch (pd_ctx->config->pd_type) {
+ case MOD_PD_TYPE_CORE:
+ case MOD_PD_TYPE_CLUSTER:
+ fwk_interrupt_clear_pending(pd_ctx->config->ppu.irq);
+ fwk_interrupt_enable(pd_ctx->config->ppu.irq);
+ break;
+ default:
+ /* Nothing to be done for other types */
+ break;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int ppu_v1_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ const struct mod_ppu_v1_config *module_config;
+ struct ppu_v1_pd_ctx *pd_ctx;
+ struct mod_pd_power_state_transition_notification_params *params;
+
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
+ module_config =
+ fwk_module_get_data(fwk_id_build_module_id(event->target_id));
+ assert(
+ fwk_id_is_equal(
+ event->id,
+ module_config->pd_notification_id));
+ (void)module_config;
+
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ event->params;
+
+ if (params->state != MOD_PD_STATE_ON)
+ return FWK_SUCCESS;
+
+ pd_ctx = ppu_v1_ctx.pd_ctx_table + fwk_id_get_element_idx(event->target_id);
+
+ switch (pd_ctx->config->pd_type) {
+ case MOD_PD_TYPE_CORE:
+ return ppu_v1_core_pd_init(pd_ctx);
+
+ case MOD_PD_TYPE_CLUSTER:
+ return ppu_v1_cluster_pd_init(pd_ctx);
+
+ default:
+ ppu_v1_init(pd_ctx->ppu);
+ return FWK_SUCCESS;
+ }
+}
+
+const struct fwk_module module_ppu_v1 = {
+ .name = "PPU_V1",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_PPU_V1_API_IDX_COUNT,
+ .init = ppu_v1_mod_init,
+ .element_init = ppu_v1_pd_init,
+ .post_init = ppu_v1_post_init,
+ .bind = ppu_v1_bind,
+ .start = ppu_v1_start,
+ .process_bind_request = ppu_v1_process_bind_request,
+ .process_notification = ppu_v1_process_notification,
+};
diff --git a/module/ppu_v1/src/ppu_v1.c b/module/ppu_v1/src/ppu_v1.c
new file mode 100644
index 00000000..14311245
--- /dev/null
+++ b/module/ppu_v1/src/ppu_v1.c
@@ -0,0 +1,360 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <ppu_v1.h>
+#include <fwk_errno.h>
+
+void ppu_v1_init(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ /* Set edge sensitivity to masked for all input edges */
+ ppu->IESR = 0;
+
+ /* Mask all interrupts */
+ ppu->IMR = PPU_V1_IMR_MASK;
+
+ /* Acknowledge any interrupt left pending */
+ ppu->ISR = PPU_V1_ISR_MASK;
+}
+
+/*
+ * PWPR and PWSR registers
+ */
+int ppu_v1_request_power_mode(struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode)
+{
+ uint32_t power_policy;
+ assert(ppu != NULL);
+ assert(ppu_mode < PPU_V1_MODE_COUNT);
+
+ power_policy = ppu->PWPR & ~(PPU_V1_PWPR_POLICY | PPU_V1_PWPR_DYNAMIC_EN);
+ ppu->PWPR = power_policy | ppu_mode;
+
+ return FWK_SUCCESS;
+}
+
+int ppu_v1_set_power_mode(struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode)
+{
+ int status;
+
+ status = ppu_v1_request_power_mode(ppu, ppu_mode);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ while ((ppu->PWSR & (PPU_V1_PWSR_PWR_STATUS | PPU_V1_PWSR_PWR_DYN_STATUS))
+ != ppu_mode)
+ continue;
+
+ return FWK_SUCCESS;
+}
+
+int ppu_v1_request_operating_mode(struct ppu_v1_reg *ppu,
+ enum ppu_v1_opmode op_mode)
+{
+ uint32_t power_policy;
+ assert(ppu != NULL);
+ assert(op_mode < PPU_V1_OPMODE_COUNT);
+
+ power_policy = ppu->PWPR & ~(PPU_V1_PWPR_OP_POLICY | PPU_V1_PWPR_OP_DYN_EN);
+ ppu->PWPR = power_policy | (op_mode << PPU_V1_PWPR_OP_POLICY_POS);
+
+ return FWK_SUCCESS;
+}
+
+void ppu_v1_opmode_dynamic_enable(struct ppu_v1_reg *ppu,
+ enum ppu_v1_opmode min_dyn_mode)
+{
+ uint32_t power_policy;
+
+ assert(ppu != NULL);
+ assert(min_dyn_mode < PPU_V1_OPMODE_COUNT);
+
+ power_policy = ppu->PWPR & ~PPU_V1_PWPR_OP_POLICY;
+ ppu->PWPR = power_policy |
+ PPU_V1_PWPR_OP_DYN_EN |
+ (min_dyn_mode << PPU_V1_PWPR_OP_POLICY_POS);
+ while ((ppu->PWSR & PPU_V1_PWSR_OP_DYN_STATUS) == 0)
+ continue;
+}
+
+void ppu_v1_dynamic_enable(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode min_dyn_state)
+{
+ uint32_t power_policy;
+
+ assert(ppu != NULL);
+ assert(min_dyn_state < PPU_V1_MODE_COUNT);
+
+ power_policy = ppu->PWPR & ~PPU_V1_PWPR_POLICY;
+ ppu->PWPR = power_policy | PPU_V1_PWPR_DYNAMIC_EN | min_dyn_state;
+ while ((ppu->PWSR & PPU_V1_PWSR_PWR_DYN_STATUS) == 0)
+ continue;
+}
+
+void ppu_v1_lock_off_enable(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ ppu->PWPR |= PPU_V1_PWPR_OFF_LOCK_EN;
+}
+
+void ppu_v1_lock_off_disable(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ ppu->PWPR &= ~PPU_V1_PWPR_OFF_LOCK_EN;
+}
+
+enum ppu_v1_mode ppu_v1_get_power_mode(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return (enum ppu_v1_mode)(ppu->PWSR & PPU_V1_PWSR_PWR_STATUS);
+}
+
+enum ppu_v1_mode ppu_v1_get_programmed_power_mode(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return (enum ppu_v1_mode)(ppu->PWPR & PPU_V1_PWPR_POLICY);
+}
+
+enum ppu_v1_opmode ppu_v1_get_operating_mode(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return (enum ppu_v1_opmode)
+ (ppu->PWSR & PPU_V1_PWSR_OP_STATUS) >> PPU_V1_PWSR_OP_STATUS_POS;
+}
+
+enum ppu_v1_opmode ppu_v1_get_programmed_operating_mode(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return (enum ppu_v1_opmode)
+ (ppu->PWPR & PPU_V1_PWPR_OP_POLICY) >> PPU_V1_PWPR_OP_POLICY_POS;
+}
+
+bool ppu_v1_is_dynamic_enabled(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return ((ppu->PWSR & PPU_V1_PWSR_PWR_DYN_STATUS) != 0);
+}
+
+bool ppu_v1_is_locked(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return ((ppu->PWSR & PPU_V1_PWSR_OFF_LOCK_STATUS) != 0);
+}
+
+/*
+ * DISR register
+ */
+bool ppu_v1_is_power_devactive_high(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode)
+{
+ assert(ppu != NULL);
+
+ return (ppu->DISR &
+ (1 << (ppu_mode + PPU_V1_DISR_PWR_DEVACTIVE_STATUS_POS))) != 0;
+}
+
+bool ppu_v1_is_op_devactive_high(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive)
+{
+ assert(ppu != NULL);
+
+ return (ppu->DISR &
+ (1 << (op_devactive + PPU_V1_DISR_OP_DEVACTIVE_STATUS_POS))) != 0;
+}
+
+/*
+ * UNLK register
+ */
+void ppu_v1_off_unlock(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ ppu->UNLK = PPU_V1_UNLK_OFF_UNLOCK;
+}
+
+/*
+ * PWCR register
+ */
+void ppu_v1_disable_devactive(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ ppu->PWCR &= ~PPU_V1_PWCR_DEV_ACTIVE_EN;
+}
+
+void ppu_v1_disable_handshake(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ ppu->PWCR &= ~PPU_V1_PWCR_DEV_REQ_EN;
+}
+
+/*
+ * Interrupt registers: IMR, AIMR, ISR, AISR, IESR, OPSR
+ */
+void ppu_v1_interrupt_mask(struct ppu_v1_reg *ppu, unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->IMR |= mask & PPU_V1_IMR_MASK;
+}
+
+void ppu_v1_additional_interrupt_mask(struct ppu_v1_reg *ppu, unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->AIMR |= mask & PPU_V1_AIMR_MASK;
+}
+
+void ppu_v1_interrupt_unmask(struct ppu_v1_reg *ppu, unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->IMR &= ~(mask & PPU_V1_IMR_MASK);
+}
+
+void ppu_v1_additional_interrupt_unmask(struct ppu_v1_reg *ppu,
+ unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->AIMR &= ~(mask & PPU_V1_AIMR_MASK);
+}
+
+bool ppu_v1_is_additional_interrupt_pending(struct ppu_v1_reg *ppu,
+ unsigned int mask)
+{
+ return (ppu->AISR & (mask & PPU_V1_AISR_MASK)) != 0;
+}
+
+void ppu_v1_ack_interrupt(struct ppu_v1_reg *ppu, unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->ISR &= mask & PPU_V1_IMR_MASK;
+}
+
+void ppu_v1_ack_additional_interrupt(struct ppu_v1_reg *ppu, unsigned int mask)
+{
+ assert(ppu != NULL);
+
+ ppu->AISR &= mask & PPU_V1_AIMR_MASK;
+}
+
+void ppu_v1_set_input_edge_sensitivity(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode, enum ppu_v1_edge_sensitivity edge_sensitivity)
+{
+ assert(ppu != NULL);
+ assert(ppu_mode < PPU_V1_MODE_COUNT);
+ assert((edge_sensitivity & ~PPU_V1_EDGE_SENSITIVITY_MASK) == 0);
+
+ /* Clear current settings */
+ ppu->IESR &= ~(PPU_V1_EDGE_SENSITIVITY_MASK <<
+ (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY));
+
+ /* Update settings */
+ ppu->IESR |= edge_sensitivity <<
+ (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY);
+}
+
+enum ppu_v1_edge_sensitivity ppu_v1_get_input_edge_sensitivity(
+ struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode)
+{
+ assert(ppu != NULL);
+ assert(ppu_mode < PPU_V1_MODE_COUNT);
+
+ return (enum ppu_v1_edge_sensitivity)(
+ (ppu->IESR >> (ppu_mode * PPU_V1_BITS_PER_EDGE_SENSITIVITY)) &
+ PPU_V1_EDGE_SENSITIVITY_MASK);
+}
+
+void ppu_v1_ack_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode)
+{
+ ppu->ISR = 1 << (ppu_mode + PPU_V1_ISR_ACTIVE_EDGE_POS);
+}
+
+bool ppu_v1_is_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode)
+{
+ return ppu->ISR & (1 << (ppu_mode + PPU_V1_ISR_ACTIVE_EDGE_POS));
+}
+
+void ppu_v1_set_op_active_edge_sensitivity(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive,
+ enum ppu_v1_edge_sensitivity edge_sensitivity)
+{
+ assert(ppu != NULL);
+ assert(op_devactive < PPU_V1_OP_DEVACTIVE_COUNT);
+ assert((edge_sensitivity & ~PPU_V1_EDGE_SENSITIVITY_MASK) == 0);
+
+ /* Clear current settings */
+ ppu->OPSR &= ~(PPU_V1_EDGE_SENSITIVITY_MASK <<
+ (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY));
+
+ /* Update settings */
+ ppu->OPSR |= edge_sensitivity <<
+ (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY);
+}
+
+enum ppu_v1_edge_sensitivity ppu_v1_get_op_active_edge_sensitivity(
+ struct ppu_v1_reg *ppu, enum ppu_v1_op_devactive op_devactive)
+{
+ assert(ppu != NULL);
+ assert(op_devactive < PPU_V1_OP_DEVACTIVE_COUNT);
+
+ return (enum ppu_v1_edge_sensitivity)(
+ (ppu->OPSR >> (op_devactive * PPU_V1_BITS_PER_EDGE_SENSITIVITY)) &
+ PPU_V1_EDGE_SENSITIVITY_MASK);
+}
+
+void ppu_v1_ack_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive)
+{
+ ppu->ISR = 1 << (op_devactive + PPU_V1_ISR_OP_ACTIVE_EDGE_POS);
+}
+
+bool ppu_v1_is_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive)
+{
+ return ppu->ISR & (1 << (op_devactive + PPU_V1_ISR_OP_ACTIVE_EDGE_POS));
+}
+
+bool ppu_v1_is_dyn_policy_min_interrupt(struct ppu_v1_reg *ppu)
+{
+ return ppu->ISR & PPU_V1_ISR_DYN_POLICY_MIN_IRQ;
+}
+
+/*
+ * IDR0 register
+ */
+unsigned int ppu_v1_get_num_opmode(struct ppu_v1_reg *ppu)
+{
+ return ((ppu->IDR0 & PPU_V1_IDR0_NUM_OPMODE)
+ >> PPU_V1_IDR0_NUM_OPMODE_POS) + 1;
+}
+
+/*
+ * AIDR register
+ */
+unsigned int ppu_v1_get_arch_id(struct ppu_v1_reg *ppu)
+{
+ assert(ppu != NULL);
+
+ return (ppu->AIDR & (PPU_V1_AIDR_ARCH_REV_MINOR |
+ PPU_V1_AIDR_ARCH_REV_MAJOR));
+}
diff --git a/module/ppu_v1/src/ppu_v1.h b/module/ppu_v1/src/ppu_v1.h
new file mode 100644
index 00000000..a4e37647
--- /dev/null
+++ b/module/ppu_v1/src/ppu_v1.h
@@ -0,0 +1,467 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PPU_V1_H
+#define PPU_V1_H
+
+/*!
+ * \cond
+ * @{
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*
+ * PPU 1.1 register definitions
+ */
+struct ppu_v1_reg {
+ FWK_RW uint32_t PWPR;
+ FWK_RW uint32_t PMER;
+ FWK_R uint32_t PWSR;
+ uint32_t RESERVED0;
+ FWK_R uint32_t DISR;
+ FWK_R uint32_t MISR;
+ FWK_R uint32_t STSR;
+ FWK_RW uint32_t UNLK;
+ FWK_RW uint32_t PWCR;
+ FWK_RW uint32_t PTCR;
+ uint32_t RESERVED1[2];
+ FWK_RW uint32_t IMR;
+ FWK_RW uint32_t AIMR;
+ FWK_RW uint32_t ISR;
+ FWK_RW uint32_t AISR;
+ FWK_RW uint32_t IESR;
+ FWK_RW uint32_t OPSR;
+ uint32_t RESERVED2[2];
+ FWK_RW uint32_t FUNRR;
+ FWK_RW uint32_t FULRR;
+ FWK_RW uint32_t MEMRR;
+ uint8_t RESERVED3[0x160 - 0x5C];
+ FWK_RW uint32_t EDTR0;
+ FWK_RW uint32_t EDTR1;
+ uint32_t RESERVED4[2];
+ FWK_RW uint32_t DCCR0;
+ FWK_RW uint32_t DCCR1;
+ uint8_t RESERVED5[0xFB0 - 0x178];
+ FWK_R uint32_t IDR0;
+ FWK_R uint32_t IDR1;
+ uint8_t RESERVED6[0xFC8 - 0xFB8];
+ FWK_R uint32_t IIDR;
+ FWK_R uint32_t AIDR;
+ uint8_t RESERVED7[0x1000 - 0xFD0];
+};
+
+enum ppu_v1_mode {
+ PPU_V1_MODE_OFF = 0,
+ PPU_V1_MODE_OFF_EMU = 1,
+ PPU_V1_MODE_MEM_RET = 2,
+ PPU_V1_MODE_MEM_RET_EMU = 3,
+ PPU_V1_MODE_LOGIC_RET = 4,
+ PPU_V1_MODE_FULL_RET = 5,
+ PPU_V1_MODE_MEM_OFF = 6,
+ PPU_V1_MODE_FUNC_RET = 7,
+ PPU_V1_MODE_ON = 8,
+ PPU_V1_MODE_WARM_RST = 9,
+ PPU_V1_MODE_DBG_RECOV = 10,
+ /* No valid modes after this line */
+ PPU_V1_MODE_COUNT
+};
+
+enum ppu_v1_opmode {
+ PPU_V1_OPMODE_00,
+ PPU_V1_OPMODE_01,
+ PPU_V1_OPMODE_02,
+ PPU_V1_OPMODE_03,
+ PPU_V1_OPMODE_04,
+ PPU_V1_OPMODE_05,
+ PPU_V1_OPMODE_06,
+ PPU_V1_OPMODE_07,
+ PPU_V1_OPMODE_08,
+ PPU_V1_OPMODE_09,
+ PPU_V1_OPMODE_10,
+ PPU_V1_OPMODE_11,
+ PPU_V1_OPMODE_12,
+ PPU_V1_OPMODE_13,
+ PPU_V1_OPMODE_14,
+ PPU_V1_OPMODE_15,
+ /* No valid operating modes after this line */
+ PPU_V1_OPMODE_COUNT
+};
+
+enum ppu_v1_op_devactive {
+ PPU_V1_OP_DEVACTIVE_0,
+ PPU_V1_OP_DEVACTIVE_1,
+ PPU_V1_OP_DEVACTIVE_2,
+ PPU_V1_OP_DEVACTIVE_3,
+ PPU_V1_OP_DEVACTIVE_INDEPENDENT_COUNT,
+ PPU_V1_OP_DEVACTIVE_4 = 4,
+ PPU_V1_OP_DEVACTIVE_5,
+ PPU_V1_OP_DEVACTIVE_6,
+ PPU_V1_OP_DEVACTIVE_7,
+ /* No valid operating mode devactive signal number after this line */
+ PPU_V1_OP_DEVACTIVE_COUNT
+};
+
+/*
+ * Bit definitions for PWPR
+ */
+#define PPU_V1_PWPR_PWR_POLICY_POS 0
+#define PPU_V1_PWPR_OP_POLICY_POS 16
+
+#define PPU_V1_PWPR_POLICY UINT32_C(0x0000000F)
+#define PPU_V1_PWPR_DYNAMIC_EN UINT32_C(0x00000100)
+#define PPU_V1_PWPR_OFF_LOCK_EN UINT32_C(0x00001000)
+#define PPU_V1_PWPR_OP_POLICY UINT32_C(0x000F0000)
+#define PPU_V1_PWPR_OP_DYN_EN UINT32_C(0x01000000)
+
+/*
+ * Bit definitions for PWSR
+ */
+#define PPU_V1_PWSR_PWR_STATUS_POS 0
+#define PPU_V1_PWSR_OP_STATUS_POS 16
+
+#define PPU_V1_PWSR_PWR_STATUS UINT32_C(0x0000000F)
+#define PPU_V1_PWSR_PWR_DYN_STATUS UINT32_C(0x00000100)
+#define PPU_V1_PWSR_OFF_LOCK_STATUS UINT32_C(0x00001000)
+#define PPU_V1_PWSR_OP_STATUS UINT32_C(0x000F0000)
+#define PPU_V1_PWSR_OP_DYN_STATUS UINT32_C(0x01000000)
+
+/*
+ * Bit definitions for DISR
+ */
+#define PPU_V1_DISR_PWR_DEVACTIVE_STATUS_POS 0
+#define PPU_V1_DISR_OP_DEVACTIVE_STATUS_POS 24
+
+/*
+ * Bit definitions for UNLK
+ */
+#define PPU_V1_UNLK_OFF_UNLOCK UINT32_C(0x00000001)
+
+/*
+ * Bit definitions for PWCR
+ */
+#define PPU_V1_PWCR_DEV_REQ_EN UINT32_C(0x000000FF)
+#define PPU_V1_PWCR_DEV_ACTIVE_EN UINT32_C(0x0007FF00)
+#define PPU_V1_PWCR_OP_DEV_ACTIVE_EN UINT32_C(0xFF000000)
+
+/*
+ * Definitions for IESR and OPSR
+ */
+enum ppu_v1_edge_sensitivity {
+ PPU_V1_EDGE_SENSITIVITY_MASKED,
+ PPU_V1_EDGE_SENSITIVITY_RISING_EDGE,
+ PPU_V1_EDGE_SENSITIVITY_FALLING_EDGE,
+ PPU_V1_EDGE_SENSITIVITY_BOTH_EDGES,
+ /* No valid edge sensitivity after this line */
+ PPU_V1_EDGE_SENSITIVITY_COUNT
+};
+
+#define PPU_V1_BITS_PER_EDGE_SENSITIVITY 2
+#define PPU_V1_EDGE_SENSITIVITY_MASK 0x3
+
+/*
+ * Bit definitions for IMR
+ */
+#define PPU_V1_IMR_MASK UINT32_C(0x0000003F)
+#define PPU_V1_IMR_STA_POLICY_TRN_IRQ_MASK UINT32_C(0x00000001)
+#define PPU_V1_IMR_STA_ACCEPT_IRQ_MASK UINT32_C(0x00000002)
+#define PPU_V1_IMR_STA_DENY_IRQ_MASK UINT32_C(0x00000004)
+#define PPU_V1_IMR_EMU_ACCEPT_IRQ_MASK UINT32_C(0x00000008)
+#define PPU_V1_IMR_EMU_DENY_IRQ_MASK UINT32_C(0x00000010)
+#define PPU_V1_IMR_DYN_POLICY_MIN_IRQ_MASK UINT32_C(0x00000020)
+
+/*
+ * Bit definitions for AIMR
+ */
+#define PPU_V1_AIMR_MASK UINT32_C(0x0000001F)
+#define PPU_V1_AIMR_UNSPT_POLICY_IRQ_MASK UINT32_C(0x00000001)
+#define PPU_V1_AIMR_DYN_ACCEPT_IRQ_MASK UINT32_C(0x00000002)
+#define PPU_V1_AIMR_DYN_DENY_IRQ_MASK UINT32_C(0x00000004)
+#define PPU_V1_AIMR_STA_POLICY_PWR_IRQ_MASK UINT32_C(0x00000008)
+#define PPU_V1_AIMR_STA_POLICY_OP_IRQ_MASK UINT32_C(0x00000010)
+
+/*
+ * Bit definitions for ISR
+ */
+#define PPU_V1_ISR_MASK UINT32_C(0xFF07FFBF)
+#define PPU_V1_ISR_STA_POLICY_TRN_IRQ UINT32_C(0x00000001)
+#define PPU_V1_ISR_STA_ACCEPT_IRQ UINT32_C(0x00000002)
+#define PPU_V1_ISR_STA_DENY_IRQ UINT32_C(0x00000004)
+#define PPU_V1_ISR_EMU_ACCEPT_IRQ UINT32_C(0x00000008)
+#define PPU_V1_ISR_EMU_DENY_IRQ UINT32_C(0x00000010)
+#define PPU_V1_ISR_DYN_POLICY_MIN_IRQ UINT32_C(0x00000020)
+#define PPU_V1_ISR_OTHER_IRQ UINT32_C(0x00000080)
+#define PPU_V1_ISR_ACTIVE_EDGE_POS 8
+#define PPU_V1_ISR_ACTIVE_EDGE_IRQ_MASK UINT32_C(0x0007FF00)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE0_IRQ UINT32_C(0x00000100)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE1_IRQ UINT32_C(0x00000200)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE2_IRQ UINT32_C(0x00000400)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE3_IRQ UINT32_C(0x00000800)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE4_IRQ UINT32_C(0x00001000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE5_IRQ UINT32_C(0x00002000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE6_IRQ UINT32_C(0x00004000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE7_IRQ UINT32_C(0x00008000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE8_IRQ UINT32_C(0x00010000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE9_IRQ UINT32_C(0x00020000)
+#define PPU_V1_ISR_ACTIVE_EDGE_ACTIVE10_IRQ UINT32_C(0x00040000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_POS 24
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_IRQ_MASK UINT32_C(0xFF000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE0_IRQ UINT32_C(0x01000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE1_IRQ UINT32_C(0x02000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE2_IRQ UINT32_C(0x04000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE3_IRQ UINT32_C(0x08000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE4_IRQ UINT32_C(0x10000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE5_IRQ UINT32_C(0x20000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE6_IRQ UINT32_C(0x40000000)
+#define PPU_V1_ISR_OP_ACTIVE_EDGE_ACTIVE7_IRQ UINT32_C(0x80000000)
+
+/*
+ * Bit definitions for AISR
+ */
+#define PPU_V1_AISR_MASK UINT32_C(0x0000001F)
+#define PPU_V1_AISR_UNSPT_POLICY_IRQ UINT32_C(0x00000001)
+#define PPU_V1_AISR_DYN_ACCEPT_IRQ UINT32_C(0x00000002)
+#define PPU_V1_AISR_DYN_DENY_IRQ UINT32_C(0x00000004)
+#define PPU_V1_AISR_STA_POLICY_PWR_IRQ UINT32_C(0x00000008)
+#define PPU_V1_AISR_STA_POLICY_OP_IRQ UINT32_C(0x00000010)
+
+/*
+ * Bit definitions for AIDR
+ */
+#define PPU_V1_AIDR_ARCH_REV_MINOR UINT32_C(0x0000000F)
+#define PPU_V1_AIDR_ARCH_REV_MAJOR UINT32_C(0x000000F0)
+
+/*
+ * Definitions for PPU Arch version ID
+ */
+#define PPU_V1_ARCH_ID 0x11
+
+/*
+ * Bit definitions for IDR0
+ */
+#define PPU_V1_IDR0_NUM_OPMODE_POS 4
+#define PPU_V1_IDR0_NUM_OPMODE UINT32_C(0x000000F0)
+
+/*
+ * Initializes the PPU by masking all interrupts and acknowledging any
+ * previously pending interrupt.
+ */
+void ppu_v1_init(struct ppu_v1_reg *ppu);
+
+/*
+ * Set PPU's power mode and wait for the transition.
+ * Note: This function currently supports only synchronous transitions with
+ * limited error detection.
+ */
+int ppu_v1_set_power_mode(struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode);
+
+/*
+ * Request PPU's power mode and don't wait for the transition.
+ */
+int ppu_v1_request_power_mode(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode);
+
+/*
+ * Request a change to the PPU's operating mode.
+ */
+int ppu_v1_request_operating_mode(struct ppu_v1_reg *ppu,
+ enum ppu_v1_opmode op_mode);
+
+/*
+ * Enable PPU's dynamic operating mode transitions
+ */
+void ppu_v1_opmode_dynamic_enable(struct ppu_v1_reg *ppu,
+ enum ppu_v1_opmode min_dyn_mode);
+
+/*
+ * Enable PPU's dynamic power mode transitions
+ */
+void ppu_v1_dynamic_enable(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode min_dyn_state);
+
+/*
+ * Enable the lock in the OFF state
+ */
+void ppu_v1_lock_off_enable(struct ppu_v1_reg *ppu);
+
+/*
+ * Disable the lock in the OFF state
+ */
+void ppu_v1_lock_off_disable(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the current power mode.
+ */
+enum ppu_v1_mode ppu_v1_get_power_mode(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the current programmed power policy mode.
+ */
+enum ppu_v1_mode ppu_v1_get_programmed_power_mode(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the current operating mode.
+ */
+enum ppu_v1_opmode ppu_v1_get_operating_mode(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the current programmed operating mode policy.
+ */
+enum ppu_v1_opmode ppu_v1_get_programmed_operating_mode(struct ppu_v1_reg *ppu);
+
+/*
+ * Check whether the dynamic transitions are enabled or not.
+ */
+bool ppu_v1_is_dynamic_enabled(struct ppu_v1_reg *ppu);
+
+/*
+ * Check whether the locked in the MEM_RET or OFF state.
+ */
+bool ppu_v1_is_locked(struct ppu_v1_reg *ppu);
+
+/*
+ * Check if the DEVACTIVE signal associated to a power mode is high.
+ */
+bool ppu_v1_is_power_devactive_high(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode);
+
+/*
+ * Check if the DEVACTIVE signal associated to an operating mode is high.
+ */
+bool ppu_v1_is_op_devactive_high(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive);
+
+/*
+ * Unlock the power domain from the OFF power mode.
+ */
+void ppu_v1_off_unlock(struct ppu_v1_reg *ppu);
+
+/*
+ * Disable the check of the DEVACTIVE signals by the PPU logic for state
+ * transition.
+ */
+void ppu_v1_disable_devactive(struct ppu_v1_reg *ppu);
+
+/*
+ * Disable the handshake with the P-channel or the Q-channels
+ */
+void ppu_v1_disable_handshake(struct ppu_v1_reg *ppu);
+
+/*
+ * Set one or more bits of the interrupt mask register.
+ */
+void ppu_v1_interrupt_mask(struct ppu_v1_reg *ppu, unsigned int mask);
+
+/*
+ * Set one or more bits of the additional interrupt mask register.
+ */
+void ppu_v1_additional_interrupt_mask(struct ppu_v1_reg *ppu,
+ unsigned int mask);
+
+/*
+ * Clear one or more bits of the interrupt mask register.
+ */
+void ppu_v1_interrupt_unmask(struct ppu_v1_reg *ppu, unsigned int mask);
+
+/*
+ * Clear one or more bits of the additional interrupt mask register.
+ */
+void ppu_v1_additional_interrupt_unmask(struct ppu_v1_reg *ppu,
+ unsigned int mask);
+
+/*
+ * Check if some additional interrupts are pending.
+ */
+bool ppu_v1_is_additional_interrupt_pending(struct ppu_v1_reg *ppu,
+ unsigned int mask);
+
+/*
+ * Acknowledge one or more interrupts.
+ */
+void ppu_v1_ack_interrupt(struct ppu_v1_reg *ppu, unsigned int mask);
+
+/*
+ * Acknowledge one or more additional interrupts.
+ */
+void ppu_v1_ack_additional_interrupt(struct ppu_v1_reg *ppu, unsigned int mask);
+
+/*
+ * Set input edge sensitivity. See 'enum ppu_v1_edge_sensitivity' for the
+ * available sensitivities.
+ */
+void ppu_v1_set_input_edge_sensitivity(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode, enum ppu_v1_edge_sensitivity edge_sensitivity);
+
+/*
+ * Get input edge sensitivity. See 'enum ppu_v1_edge_sensitivity' for the
+ * available sensitivities.
+ */
+enum ppu_v1_edge_sensitivity ppu_v1_get_input_edge_sensitivity(
+ struct ppu_v1_reg *ppu, enum ppu_v1_mode ppu_mode);
+
+/*
+ * Acknowledge a power active edge interrupt.
+ */
+void ppu_v1_ack_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode);
+
+/*
+ * Check if a power active edge interrupt is pending.
+ */
+bool ppu_v1_is_power_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_mode ppu_mode);
+
+/*
+ * Set operating mode active edge sensitivity. See
+ * 'enum ppu_v1_edge_sensitivity' for the available sensitivities.
+ */
+void ppu_v1_set_op_active_edge_sensitivity(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive,
+ enum ppu_v1_edge_sensitivity edge_sensitivity);
+
+/*
+ * Get operating mode active edge sensitivity.
+ * See 'enum ppu_v1_edge_sensitivity for the available sensitivities.
+ */
+enum ppu_v1_edge_sensitivity ppu_v1_get_op_active_edge_sensitivity(
+ struct ppu_v1_reg *ppu, enum ppu_v1_op_devactive op_devactive);
+
+/*
+ * Acknowledge operating mode active edge interrupt.
+ */
+void ppu_v1_ack_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive);
+
+/*
+ * Check if an operating mode active edge interrupt is pending.
+ */
+bool ppu_v1_is_op_active_edge_interrupt(struct ppu_v1_reg *ppu,
+ enum ppu_v1_op_devactive op_devactive);
+
+/*
+ * Check if the DYN input edge interrupt is pending.
+ */
+bool ppu_v1_is_dyn_policy_min_interrupt(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the number of operating modes.
+ */
+unsigned int ppu_v1_get_num_opmode(struct ppu_v1_reg *ppu);
+
+/*
+ * Get the PPU architecture ID.
+ */
+unsigned int ppu_v1_get_arch_id(struct ppu_v1_reg *ppu);
+
+/*!
+ * \endcond
+ * @}
+ */
+
+#endif /* PPU_V1_H */
diff --git a/module/psu/include/mod_psu.h b/module/psu/include/mod_psu.h
new file mode 100644
index 00000000..026a2221
--- /dev/null
+++ b/module/psu/include/mod_psu.h
@@ -0,0 +1,257 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PSU_H
+#define MOD_PSU_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \ingroup GroupModules
+ * \defgroup GroupPsu PSU HAL
+ * \{
+ */
+
+/*!
+ * \defgroup GroupPsuApis APIs
+ * \{
+ */
+
+/*!
+ * \brief Device API.
+ */
+struct mod_psu_device_api {
+ /*!
+ * \brief Get the enabled state of a device.
+ *
+ * \param device_id Identifier of the device to get the state of.
+ * \param [out] enabled \c true if the device is enabled, or \c if it is
+ * disabled.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_HANDLER An error occurred in the device driver.
+ */
+ int (*get_enabled)(fwk_id_t device_id, bool *enabled);
+
+ /*!
+ * \brief Set the enabled state of a device.
+ *
+ * \param device_id Identifier of the device to set the state of.
+ * \param enable \c true to enable the device, or \c false to disable it.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_HANDLER An error occurred in the device driver.
+ */
+ int (*set_enabled)(fwk_id_t device_id, bool enable);
+
+ /*!
+ * \brief Set the enabled state of a device.
+ *
+ * \param device_id Identifier of the device to set the state of.
+ * \param enable \c true to enable the device, or \c false to disable it.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_NOMEM The event queue is full.
+ * \retval FWK_E_PANIC An error in the framework occurred.
+ */
+ int (*set_enabled_async)(fwk_id_t device_id, bool enable);
+
+ /*!
+ * \brief Get the voltage of a device.
+ *
+ * \param device_id Identifier of the device to get the voltage of.
+ * \param [out] voltage Voltage in mV.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_HANDLER An error occurred in the device driver.
+ */
+ int (*get_voltage)(fwk_id_t device_id, uintmax_t *voltage);
+
+ /*!
+ * \brief Set the voltage of a device.
+ *
+ * \param device_id Identifier of the device to set the voltage of.
+ * \param voltage New voltage in mV.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_HANDLER An error occurred in the device driver.
+ */
+ int (*set_voltage)(fwk_id_t device_id, uintmax_t voltage);
+
+ /*!
+ * \brief Set the voltage of a device.
+ *
+ * \param device_id Identifier of the device to set the voltage of.
+ * \param voltage New voltage in mV.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE The element cannot accept the request.
+ * \retval FWK_E_NOMEM The event queue is full.
+ * \retval FWK_E_PANIC An error in the framework occurred.
+ */
+ int (*set_voltage_async)(fwk_id_t device_id, uintmax_t voltage);
+};
+
+/*!
+ * \brief Driver API.
+ */
+struct mod_psu_driver_api {
+ /*!
+ * \brief Set the enabled state of a device.
+ *
+ * \param id Identifier of the device to set the state of.
+ * \param enable \c true to enable the device, or \c false to disable it.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the other driver-defined error codes.
+ */
+ int (*set_enabled)(fwk_id_t id, bool enable);
+
+ /*!
+ * \brief Get the enabled state of a device.
+ *
+ * \param id Identifier of the device to get the state of.
+ * \param [out] enabled \c true if the device is enabled, or \c if it is
+ * disabled.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the other driver-defined error codes.
+ */
+ int (*get_enabled)(fwk_id_t id, bool *enabled);
+
+ /*!
+ * \brief Set the voltage of a device.
+ *
+ * \param id Identifier of the device to set the voltage of.
+ * \param voltage New voltage in millivolts (mV).
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the other driver-defined error codes.
+ */
+ int (*set_voltage)(fwk_id_t id, uintmax_t voltage);
+
+ /*!
+ * \brief Get the voltage of a device.
+ *
+ * \param id Identifier of the device to get the voltage of.
+ * \param [out] voltage Voltage in millivolts (mV).
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the other driver-defined error codes.
+ */
+ int (*get_voltage)(fwk_id_t id, uintmax_t *voltage);
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupPsuConfig Configuration
+ * \{
+ */
+
+/*!
+ * \brief Device configuration.
+ */
+struct mod_psu_device_config {
+ fwk_id_t driver_id; /*!< Driver identifier */
+ fwk_id_t driver_api_id; /*!< Driver API identifier */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupPsuEvents Events
+ * \{
+ */
+
+/*!
+ * \brief <tt>Set enabled</tt> event response parameters.
+ */
+struct mod_psu_event_params_set_enabled_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \brief <tt>Set voltage</tt> event response parameters.
+ */
+struct mod_psu_event_params_set_voltage_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupPsuIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_psu_api_idx {
+ /*! API index for mod_psu_api_id_psu_device */
+ MOD_PSU_API_IDX_PSU_DEVICE,
+
+ /*! Number of defined APIs */
+ MOD_PSU_API_IDX_COUNT
+};
+
+/*! Device API identifier */
+static const fwk_id_t mod_psu_api_id_psu_device =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_PSU, MOD_PSU_API_IDX_PSU_DEVICE);
+
+/*!
+ * \brief Event indices.
+ */
+enum mod_psu_event_idx {
+ /*! Event index for mod_psu_event_id_set_enabled */
+ MOD_PSU_EVENT_IDX_SET_ENABLED,
+
+ /*! Event index for mod_psu_event_id_set_voltage */
+ MOD_PSU_EVENT_IDX_SET_VOLTAGE,
+
+ /*! Number of defined events */
+ MOD_PSU_EVENT_IDX_COUNT
+};
+
+/*! <tt>Set enabled</tt> event identifier */
+static const fwk_id_t mod_psu_event_id_set_enabled =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_PSU, MOD_PSU_EVENT_IDX_SET_ENABLED);
+
+/*! <tt>Set voltage</tt> event identifier */
+static const fwk_id_t mod_psu_event_id_set_voltage =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_PSU, MOD_PSU_EVENT_IDX_SET_VOLTAGE);
+
+/*!
+ * \}
+ */
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_PSU_H */
diff --git a/module/psu/src/Makefile b/module/psu/src/Makefile
new file mode 100644
index 00000000..da070113
--- /dev/null
+++ b/module/psu/src/Makefile
@@ -0,0 +1,14 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := psu
+BS_LIB_SOURCES := \
+ mod_psu_device_api.c \
+ mod_psu_event.c \
+ mod_psu_module.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/psu/src/mod_psu_device_api.c b/module/psu/src/mod_psu_device_api.c
new file mode 100644
index 00000000..f3500d7a
--- /dev/null
+++ b/module/psu/src/mod_psu_device_api.c
@@ -0,0 +1,224 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_thread.h>
+#include <mod_psu_private.h>
+
+static int api_get_enabled(fwk_id_t device_id, bool *enabled)
+{
+ int status;
+ const struct mod_psu_device_ctx *ctx;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to a valid element */
+ if (!fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ ctx = __mod_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Get the enabled state from the driver */
+ status = ctx->apis.driver->get_enabled(ctx->config->driver_id, enabled);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_enabled(fwk_id_t device_id, bool enable)
+{
+ int status;
+ const struct mod_psu_device_ctx *ctx;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to a valid element */
+ if (!fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ ctx = __mod_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Set the enabled state through the driver */
+ status = ctx->apis.driver->set_enabled(ctx->config->driver_id, enable);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_enabled_async(fwk_id_t device_id, bool enable)
+{
+ int status;
+ struct fwk_event event;
+ struct mod_psu_event_params_set_enabled *params;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to an existing element */
+ if (fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ /* Build and submit the event */
+ event = (struct fwk_event) {
+ .id = mod_psu_event_id_set_enabled,
+ .target_id = device_id,
+ .response_requested = true,
+ };
+
+ params = (void *)&event.params;
+ *params = (struct mod_psu_event_params_set_enabled) {
+ .enable = enable,
+ };
+
+ /* Submit the event for processing */
+ status = fwk_thread_put_event(&event);
+ if (status == FWK_E_NOMEM)
+ return FWK_E_NOMEM;
+ else if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
+
+static int api_get_voltage(fwk_id_t device_id, uintmax_t *voltage)
+{
+ int status;
+ const struct mod_psu_device_ctx *ctx;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to a valid element */
+ if (!fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ ctx = __mod_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Get the voltage from the driver */
+ status = ctx->apis.driver->get_voltage(ctx->config->driver_id, voltage);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_voltage(fwk_id_t device_id, uintmax_t voltage)
+{
+ int status;
+ const struct mod_psu_device_ctx *ctx;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to a valid element */
+ if (!fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ ctx = __mod_psu_get_valid_device_ctx(device_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Set the voltage state through the driver */
+ status = ctx->apis.driver->set_voltage(ctx->config->driver_id, voltage);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int api_set_voltage_async(fwk_id_t device_id, uintmax_t voltage)
+{
+ int status;
+ struct fwk_event event;
+ struct mod_psu_event_params_set_voltage *params;
+
+ /* This API call cannot target another module */
+ if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the identifier refers to an existing element */
+ if (fwk_module_is_valid_element_id(device_id))
+ return FWK_E_PARAM;
+
+ /* Validate the API call */
+ status = fwk_module_check_call(device_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_STATE;
+
+ /* Build and submit the event */
+ event = (struct fwk_event) {
+ .id = mod_psu_event_id_set_enabled,
+ .target_id = device_id,
+ .response_requested = true,
+ };
+
+ params = (void *)&event.params;
+ *params = (struct mod_psu_event_params_set_voltage) {
+ .voltage = voltage,
+ };
+
+ status = fwk_thread_put_event(&event);
+ if (status == FWK_E_NOMEM)
+ return FWK_E_NOMEM;
+ else if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
+
+/* Module API implementation */
+const struct mod_psu_device_api __mod_psu_device_api = {
+ .get_enabled = api_get_enabled,
+ .set_enabled = api_set_enabled,
+ .set_enabled_async = api_set_enabled_async,
+ .get_voltage = api_get_voltage,
+ .set_voltage = api_set_voltage,
+ .set_voltage_async = api_set_voltage_async,
+};
diff --git a/module/psu/src/mod_psu_device_api_private.h b/module/psu/src/mod_psu_device_api_private.h
new file mode 100644
index 00000000..6959f594
--- /dev/null
+++ b/module/psu/src/mod_psu_device_api_private.h
@@ -0,0 +1,16 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PSU_DEVICE_API_PRIVATE_H
+#define MOD_PSU_DEVICE_API_PRIVATE_H
+
+#include <mod_psu.h>
+
+/* Module API implementation */
+extern const struct mod_psu_device_api __mod_psu_device_api;
+
+#endif /* MOD_PSU_DEVICE_API_PRIVATE_H */
diff --git a/module/psu/src/mod_psu_event.c b/module/psu/src/mod_psu_event.c
new file mode 100644
index 00000000..104045a6
--- /dev/null
+++ b/module/psu/src/mod_psu_event.c
@@ -0,0 +1,98 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_psu_private.h>
+
+int mod_psu_event_set_enabled(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ const struct mod_psu_device_ctx *ctx;
+ const struct mod_psu_event_params_set_enabled *params;
+ struct mod_psu_event_params_set_enabled_response *response_params;
+
+ /* These conditions were checked when we submitted the event */
+ assert(fwk_id_get_module_idx(event->target_id) == FWK_MODULE_IDX_PSU);
+ assert(fwk_module_is_valid_element_id(event->target_id));
+
+ /* Explicitly cast to our parameter types */
+ params = (void *)&event->params;
+ response_params = (void *)&response->params;
+
+ ctx = __mod_psu_get_device_ctx(event->target_id);
+
+ /* Set the enabled state through the driver */
+ response_params->status = ctx->apis.driver->set_enabled(
+ ctx->config->driver_id,
+ params->enable);
+
+ return FWK_SUCCESS;
+}
+
+int mod_psu_event_set_voltage(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ const struct mod_psu_device_ctx *ctx;
+ const struct mod_psu_event_params_set_voltage *params;
+ struct mod_psu_event_params_set_voltage_response *response_params;
+
+ /* These conditions were checked when we submitted the event */
+ assert(fwk_id_get_module_idx(event->target_id) == FWK_MODULE_IDX_PSU);
+ assert(fwk_module_is_valid_element_id(event->target_id));
+
+ /* Explicitly cast to our parameter types */
+ params = (void *)&event->params;
+ response_params = (void *)&response->params;
+
+ ctx = __mod_psu_get_device_ctx(event->target_id);
+
+ /* Set the voltage through the driver */
+ response_params->status = ctx->apis.driver->set_voltage(
+ ctx->config->driver_id,
+ params->voltage);
+
+ return FWK_SUCCESS;
+}
+
+int __mod_psu_process_event(
+ const struct fwk_event *event,
+ struct fwk_event *response)
+{
+ typedef int (*handler_t)(
+ const struct fwk_event *event,
+ struct fwk_event *response);
+
+ static const handler_t handlers[] = {
+ [MOD_PSU_EVENT_IDX_SET_ENABLED] = mod_psu_event_set_enabled,
+ [MOD_PSU_EVENT_IDX_SET_VOLTAGE] = mod_psu_event_set_voltage,
+ };
+
+ unsigned int event_idx;
+ handler_t handler;
+
+ /* We only handle the events defined by us */
+ if (fwk_id_get_module_idx(event->id) != FWK_MODULE_IDX_PSU)
+ return FWK_E_PARAM;
+
+ /* Ensure the event index is within bounds we can handle */
+ event_idx = fwk_id_get_event_idx(event->id);
+ if (event_idx >= FWK_ARRAY_SIZE(handlers))
+ return FWK_E_PARAM;
+
+ /* Ensure we have an implemented handler for this event */
+ handler = handlers[event_idx];
+ if (handler == NULL)
+ return FWK_E_PARAM;
+
+ /* Delegate event handling to the relevant handler */
+ return handler(event, response);
+}
diff --git a/module/psu/src/mod_psu_event_private.h b/module/psu/src/mod_psu_event_private.h
new file mode 100644
index 00000000..e451c8ac
--- /dev/null
+++ b/module/psu/src/mod_psu_event_private.h
@@ -0,0 +1,29 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PSU_EVENT_PRIVATE_H
+#define MOD_PSU_EVENT_PRIVATE_H
+
+#include <fwk_event.h>
+#include <fwk_id.h>
+
+/* "Set enabled" event */
+struct mod_psu_event_params_set_enabled {
+ bool enable;
+};
+
+/* "Set voltage" event */
+struct mod_psu_event_params_set_voltage {
+ uintmax_t voltage;
+};
+
+/* Event handler */
+int __mod_psu_process_event(
+ const struct fwk_event *event,
+ struct fwk_event *response);
+
+#endif /* MOD_PSU_EVENT_PRIVATE_H */
diff --git a/module/psu/src/mod_psu_module.c b/module/psu/src/mod_psu_module.c
new file mode 100644
index 00000000..9eab2f1d
--- /dev/null
+++ b/module/psu/src/mod_psu_module.c
@@ -0,0 +1,128 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_psu_private.h>
+
+/* Device context table */
+static struct mod_psu_device_ctx (*device_ctx)[];
+
+static int psu_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ device_ctx = fwk_mm_calloc(
+ element_count,
+ sizeof((*device_ctx)[0]));
+ if (device_ctx == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int psu_element_init(
+ fwk_id_t device_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ assert(sub_element_count == 0);
+
+ __mod_psu_get_device_ctx(device_id)->config = data;
+
+ return FWK_SUCCESS;
+}
+
+static int psu_bind_element(fwk_id_t device_id, unsigned int round)
+{
+ int status;
+ const struct mod_psu_device_ctx *ctx;
+
+ /* Only handle the first round */
+ if (round > 0)
+ return FWK_SUCCESS;
+
+ ctx = __mod_psu_get_device_ctx(device_id);
+
+ /* Bind to the driver */
+ status = fwk_module_bind(
+ ctx->config->driver_id,
+ ctx->config->driver_api_id,
+ &ctx->apis.driver);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+
+ return FWK_E_PANIC;
+ }
+
+ assert(ctx->apis.driver->set_enabled != NULL);
+ assert(ctx->apis.driver->get_enabled != NULL);
+ assert(ctx->apis.driver->set_voltage != NULL);
+ assert(ctx->apis.driver->get_voltage != NULL);
+
+ return FWK_SUCCESS;
+}
+
+static int psu_bind(fwk_id_t id, unsigned int round)
+{
+ /* We only need to handle element binding */
+ if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return psu_bind_element(id, round);
+
+ return FWK_SUCCESS;
+}
+
+static int psu_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only accept binds to the elements */
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_PARAM;
+
+ /* Only expose the device API */
+ if (!fwk_id_is_equal(api_id, mod_psu_api_id_psu_device))
+ return FWK_E_PARAM;
+
+ *api = &__mod_psu_device_api;
+
+ return FWK_SUCCESS;
+}
+
+struct mod_psu_device_ctx *__mod_psu_get_device_ctx(fwk_id_t device_id)
+{
+ unsigned int element_idx = fwk_id_get_element_idx(device_id);
+
+ return &(*device_ctx)[element_idx];
+}
+
+struct mod_psu_device_ctx *__mod_psu_get_valid_device_ctx(fwk_id_t device_id)
+{
+ if (fwk_module_check_call(device_id) != FWK_SUCCESS)
+ return NULL;
+
+ return __mod_psu_get_device_ctx(device_id);
+}
+
+/* Module description */
+const struct fwk_module module_psu = {
+ .name = "PSU",
+ .type = FWK_MODULE_TYPE_HAL,
+ .init = psu_init,
+ .element_init = psu_element_init,
+ .bind = psu_bind,
+ .process_bind_request = psu_process_bind_request,
+ .process_event = __mod_psu_process_event,
+ .api_count = MOD_PSU_API_IDX_COUNT,
+ .event_count = MOD_PSU_EVENT_IDX_COUNT,
+};
diff --git a/module/psu/src/mod_psu_module_private.h b/module/psu/src/mod_psu_module_private.h
new file mode 100644
index 00000000..adf01784
--- /dev/null
+++ b/module/psu/src/mod_psu_module_private.h
@@ -0,0 +1,28 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PSU_DEVICE_CTX_PRIVATE_H
+#define MOD_PSU_DEVICE_CTX_PRIVATE_H
+
+#include <fwk_id.h>
+#include <mod_psu.h>
+
+/* Device context */
+struct mod_psu_device_ctx {
+ /* Device configuration */
+ const struct mod_psu_device_config *config;
+
+ struct {
+ /* Driver API */
+ const struct mod_psu_driver_api *driver;
+ } apis;
+};
+
+struct mod_psu_device_ctx *__mod_psu_get_device_ctx(fwk_id_t device_id);
+struct mod_psu_device_ctx *__mod_psu_get_valid_device_ctx(fwk_id_t device_id);
+
+#endif /* MOD_PSU_DEVICE_CTX_PRIVATE_H */
diff --git a/module/psu/src/mod_psu_private.h b/module/psu/src/mod_psu_private.h
new file mode 100644
index 00000000..446e9f63
--- /dev/null
+++ b/module/psu/src/mod_psu_private.h
@@ -0,0 +1,16 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_PSU_PRIVATE_H
+#define MOD_PSU_PRIVATE_H
+
+#include <fwk_id.h>
+#include <mod_psu_device_api_private.h>
+#include <mod_psu_event_private.h>
+#include <mod_psu_module_private.h>
+
+#endif /* MOD_PSU_PRIVATE_H */
diff --git a/module/reg_sensor/include/mod_reg_sensor.h b/module/reg_sensor/include/mod_reg_sensor.h
new file mode 100644
index 00000000..73ba9156
--- /dev/null
+++ b/module/reg_sensor/include/mod_reg_sensor.h
@@ -0,0 +1,38 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_REG_SENSOR_H
+#define MOD_REG_SENSOR_H
+
+#include <stdint.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModuleRegSensor Register Sensor Driver
+ *
+ * \brief Driver for simple, register-based sensors.
+ * @{
+ */
+
+/*! \brief Element configuration */
+struct mod_reg_sensor_dev_config {
+ uintptr_t reg; /*!< Address of the sensor register */
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_REG_SENSOR_H */
diff --git a/module/reg_sensor/src/Makefile b/module/reg_sensor/src/Makefile
new file mode 100644
index 00000000..817a1efe
--- /dev/null
+++ b/module/reg_sensor/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := reg_sensor
+BS_LIB_SOURCES := mod_reg_sensor.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/reg_sensor/src/mod_reg_sensor.c b/module/reg_sensor/src/mod_reg_sensor.c
new file mode 100644
index 00000000..65157075
--- /dev/null
+++ b/module/reg_sensor/src/mod_reg_sensor.c
@@ -0,0 +1,101 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <mod_reg_sensor.h>
+#include <mod_sensor.h>
+
+static struct mod_reg_sensor_dev_config **config_table;
+
+/*
+ * Module API
+ */
+static int get_value(fwk_id_t id, uint64_t *value)
+{
+ int status;
+ struct mod_reg_sensor_dev_config *config;
+
+ status = fwk_module_check_call(id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ config = config_table[fwk_id_get_element_idx(id)];
+
+ if (value == NULL) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ *value = *(uint64_t*)config->reg;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_sensor_driver_api reg_sensor_api = {
+ .get_value = get_value,
+};
+
+/*
+ * Framework handlers
+ */
+static int reg_sensor_init(fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ config_table = fwk_mm_alloc(element_count, sizeof(*config_table));
+
+ if (config_table == NULL) {
+ /* Unable to allocate device context memory */
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int reg_sensor_element_init(fwk_id_t element_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ struct mod_reg_sensor_dev_config *config =
+ (struct mod_reg_sensor_dev_config *)data;
+
+ if (config->reg == 0) {
+ /* Invalid element configuration */
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ config_table[fwk_id_get_element_idx(element_id)] = config;
+
+ return FWK_SUCCESS;
+}
+
+static int reg_sensor_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_type,
+ const void **api)
+{
+ *api = &reg_sensor_api;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_reg_sensor = {
+ .name = "Register Sensor",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .init = reg_sensor_init,
+ .element_init = reg_sensor_element_init,
+ .process_bind_request = reg_sensor_process_bind_request,
+};
diff --git a/module/scmi/include/internal/mod_scmi.h b/module/scmi/include/internal/mod_scmi.h
new file mode 100644
index 00000000..822e196b
--- /dev/null
+++ b/module/scmi/include/internal/mod_scmi.h
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) support.
+ */
+
+#ifndef MOD_INTERNAL_SCMI_H
+#define MOD_INTERNAL_SCMI_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <mod_scmi.h>
+
+#define SCMI_VERSION 0x10000
+
+#define SCMI_MESSAGE_HEADER_MESSAGE_ID_POS 0
+#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS 10
+#define SCMI_MESSAGE_HEADER_TOKEN_POS 18
+
+#define SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK \
+ (UINT32_C(0x3FF) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS)
+#define SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK \
+ (UINT32_C(0xFF) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS)
+#define SCMI_MESSAGE_HEADER_TOKEN_MASK \
+ (UINT32_C(0x3FF) << SCMI_MESSAGE_HEADER_TOKEN_POS)
+
+#define SCMI_MESSAGE_HEADER(MESSAGE_ID, PROTOCOL_ID, TOKEN) \
+ ((((MESSAGE_ID) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS) & \
+ SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) | \
+ (((PROTOCOL_ID) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS) & \
+ SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) | \
+ (((TOKEN) << SCMI_MESSAGE_HEADER_TOKEN_POS) & \
+ SCMI_MESSAGE_HEADER_TOKEN_POS))
+
+/* SCMI service context */
+struct scmi_service_ctx {
+ /* Pointer to SCMI service configuration data */
+ const struct mod_scmi_service_config *config;
+
+ /*
+ * Identifier of the transport entity used by the service to read/respond
+ * to SCMI messages.
+ */
+ fwk_id_t transport_id;
+
+ /* Pointer to the transport API used to read and respond to messages */
+ const struct mod_scmi_to_transport_api *transport_api;
+
+ /* Copy of the pointer to the 'respond' function within the transport API */
+ int (*respond)(fwk_id_t transport_id, const void *payload, size_t size);
+
+ /* SCMI identifier of the protocol processing the current message */
+ unsigned int scmi_protocol_id;
+
+ /* SCMI identifier of the message currently being processed */
+ unsigned int scmi_message_id;
+};
+
+#endif /* MOD_INTERNAL_SCMI_H */
diff --git a/module/scmi/include/internal/scmi.h b/module/scmi/include/internal/scmi.h
new file mode 100644
index 00000000..ae910a7c
--- /dev/null
+++ b/module/scmi/include/internal/scmi.h
@@ -0,0 +1,171 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) protocol independent
+ * definitions.
+ */
+
+#ifndef SCMI_H
+#define SCMI_H
+
+#include <stdint.h>
+
+/*!
+ * \defgroup GroupSCMI System Control & Management Interface (SCMI)
+ * @{
+ */
+
+/*!
+ * \brief Maximum value for an agent identifier. The limit is derived from the
+ * the base protocol's "PROTOCOL_ATTRIBUTES" command. This command returns
+ * a 32-bits "attributes" value which, in turn, contains an 8-bit field
+ * giving the number of agents in the system.
+ */
+#define SCMI_AGENT_ID_MAX 0xFF
+
+/*!
+ * \brief SCMI identifier of the platform.
+ */
+#define SCMI_PLATFORM_ID 0
+
+/*! Maximum value of an SCMI protocol identifier */
+#define SCMI_PROTOCOL_ID_MAX 0xFF
+
+/*!
+ * \brief Return a packed 32-bit message header comprised of a 10-bit message
+ * identifier, an 8-bit protocol identifier, and a 10-bit token.
+ *
+ * \param MESSAGE_ID Message identifier.
+ * \param PROTOCOL_ID Protocol identifier.
+ * \param TOKEN Token.
+ */
+#define SCMI_MESSAGE_HEADER(MESSAGE_ID, PROTOCOL_ID, TOKEN) \
+ ((((MESSAGE_ID) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS) & \
+ SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) | \
+ (((PROTOCOL_ID) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS) & \
+ SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) | \
+ (((TOKEN) << SCMI_MESSAGE_HEADER_TOKEN_POS) & \
+ SCMI_MESSAGE_HEADER_TOKEN_POS))
+
+/*!
+ * \brief Entity role.
+ */
+enum scmi_role {
+ /*! Agent entity */
+ SCMI_ROLE_AGENT,
+
+ /*! Platform entity */
+ SCMI_ROLE_PLATFORM
+};
+
+/*!
+ * \brief Agent type
+ *
+ * \details The SCMI specification defines three specific agent types:
+ * - a PSCI implementation on AP processors.
+ * - a management agent.
+ * - an OSPM.
+ * The POWER_STATE_SET command targeting a power domain, including AP cores, is
+ * processed only if issued by a PSCI agent. The processing of the
+ * SYSTEM_POWER_STATE_SET command depends on the type of the agent that issued
+ * it. The OTHER type is added here to cover the other type of agents.
+ */
+enum scmi_agent_type {
+ /*! PSCI agent */
+ SCMI_AGENT_TYPE_PSCI,
+
+ /*! Management agent */
+ SCMI_AGENT_TYPE_MANAGEMENT,
+
+ /*! OSPM agent */
+ SCMI_AGENT_TYPE_OSPM,
+
+ /*! Other agent */
+ SCMI_AGENT_TYPE_OTHER,
+
+ /*! Number of agent types */
+ SCMI_AGENT_TYPE_COUNT,
+};
+
+/*!
+ * \brief Channel type.
+ *
+ * \details Defines the channel direction in terms of the master to the slave.
+ *
+ * \note The integer values of this enumeration are based on the master of
+ * communications in that configuration.
+ */
+enum scmi_channel_type {
+ /*!< Agent-to-platform */
+ SCMI_CHANNEL_TYPE_A2P = SCMI_ROLE_AGENT,
+
+ /*!< Platform-to-agent */
+ SCMI_CHANNEL_TYPE_P2A = SCMI_ROLE_PLATFORM
+};
+
+/*!
+ * \brief SCMI error codes.
+ */
+enum scmi_error {
+ SCMI_SUCCESS = 0,
+ SCMI_NOT_SUPPORTED = -1,
+ SCMI_INVALID_PARAMETERS = -2,
+ SCMI_DENIED = -3,
+ SCMI_NOT_FOUND = -4,
+ SCMI_OUT_OF_RANGE = -5,
+ SCMI_BUSY = -6,
+ SCMI_COMMS_ERROR = -7,
+ SCMI_GENERIC_ERROR = -8,
+ SCMI_HARDWARE_ERROR = -9,
+ SCMI_PROTOCOL_ERROR = -10,
+};
+
+/*!
+ * \brief Common command identifiers.
+ */
+enum scmi_command_id {
+ SCMI_PROTOCOL_VERSION = 0x000,
+ SCMI_PROTOCOL_ATTRIBUTES = 0x001,
+ SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x002
+};
+
+/*!
+ * \brief Generic platform-to-agent PROTOCOL_VERSION structure.
+ */
+struct __attribute((packed)) scmi_protocol_version_p2a {
+ int32_t status;
+ uint32_t version;
+};
+
+/*!
+ * \brief Generic platform-to-agent PROTOCOL_ATTRIBUTES structure.
+ */
+struct __attribute((packed)) scmi_protocol_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+};
+
+/*!
+ * \brief Generic agent-to-platform PROTOCOL_MESSAGE_ATTRIBUTES structure.
+ */
+struct __attribute((packed)) scmi_protocol_message_attributes_a2p {
+ uint32_t message_id;
+};
+
+/*!
+ * \brief Generic platform-to-agent PROTOCOL_MESSAGE_ATTRIBUTES structure.
+ */
+struct __attribute((packed)) scmi_protocol_message_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+};
+
+/*!
+ * @}
+ */
+
+#endif /* SCMI_H */
diff --git a/module/scmi/include/internal/scmi_base.h b/module/scmi/include/internal/scmi_base.h
new file mode 100644
index 00000000..4e5c255b
--- /dev/null
+++ b/module/scmi/include/internal/scmi_base.h
@@ -0,0 +1,90 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI base protocol definitions.
+ */
+
+#ifndef SCMI_BASE_H
+#define SCMI_BASE_H
+
+#define SCMI_PROTOCOL_ID_BASE UINT32_C(0x10)
+#define SCMI_PROTOCOL_VERSION_BASE UINT32_C(0x10000)
+
+enum scmi_base_command_id {
+ SCMI_BASE_DISCOVER_VENDOR = 0x003,
+ SCMI_BASE_DISCOVER_SUB_VENDOR = 0x004,
+ SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION = 0x005,
+ SCMI_BASE_DISCOVER_LIST_PROTOCOLS = 0x006,
+ SCMI_BASE_DISCOVER_AGENT = 0x007,
+ SCMI_BASE_NOTIFY_ERRORS = 0x008,
+};
+
+/*
+ * PROTOCOL_ATTRIBUTES
+ */
+
+#define SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_PROTOCOLS_POS 0
+#define SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_AGENTS_POS 8
+
+#define SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_PROTOCOLS_MASK 0xFF
+#define SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_AGENTS_MASK 0xFF00
+
+#define SCMI_BASE_PROTOCOL_ATTRIBUTES(NUM_PROTOCOLS, NUM_AGENTS) \
+ ((((NUM_PROTOCOLS) << SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_PROTOCOLS_POS) \
+ & SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_PROTOCOLS_MASK) | \
+ (((NUM_AGENTS) << SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_AGENTS_POS) \
+ & SCMI_BASE_PROTOCOL_ATTRIBUTES_NUM_AGENTS_MASK))
+
+/*
+ * BASE_DISCOVER_VENDOR
+ */
+struct __attribute((packed)) scmi_base_discover_vendor_p2a {
+ int32_t status;
+ char vendor_identifier[16];
+};
+
+/*
+ * BASE_DISCOVER_SUB_VENDOR
+ */
+struct __attribute((packed)) scmi_base_discover_sub_vendor_p2a {
+ int32_t status;
+ char sub_vendor_identifier[16];
+};
+
+/*
+ * BASE_DISCOVER_IMPLEMENTATION_VERSION
+ * No special structure right now, see protocol_version.
+ */
+
+/*
+ * BASE_DISCOVER_LIST_PROTOCOLS
+ */
+struct __attribute((packed)) scmi_base_discover_list_protocols_a2p {
+ uint32_t skip;
+};
+
+struct __attribute((packed)) scmi_base_discover_list_protocols_p2a {
+ int32_t status;
+ uint32_t num_protocols;
+ uint32_t protocols[];
+};
+
+/*
+ * BASE_DISCOVER_AGENT
+ */
+struct __attribute((packed)) scmi_base_discover_agent_a2p {
+ uint32_t agent_id;
+};
+
+struct __attribute((packed)) scmi_base_discover_agent_p2a {
+ int32_t status;
+ char name[16];
+};
+
+extern struct scp_scmi_protocol scmi_base_protocol;
+
+#endif /* SCMI_BASE_H */
diff --git a/module/scmi/include/mod_scmi.h b/module/scmi/include/mod_scmi.h
new file mode 100644
index 00000000..b846064b
--- /dev/null
+++ b/module/scmi/include/mod_scmi.h
@@ -0,0 +1,425 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) support.
+ */
+
+#ifndef MOD_SCMI_H
+#define MOD_SCMI_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <internal/scmi.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSCMI System Control & Management Interface (SCMI)
+ * @{
+ */
+
+/*!
+ * \brief Index of the interfaces exposed by the SCMI module.
+ */
+enum mod_scmi_api_idx {
+ MOD_SCMI_API_IDX_PROTOCOL,
+ MOD_SCMI_API_IDX_TRANSPORT,
+ MOD_SCMI_API_IDX_COUNT,
+};
+
+/*!
+ * \brief Agent descriptor
+ */
+struct mod_scmi_agent {
+ /*! \brief Type of the agent. */
+ enum scmi_agent_type type;
+
+ /*!
+ * \brief Pointer to the agent's name. This pointer may be equal to NULL.
+ * In that case, the agent will be assigned a default name based on
+ * its type: "PSCI", "MANAGEMENT", "OSPM", or "OTHER".
+ *
+ * \details The agent name of the BASE_DISCOVER_AGENT command can be at
+ * most 16 bytes in length (null terminator included). If the value
+ * assigned to this variable is longer than the limit then the agents
+ * in the system will be provided with a truncated version of it.
+ */
+ const char *name;
+};
+
+/*!
+ * \brief SCMI module configuration data.
+ */
+struct mod_scmi_config {
+ /*!
+ * \brief Maximum number of SCMI protocol modules that can bind to the
+ * SCMI module.
+ */
+ unsigned int protocol_count_max;
+
+ /*!
+ * \brief Number of agents in the system. Must be smaller than or equal to
+ * SCMI_AGENT_ID_MAX.
+ */
+ unsigned int agent_count;
+
+ /*!
+ * \brief Table of descriptors of the agents in the system.
+ *
+ * \details Entry 'i' in the table contains the descriptor of the agent
+ * with identifier 'i'. The agent identifiers are sequential and start
+ * from one, with the identifier 0 being reserved to identify the
+ * platform. The table must therefore have space for 'agent_count + 1'
+ * entries. The first entry is ignored.
+ */
+ const struct mod_scmi_agent *agent_table;
+
+ /*!
+ * \brief Pointer to the vendor identifier.
+ *
+ * \note The vendor identifier in the BASE_DISCOVER_VENDOR command is
+ * up to 16 bytes in length (null terminator included). The vendor
+ * identifier provided to agents will be truncated if it exceeds
+ * this limit.
+ */
+ const char *vendor_identifier;
+
+ /*!
+ * \brief Pointer to the sub-vendor identifier.
+ *
+ * \note The sub-vendor identifier in the BASE_DISCOVER_SUB_VENDOR
+ * command is up to 16 bytes in length (null terminator included).
+ * The sub-vendor identifier provided to agents will be truncated
+ * if it exceeds this limit.
+ */
+ const char *sub_vendor_identifier;
+};
+
+/*!
+ * \brief Service configuration data.
+ */
+struct mod_scmi_service_config {
+ /*!
+ * \brief Identifier of the transport entity.
+ */
+ fwk_id_t transport_id;
+
+ /*!
+ * \brief Identifier of the API of the transport entity.
+ */
+ fwk_id_t transport_api_id;
+
+ /*!
+ * \brief Identifier of the agent.
+ *
+ * \details An SCMI channel is the communication channel between an agent
+ * and the platform. This is the identifier assigned in the system
+ * to the agent using the transport channel identified by
+ * 'scmi_channel_id'. The agent identifier must be greater than or
+ * equal to one (the identifier 0 is assigned to the platform) and
+ * lower than or equal to the number of agents declared in SCMI
+ * module configuration data.
+ */
+ unsigned int scmi_agent_id;
+};
+
+/*!
+ * \brief SCMI module to transport entity API.
+ */
+struct mod_scmi_to_transport_api {
+ /*!
+ * \brief Check whether a channel is secure or non-secure.
+ *
+ * \param channel_id Channel identifier.
+ * \param secure Pointer to storage for the channel security state. True
+ * if the channel is secure, or false if it is non-secure.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The secure parameter is NULL.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*get_secure)(fwk_id_t channel_id, bool *secure);
+
+ /*!
+ * \brief Get the maximum permitted payload size of a channel.
+ *
+ * \param channel_id Channel identifier.
+ * \param size Pointer to storage for the maximum payload size in bytes.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The size parameter is NULL.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*get_max_payload_size)(fwk_id_t channel_id, size_t *size);
+
+ /*!
+ * \brief Get the SCMI message header from a channel.
+ *
+ * \param channel_id Channel identifier.
+ * \param message_header Pointer to storage for the SCMI message header.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The message_header parameter is NULL.
+ * \retval FWK_E_ACCESS No message is available to read.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*get_message_header)(fwk_id_t channel_id, uint32_t *message_header);
+
+ /*!
+ * \brief Get the SCMI payload from a channel.
+ *
+ * \param channel_id Channel identifier.
+ * \param payload Pointer to storage for the pointer to the payload.
+ * \param size Pointer to storage for the payload size. May be NULL, in
+ * which case the parameter should be ignored.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The payload parameter is NULL.
+ * \retval FWK_E_ACCESS No message is available to read.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*get_payload)(fwk_id_t channel_id, const void **payload,
+ size_t *size);
+
+ /*!
+ * \brief Write part of a payload to a channel.
+ *
+ * \param channel_id Channel identifier.
+ * \param offset Offset to begin writing at.
+ * \param payload Payload data to write.
+ * \param size Size of the payload data.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The payload parameter is NULL.
+ * \retval FWK_E_PARAM The offset and size provided are not within the
+ * bounds of the payload area.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*write_payload)(fwk_id_t channel_id, size_t offset,
+ const void *payload, size_t size);
+
+ /*!
+ * \brief Respond to an SCMI message on a channel.
+ *
+ * \param channel_id Channel identifier.
+ * \param payload Payload data to write, or NULL if a payload has already
+ * been written.
+ * \param size Size of the payload source.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The size parameter is less than the size of one
+ * payload entry.
+ * \retval FWK_E_ACCESS No message is available to respond to.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*respond)(fwk_id_t channel_id, const void *payload, size_t size);
+};
+
+/*!
+ * \brief Transport entity API to SCMI module API.
+ */
+struct mod_scmi_from_transport_api {
+ /*!
+ * \brief Signal to a service that a message is incoming.
+ *
+ * \param service_id SCMI service identifier.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The channel_id parameter is invalid.
+ * \retval FWK_E_PARAM The secure parameter is NULL.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*signal_message)(fwk_id_t service_id);
+};
+
+/*!
+ * \brief SCMI protocol message handler prototype.
+ *
+ * \details Prototype of a message handler called by the SCMI module when it
+ * receives a message for a SCMI protocol module.
+ *
+ * \note A return value of FWK_SUCCESS indicates only that no internal error
+ * was encountered, not that the SCMI command has returned a successful
+ * result to the SCMI agent. In the case where the return value indicates
+ * an internal failure, the SCMI command is expected to return the status
+ * code SCMI_GENERIC_ERROR per the specification.
+ *
+ * \param protocol_id Identifier of the protocol module.
+ * \param service_id Identifer of the SCMI service which received the message.
+ * \param payload Pointer to the message payload.
+ * \param payload_size Size in number of bytes of the message payload.
+ * \param message_id Identifier of the message to be handled by the protocol
+ * handler.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard error codes for implementation-defined errors.
+ *
+ */
+typedef int mod_scmi_message_handler_t(fwk_id_t protocol_id,
+ fwk_id_t service_id, const uint32_t *payload, size_t payload_size,
+ unsigned int message_id);
+
+/*!
+ * \brief SCMI module to SCMI protocol module API.
+ */
+struct mod_scmi_to_protocol_api {
+ /*!
+ * \brief Get the SCMI protocol identifier of the SCMI protocol implemented
+ * by the SCMI protocol module implementing this API.
+ *
+ * \param protocol_id Identifier of the protocol module.
+ * \param scmi_protocol_id SCMI protocol identifier.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ *
+ */
+ int (*get_scmi_protocol_id)(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id);
+
+ /*! Protocol message handler. */
+ mod_scmi_message_handler_t *message_handler;
+};
+
+/*!
+ * \brief SCMI protocol module to SCMI module API.
+ */
+struct mod_scmi_from_protocol_api {
+ /*!
+ * \brief Get the identifier of the agent associated with a service
+ *
+ * \param service_id Identifier of the service.
+ * \param agent_id Pointer to storage for the agent identifier.
+ *
+ * \retval FWK_SUCCESS The agent identifier was returned.
+ * \retval FWK_E_PARAM The service identifier is not valid.
+ * \retval FWK_E_PARAM The parameter 'agent_id' is equal to NULL.
+ * \retval FWK_E_STATE The service is not initialized.
+ */
+ int (*get_agent_id)(fwk_id_t service_id, unsigned int *agent_id);
+
+ /*!
+ * \brief Get the type of the agent given its identifier.
+ *
+ * \details This API can be used by SCMI protocols to check the validity
+ * of an agent identifier.
+ *
+ * \param agent_id Identifier of the agent.
+ * \param agent_type Pointer to storage for the agent type.
+ *
+ * \retval FWK_SUCCESS The agent identifier was returned.
+ * \retval FWK_E_PARAM The agent identifier is not valid.
+ * \retval FWK_E_PARAM The parameter 'agent_type' is equal to NULL.
+ * \retval FWK_E_STATE The component is not initialized.
+ */
+ int (*get_agent_type)(uint32_t agent_id,
+ enum scmi_agent_type *agent_type);
+
+ /*!
+ * \brief Get the maximum permitted payload size of a channel associated
+ * with a service.
+ *
+ * \param service_id Service identifier.
+ * \param size Pointer to storage for the maximum payload size in bytes.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The service_id parameter is invalid.
+ * \retval FWK_E_PARAM The size parameter is NULL.
+ * \retval FWK_E_STATE The service is not initialized.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*get_max_payload_size)(fwk_id_t service_id, size_t *size);
+
+ /*!
+ * \brief Write part of a payload through a service.
+ *
+ * \param service_id Service identifier.
+ * \param offset Offset to begin writing at.
+ * \param payload Payload data to write.
+ * \param size Size of the payload data.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \retval FWK_E_PARAM The payload parameter is NULL.
+ * \retval FWK_E_PARAM The offset and size provided are not within the
+ * bounds of the payload area.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*write_payload)(fwk_id_t service_id, size_t offset,
+ const void *payload, size_t size);
+
+ /*!
+ * \brief Respond to an SCMI message on a service.
+ *
+ * \param service_id Service identifier.
+ * \param payload Payload data to write, or NULL if a payload has already
+ * been written.
+ * \param size Size of the payload.
+ */
+ void (*respond)(fwk_id_t service_id, const void *payload, size_t size);
+};
+
+
+/*!
+ * \brief Identify if an SCMI entity is the communications master for a given
+ * channel type.
+ *
+ * \param type Channel type.
+ * \param role Entity role.
+ *
+ * \retval true The entity is the communications master of the channel.
+ * \retval false The entity is the communications slave of the channel.
+ */
+static inline bool mod_scmi_is_master(enum scmi_channel_type type,
+ enum scmi_role role)
+{
+ return (int)type == (int)role;
+}
+
+/*!
+ * \brief Identify if an SCMI entity is the communications slave for a given
+ * channel type.
+ *
+ * \param type Channel type.
+ * \param role Entity role.
+ *
+ * \retval true The entity is the communications slave of the channel.
+ * \retval false The entity is the communications master of the channel.
+ */
+static inline bool mod_scmi_is_slave(enum scmi_channel_type type,
+ enum scmi_role role)
+{
+ return (int)type != (int)role;
+}
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SCMI_H */
diff --git a/module/scmi/src/Makefile b/module/scmi/src/Makefile
new file mode 100644
index 00000000..ae55ccc9
--- /dev/null
+++ b/module/scmi/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI
+BS_LIB_SOURCES := mod_scmi.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi/src/mod_scmi.c b/module/scmi/src/mod_scmi.c
new file mode 100644
index 00000000..7a86d2e3
--- /dev/null
+++ b/module/scmi/src/mod_scmi.c
@@ -0,0 +1,794 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) support.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_multi_thread.h>
+#include <internal/mod_scmi.h>
+#include <internal/scmi.h>
+#include <internal/scmi_base.h>
+#include <mod_log.h>
+
+struct scmi_protocol {
+ /* SCMI protocol message handler */
+ mod_scmi_message_handler_t *message_handler;
+
+ /* SCMI protocol framework identifier */
+ fwk_id_t id;
+};
+
+struct scmi_ctx {
+ /* SCMI module configuration data */
+ struct mod_scmi_config *config;
+
+ /* Table of bound protocols */
+ struct scmi_protocol *protocol_table;
+
+ /* Number of bound protocols */
+ unsigned int protocol_count;
+
+ /*
+ * SCMI protocol identifier to the index of the entry in protocol_table[]
+ * dedicated to the protocol.
+ */
+ uint8_t scmi_protocol_id_to_idx[SCMI_PROTOCOL_ID_MAX + 1];
+
+ /* Table of service contexts */
+ struct scmi_service_ctx *service_ctx_table;
+
+ /* Log module API */
+ struct mod_log_api *log_api;
+};
+
+/*
+ * Entry zero (0) of the protocol table 'protocol_table' is not used, as index
+ * 0 is the index of the unused entries of the 'scmi_protocol_id_to_idx[]'
+ * table. Entry one (1) is reserved for the base protocol implemented in this
+ * file.
+ */
+#define PROTOCOL_TABLE_BASE_PROTOCOL_IDX 1
+#define PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT 2
+
+static int scmi_base_protocol_version_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_protocol_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_discover_vendor_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_discover_sub_vendor_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_discover_implementation_version_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_discover_list_protocols_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_base_discover_agent_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+
+static int (* const base_handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] =
+ scmi_base_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] =
+ scmi_base_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_base_protocol_message_attributes_handler,
+ [SCMI_BASE_DISCOVER_VENDOR] =
+ scmi_base_discover_vendor_handler,
+ [SCMI_BASE_DISCOVER_SUB_VENDOR] =
+ scmi_base_discover_sub_vendor_handler,
+ [SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] =
+ scmi_base_discover_implementation_version_handler,
+ [SCMI_BASE_DISCOVER_LIST_PROTOCOLS] =
+ scmi_base_discover_list_protocols_handler,
+ [SCMI_BASE_DISCOVER_AGENT] =
+ scmi_base_discover_agent_handler,
+};
+
+static const unsigned int base_payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_BASE_DISCOVER_VENDOR] = 0,
+ [SCMI_BASE_DISCOVER_SUB_VENDOR] = 0,
+ [SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION] = 0,
+ [SCMI_BASE_DISCOVER_LIST_PROTOCOLS] =
+ sizeof(struct scmi_base_discover_list_protocols_a2p),
+ [SCMI_BASE_DISCOVER_AGENT] =
+ sizeof(struct scmi_base_discover_agent_a2p),
+};
+
+static const char * const default_agent_names[] = {
+ [SCMI_AGENT_TYPE_PSCI] = "PSCI",
+ [SCMI_AGENT_TYPE_MANAGEMENT] = "MANAGEMENT",
+ [SCMI_AGENT_TYPE_OSPM] = "OSPM",
+ [SCMI_AGENT_TYPE_OTHER] = "OTHER",
+};
+
+static struct scmi_ctx scmi_ctx;
+
+/*
+ * Utility functions
+ */
+static uint16_t read_message_id(uint32_t message_header)
+{
+ return (message_header & SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) >>
+ SCMI_MESSAGE_HEADER_MESSAGE_ID_POS;
+}
+
+static uint8_t read_protocol_id(uint32_t message_header)
+{
+ return (message_header & SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) >>
+ SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS;
+}
+
+/*
+ * Transport entity -> SCMI module
+ */
+
+static int signal_message(fwk_id_t service_id)
+{
+ int32_t status;
+ struct fwk_event event = { };
+
+ status = fwk_module_check_call(service_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ event.source_id = FWK_ID_MODULE(FWK_MODULE_IDX_SCMI);
+ event.target_id = service_id;
+ event.id = FWK_ID_EVENT(FWK_MODULE_IDX_SCMI, 0);
+
+ return fwk_thread_put_event(&event);
+}
+
+static const struct mod_scmi_from_transport_api mod_scmi_from_transport_api = {
+ .signal_message = signal_message
+};
+
+/*
+ * SCMI protocol module -> SCMI module interface
+ */
+
+static int get_agent_id(fwk_id_t service_id, unsigned int *agent_id)
+{
+ int status;
+ struct scmi_service_ctx *ctx;
+
+ status = fwk_module_check_call(service_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (agent_id == NULL)
+ return FWK_E_PARAM;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
+
+ *agent_id = ctx->config->scmi_agent_id;
+
+ return FWK_SUCCESS;
+}
+
+static int get_agent_type(uint32_t scmi_agent_id,
+ enum scmi_agent_type *agent_type)
+{
+ if ((agent_type == NULL) ||
+ (scmi_agent_id > scmi_ctx.config->agent_count) ||
+ (scmi_agent_id == SCMI_PLATFORM_ID))
+ return FWK_E_PARAM;
+
+ *agent_type = scmi_ctx.config->agent_table[scmi_agent_id].type;
+
+ return FWK_SUCCESS;
+}
+
+static int get_max_payload_size(fwk_id_t service_id, size_t *size)
+{
+ int status;
+ struct scmi_service_ctx *ctx;
+
+ status = fwk_module_check_call(service_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (size == NULL)
+ return FWK_E_PARAM;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
+
+ return ctx->transport_api->get_max_payload_size(ctx->transport_id, size);
+}
+
+static int write_payload(fwk_id_t service_id, size_t offset,
+ const void *payload, size_t size)
+{
+ int status;
+ const struct scmi_service_ctx *ctx;
+
+ status = fwk_module_check_call(service_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
+
+ return ctx->transport_api->write_payload(ctx->transport_id,
+ offset, payload, size);
+}
+
+static void respond(fwk_id_t service_id, const void *payload, size_t size)
+{
+ int status;
+ const struct scmi_service_ctx *ctx;
+
+ status = fwk_module_check_call(service_id);
+ if (status != FWK_SUCCESS)
+ return;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
+
+ /*
+ * Print to the error log if the message was not successfully processed.
+ * We assume here that the first payload entry of the command response
+ * holds an SCMI status code. This is the case for all the SCMI commands
+ * specified so far, but it is not explicitly stated (yet) in the
+ * specification it should be like that for all commands.
+ */
+ if ((payload != NULL) && (*((int32_t *)payload) < SCMI_SUCCESS)) {
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Protocol 0x%x, message_id 0x%x returned with error %d\n",
+ ctx->scmi_protocol_id, ctx->scmi_message_id, *((int *)payload));
+ }
+
+ status = ctx->respond(ctx->transport_id, payload, size);
+ if (status != FWK_SUCCESS)
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Failed to send response (%e)\n", status);
+}
+
+static const struct mod_scmi_from_protocol_api mod_scmi_from_protocol_api = {
+ .get_agent_id = get_agent_id,
+ .get_agent_type = get_agent_type,
+ .get_max_payload_size = get_max_payload_size,
+ .write_payload = write_payload,
+ .respond = respond,
+};
+
+/*
+ * Base protocol implementation
+ */
+/*
+ * Base Protocol - PROTOCOL_VERSION
+ */
+static int scmi_base_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_BASE,
+ };
+
+ respond(service_id, &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Base Protocol - PROTOCOL_ATTRIBUTES
+ */
+static int scmi_base_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ return_values.attributes =
+ SCMI_BASE_PROTOCOL_ATTRIBUTES(scmi_ctx.protocol_count,
+ scmi_ctx.config->agent_count);
+
+ respond(service_id, &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Base Protocol - PROTOCOL_MESSAGE_ATTRIBUTES
+ */
+static int scmi_base_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values = {
+ .status = SCMI_NOT_FOUND,
+ };
+
+ parameters = (struct scmi_protocol_message_attributes_a2p *)payload;
+ message_id = parameters->message_id;
+
+ if ((message_id < FWK_ARRAY_SIZE(base_handler_table)) &&
+ (base_handler_table[message_id] != NULL))
+ return_values.status = SCMI_SUCCESS;
+
+ /* For this protocol, all commands have an attributes value of 0, which
+ * has already been set by the initialization of "return_values".
+ */
+
+ respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Base Protocol - BASE_DISCOVER_VENDOR
+ */
+static int scmi_base_discover_vendor_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_base_discover_vendor_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ if (scmi_ctx.config->vendor_identifier != NULL)
+ strncpy(return_values.vendor_identifier,
+ scmi_ctx.config->vendor_identifier,
+ sizeof(return_values.vendor_identifier) - 1);
+
+ respond(service_id, &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * BASE_DISCOVER_SUB_VENDOR
+ */
+static int scmi_base_discover_sub_vendor_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_base_discover_sub_vendor_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ if (scmi_ctx.config->sub_vendor_identifier != NULL)
+ strncpy(return_values.sub_vendor_identifier,
+ scmi_ctx.config->sub_vendor_identifier,
+ sizeof(return_values.sub_vendor_identifier) - 1);
+
+ respond(service_id, &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * BASE_DISCOVER_IMPLEMENTATION_VERSION
+ */
+static int scmi_base_discover_implementation_version_handler(
+ fwk_id_t service_id, const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = FWK_BUILD_VERSION
+ };
+
+ respond(service_id, &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * BASE_DISCOVER_LIST_PROTOCOLS
+ */
+static int scmi_base_discover_list_protocols_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct scmi_base_discover_list_protocols_a2p *parameters;
+ struct scmi_base_discover_list_protocols_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ .num_protocols = 0,
+ };
+ unsigned int skip;
+ size_t max_payload_size;
+ size_t payload_size;
+ size_t entry_count;
+ size_t protocol_count, protocol_count_max;
+ unsigned int index;
+ uint8_t protocol_id;
+
+ status = get_max_payload_size(service_id, &max_payload_size);
+ if (status != FWK_SUCCESS)
+ goto error;
+
+ if (max_payload_size <
+ (sizeof(struct scmi_base_discover_list_protocols_p2a)
+ + sizeof(return_values.protocols[0]))) {
+ status = FWK_E_SIZE;
+ goto error;
+ }
+
+ entry_count = max_payload_size -
+ sizeof(struct scmi_base_discover_list_protocols_p2a);
+
+ parameters = (const struct scmi_base_discover_list_protocols_a2p *)payload;
+ skip = parameters->skip;
+
+ if (skip > scmi_ctx.protocol_count) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto error;
+ }
+
+ protocol_count_max = (scmi_ctx.protocol_count < (skip + entry_count)) ?
+ scmi_ctx.protocol_count : (skip + entry_count);
+
+ for (index = 0, protocol_count = 0,
+ payload_size = sizeof(struct scmi_base_discover_list_protocols_p2a);
+ (index < FWK_ARRAY_SIZE(scmi_ctx.scmi_protocol_id_to_idx)) &&
+ (protocol_count < protocol_count_max);
+ index++) {
+ if ((scmi_ctx.scmi_protocol_id_to_idx[index] == 0) ||
+ (index == SCMI_PROTOCOL_ID_BASE))
+ continue;
+
+ protocol_count++;
+ if (protocol_count <= skip)
+ continue;
+
+ protocol_id = index;
+ status = write_payload(service_id, payload_size, &protocol_id,
+ sizeof(protocol_id));
+ if (status != FWK_SUCCESS)
+ goto error;
+ payload_size += sizeof(protocol_id);
+ }
+
+ return_values.status = SCMI_SUCCESS;
+ return_values.num_protocols = protocol_count_max - skip;
+
+ status = write_payload(service_id, 0,
+ &return_values, sizeof(return_values));
+ if (status != FWK_SUCCESS)
+ goto error;
+
+ payload_size = FWK_ALIGN_NEXT(payload_size, sizeof(uint32_t));
+
+ respond(service_id, NULL, payload_size);
+
+ return status;
+
+error:
+ respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * BASE_DISCOVER_AGENT
+ */
+static int scmi_base_discover_agent_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ const struct scmi_base_discover_agent_a2p *parameters;
+ struct scmi_base_discover_agent_p2a return_values = {
+ .status = SCMI_NOT_FOUND,
+ };
+ const struct mod_scmi_agent *agent;
+
+ parameters = (const struct scmi_base_discover_agent_a2p *)payload;
+
+ if (parameters->agent_id > scmi_ctx.config->agent_count)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+ if (parameters->agent_id == SCMI_PLATFORM_ID) {
+ strcpy(return_values.name, "platform");
+ goto exit;
+ }
+
+ agent = &scmi_ctx.config->agent_table[parameters->agent_id];
+
+ strncpy(return_values.name,
+ (agent->name != NULL) ? agent->name :
+ default_agent_names[agent->type],
+ sizeof(return_values.name) - 1);
+
+exit:
+ respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_base_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
+ const uint32_t *payload, size_t payload_size, unsigned int message_id)
+{
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(base_handler_table) ==
+ FWK_ARRAY_SIZE(base_payload_size_table),
+ "[SCMI] Base protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ if (message_id >= FWK_ARRAY_SIZE(base_handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != base_payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return base_handler_table[message_id](service_id, payload);
+
+error:
+ respond(service_id, &return_value, sizeof(return_value));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Framework handlers
+ */
+
+static int scmi_init(fwk_id_t module_id, unsigned int service_count,
+ const void *data)
+{
+ struct mod_scmi_config *config = (struct mod_scmi_config *)data;
+ unsigned int agent_idx;
+ const struct mod_scmi_agent *agent;
+
+ if (config == NULL)
+ return FWK_E_PARAM;
+
+ if ((config->agent_count == 0) ||
+ (config->agent_count > SCMI_AGENT_ID_MAX))
+ return FWK_E_PARAM;
+
+ /*
+ * Loop over the agent descriptors. The SCMI_PLATFORM_ID(0) entry of
+ * the table - that would refer to the platform - is ignored.
+ */
+ for (agent_idx = SCMI_PLATFORM_ID + 1;
+ agent_idx <= config->agent_count; agent_idx++) {
+ agent = &config->agent_table[agent_idx];
+ if (agent->type >= SCMI_AGENT_TYPE_COUNT)
+ return FWK_E_PARAM;
+ }
+
+ scmi_ctx.protocol_table = fwk_mm_calloc(
+ config->protocol_count_max + PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT,
+ sizeof(scmi_ctx.protocol_table[0]));
+ if (scmi_ctx.protocol_table == NULL)
+ return FWK_E_NOMEM;
+
+ scmi_ctx.service_ctx_table = fwk_mm_calloc(
+ service_count, sizeof(scmi_ctx.service_ctx_table[0]));
+ if (scmi_ctx.service_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ scmi_ctx.protocol_table[PROTOCOL_TABLE_BASE_PROTOCOL_IDX].message_handler =
+ scmi_base_message_handler;
+ scmi_ctx.scmi_protocol_id_to_idx[SCMI_PROTOCOL_ID_BASE] =
+ PROTOCOL_TABLE_BASE_PROTOCOL_IDX;
+
+ scmi_ctx.config = config;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_service_init(fwk_id_t service_id, unsigned int unused,
+ const void *data)
+{
+ const struct mod_scmi_service_config *config =
+ (struct mod_scmi_service_config *)data;
+ struct scmi_service_ctx *ctx;
+
+ if ((config->scmi_agent_id == SCMI_PLATFORM_ID) ||
+ (config->scmi_agent_id > scmi_ctx.config->agent_count))
+ return FWK_E_PARAM;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
+ ctx->config = config;
+
+ return fwk_thread_create(service_id);
+}
+
+static int scmi_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct scmi_service_ctx *ctx;
+ const struct mod_scmi_to_transport_api *transport_api;
+ unsigned int protocol_idx;
+ struct scmi_protocol *protocol;
+ struct mod_scmi_to_protocol_api *protocol_api;
+ uint8_t scmi_protocol_id;
+
+ if (round == 0) {
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &scmi_ctx.log_api);
+ }
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(id)];
+ status = fwk_module_bind(ctx->config->transport_id,
+ ctx->config->transport_api_id, &transport_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if ((transport_api->get_secure == NULL) ||
+ (transport_api->get_max_payload_size == NULL) ||
+ (transport_api->get_message_header == NULL) ||
+ (transport_api->get_payload == NULL) ||
+ (transport_api->write_payload == NULL) ||
+ (transport_api->respond == NULL))
+ return FWK_E_DATA;
+
+ ctx->transport_api = transport_api;
+ ctx->transport_id = ctx->config->transport_id;
+ ctx->respond = transport_api->respond;
+
+ return FWK_SUCCESS;
+ }
+
+ if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ for (protocol_idx = 0;
+ protocol_idx < scmi_ctx.protocol_count; protocol_idx++) {
+ protocol = &scmi_ctx.protocol_table[
+ PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + protocol_idx];
+
+ status = fwk_module_bind(protocol->id,
+ FWK_ID_API(fwk_id_get_module_idx(protocol->id), 0), &protocol_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if ((protocol_api->get_scmi_protocol_id == NULL) ||
+ (protocol_api->message_handler == NULL))
+ return FWK_E_DATA;
+ status = protocol_api->get_scmi_protocol_id(protocol->id,
+ &scmi_protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (scmi_ctx.scmi_protocol_id_to_idx[scmi_protocol_id] != 0)
+ return FWK_E_STATE;
+
+ scmi_ctx.scmi_protocol_id_to_idx[scmi_protocol_id] =
+ protocol_idx + PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT;
+ protocol->message_handler = protocol_api->message_handler;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ unsigned int api_idx;
+ struct scmi_service_ctx *ctx;
+
+ api_idx = fwk_id_get_api_idx(api_id);
+
+ switch (api_idx) {
+ case MOD_SCMI_API_IDX_PROTOCOL:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_SUPPORT;
+
+ if (scmi_ctx.protocol_count >= scmi_ctx.config->protocol_count_max)
+ return FWK_E_NOMEM;
+
+ scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT +
+ scmi_ctx.protocol_count++].id = source_id;
+ *api = &mod_scmi_from_protocol_api;
+ break;
+
+ case MOD_SCMI_API_IDX_TRANSPORT:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_SUPPORT;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(target_id)];
+ if (!fwk_id_is_equal(source_id, ctx->transport_id))
+ return FWK_E_ACCESS;
+
+ *api = &mod_scmi_from_transport_api;
+ break;
+
+ default:
+ return FWK_E_SUPPORT;
+ };
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_process_event(const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ int status;
+ struct scmi_service_ctx *ctx;
+ const struct mod_scmi_to_transport_api *transport_api;
+ fwk_id_t transport_id;
+ uint32_t message_header;
+ const void *payload;
+ size_t payload_size;
+ unsigned int protocol_idx;
+ struct scmi_protocol *protocol;
+
+ ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(event->target_id)];
+ transport_api = ctx->transport_api;
+ transport_id = ctx->transport_id;
+
+ status = transport_api->get_message_header(transport_id, &message_header);
+ if (status != FWK_SUCCESS) {
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Unable to read message header\n");
+ return status;
+ }
+
+ status = transport_api->get_payload(transport_id, &payload, &payload_size);
+ if (status != FWK_SUCCESS) {
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Unable to read message payload\n");
+ return status;
+ }
+
+ ctx->scmi_protocol_id = read_protocol_id(message_header);
+ ctx->scmi_message_id = read_message_id(message_header);
+
+ protocol_idx = scmi_ctx.scmi_protocol_id_to_idx[ctx->scmi_protocol_id];
+
+ if (protocol_idx == 0) {
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Protocol 0x%x not supported\n", ctx->scmi_protocol_id);
+ ctx->respond(transport_id, &(int32_t) { SCMI_NOT_SUPPORTED },
+ sizeof(int32_t));
+ return FWK_SUCCESS;
+ }
+
+ protocol = &scmi_ctx.protocol_table[protocol_idx];
+ status = protocol->message_handler(protocol->id, event->target_id,
+ payload, payload_size, ctx->scmi_message_id);
+
+ if (status != FWK_SUCCESS) {
+ scmi_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI] Protocol 0x%x handler error (%e), message_id = 0x%x\n",
+ ctx->scmi_protocol_id, status, ctx->scmi_message_id);
+ }
+
+ return FWK_SUCCESS;
+}
+
+/* SCMI module definition */
+const struct fwk_module module_scmi = {
+ .name = "SCMI",
+ .api_count = MOD_SCMI_API_IDX_COUNT,
+ .event_count = 1,
+ .type = FWK_MODULE_TYPE_SERVICE,
+ .init = scmi_init,
+ .element_init = scmi_service_init,
+ .bind = scmi_bind,
+ .process_bind_request = scmi_process_bind_request,
+ .process_event = scmi_process_event
+};
diff --git a/module/scmi_apcore/doc/scmi_apcore.md b/module/scmi_apcore/doc/scmi_apcore.md
new file mode 100644
index 00000000..9af53a93
--- /dev/null
+++ b/module/scmi_apcore/doc/scmi_apcore.md
@@ -0,0 +1,153 @@
+SCMI AP Core Configuration Protocol v1.0
+========================================
+
+Protocol Overview {#scmi_apcore_protocol_overview}
+=================
+
+This protocol is an extension of the [Arm System Control and Management
+Interface (SCMI)]
+(http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html).
+
+The goal of this protocol is for the SCP to provide an interface to the AP
+firmware that supports changing the configuration of one or more AP cores.
+For example, one of the supported commands allows programming of the AP core
+address and is used when AP firmware is unable to program the reset address
+directly.
+
+The protocol identifier used for this protocol (0x90) is within the range that
+the SCMI specification provides for platform-specific extensions (0x80 - 0xFF).
+For further information on protocol identifiers refer to section 4.1.2 of the
+SCMI specification.
+
+Protocol Commands {#scmi_apcore_protocol}
+=================
+
+Protocol Version {#scmi_apcore_protocol_version}
+----------------
+
+On success, this command returns the version of the protocol. For this version
+of the specification the return value must be 0x10000, which corresponds to 1.0.
+
+message_id: 0x0<br>
+protocol_id: 0x90
+
+This command is mandatory.
+
+Return values:
+* int32 status
+ * See section 4.1.4 of the SCMI specification for status code
+ definitions
+* uint32 version
+ * For this version of the specification the return value must be 0x10000
+
+Protocol Attributes {#scmi_apcore_protocol_attributes}
+-------------------
+
+This command returns the implementation details associated with this protocol.
+
+message_id: 0x1<br>
+protocol_id: 0x90
+
+This command is mandatory.
+
+Return values:
+* int32 status
+ * See section 4.1.4 of the SCMI specification for status code
+ definitions
+* uint32 attributes
+ * Bits [31:1] Reserved, must be zero.
+ * Bit [0] If set to 1, the platform supports 64-bit reset addresses. If set
+ to 0, the platform supports 32-bit reset addresses.
+
+Protocol Message Attributes {#scmi_apcore_protocol_message__attributes}
+---------------------------
+
+On success, this command returns the implementation details associated with a
+specific message in this protocol. In addition to the standard status codes
+described in section 4.1.4 of the SCMI specification, the command can return the
+error NOT_FOUND if the message identified by message_id is not provided by
+the implementation.
+
+message_id: 0x2<br>
+protocol_id: 0x90
+
+This command is mandatory.
+
+Parameters:
+* uint32 message_id
+ * message_id of the message.
+
+Return values:
+* int32 status
+ * See section 4.1.4 of the SCMI specification for status code
+ definitions.
+* uint32 attributes
+ * Flags associated with a specific command in the protocol. For all commands
+ in this protocol this parameter has a value of 0.
+
+Core Reset Address Set {#scmi_apcore_protocol_set_address}
+----------------------
+
+Set the application core reset address. The address applies to all cores.
+
+In some platforms only the SCP is capable of programming the application core
+reset address. This command allows the SCP to carry out the programming on
+behalf of AP firmware. Such a feature is supported in the [Arm Trusted Firmware]
+(https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/reset-design.rst)
+
+message_id: 0x3<br>
+protocol_id: 0x90
+
+This command is optional.
+
+Parameters:
+* uint32 Reset address (lower word)
+ * Bit[31:0] AP core reset address (low)
+* uint32 Reset address (higher word)
+ * Bit[31:0] AP core reset address (high)
+ * On platforms that support only 32-bit addresses, only the lower word is
+ used - this higher word must be zero and the address must be 4-byte
+ aligned. For platforms supporting 64-bit addresses both words may be used
+ and the address must be 8-byte aligned.
+* uint32 attributes
+ * Bit[31:1] Reserved, must be zero.
+ * Bit[0] Lock. When set to 1, the platform will deny any further attempts to
+ change the reset address.
+
+Return values:
+* int32 status
+ * SUCCESS if the reset address was set successfully.
+ * INVALID_PARAMETERS:
+ * Reset address alignment is invalid.
+ * Platform supports only 32-bit addresses and the reset address received
+ is larger than 32-bits.
+ * DENIED: The reset address is locked and changes are not permitted.
+ * DENIED: The calling agent is not permitted to modify the reset address.
+ * See section 4.1.4 of the SCMI specification for status code
+ definitions.
+
+Core Reset Address Get {#scmi_apcore_protocol_get_address}
+----------------------
+
+Get the application core reset address. The address applies to all cores.
+
+message_id: 0x4<br>
+protocol_id: 0x90
+
+This command is optional.
+
+Return values:
+* int32 status
+ * SUCCESS if the reset address was retrieved successfully.
+ * DENIED: The calling agent is not permitted to retrieve the reset address.
+ * See section 4.1.4 of the SCMI specification for status code
+ definitions.
+* uint32 Reset address (lower word)
+ * Bit[31:0] AP core reset address (low)
+* uint32 Reset address (higher word)
+ * Bit[31:0] AP core reset address (high)
+ * On platforms that support only 32-bit addresses, only the lower word is
+ used and this higher word must be zero.
+* uint32 attributes
+ * Bit[31:1] Reserved, must be zero.
+ * Bit[0] Lock. When set to 1, changing the reset address is not permitted.
diff --git a/module/scmi_apcore/include/internal/scmi_apcore.h b/module/scmi_apcore/include/internal/scmi_apcore.h
new file mode 100644
index 00000000..d55e676b
--- /dev/null
+++ b/module/scmi_apcore/include/internal/scmi_apcore.h
@@ -0,0 +1,71 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Core Configuration Protocol Support
+ */
+
+#ifndef SCMI_APCORE_H
+#define SCMI_APCORE_H
+
+#include <stdint.h>
+
+#define SCMI_PROTOCOL_ID_APCORE UINT32_C(0x90)
+#define SCMI_PROTOCOL_VERSION_APCORE UINT32_C(0x10000)
+
+/*
+ * Identifiers of the SCMI Core Configuration Protocol commands
+ */
+enum scmi_apcore_command_id {
+ SCMI_APCORE_RESET_ADDRESS_SET = 0x3,
+ SCMI_APCORE_RESET_ADDRESS_GET = 0x4,
+};
+
+/*
+ * Protocol Attributes
+ */
+
+#define SCMI_APCORE_PROTOCOL_ATTRIBUTES_64BIT_POS 0
+
+#define SCMI_APCORE_PROTOCOL_ATTRIBUTES_64BIT_MASK \
+ (UINT32_C(0x1) << SCMI_APCORE_PROTOCOL_ATTRIBUTES_64BIT_POS)
+
+/*
+ * Reset Address Set
+ */
+
+#define SCMI_APCORE_RESET_ADDRESS_SET_LOCK_POS 0
+
+#define SCMI_APCORE_RESET_ADDRESS_SET_LOCK_MASK \
+ (UINT32_C(0x1) << SCMI_APCORE_RESET_ADDRESS_SET_LOCK_POS)
+
+struct __attribute((packed)) scmi_apcore_reset_address_set_a2p {
+ uint32_t reset_address_low;
+ uint32_t reset_address_high;
+ uint32_t attributes;
+};
+
+struct __attribute((packed)) scmi_apcore_reset_address_set_p2a {
+ int32_t status;
+};
+
+/*
+ * Reset Address Get
+ */
+
+#define SCMI_APCORE_RESET_ADDRESS_GET_LOCK_POS 0
+
+#define SCMI_APCORE_RESET_ADDRESS_GET_LOCK_MASK \
+ (UINT32_C(0x1) << SCMI_APCORE_RESET_ADDRESS_GET_LOCK_POS)
+
+struct __attribute((packed)) scmi_apcore_reset_address_get_p2a {
+ int32_t status;
+ uint32_t reset_address_low;
+ uint32_t reset_address_high;
+ uint32_t attributes;
+};
+
+#endif /* SCMI_APCORE_H */
diff --git a/module/scmi_apcore/include/mod_scmi_apcore.h b/module/scmi_apcore/include/mod_scmi_apcore.h
new file mode 100644
index 00000000..82fe61d6
--- /dev/null
+++ b/module/scmi_apcore/include/mod_scmi_apcore.h
@@ -0,0 +1,75 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Core Configuration Protocol Support.
+ */
+
+#ifndef MOD_SCMI_APCORE_H
+#define MOD_SCMI_APCORE_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*!
+ * \ingroup GroupModules Modules
+ * \defgroup GroupSCMI_APCORE SCMI Core Configuration Protocol
+ * \{
+ */
+
+/*!
+ * \brief Platform reset register widths.
+ */
+enum mod_scmi_apcore_register_width {
+ /*! Single-word, 32-bit reset address registers supported */
+ MOD_SCMI_APCORE_REG_WIDTH_32,
+
+ /*! Double-word, 64-bit reset address registers supported */
+ MOD_SCMI_APCORE_REG_WIDTH_64,
+
+ /*! Number of valid register widths */
+ MOD_SCMI_APCORE_REG_WIDTH_COUNT,
+};
+
+/*!
+ * \brief Reset register group.
+ *
+ * \details Describes a set of reset registers that are contiguous in memory.
+ */
+struct mod_scmi_apcore_reset_register_group {
+ /*! Address of the first register in the group */
+ uintptr_t base_register;
+
+ /*! The number of registers in the group */
+ size_t register_count;
+};
+
+/*!
+ * \brief Module configuration.
+ */
+struct mod_scmi_apcore_config {
+ /*!
+ * \brief Pointer to the table of \ref mod_scmi_apcore_reset_register_group
+ * structures that define the reset registers within the platform.
+ */
+ const struct mod_scmi_apcore_reset_register_group
+ *reset_register_group_table;
+
+ /*!
+ * \brief Number of \ref mod_scmi_apcore_reset_register_group structures in
+ * \ref reset_register_group_table.
+ */
+ size_t reset_register_group_count;
+
+ /*! Width of the reset address supported by the platform */
+ enum mod_scmi_apcore_register_width reset_register_width;
+};
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_SCMI_APCORE_H */
diff --git a/module/scmi_apcore/src/Makefile b/module/scmi_apcore/src/Makefile
new file mode 100644
index 00000000..c5cf7079
--- /dev/null
+++ b/module/scmi_apcore/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI Core Configuration Protocol
+BS_LIB_SOURCES := mod_scmi_apcore.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_apcore/src/mod_scmi_apcore.c b/module/scmi_apcore/src/mod_scmi_apcore.c
new file mode 100644
index 00000000..afedf8fc
--- /dev/null
+++ b/module/scmi_apcore/src/mod_scmi_apcore.c
@@ -0,0 +1,432 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Core Configuration Protocol Support.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_apcore.h>
+#include <mod_scmi.h>
+#include <mod_scmi_apcore.h>
+
+struct scmi_apcore_ctx {
+ /* Module Configuration */
+ const struct mod_scmi_apcore_config *config;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /*
+ * Tracks whether an agent has requested that the configuration be locked.
+ * \c true if the configuration is locked and the reset address of the CPUs
+ * can no longer be altered, \c false otherwise.
+ */
+ bool locked;
+};
+
+static int scmi_apcore_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_apcore_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_apcore_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_apcore_reset_address_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_apcore_reset_address_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables.
+ */
+static struct scmi_apcore_ctx scmi_apcore_ctx;
+
+static int (* const handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] = scmi_apcore_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] = scmi_apcore_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_apcore_protocol_message_attributes_handler,
+ [SCMI_APCORE_RESET_ADDRESS_SET] = scmi_apcore_reset_address_set_handler,
+ [SCMI_APCORE_RESET_ADDRESS_GET] = scmi_apcore_reset_address_get_handler,
+};
+
+static const unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_APCORE_RESET_ADDRESS_SET] =
+ sizeof(struct scmi_apcore_reset_address_set_a2p),
+ [SCMI_APCORE_RESET_ADDRESS_GET] = 0,
+};
+
+/*
+ * Static, Helper Functions
+ */
+static int set_reset_address(uint32_t address_low, uint32_t address_high)
+{
+ uint64_t address_composite;
+ unsigned int grp_idx;
+ unsigned int reg_idx;
+ const struct mod_scmi_apcore_reset_register_group *reg_group;
+ uintptr_t reset_reg;
+
+ address_composite = ((uint64_t)address_high << 32) | address_low;
+
+ /* Iterate over the reset register group structures */
+ for (grp_idx = 0;
+ grp_idx < scmi_apcore_ctx.config->reset_register_group_count;
+ grp_idx++) {
+
+ reg_group =
+ &scmi_apcore_ctx.config->reset_register_group_table[grp_idx];
+ assert(reg_group->base_register != 0);
+
+ /* Begin with the first register in the group */
+ reset_reg = reg_group->base_register;
+
+ /* Program each reset vector register within the group */
+ for (reg_idx = 0; reg_idx < reg_group->register_count; reg_idx++) {
+ if (scmi_apcore_ctx.config->reset_register_width ==
+ MOD_SCMI_APCORE_REG_WIDTH_32) {
+ /* Treat the register as 32-bit */
+ *(uint32_t *)reset_reg = address_low;
+ reset_reg += sizeof(uint32_t);
+ } else {
+ /* Treat the register as 64-bit */
+ *(uint64_t *)reset_reg = address_composite;
+ reset_reg += sizeof(uint64_t);
+ }
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Version
+ */
+static int scmi_apcore_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_APCORE,
+ };
+
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id, &return_values, sizeof(return_values));
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Attributes
+ */
+static int scmi_apcore_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ if (scmi_apcore_ctx.config->reset_register_width ==
+ MOD_SCMI_APCORE_REG_WIDTH_64)
+ return_values.attributes |= SCMI_APCORE_PROTOCOL_ATTRIBUTES_64BIT_MASK;
+
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id,
+ &return_values,
+ sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Message Attributes
+ */
+static int scmi_apcore_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ size_t response_size;
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p *)
+ payload;
+ message_id = parameters->message_id;
+
+ if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
+ (handler_table[message_id] == NULL))
+ return_values.status = SCMI_NOT_FOUND;
+
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id, &return_values, response_size);
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Reset Address Set
+ */
+static int scmi_apcore_reset_address_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ unsigned int agent_id;
+ enum scmi_agent_type agent_type;
+ const struct scmi_apcore_reset_address_set_a2p *parameters;
+ struct scmi_apcore_reset_address_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_apcore_reset_address_set_a2p *)payload;
+
+ status = scmi_apcore_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_apcore_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Only the PSCI agent may set the reset address */
+ if (agent_type != SCMI_AGENT_TYPE_PSCI) {
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ /* An agent previously requested that the configuration be locked */
+ if (scmi_apcore_ctx.locked) {
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ /*
+ * Ensure that the platform has 64-bit reset vector registers if a reset
+ * address utilizing more that 32 bits has been provided.
+ */
+ if ((parameters->reset_address_high != 0) &&
+ (scmi_apcore_ctx.config->reset_register_width ==
+ MOD_SCMI_APCORE_REG_WIDTH_32)) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ /* Check for alignment */
+ if (scmi_apcore_ctx.config->reset_register_width ==
+ MOD_SCMI_APCORE_REG_WIDTH_32) {
+ if ((parameters->reset_address_low % 4) != 0) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+ } else if ((parameters->reset_address_low % 8) != 0) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ status = set_reset_address(
+ parameters->reset_address_low, parameters->reset_address_high);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+ /* Lock the configuration if requested */
+ if (parameters->attributes & SCMI_APCORE_RESET_ADDRESS_SET_LOCK_MASK)
+ scmi_apcore_ctx.locked = true;
+
+exit:
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id, &return_values, sizeof(return_values));
+ return status;
+}
+
+/*
+ * Reset Address Get
+ */
+static int scmi_apcore_reset_address_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ unsigned int agent_id;
+ const struct mod_scmi_apcore_reset_register_group *reg_group;
+ uint64_t reset_address;
+ enum scmi_agent_type agent_type;
+ struct scmi_apcore_reset_address_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ status = scmi_apcore_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_apcore_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Only the PSCI agent may get the current reset address */
+ if (agent_type != SCMI_AGENT_TYPE_PSCI) {
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ /* The reset address is common across all reset address registers */
+ reg_group = &scmi_apcore_ctx.config->reset_register_group_table[0];
+
+ if (scmi_apcore_ctx.config->reset_register_width ==
+ MOD_SCMI_APCORE_REG_WIDTH_32) {
+ reset_address = *(uint32_t *)reg_group->base_register;
+ return_values.reset_address_high = 0;
+ } else {
+ reset_address = *(uint64_t *)reg_group->base_register;
+ return_values.reset_address_high = (reset_address >> 32) & UINT32_MAX;
+ }
+
+ return_values.reset_address_low = (uint32_t)reset_address;
+
+ return_values.attributes |=
+ (scmi_apcore_ctx.locked << SCMI_APCORE_RESET_ADDRESS_GET_LOCK_POS);
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id, &return_values, sizeof(return_values));
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI AP Core Configuration module interface
+ */
+static int scmi_apcore_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_APCORE;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_apcore_message_handler(
+ fwk_id_t protocol_id,
+ fwk_id_t service_id,
+ const uint32_t *payload,
+ size_t payload_size,
+ unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Core configuration protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_apcore_ctx.scmi_api->respond(
+ service_id,
+ &return_value,
+ sizeof(return_value));
+
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_apcore_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_apcore_get_scmi_protocol_id,
+ .message_handler = scmi_apcore_message_handler
+};
+
+/*
+ * Framework handlers
+ */
+
+static int scmi_apcore_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ const struct mod_scmi_apcore_config *config =
+ (const struct mod_scmi_apcore_config *)data;
+
+ if (config == NULL)
+ return FWK_E_PARAM;
+ if (config->reset_register_group_table == NULL)
+ return FWK_E_PARAM;
+ if (config->reset_register_group_count == 0)
+ return FWK_E_PARAM;
+ if (config->reset_register_width >= MOD_SCMI_APCORE_REG_WIDTH_COUNT)
+ return FWK_E_PARAM;
+
+ scmi_apcore_ctx.config = config;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_apcore_bind(fwk_id_t id, unsigned int round)
+{
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ /* Bind to the SCMI module, storing an API pointer for later use. */
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_apcore_ctx.scmi_api);
+}
+
+static int scmi_apcore_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id, const void **api)
+{
+ /* Only accept binding requests from the SCMI module. */
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_apcore_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+
+/* SCMI Clock Management Protocol Definition */
+const struct fwk_module module_scmi_apcore = {
+ .name = "SCMI Core Configuration Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_apcore_init,
+ .bind = scmi_apcore_bind,
+ .process_bind_request = scmi_apcore_process_bind_request,
+};
diff --git a/module/scmi_clock/include/internal/scmi_clock.h b/module/scmi_clock/include/internal/scmi_clock.h
new file mode 100644
index 00000000..50d65d99
--- /dev/null
+++ b/module/scmi_clock/include/internal/scmi_clock.h
@@ -0,0 +1,191 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Clock Management Protocol Support
+ */
+
+#ifndef SCMI_CLOCK_H
+#define SCMI_CLOCK_H
+
+#define SCMI_PROTOCOL_ID_CLOCK UINT32_C(0x14)
+#define SCMI_PROTOCOL_VERSION_CLOCK UINT32_C(0x10000)
+
+/*
+ * Identifiers of the SCMI Clock Management Protocol commands
+ */
+enum scmi_clock_command_id {
+ SCMI_CLOCK_ATTRIBUTES = 0x003,
+ SCMI_CLOCK_DESCRIBE_RATES = 0x004,
+ SCMI_CLOCK_RATE_SET = 0x005,
+ SCMI_CLOCK_RATE_GET = 0x006,
+ SCMI_CLOCK_CONFIG_SET = 0x007,
+};
+
+/*
+ * Protocol Attributes
+ */
+
+#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES_MAX_PENDING_TRANSITIONS_POS 16
+#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES_CLOCK_COUNT_POS 0
+
+#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES_MAX_PENDING_TRANSITIONS_MASK \
+ (UINT32_C(0xFF) << \
+ SCMI_CLOCK_PROTOCOL_ATTRIBUTES_MAX_PENDING_TRANSITIONS_POS)
+#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES_CLOCK_COUNT_MASK \
+ (UINT32_C(0xFFFF) << SCMI_CLOCK_PROTOCOL_ATTRIBUTES_CLOCK_COUNT_POS)
+
+#define SCMI_CLOCK_PROTOCOL_ATTRIBUTES(MAX_PENDING_TRANSACTIONS, CLOCK_COUNT) \
+ ( \
+ ((MAX_PENDING_TRANSACTIONS << \
+ SCMI_CLOCK_PROTOCOL_ATTRIBUTES_MAX_PENDING_TRANSITIONS_POS) & \
+ SCMI_CLOCK_PROTOCOL_ATTRIBUTES_MAX_PENDING_TRANSITIONS_MASK) | \
+ (((CLOCK_COUNT) << SCMI_CLOCK_PROTOCOL_ATTRIBUTES_CLOCK_COUNT_POS) & \
+ SCMI_CLOCK_PROTOCOL_ATTRIBUTES_CLOCK_COUNT_MASK) \
+ )
+
+/*
+ * Clock Attributes
+ */
+
+#define SCMI_CLOCK_ATTRIBUTES_ENABLED_POS 0
+
+#define SCMI_CLOCK_ATTRIBUTES_ENABLED_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_ATTRIBUTES_ENABLED_POS)
+
+#define SCMI_CLOCK_ATTRIBUTES(ENABLED) \
+ ( \
+ (((ENABLED) << SCMI_CLOCK_ATTRIBUTES_ENABLED_POS) & \
+ SCMI_CLOCK_ATTRIBUTES_ENABLED_MASK) \
+ )
+
+struct __attribute((packed)) scmi_clock_attributes_a2p {
+ uint32_t clock_id;
+};
+
+#define SCMI_CLOCK_NAME_LENGTH_MAX 16
+
+struct __attribute((packed)) scmi_clock_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ char clock_name[SCMI_CLOCK_NAME_LENGTH_MAX];
+};
+
+/*
+ * Clock Rate Get
+ */
+
+struct __attribute((packed)) scmi_clock_rate_get_a2p {
+ uint32_t clock_id;
+};
+
+struct __attribute((packed)) scmi_clock_rate_get_p2a {
+ int32_t status;
+ uint32_t rate[2];
+};
+
+/*
+ * Clock Rate Set
+ */
+
+/* If set, set the new clock rate asynchronously */
+#define SCMI_CLOCK_RATE_SET_ASYNC_POS 0
+/* If set, do not send a delayed asynchronous response */
+#define SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_POS 1
+/* Round up, if set, otherwise round down */
+#define SCMI_CLOCK_RATE_SET_ROUND_UP_POS 2
+/* If set, the platform chooses the appropriate rounding mode */
+#define SCMI_CLOCK_RATE_SET_ROUND_AUTO_POS 3
+
+#define SCMI_CLOCK_RATE_SET_ASYNC_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_RATE_SET_ASYNC_POS)
+#define SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_RATE_SET_NO_DELAYED_RESPONSE_POS)
+#define SCMI_CLOCK_RATE_SET_ROUND_UP_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_RATE_SET_ROUND_UP_POS)
+#define SCMI_CLOCK_RATE_SET_ROUND_AUTO_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_RATE_SET_ROUND_AUTO_POS)
+
+struct __attribute((packed)) scmi_clock_rate_set_a2p {
+ uint32_t flags;
+ uint32_t clock_id;
+ uint32_t rate[2];
+};
+
+struct __attribute((packed)) scmi_clock_rate_set_p2a {
+ int32_t status;
+};
+
+/*
+ * Clock Config Set
+ */
+
+#define SCMI_CLOCK_CONFIG_SET_ENABLE_POS 0
+
+#define SCMI_CLOCK_CONFIG_SET_ENABLE_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_CONFIG_SET_ENABLE_POS)
+
+struct __attribute((packed)) scmi_clock_config_set_a2p {
+ uint32_t clock_id;
+ uint32_t attributes;
+};
+
+struct __attribute((packed)) scmi_clock_config_set_p2a {
+ int32_t status;
+};
+
+/*
+ * Clock Describe Rates
+ */
+
+#define SCMI_CLOCK_RATE_FORMAT_RANGE 1
+#define SCMI_CLOCK_RATE_FORMAT_LIST 0
+
+#define SCMI_CLOCK_DESCRIBE_RATES_REMAINING_POS 16
+#define SCMI_CLOCK_DESCRIBE_RATES_FORMAT_POS 12
+#define SCMI_CLOCK_DESCRIBE_RATES_COUNT_POS 0
+
+#define SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS( \
+ RATE_COUNT, RETURN_FORMAT, REMAINING_RATES) \
+ ( \
+ ((RATE_COUNT << \
+ SCMI_CLOCK_DESCRIBE_RATES_COUNT_POS) & \
+ SCMI_CLOCK_DESCRIBE_RATES_COUNT_MASK) | \
+ ((REMAINING_RATES << SCMI_CLOCK_DESCRIBE_RATES_REMAINING_POS) & \
+ SCMI_CLOCK_DESCRIBE_RATES_REMAINING_MASK) | \
+ ((RETURN_FORMAT << SCMI_CLOCK_DESCRIBE_RATES_FORMAT_POS) & \
+ SCMI_CLOCK_DESCRIBE_RATES_FORMAT_MASK) \
+ )
+
+#define SCMI_CLOCK_DESCRIBE_RATES_REMAINING_MASK \
+ (UINT32_C(0xFFFF) << SCMI_CLOCK_DESCRIBE_RATES_REMAINING_POS)
+#define SCMI_CLOCK_DESCRIBE_RATES_FORMAT_MASK \
+ (UINT32_C(0x1) << SCMI_CLOCK_DESCRIBE_RATES_FORMAT_POS)
+#define SCMI_CLOCK_DESCRIBE_RATES_COUNT_MASK \
+ (UINT32_C(0xFFF) << SCMI_CLOCK_DESCRIBE_RATES_COUNT_POS)
+
+#define SCMI_CLOCK_RATES_MAX(MAILBOX_SIZE) \
+ ((sizeof(struct scmi_clock_describe_rates_p2a) < (MAILBOX_SIZE)) ? \
+ (((MAILBOX_SIZE) - sizeof(struct scmi_clock_describe_rates_p2a)) \
+ / sizeof(struct scmi_clock_rate)) : 0)
+
+struct __attribute((packed)) scmi_clock_rate {
+ uint32_t low;
+ uint32_t high;
+};
+
+struct __attribute((packed)) scmi_clock_describe_rates_a2p {
+ uint32_t clock_id;
+ uint32_t rate_index;
+};
+
+struct __attribute((packed)) scmi_clock_describe_rates_p2a {
+ int32_t status;
+ uint32_t num_rates_flags;
+ struct scmi_clock_rate rates[];
+};
+
+#endif /* SCMI_CLOCK_H */
diff --git a/module/scmi_clock/include/mod_scmi_clock.h b/module/scmi_clock/include/mod_scmi_clock.h
new file mode 100644
index 00000000..82874510
--- /dev/null
+++ b/module/scmi_clock/include/mod_scmi_clock.h
@@ -0,0 +1,111 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Clock Management Protocol Support.
+ */
+
+#ifndef MOD_SCMI_CLOCK_H
+#define MOD_SCMI_CLOCK_H
+
+#include <stdint.h>
+
+/*!
+ * \ingroup GroupModules Modules
+ * \defgroup GroupSCMI_CLOCK SCMI Clock Management Protocol
+ * \{
+ */
+
+/*!
+ * \brief Permission flags governing the ability to use certain SCMI commands to
+ * interact with a clock.
+ *
+ * \details Setting a permission flag for a clock enables the corresponding
+ * functionality for any agent that has visibilty of the clock
+ * through its clock device table.
+ */
+enum mod_scmi_clock_permissions {
+ /*! No permissions (at least one must be granted) */
+ MOD_SCMI_CLOCK_PERM_INVALID = 0,
+
+ /*! The clock's attributes can be queried */
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES = (1 << 0),
+
+ /*! The clock's supported rates can be queried */
+ MOD_SCMI_CLOCK_PERM_DESCRIBE_RATES = (1 << 1),
+
+ /*! The clock's current rate can be queried */
+ MOD_SCMI_CLOCK_PERM_GET_RATE = (1 << 2),
+
+ /*! The clock can be set to a new rate */
+ MOD_SCMI_CLOCK_PERM_SET_RATE = (1 << 3),
+
+ /*! The clock can be enabled and disabled */
+ MOD_SCMI_CLOCK_PERM_SET_CONFIG = (1 << 4),
+};
+
+/*!
+ * \brief Clock device.
+ *
+ * \details Clock device structures are used in per-agent clock device tables.
+ * Each contains an identifier of an element that will be bound to in order
+ * to use the clock device. The permission flags for the clock are applied
+ * to any agent that uses the device configuration in its clock device
+ * table.
+ */
+struct mod_scmi_clock_device {
+ /*!
+ * \brief Clock element identifier.
+ *
+ * \details The module that owns the element must implement the Clock API
+ * that is defined by the \c clock module.
+ */
+ fwk_id_t element_id;
+
+ /*! Mask of permission flags defined by \ref mod_scmi_clock_permissions */
+ uint8_t permissions;
+};
+
+/*!
+ * \brief Agent descriptor.
+ *
+ * \details Describes an agent that uses the SCMI Clock Management protocol.
+ * Provides a pointer to the agent's clock device table and the number of
+ * devices within the table.
+ */
+struct mod_scmi_clock_agent {
+ /*! Pointer to a table of clock devices that are visible to the agent */
+ const struct mod_scmi_clock_device *device_table;
+
+ /*!
+ * \brief The number of \c mod_scmi_clock_device structures in the table
+ * pointed to by \c device_table.
+ */
+ uint8_t device_count;
+};
+
+/*!
+ * \brief Module configuration.
+ */
+struct mod_scmi_clock_config {
+ /*! Maximum supported number of pending, asynchronous clock rate changes */
+ uint8_t max_pending_transactions;
+
+ /*!
+ * \brief Pointer to the table of agent descriptors, used to provide
+ * per-agent views of clocks in the system.
+ */
+ const struct mod_scmi_clock_agent *agent_table;
+
+ /*! Number of agents in \ref agent_table */
+ size_t agent_count;
+};
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_SCMI_CLOCK_H */
diff --git a/module/scmi_clock/src/Makefile b/module/scmi_clock/src/Makefile
new file mode 100644
index 00000000..3b814d07
--- /dev/null
+++ b/module/scmi_clock/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI Clock Protocol
+BS_LIB_SOURCES := mod_scmi_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_clock/src/mod_scmi_clock.c b/module/scmi_clock/src/mod_scmi_clock.c
new file mode 100644
index 00000000..0e95ea69
--- /dev/null
+++ b/module/scmi_clock/src/mod_scmi_clock.c
@@ -0,0 +1,763 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Clock Management Protocol Support.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_clock.h>
+#include <mod_clock.h>
+#include <mod_scmi.h>
+#include <mod_scmi_clock.h>
+
+struct scmi_clock_ctx {
+ /*! SCMI Clock Module Configuration */
+ const struct mod_scmi_clock_config *config;
+
+ /*! Maximum supported number of pending, asynchronous clock rate changes */
+ uint8_t max_pending_transactions;
+
+ /*!
+ * Pointer to the table of agent descriptors, used to provide per-agent
+ * views of clocks in the system.
+ */
+ const struct mod_scmi_clock_agent *agent_table;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /* Clock module API */
+ const struct mod_clock_api *clock_api;
+};
+
+static int scmi_clock_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_clock_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_rate_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_rate_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_config_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_describe_rates_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables.
+ */
+static struct scmi_clock_ctx scmi_clock_ctx;
+
+static int (* const handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] = scmi_clock_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] = scmi_clock_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_clock_protocol_message_attributes_handler,
+ [SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes_handler,
+ [SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get_handler,
+ [SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set_handler,
+ [SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set_handler,
+ [SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates_handler,
+};
+
+static const unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_CLOCK_ATTRIBUTES] = sizeof(struct scmi_clock_attributes_a2p),
+ [SCMI_CLOCK_RATE_GET] = sizeof(struct scmi_clock_rate_get_a2p),
+ [SCMI_CLOCK_RATE_SET] = sizeof(struct scmi_clock_rate_set_a2p),
+ [SCMI_CLOCK_CONFIG_SET] = sizeof(struct scmi_clock_config_set_a2p),
+ [SCMI_CLOCK_DESCRIBE_RATES] = sizeof(struct scmi_clock_describe_rates_a2p),
+};
+
+/*
+ * Given a service identifier, retrieve a pointer to its agent's
+ * \c mod_scmi_clock_agent structure within the agent table.
+ */
+static int get_agent_entry(
+ fwk_id_t service_id,
+ const struct mod_scmi_clock_agent **agent)
+{
+ int status;
+ unsigned int agent_id;
+
+ if (agent == NULL)
+ return FWK_E_PARAM;
+
+ status = scmi_clock_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (agent_id >= scmi_clock_ctx.config->agent_count)
+ return FWK_E_PARAM;
+
+ *agent =
+ (struct mod_scmi_clock_agent *)&scmi_clock_ctx.agent_table[agent_id];
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Given a service identifier and a clock index, retrieve a pointer to the
+ * clock's \c mod_scmi_clock_device structure within the agent's device table.
+ * Optionally, a pointer to the agent may be retrieved as well.
+ */
+static int get_clock_device_entry(
+ fwk_id_t service_id,
+ unsigned int clock_idx,
+ const struct mod_scmi_clock_device **clock_device,
+ const struct mod_scmi_clock_agent **agent)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent_entry;
+
+ if (clock_device == NULL)
+ return FWK_E_PARAM;
+
+ status = get_agent_entry(service_id, &agent_entry);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (clock_idx >= agent_entry->device_count)
+ return FWK_E_RANGE;
+
+ *clock_device = &agent_entry->device_table[clock_idx];
+
+ assert((*clock_device)->permissions != MOD_SCMI_CLOCK_PERM_INVALID);
+ assert(fwk_module_is_valid_element_id((*clock_device)->element_id));
+
+ if (agent != NULL)
+ *agent = agent_entry;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Query the permissions for a service making a request to determine if
+ * the operation is permitted on the given clock device.
+ */
+static int check_service_permission(
+ const struct mod_scmi_clock_device *device,
+ enum mod_scmi_clock_permissions requested_permission,
+ bool *granted)
+{
+ if (granted == NULL)
+ return FWK_E_PARAM;
+
+ *granted = device->permissions & requested_permission;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Version
+ */
+static int scmi_clock_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_CLOCK,
+ };
+
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Attributes
+ */
+static int scmi_clock_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ struct scmi_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ status = get_agent_entry(service_id, &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_GENERIC_ERROR;
+ goto exit;
+ }
+
+ return_values.attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(
+ scmi_clock_ctx.max_pending_transactions,
+ agent->device_count
+ );
+
+exit:
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return status;
+}
+
+/*
+ * Protocol Message Attributes
+ */
+static int scmi_clock_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ size_t response_size;
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p*)
+ payload;
+ message_id = parameters->message_id;
+
+ if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
+ (handler_table[message_id] == NULL)) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Attributes
+ */
+static int scmi_clock_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ enum mod_clock_state clock_state;
+ const struct scmi_clock_attributes_a2p *parameters;
+ struct scmi_clock_attributes_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_attributes_a2p*)payload;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->get_state(clock_device->element_id,
+ &clock_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values = (struct scmi_clock_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ .attributes = SCMI_CLOCK_ATTRIBUTES(
+ clock_state == MOD_CLOCK_STATE_RUNNING),
+ };
+
+ strncpy(return_values.clock_name,
+ fwk_module_get_name(clock_device->element_id),
+ sizeof(return_values.clock_name) - 1);
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return status;
+}
+
+/*
+ * Clock Rate Get
+ */
+static int scmi_clock_rate_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ uint64_t rate;
+ const struct scmi_clock_rate_get_a2p *parameters;
+ struct scmi_clock_rate_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_rate_get_a2p*)payload;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->get_rate(
+ clock_device->element_id, &rate);
+ return_values.rate[0] = (uint32_t)rate;
+ return_values.rate[1] = (uint32_t)(rate >> 32);
+
+ if (status == FWK_SUCCESS)
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return status;
+}
+
+/*
+ * Clock Rate Set (Synchronous Only)
+ */
+static int scmi_clock_rate_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ uint64_t rate;
+ bool round_auto;
+ bool round_up;
+ bool asynchronous;
+ const struct scmi_clock_rate_set_a2p *parameters;
+ struct scmi_clock_rate_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_rate_set_a2p*)payload;
+ round_up = parameters->flags & SCMI_CLOCK_RATE_SET_ROUND_UP_MASK;
+ round_auto = parameters->flags & SCMI_CLOCK_RATE_SET_ROUND_AUTO_MASK;
+ asynchronous = parameters->flags & SCMI_CLOCK_RATE_SET_ASYNC_MASK;
+ rate = (uint64_t)parameters->rate[0] +
+ (((uint64_t)parameters->rate[1]) << 32);
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ if (asynchronous) {
+ /* Support for async clock set commands not yet implemented */
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->set_rate(clock_device->element_id, rate,
+ round_auto ? MOD_CLOCK_ROUND_MODE_NEAREST :
+ (round_up ? MOD_CLOCK_ROUND_MODE_UP : MOD_CLOCK_ROUND_MODE_DOWN));
+ if (status == FWK_E_RANGE) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Config Set
+ */
+static int scmi_clock_config_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ bool enable;
+ bool service_permission_granted;
+ size_t response_size;
+ const struct scmi_clock_config_set_a2p *parameters;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ struct scmi_clock_rate_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_config_set_a2p*)payload;
+ enable = parameters->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->set_state(
+ clock_device->element_id,
+ enable ? MOD_CLOCK_STATE_RUNNING : MOD_CLOCK_STATE_STOPPED);
+ if (status == FWK_E_SUPPORT) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Describe Rates
+ */
+static int scmi_clock_describe_rates_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ unsigned int i;
+ size_t max_payload_size;
+ uint32_t payload_size;
+ uint32_t index;
+ unsigned int rate_count;
+ unsigned int remaining_rates;
+ uint64_t rate;
+ struct scmi_clock_rate scmi_rate;
+ struct scmi_clock_rate clock_range[3];
+ struct mod_clock_info info;
+ const struct scmi_clock_describe_rates_a2p *parameters;
+ struct scmi_clock_describe_rates_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_describe_rates_a2p*)payload;
+ index = parameters->rate_index;
+ payload_size = sizeof(return_values);
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ /*
+ * Get the maximum payload size to determine how many clock rate entries can
+ * be returned in one response.
+ */
+ status = scmi_clock_ctx.scmi_api->get_max_payload_size(
+ service_id, &max_payload_size);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_clock_ctx.clock_api->get_info(clock_device->element_id,
+ &info);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (info.range.rate_type == MOD_CLOCK_RATE_TYPE_DISCRETE) {
+ /* The clock has a discrete list of frequencies */
+
+ if (index >= info.range.rate_count) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ /* Can at least one entry be returned? */
+ if (SCMI_CLOCK_RATES_MAX(max_payload_size) == 0) {
+ status = FWK_E_SIZE;
+ goto exit;
+ }
+
+ /* The number of rates being returned in this payload is defined as the
+ * smaller of:
+ * - The clock rates that are available between the index and the
+ clock's maximum rate.
+ - The number of rates that can be returned in each payload.
+ */
+ rate_count = FWK_MIN(SCMI_CLOCK_RATES_MAX(max_payload_size),
+ info.range.rate_count - index);
+
+ /*
+ * Because the agent gives a starting index into the clock's rate list
+ * the number of rates remaining is calculated as the number of rates
+ * the clock supports minus the index, with the number of rates being
+ * returned in this payload subtracted.
+ */
+ remaining_rates = (info.range.rate_count - index) - rate_count;
+
+ /* Give the number of rates sent in the message payload */
+ return_values.num_rates_flags =
+ SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(
+ rate_count,
+ SCMI_CLOCK_RATE_FORMAT_LIST,
+ remaining_rates
+ );
+
+ /* Set each rate entry in the payload to the associated frequency */
+ for (i = 0; i < rate_count; i++,
+ payload_size += sizeof(struct scmi_clock_rate)) {
+ status = scmi_clock_ctx.clock_api->get_rate_from_index(
+ clock_device->element_id,
+ index + i,
+ &rate);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ scmi_rate.low = (uint32_t)rate;
+ scmi_rate.high = (uint32_t)(rate >> 32);
+
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id,
+ payload_size, &scmi_rate, sizeof(scmi_rate));
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+ } else {
+ /* The clock has a linear stepping */
+
+ /* Is the payload area large enough to return the complete triplet? */
+ if (SCMI_CLOCK_RATES_MAX(max_payload_size) < 3) {
+ status = FWK_E_SIZE;
+ goto exit;
+ }
+
+ return_values.num_rates_flags =
+ SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(
+ /* Only a single rate is returned */
+ 1,
+ SCMI_CLOCK_RATE_FORMAT_RANGE,
+ /* No further rates are available */
+ 0
+ );
+
+ /* Store the range data in the range entry in the payload */
+ clock_range[0].low = (uint32_t)info.range.min;
+ clock_range[0].high = (uint32_t)(info.range.min >> 32);
+ clock_range[1].low = (uint32_t)info.range.max;
+ clock_range[1].high = (uint32_t)(info.range.max >> 32);
+ clock_range[2].low = (uint32_t)info.range.step;
+ clock_range[2].high = (uint32_t)(info.range.step >> 32);
+
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id,
+ payload_size, &clock_range, sizeof(clock_range));
+ if (status != FWK_SUCCESS)
+ goto exit;
+ payload_size += sizeof(clock_range);
+ }
+
+ return_values.status = SCMI_SUCCESS;
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id, 0,
+ &return_values, sizeof(return_values));
+
+exit:
+ scmi_clock_ctx.scmi_api->respond(service_id,
+ (return_values.status == SCMI_SUCCESS) ?
+ NULL : &return_values.status,
+ (return_values.status == SCMI_SUCCESS) ?
+ payload_size : sizeof(return_values.status));
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI clock module interface
+ */
+static int scmi_clock_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_CLOCK;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_clock_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
+ const uint32_t *payload, size_t payload_size, unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Clock management protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_clock_ctx.scmi_api->respond(service_id,
+ &return_value, sizeof(return_value));
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_clock_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_clock_get_scmi_protocol_id,
+ .message_handler = scmi_clock_message_handler
+};
+
+/*
+ * Framework handlers
+ */
+
+static int scmi_clock_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ const struct mod_scmi_clock_config *config =
+ (const struct mod_scmi_clock_config *)data;
+
+ if ((config == NULL) || (config->agent_table == NULL))
+ return FWK_E_PARAM;
+
+ scmi_clock_ctx.config = config;
+ scmi_clock_ctx.max_pending_transactions = config->max_pending_transactions;
+ scmi_clock_ctx.agent_table = config->agent_table;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_clock_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_clock_ctx.scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_CLOCK),
+ FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0), &scmi_clock_ctx.clock_api);
+}
+
+static int scmi_clock_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id, const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_clock_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+
+/* SCMI Clock Management Protocol Definition */
+const struct fwk_module module_scmi_clock = {
+ .name = "SCMI Clock Management Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_clock_init,
+ .bind = scmi_clock_bind,
+ .process_bind_request = scmi_clock_process_bind_request,
+};
diff --git a/module/scmi_perf/include/internal/scmi_perf.h b/module/scmi_perf/include/internal/scmi_perf.h
new file mode 100644
index 00000000..b3690997
--- /dev/null
+++ b/module/scmi_perf/include/internal/scmi_perf.h
@@ -0,0 +1,248 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI performance domain management protocol support.
+ */
+
+#ifndef SCMI_PERF_H
+#define SCMI_PERF_H
+
+#define SCMI_PROTOCOL_ID_PERF UINT32_C(0x13)
+#define SCMI_PROTOCOL_VERSION_PERF UINT32_C(0x10000)
+
+#define SCMI_PERF_SUPPORTS_STATS_SHARED_MEM_REGION 0
+#define SCMI_PERF_STATS_SHARED_MEM_REGION_ADDR_LOW 0
+#define SCMI_PERF_STATS_SHARED_MEM_REGION_ADDR_HIGH 0
+#define SCMI_PERF_STATS_SHARED_MEM_REGION_LENGTH 0
+
+/*
+ * Identifier of the SCMI Performance Domain Management Protocol commands
+ */
+
+enum scmi_perf_command_id {
+ SCMI_PERF_DOMAIN_ATTRIBUTES = 0x003,
+ SCMI_PERF_DESCRIBE_LEVELS = 0x004,
+ SCMI_PERF_LIMITS_SET = 0x005,
+ SCMI_PERF_LIMITS_GET = 0x006,
+ SCMI_PERF_LEVEL_SET = 0x007,
+ SCMI_PERF_LEVEL_GET = 0x008,
+ SCMI_PERF_NOTIFY_LIMITS = 0x009,
+ SCMI_PERF_NOTIFY_LEVEL = 0x00A
+};
+
+/*
+ * PROTOCOL_ATTRIBUTES
+ */
+
+#define SCMI_PERF_PROTOCOL_ATTRIBUTES_POWER_MW_POS 16
+#define SCMI_PERF_PROTOCOL_ATTRIBUTES_NUM_DOMAINS_POS 0
+
+#define SCMI_PERF_PROTOCOL_ATTRIBUTES_POWER_MW_MASK \
+ (UINT32_C(0x1) << SCMI_PERF_PROTOCOL_ATTRIBUTES_POWER_MW_POS)
+#define SCMI_PERF_PROTOCOL_ATTRIBUTES_NUM_DOMAINS_MASK \
+ (UINT32_C(0xFFFF) << SCMI_PERF_PROTOCOL_ATTRIBUTES_NUM_DOMAINS_POS)
+
+#define SCMI_PERF_PROTOCOL_ATTRIBUTES(POWER_MW, NUM_DOMAINS) \
+ ( \
+ (((POWER_MW) << SCMI_PERF_PROTOCOL_ATTRIBUTES_POWER_MW_POS) & \
+ SCMI_PERF_PROTOCOL_ATTRIBUTES_POWER_MW_MASK) | \
+ (((NUM_DOMAINS) << SCMI_PERF_PROTOCOL_ATTRIBUTES_NUM_DOMAINS_POS) & \
+ SCMI_PERF_PROTOCOL_ATTRIBUTES_NUM_DOMAINS_MASK) \
+ )
+
+struct __attribute((packed)) scmi_perf_protocol_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ uint32_t statistics_address_low;
+ uint32_t statistics_address_high;
+ uint32_t statistics_len;
+};
+
+/*
+ * PERFORMANCE_DOMAIN_ATTRIBUTES
+ */
+
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LIMITS_POS 31
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL_POS 30
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_LIMITS_NOTIFY_POS 29
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_LEVEL_NOTIFY_POS 28
+
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LIMITS_MASK \
+ (UINT32_C(0x1) << SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LIMITS_POS)
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL_MASK \
+ (UINT32_C(0x1) << SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL_POS)
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_LIMITS_NOTIFY_MASK \
+ (UINT32_C(0x1) << SCMI_PERF_DOMAIN_ATTRIBUTES_LIMITS_NOTIFY_POS)
+#define SCMI_PERF_DOMAIN_ATTRIBUTES_LEVEL_NOTIFY_MASK \
+ (UINT32_C(0x1) << SCMI_PERF_DOMAIN_ATTRIBUTES_LEVEL_NOTIFY_POS)
+
+#define SCMI_PERF_DOMAIN_ATTRIBUTES(LEVEL_NOTIFY, LIMITS_NOTIFY, \
+ CAN_SET_LEVEL, CAN_SET_LIMITS) \
+ ( \
+ (((LEVEL_NOTIFY) << \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_LEVEL_NOTIFY_POS) & \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_LEVEL_NOTIFY_MASK) | \
+ (((LIMITS_NOTIFY) << \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_LIMITS_NOTIFY_POS) & \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_LIMITS_NOTIFY_MASK) | \
+ (((CAN_SET_LEVEL) << \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL_POS) & \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL_MASK) | \
+ (((CAN_SET_LIMITS) << \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LIMITS_POS) & \
+ SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LIMITS_MASK) \
+ )
+
+struct __attribute((packed)) scmi_perf_domain_attributes_a2p {
+ uint32_t domain_id;
+};
+
+#define SCMI_PERF_DOMAIN_RATE_LIMIT_POS 0
+#define SCMI_PERF_DOMAIN_RATE_LIMIT_MASK \
+ (UINT32_C(0xFFFFF) << SCMI_PERF_DOMAIN_RATE_LIMIT_POS)
+
+struct __attribute((packed)) scmi_perf_domain_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ uint32_t rate_limit;
+ uint32_t sustained_freq;
+ uint32_t sustained_perf_level;
+ uint8_t name[16];
+};
+
+/*
+ * PERFORMANCE_DESCRIBE_LEVELS
+ */
+
+#define SCMI_PERF_LEVELS_MAX(MAILBOX_SIZE) \
+ ((sizeof(struct scmi_perf_describe_levels_p2a) < MAILBOX_SIZE) ? \
+ ((MAILBOX_SIZE - sizeof(struct scmi_perf_describe_levels_p2a)) \
+ / sizeof(struct scmi_perf_level)) : 0)
+
+#define SCMI_PERF_LEVEL_ATTRIBUTES_POS 0
+#define SCMI_PERF_LEVEL_ATTRIBUTES_MASK \
+ (UINT32_C(0xFFFF) << SCMI_PERF_LEVEL_ATTRIBUTES_POS)
+
+#define SCMI_PERF_LEVEL_ATTRIBUTES(LATENCY) \
+ (((LATENCY) << SCMI_PERF_LEVEL_ATTRIBUTES_POS) & \
+ SCMI_PERF_LEVEL_ATTRIBUTES_MASK)
+
+struct __attribute((packed)) scmi_perf_level {
+ uint32_t performance_level;
+ uint32_t power_cost;
+ uint32_t attributes;
+};
+
+struct __attribute((packed)) scmi_perf_describe_levels_a2p {
+ uint32_t domain_id;
+ uint32_t level_index;
+};
+
+#define SCMI_PERF_NUM_LEVELS_REMAINING_LEVELS_POS 16
+#define SCMI_PERF_NUM_LEVELS_NUM_LEVELS_POS 0
+
+#define SCMI_PERF_NUM_LEVELS_REMAINING_LEVELS_MASK \
+ (UINT32_C(0xFFFF) << SCMI_PERF_NUM_LEVELS_REMAINING_LEVELS_POS)
+#define SCMI_PERF_NUM_LEVELS_NUM_LEVELS_MASK \
+ (UINT32_C(0xFFF) << SCMI_PERF_NUM_LEVELS_NUM_LEVELS_POS)
+
+#define SCMI_PERF_NUM_LEVELS(NUM_LEVELS, REMAINING_LEVELS) \
+ ((((NUM_LEVELS) << SCMI_PERF_NUM_LEVELS_NUM_LEVELS_POS) & \
+ SCMI_PERF_NUM_LEVELS_NUM_LEVELS_MASK) | \
+ (((REMAINING_LEVELS) << SCMI_PERF_NUM_LEVELS_REMAINING_LEVELS_POS) & \
+ SCMI_PERF_NUM_LEVELS_REMAINING_LEVELS_MASK))
+
+struct __attribute((packed)) scmi_perf_describe_levels_p2a {
+ int32_t status;
+ uint32_t num_levels;
+
+ struct scmi_perf_level perf_levels[];
+};
+
+/*
+ * PERFORMANCE_LIMITS_SET
+ */
+
+struct __attribute((packed)) scmi_perf_limits_set_a2p {
+ uint32_t domain_id;
+ uint32_t range_max;
+ uint32_t range_min;
+};
+
+struct __attribute((packed)) scmi_perf_limits_set_p2a {
+ int32_t status;
+};
+
+/*
+ * PERFORMANCE_LIMITS_GET
+ */
+
+struct __attribute((packed)) scmi_perf_limits_get_a2p {
+ uint32_t domain_id;
+};
+
+struct __attribute((packed)) scmi_perf_limits_get_p2a {
+ int32_t status;
+ uint32_t range_max;
+ uint32_t range_min;
+};
+
+/*
+ * PERFORMANCE_LEVEL_SET
+ */
+
+struct __attribute((packed)) scmi_perf_level_set_a2p {
+ uint32_t domain_id;
+ uint32_t performance_level;
+};
+
+struct __attribute((packed)) scmi_perf_level_set_p2a {
+ int32_t status;
+};
+
+/*
+ * PERFORMANCE_LEVEL_GET
+ */
+
+struct __attribute((packed)) scmi_perf_level_get_a2p {
+ uint32_t domain_id;
+};
+
+struct __attribute((packed)) scmi_perf_level_get_p2a {
+ int32_t status;
+ uint32_t performance_level;
+};
+
+/*
+ * PERFORMANCE_NOTIFY_LIMITS
+ */
+
+struct __attribute((packed)) scmi_perf_notify_limits_a2p {
+ uint32_t domain_id;
+ uint32_t notify_enable;
+};
+
+struct __attribute((packed)) scmi_perf_notify_limits_p2a {
+ int32_t status;
+};
+
+/*
+ * PERFORMANCE_NOTIFY_LEVEL
+ */
+
+struct __attribute((packed)) scmi_perf_notify_level_a2p {
+ uint32_t domain_id;
+ uint32_t notify_enable;
+};
+
+struct __attribute((packed)) scmi_perf_notify_level_p2a {
+ int32_t status;
+};
+
+extern struct scp_scmi_protocol scmi_perf_protocol;
+
+#endif /* SCMI_PERF_H */
diff --git a/module/scmi_perf/include/mod_scmi_perf.h b/module/scmi_perf/include/mod_scmi_perf.h
new file mode 100644
index 00000000..b9510106
--- /dev/null
+++ b/module/scmi_perf/include/mod_scmi_perf.h
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI performance domain management protocol support.
+ */
+
+#ifndef MOD_SCMI_PERF_H
+#define MOD_SCMI_PERF_H
+
+#include <stddef.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSCMI_PERF SCMI Performance Domain Management Protocol
+ * @{
+ */
+
+/*!
+ * \brief Agent permissions.
+ */
+enum mod_scmi_perf_permissions {
+ /*! No permissions */
+ MOD_SCMI_PERF_PERMS_NONE = 0,
+
+ /*! Permission to set performance level */
+ MOD_SCMI_PERF_PERMS_SET_LEVEL = (1 << 0),
+
+ /*! Permission to set performance limits */
+ MOD_SCMI_PERF_PERMS_SET_LIMITS = (1 << 1),
+};
+
+/*!
+ * \brief Performance domain configuration data.
+ */
+struct mod_scmi_perf_domain_config {
+ const uint32_t (*permissions)[]; /*!< Per-agent permission flags */
+};
+
+/*!
+ * \brief SCMI Performance Domain Management Protocol configuration data.
+ */
+struct mod_scmi_perf_config {
+ /*! Per-domain configuration data */
+ const struct mod_scmi_perf_domain_config (*domains)[];
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* SCP_SCMI_PERF_H */
diff --git a/module/scmi_perf/src/Makefile b/module/scmi_perf/src/Makefile
new file mode 100644
index 00000000..4c57efdd
--- /dev/null
+++ b/module/scmi_perf/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI Performance Protocol
+BS_LIB_SOURCES := mod_scmi_perf.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_perf/src/mod_scmi_perf.c b/module/scmi_perf/src/mod_scmi_perf.c
new file mode 100644
index 00000000..5bcb0a99
--- /dev/null
+++ b/module/scmi_perf/src/mod_scmi_perf.c
@@ -0,0 +1,634 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI performance domain management protocol support.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_perf.h>
+#include <mod_dvfs.h>
+#include <mod_scmi.h>
+#include <mod_scmi_perf.h>
+
+struct scmi_perf_ctx {
+ /* SCMI Performance Module Configuration */
+ const struct mod_scmi_perf_config *config;
+
+ /* Number of power domains */
+ unsigned int domain_count;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /* DVFS module API */
+ const struct mod_dvfs_domain_api *dvfs_api;
+};
+
+static int scmi_perf_protocol_version_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_protocol_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_domain_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_describe_levels_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_level_set_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_level_get_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_limits_set_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_perf_limits_get_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+
+static struct scmi_perf_ctx scmi_perf_ctx;
+
+static int (*handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] =
+ scmi_perf_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] =
+ scmi_perf_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_perf_protocol_message_attributes_handler,
+ [SCMI_PERF_DOMAIN_ATTRIBUTES] =
+ scmi_perf_domain_attributes_handler,
+ [SCMI_PERF_DESCRIBE_LEVELS] =
+ scmi_perf_describe_levels_handler,
+ [SCMI_PERF_LIMITS_SET] =
+ scmi_perf_limits_set_handler,
+ [SCMI_PERF_LIMITS_GET] =
+ scmi_perf_limits_get_handler,
+ [SCMI_PERF_LEVEL_SET] =
+ scmi_perf_level_set_handler,
+ [SCMI_PERF_LEVEL_GET] =
+ scmi_perf_level_get_handler
+};
+
+static unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_PERF_DOMAIN_ATTRIBUTES] =
+ sizeof(struct scmi_perf_domain_attributes_a2p),
+ [SCMI_PERF_DESCRIBE_LEVELS] =
+ sizeof(struct scmi_perf_describe_levels_a2p),
+ [SCMI_PERF_LEVEL_SET] =
+ sizeof(struct scmi_perf_level_set_a2p),
+ [SCMI_PERF_LEVEL_GET] =
+ sizeof(struct scmi_perf_level_get_a2p),
+ [SCMI_PERF_LIMITS_SET] =
+ sizeof(struct scmi_perf_limits_set_a2p),
+ [SCMI_PERF_LIMITS_GET] =
+ sizeof(struct scmi_perf_limits_get_a2p),
+};
+
+/*
+ * Protocol command handlers
+ */
+
+static int scmi_perf_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_PERF,
+ };
+
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_perf_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes =
+ SCMI_PERF_PROTOCOL_ATTRIBUTES(true, scmi_perf_ctx.domain_count),
+ .statistics_len = 0, /* Unsupported */
+ .statistics_address_low = 0, /* Unsupported */
+ .statistics_address_high = 0, /* Unsupported */
+ };
+
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values;
+
+ parameters =
+ (const struct scmi_protocol_message_attributes_a2p *)payload;
+ message_id = parameters->message_id;
+
+ if ((message_id < FWK_ARRAY_SIZE(handler_table)) &&
+ (handler_table[message_id] != NULL)) {
+ return_values = (struct scmi_protocol_message_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ .attributes = 0, /* All commands have an attributes value of 0 */
+ };
+ } else
+ return_values.status = SCMI_NOT_FOUND;
+
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_domain_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ unsigned int agent_id;
+ const struct mod_scmi_perf_domain_config *domain;
+ const struct scmi_perf_domain_attributes_a2p *parameters;
+ struct scmi_perf_domain_attributes_p2a return_values;
+ uint32_t permissions;
+ fwk_id_t domain_id;
+ struct mod_dvfs_opp opp;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ /* Validate the domain identifier */
+ parameters = (const struct scmi_perf_domain_attributes_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ domain = &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
+ permissions = (*domain->permissions)[agent_id];
+
+ domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
+ status = scmi_perf_ctx.dvfs_api->get_sustained_opp(domain_id, &opp);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values = (struct scmi_perf_domain_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ .attributes = SCMI_PERF_DOMAIN_ATTRIBUTES(
+ false, false,
+ !!(permissions & MOD_SCMI_PERF_PERMS_SET_LEVEL),
+ !!(permissions & MOD_SCMI_PERF_PERMS_SET_LIMITS)
+ ),
+ .rate_limit = 0, /* Unsupported */
+ .sustained_freq = opp.frequency / FWK_KHZ,
+ .sustained_perf_level = opp.frequency,
+ };
+
+ /* Copy the domain name into the mailbox */
+ strncpy((char *)return_values.name, fwk_module_get_name(domain_id),
+ sizeof(return_values.name) - 1);
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_perf_describe_levels_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ size_t max_payload_size;
+ const struct scmi_perf_describe_levels_a2p *parameters;
+ struct scmi_perf_describe_levels_p2a return_values;
+ fwk_id_t domain_id;
+ struct scmi_perf_level perf_level;
+ unsigned int num_levels, level_index, level_index_max;
+ size_t payload_size;
+ size_t opp_count;
+ struct mod_dvfs_opp opp;
+ uint16_t latency;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+ payload_size = sizeof(return_values);
+
+ status = scmi_perf_ctx.scmi_api->get_max_payload_size(service_id,
+ &max_payload_size);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = (SCMI_PERF_LEVELS_MAX(max_payload_size) > 0) ?
+ FWK_SUCCESS : FWK_E_SIZE;
+ assert(status == FWK_SUCCESS);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+
+ /* Validate the domain identifier */
+ parameters = (const struct scmi_perf_describe_levels_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ /* Get the number of operating points for the domain */
+ domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id);
+ status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id, &opp_count);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Validate level index */
+ level_index = parameters->level_index;
+ if (level_index >= opp_count) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+
+ goto exit;
+ }
+
+ /* Identify the maximum number of performance levels we can send at once */
+ num_levels =
+ (SCMI_PERF_LEVELS_MAX(max_payload_size) <
+ (opp_count - level_index)) ?
+ SCMI_PERF_LEVELS_MAX(max_payload_size) :
+ (opp_count - level_index);
+ level_index_max = (level_index + num_levels - 1);
+
+ status = scmi_perf_ctx.dvfs_api->get_latency(domain_id, &latency);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Copy DVFS data into returned data structure */
+ for (; level_index <= level_index_max; level_index++,
+ payload_size += sizeof(perf_level)) {
+
+ status = scmi_perf_ctx.dvfs_api->get_nth_opp(
+ domain_id, level_index, &opp);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ perf_level.power_cost = opp.voltage;
+ perf_level.performance_level = opp.frequency;
+ perf_level.attributes = latency;
+
+ status = scmi_perf_ctx.scmi_api->write_payload(service_id, payload_size,
+ &perf_level, sizeof(perf_level));
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+
+ return_values = (struct scmi_perf_describe_levels_p2a) {
+ .status = SCMI_SUCCESS,
+ .num_levels = SCMI_PERF_NUM_LEVELS(num_levels,
+ (opp_count - level_index_max - 1))
+ };
+
+ status = scmi_perf_ctx.scmi_api->write_payload(service_id, 0,
+ &return_values, sizeof(return_values));
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id,
+ (return_values.status == SCMI_SUCCESS) ?
+ NULL : &return_values.status,
+ (return_values.status == SCMI_SUCCESS) ?
+ payload_size : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_perf_limits_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ unsigned int agent_id;
+ const struct mod_scmi_perf_domain_config *domain;
+ const struct scmi_perf_limits_set_a2p *parameters;
+ struct scmi_perf_limits_set_p2a return_values;
+ uint32_t permissions;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ parameters = (const struct scmi_perf_limits_set_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Ensure the agent has permission to do this */
+ domain = &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
+ permissions = (*domain->permissions)[agent_id];
+ if (!(permissions & MOD_SCMI_PERF_PERMS_SET_LIMITS)) {
+ return_values.status = SCMI_DENIED;
+
+ goto exit;
+ }
+
+ if (parameters->range_min > parameters->range_max) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+
+ goto exit;
+ }
+
+ /* Execute the transition asynchronously */
+ status = scmi_perf_ctx.dvfs_api->set_frequency_limits(
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
+ &((struct mod_dvfs_frequency_limits) {
+ .minimum = parameters->range_min,
+ .maximum = parameters->range_max
+ }));
+ if (status != FWK_SUCCESS) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_OUT_OF_RANGE;
+
+ goto exit;
+ }
+
+ return_values = (struct scmi_perf_limits_set_p2a) {
+ .status = SCMI_SUCCESS,
+ };
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_perf_limits_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct scmi_perf_limits_get_a2p *parameters;
+ struct scmi_perf_limits_get_p2a return_values;
+ struct mod_dvfs_frequency_limits limits;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ parameters = (const struct scmi_perf_limits_get_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ status = scmi_perf_ctx.dvfs_api->get_frequency_limits(
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id), &limits);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values = (struct scmi_perf_limits_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .range_min = (uint32_t)limits.minimum,
+ .range_max = (uint32_t)limits.maximum
+ };
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_perf_level_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ unsigned int agent_id;
+ const struct mod_scmi_perf_domain_config *domain;
+ const struct scmi_perf_level_set_a2p *parameters;
+ struct scmi_perf_level_set_p2a return_values;
+ uint32_t permissions;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ parameters = (const struct scmi_perf_level_set_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Ensure the agent has permission to do this */
+ domain = &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
+ permissions = (*domain->permissions)[agent_id];
+ if (!(permissions & MOD_SCMI_PERF_PERMS_SET_LEVEL)) {
+ return_values.status = SCMI_DENIED;
+
+ goto exit;
+ }
+
+ /* Execute the transition synchronously */
+ status = scmi_perf_ctx.dvfs_api->set_frequency(
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
+ parameters->performance_level);
+
+ if (status == FWK_E_RANGE) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_OUT_OF_RANGE;
+ } else if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_GENERIC_ERROR;
+ } else {
+ return_values = (struct scmi_perf_level_set_p2a) {
+ .status = SCMI_SUCCESS,
+ };
+ }
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_perf_level_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct scmi_perf_level_get_a2p *parameters;
+ struct scmi_perf_level_get_p2a return_values;
+ struct mod_dvfs_opp opp;
+
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ parameters = (const struct scmi_perf_level_get_a2p *)payload;
+ if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+
+ goto exit;
+ }
+
+ status = scmi_perf_ctx.dvfs_api->get_current_opp(
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id), &opp);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ /* Return the frequency as the performance level */
+ return_values = (struct scmi_perf_level_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .performance_level = (uint32_t)opp.frequency,
+ };
+
+exit:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI performance module interface
+ */
+
+static int scmi_perf_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_PERF;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
+ const uint32_t *payload, size_t payload_size, unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Performance management protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_perf_ctx.scmi_api->respond(service_id, &return_value,
+ sizeof(return_value));
+
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_perf_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_perf_get_scmi_protocol_id,
+ .message_handler = scmi_perf_message_handler
+};
+
+/*
+ * Framework handlers
+ */
+static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ int return_val;
+ const struct mod_scmi_perf_config *config =
+ (const struct mod_scmi_perf_config *)data;
+
+ if ((config == NULL) || (config->domains == NULL))
+ return FWK_E_PARAM;
+
+ return_val = fwk_module_get_element_count(
+ FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
+ if (return_val <= 0)
+ return FWK_E_SUPPORT;
+
+ scmi_perf_ctx.config = config;
+ scmi_perf_ctx.domain_count = return_val;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_perf_ctx.scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS),
+ FWK_ID_API(FWK_MODULE_IDX_DVFS, 0), &scmi_perf_ctx.dvfs_api);
+}
+
+static int scmi_perf_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id, const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_perf_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+/* SCMI Performance Management Protocol Definition */
+const struct fwk_module module_scmi_perf = {
+ .name = "SCMI Performance Management Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_perf_init,
+ .bind = scmi_perf_bind,
+ .process_bind_request = scmi_perf_process_bind_request,
+};
diff --git a/module/scmi_power_domain/include/internal/scmi_power_domain.h b/module/scmi_power_domain/include/internal/scmi_power_domain.h
new file mode 100644
index 00000000..61595f73
--- /dev/null
+++ b/module/scmi_power_domain/include/internal/scmi_power_domain.h
@@ -0,0 +1,121 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) support.
+ */
+
+#ifndef SCMI_POWER_H
+#define SCMI_POWER_H
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSCMI_PERF SCMI Power Domain Management Protocol
+ * @{
+ */
+
+#define SCMI_PROTOCOL_ID_POWER_DOMAIN UINT32_C(0x11)
+#define SCMI_PROTOCOL_VERSION_POWER_DOMAIN UINT32_C(0x10000)
+
+#define SCMI_PD_DEVICE_STATE_ID_OFF 0
+#define SCMI_PD_DEVICE_STATE_ID_ON 0
+#define SCMI_PD_DEVICE_STATE_ID_MASK 0xFFFFFFF
+#define SCMI_PD_DEVICE_STATE_TYPE (1 << 30)
+
+/*
+ * Identifier of the SCMI Power Domain Management Protocol commands
+ */
+enum scmi_pd_command_id {
+ SCMI_PD_POWER_DOMAIN_ATTRIBUTES = 0x03,
+ SCMI_PD_POWER_STATE_SET = 0x04,
+ SCMI_PD_POWER_STATE_GET = 0x05,
+ SCMI_PD_POWER_STATE_NOTIFY = 0x06,
+};
+
+/*
+ * PROTOCOL_ATTRIBUTES
+ */
+
+struct __attribute((packed)) scmi_pd_protocol_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ uint32_t statistics_address_low;
+ uint32_t statistics_address_high;
+ uint32_t statistics_len;
+};
+
+/*
+ * POWER_DOMAIN_ATTRIBUTES
+ */
+
+struct __attribute((packed)) scmi_pd_power_domain_attributes_a2p {
+ uint32_t domain_id;
+};
+
+#define SCMI_PD_POWER_STATE_SET_ASYNC (1 << 30)
+#define SCMI_PD_POWER_STATE_SET_SYNC (1 << 29)
+
+struct __attribute((packed)) scmi_pd_power_domain_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ uint8_t name[16];
+};
+
+/*
+ * POWER_STATE_SET
+ */
+
+#define SCMI_PD_POWER_STATE_SET_ASYNC_FLAG_MASK (1 << 0)
+
+struct __attribute((packed)) scmi_pd_power_state_set_a2p {
+ uint32_t flags;
+ uint32_t domain_id;
+ uint32_t power_state;
+};
+
+struct __attribute((packed)) scmi_pd_power_state_set_p2a {
+ int32_t status;
+};
+
+/*
+ * POWER_STATE_GET
+ */
+
+struct __attribute((packed)) scmi_pd_power_state_get_a2p {
+ uint32_t domain_id;
+};
+
+struct __attribute((packed)) scmi_pd_power_state_get_p2a {
+ int32_t status;
+ uint32_t power_state;
+};
+
+/*
+ * POWER_STATE_NOTIFY
+ */
+
+struct __attribute((packed)) scmi_pd_power_state_notify_a2p {
+ uint32_t domain_id;
+ uint32_t notify_enable;
+};
+
+struct __attribute((packed)) scmi_pd_power_state_notify_p2a {
+ int32_t status;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* SCMI_POWER_DOMAIN_H */
diff --git a/module/scmi_power_domain/src/Makefile b/module/scmi_power_domain/src/Makefile
new file mode 100644
index 00000000..727fa169
--- /dev/null
+++ b/module/scmi_power_domain/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI Power Domain Protocol
+BS_LIB_SOURCES := mod_scmi_power_domain.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_power_domain/src/mod_scmi_power_domain.c b/module/scmi_power_domain/src/mod_scmi_power_domain.c
new file mode 100644
index 00000000..3f9703fc
--- /dev/null
+++ b/module/scmi_power_domain/src/mod_scmi_power_domain.c
@@ -0,0 +1,605 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI power domain management protocol support.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_power_domain.h>
+#include <mod_log.h>
+#include <mod_power_domain.h>
+#include <mod_scmi.h>
+
+struct scmi_pd_ctx {
+ /* Number of power domains */
+ unsigned int domain_count;
+
+ /* Log module API */
+ const struct mod_log_api *log_api;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /* Power domain module API */
+ const struct mod_pd_restricted_api *pd_api;
+};
+
+static int scmi_pd_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_pd_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_pd_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_pd_power_domain_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_pd_power_state_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_pd_power_state_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables
+ */
+
+static struct scmi_pd_ctx scmi_pd_ctx;
+
+static int (*handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] =
+ scmi_pd_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] =
+ scmi_pd_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_pd_protocol_message_attributes_handler,
+ [SCMI_PD_POWER_DOMAIN_ATTRIBUTES] =
+ scmi_pd_power_domain_attributes_handler,
+ [SCMI_PD_POWER_STATE_SET] =
+ scmi_pd_power_state_set_handler,
+ [SCMI_PD_POWER_STATE_GET] =
+ scmi_pd_power_state_get_handler,
+};
+
+static unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_PD_POWER_DOMAIN_ATTRIBUTES] =
+ sizeof(struct scmi_pd_power_domain_attributes_a2p),
+ [SCMI_PD_POWER_STATE_SET] =
+ sizeof(struct scmi_pd_power_state_set_a2p),
+ [SCMI_PD_POWER_STATE_GET] =
+ sizeof(struct scmi_pd_power_state_get_a2p),
+};
+
+static unsigned int scmi_dev_state_id_lost_ctx_to_pd_state[] = {
+ [SCMI_PD_DEVICE_STATE_ID_OFF] = MOD_PD_STATE_OFF,
+};
+
+static unsigned int scmi_dev_state_id_preserved_ctx_to_pd_state[] = {
+ [SCMI_PD_DEVICE_STATE_ID_ON] = MOD_PD_STATE_ON,
+};
+
+static uint32_t pd_state_to_scmi_dev_state[] = {
+ [MOD_PD_STATE_OFF] = SCMI_PD_DEVICE_STATE_ID_OFF |
+ SCMI_PD_DEVICE_STATE_TYPE,
+ [MOD_PD_STATE_ON] = SCMI_PD_DEVICE_STATE_ID_ON,
+ /* In case of more supported device states review the map functions */
+};
+
+/*
+ * Power domain management protocol implementation
+ */
+
+static int scmi_device_state_to_pd_state(uint32_t scmi_state,
+ unsigned int *pd_state)
+{
+ uint32_t scmi_state_id;
+ bool ctx_lost;
+
+ ctx_lost = !!(scmi_state & SCMI_PD_DEVICE_STATE_TYPE);
+ scmi_state_id = scmi_state & SCMI_PD_DEVICE_STATE_ID_MASK;
+
+ if (ctx_lost) {
+ if (scmi_state_id >=
+ FWK_ARRAY_SIZE(scmi_dev_state_id_lost_ctx_to_pd_state))
+ return FWK_E_PWRSTATE;
+
+ *pd_state = scmi_dev_state_id_lost_ctx_to_pd_state[scmi_state_id];
+ } else {
+ if (scmi_state_id >=
+ FWK_ARRAY_SIZE(scmi_dev_state_id_preserved_ctx_to_pd_state))
+ return FWK_E_PWRSTATE;
+
+ *pd_state = scmi_dev_state_id_preserved_ctx_to_pd_state[scmi_state_id];
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_state_to_scmi_device_state(unsigned int pd_state,
+ uint32_t *scmi_state)
+{
+ if (pd_state >= FWK_ARRAY_SIZE(pd_state_to_scmi_dev_state))
+ return FWK_E_PWRSTATE;
+
+ *scmi_state = pd_state_to_scmi_dev_state[pd_state];
+ return FWK_SUCCESS;
+}
+
+static int scmi_pd_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_POWER_DOMAIN,
+ };
+
+ scmi_pd_ctx.scmi_api->respond(service_id,
+ &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_pd_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_pd_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ return_values.attributes = scmi_pd_ctx.domain_count;
+
+ scmi_pd_ctx.scmi_api->respond(service_id,
+ &return_values, sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_pd_power_domain_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status = FWK_SUCCESS;
+ const struct scmi_pd_power_domain_attributes_a2p *parameters;
+ enum mod_pd_type pd_type;
+ unsigned int domain_idx;
+ fwk_id_t pd_id;
+ unsigned int agent_id;
+ enum scmi_agent_type agent_type;
+ struct scmi_pd_power_domain_attributes_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
+
+ parameters = (const struct scmi_pd_power_domain_attributes_a2p *)payload;
+
+ domain_idx = parameters->domain_id;
+ if (domain_idx > UINT16_MAX) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, domain_idx);
+ if (!fwk_module_is_valid_element_id(pd_id)) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = scmi_pd_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_pd_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_pd_ctx.pd_api->get_domain_type(pd_id, &pd_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ switch (pd_type) {
+ case MOD_PD_TYPE_CORE:
+ /*
+ * For core power domains, the POWER_STATE_SET command is supported
+ * only asynchronously for the PSCI agent. In all other cases, reply
+ * that the command is not supported either synchronously nor
+ * asynchronously.
+ */
+ if (agent_type == SCMI_AGENT_TYPE_PSCI)
+ return_values.attributes = SCMI_PD_POWER_STATE_SET_ASYNC;
+ break;
+
+ case MOD_PD_TYPE_CLUSTER:
+ /*
+ * For cluster power domains, the POWER_STATE_SET command is supported
+ * only synchronously for the PSCI agent. In all other cases, reply
+ * that the command is not supported either synchronously nor
+ * asynchronously.
+ */
+ if (agent_type == SCMI_AGENT_TYPE_PSCI)
+ return_values.attributes = SCMI_PD_POWER_STATE_SET_SYNC;
+ break;
+
+ case MOD_PD_TYPE_DEVICE:
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+ /*
+ * Support only synchronous POWER_STATE_SET for devices for any agent.
+ */
+ return_values.attributes = SCMI_PD_POWER_STATE_SET_SYNC;
+ break;
+
+ case MOD_PD_TYPE_SYSTEM:
+ return_values.status = SCMI_NOT_FOUND;
+ /* Fallthrough. */
+
+ default:
+ goto exit;
+ }
+
+ strncpy((char *)return_values.name, fwk_module_get_name(pd_id),
+ sizeof(return_values.name) - 1);
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ scmi_pd_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_pd_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload)
+{
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ struct scmi_protocol_message_attributes_p2a return_values = {
+ .status = SCMI_NOT_FOUND,
+ };
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p *)
+ payload;
+
+ if ((parameters->message_id < FWK_ARRAY_SIZE(handler_table)) &&
+ (handler_table[parameters->message_id] != NULL))
+ return_values.status = SCMI_SUCCESS;
+
+ scmi_pd_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_power_scp_set_core_state(fwk_id_t pd_id,
+ uint32_t composite_state)
+{
+ int status;
+
+ status = scmi_pd_ctx.pd_api->set_composite_state_async(pd_id, false,
+ composite_state);
+ if (status != FWK_SUCCESS) {
+ scmi_pd_ctx.log_api->log(MOD_LOG_GROUP_ERROR,
+ "[SCMI:power] Failed to send core set request (error %e)\n",
+ status);
+ }
+
+ return status;
+}
+
+static int scmi_pd_power_state_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct scmi_pd_power_state_set_a2p *parameters;
+ bool is_sync;
+ unsigned int agent_id;
+ enum scmi_agent_type agent_type;
+ unsigned int domain_idx;
+ fwk_id_t pd_id;
+ unsigned int pd_power_state;
+ struct scmi_pd_power_state_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+ enum mod_pd_type pd_type;
+
+ parameters = (const struct scmi_pd_power_state_set_a2p *)payload;
+
+ is_sync = !(parameters->flags & SCMI_PD_POWER_STATE_SET_ASYNC_FLAG_MASK);
+
+ status = scmi_pd_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_pd_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ domain_idx = parameters->domain_id;
+ if (domain_idx > UINT16_MAX) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, domain_idx);
+ if (!fwk_module_is_valid_element_id(pd_id)) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = scmi_pd_ctx.pd_api->get_domain_type(pd_id, &pd_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (((pd_type == MOD_PD_TYPE_CORE) ||
+ (pd_type == MOD_PD_TYPE_CLUSTER)) &&
+ (agent_type != SCMI_AGENT_TYPE_PSCI)) {
+
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ switch (pd_type) {
+ case MOD_PD_TYPE_CORE:
+ /*
+ * Async/sync flag is ignored for core power domains as stated
+ * by the specification.
+ */
+ status = scmi_power_scp_set_core_state(pd_id, parameters->power_state);
+ if (status == FWK_E_PARAM)
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ break;
+
+ case MOD_PD_TYPE_CLUSTER:
+ if (!is_sync) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_pd_ctx.pd_api->set_state(pd_id, parameters->power_state);
+ break;
+
+ case MOD_PD_TYPE_DEVICE:
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+ if (!is_sync) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_device_state_to_pd_state(parameters->power_state,
+ &pd_power_state);
+ if (status != FWK_SUCCESS) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ status = scmi_pd_ctx.pd_api->set_state(pd_id, pd_power_state);
+ break;
+
+ case MOD_PD_TYPE_SYSTEM:
+ return_values.status = SCMI_NOT_FOUND;
+ /* Fallthrough. */
+
+ default:
+ goto exit;
+ }
+
+ if (status == FWK_SUCCESS)
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ scmi_pd_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_pd_power_state_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status = FWK_SUCCESS;
+ const struct scmi_pd_power_state_get_a2p *parameters;
+ unsigned int domain_idx;
+ fwk_id_t pd_id;
+ struct scmi_pd_power_state_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+ enum mod_pd_type pd_type;
+ unsigned int pd_power_state;
+ unsigned int power_state;
+
+ parameters = (const struct scmi_pd_power_state_get_a2p *)payload;
+
+ domain_idx = parameters->domain_id;
+ if (domain_idx > UINT16_MAX) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, domain_idx);
+ if (!fwk_module_is_valid_element_id(pd_id)) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = scmi_pd_ctx.pd_api->get_domain_type(pd_id, &pd_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ switch (pd_type) {
+ case MOD_PD_TYPE_CORE:
+ status = scmi_pd_ctx.pd_api->get_composite_state(pd_id, &power_state);
+ break;
+
+ case MOD_PD_TYPE_CLUSTER:
+ status = scmi_pd_ctx.pd_api->get_state(pd_id, &power_state);
+ break;
+
+ case MOD_PD_TYPE_DEVICE:
+ case MOD_PD_TYPE_DEVICE_DEBUG:
+
+ status = scmi_pd_ctx.pd_api->get_state(pd_id, &pd_power_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = pd_state_to_scmi_device_state(pd_power_state,
+ (uint32_t *) &power_state);
+ break;
+
+ case MOD_PD_TYPE_SYSTEM:
+ return_values.status = SCMI_NOT_FOUND;
+ /* Fallthrough. */
+
+ default:
+ goto exit;
+ }
+
+ if (status == FWK_SUCCESS) {
+ return_values.status = SCMI_SUCCESS;
+ return_values.power_state = power_state;
+ }
+
+exit:
+ scmi_pd_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI power module interface
+ */
+static int scmi_pd_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_POWER_DOMAIN;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_pd_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
+ const uint32_t *payload, size_t payload_size, unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Power domain management protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_pd_ctx.scmi_api->respond(service_id,
+ &return_value, sizeof(return_value));
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_pd_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_pd_get_scmi_protocol_id,
+ .message_handler = scmi_pd_message_handler
+};
+
+/*
+ * Framework handlers
+ */
+
+static int scmi_pd_init(fwk_id_t module_id, unsigned int element_count,
+ const void *unused)
+{
+ if (element_count != 0)
+ return FWK_E_SUPPORT;
+
+ scmi_pd_ctx.domain_count = fwk_module_get_element_count(
+ fwk_module_id_power_domain);
+ if (scmi_pd_ctx.domain_count <= 1)
+ return FWK_E_SUPPORT;
+
+ /* Do not expose SYSTEM domain (always the last one) to agents and ... */
+ scmi_pd_ctx.domain_count--;
+ /* ... and expose no more than 0xFFFF number of domains. */
+ if (scmi_pd_ctx.domain_count > UINT16_MAX)
+ scmi_pd_ctx.domain_count = UINT16_MAX;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_pd_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &scmi_pd_ctx.log_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_pd_ctx.scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return fwk_module_bind(fwk_module_id_power_domain, mod_pd_api_id_restricted,
+ &scmi_pd_ctx.pd_api);
+}
+
+static int scmi_pd_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_pd_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+
+/* SCMI Power Domain Management Protocol Definition */
+const struct fwk_module module_scmi_power_domain = {
+ .name = "SCMI Power Domain Management Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_pd_init,
+ .bind = scmi_pd_bind,
+ .process_bind_request = scmi_pd_process_bind_request,
+};
+
+/* No elements, no module configuration data */
+struct fwk_module_config config_scmi_power_domain = {};
diff --git a/module/scmi_sensor/include/internal/scmi_sensor.h b/module/scmi_sensor/include/internal/scmi_sensor.h
new file mode 100644
index 00000000..d89e3ff0
--- /dev/null
+++ b/module/scmi_sensor/include/internal/scmi_sensor.h
@@ -0,0 +1,164 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Control and Management Interface (SCMI) support.
+ */
+
+#ifndef SCMI_SENSOR_H
+#define SCMI_SENSOR_H
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSCMI_PERF SCMI Sensor Management Protocol
+ * @{
+ */
+
+#define SCMI_PROTOCOL_ID_SENSOR UINT32_C(0x15)
+#define SCMI_PROTOCOL_VERSION_SENSOR UINT32_C(0x10000)
+
+/*
+ * Identifiers of the SCMI Sensor Management Protocol commands
+ */
+enum scmi_sensor_command_id {
+ SCMI_SENSOR_DESCRIPTION_GET = 0x003,
+ SCMI_SENSOR_CONFIG_SET = 0x004,
+ SCMI_SENSOR_TRIP_POINT_SET = 0x005,
+ SCMI_SENSOR_READING_GET = 0x006,
+};
+
+/*
+ * PROTOCOL_ATTRIBUTES
+ */
+
+struct __attribute((packed)) scmi_sensor_protocol_attributes_p2a {
+ int32_t status;
+ uint32_t attributes;
+ uint32_t sensor_reg_address_low;
+ uint32_t sensor_reg_address_high;
+ uint32_t sensor_reg_len;
+};
+
+/*
+ * SENSOR_READING_GET
+ */
+
+#define SCMI_SENSOR_PROTOCOL_READING_GET_ASYNC_FLAG_MASK (1 << 0)
+
+struct __attribute((packed)) scmi_sensor_protocol_reading_get_a2p {
+ uint32_t sensor_id;
+ uint32_t flags;
+};
+
+struct __attribute((packed)) scmi_sensor_protocol_reading_get_p2a {
+ int32_t status;
+ uint32_t sensor_value_low;
+ uint32_t sensor_value_high;
+};
+
+/*
+ * SENSOR_DESCRIPTION_GET
+ */
+
+ #define SCMI_SENSOR_DESCS_MAX(MAILBOX_SIZE) \
+ ((sizeof(struct scmi_sensor_protocol_description_get_p2a) < MAILBOX_SIZE) \
+ ? ((MAILBOX_SIZE - \
+ sizeof(struct scmi_sensor_protocol_description_get_p2a)) \
+ / sizeof(struct scmi_sensor_desc)) \
+ : 0)
+
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS 0
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS 11
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS 22
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS 27
+
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_MASK \
+ (UINT32_C(0xFF) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS)
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK \
+ (UINT32_C(0x1F) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS)
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MASK \
+ (UINT32_C(0x1F) \
+ << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS)
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK \
+ (UINT32_C(0x1F) << SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS)
+
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MAX \
+ (int32_t)(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK >> 1)
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MIN \
+ (-(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MAX + 1))
+
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MAX \
+ (int32_t)(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK >> 1)
+#define SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MIN \
+ (-(SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MAX + 1))
+
+#define SCMI_SENSOR_DESC_ATTRIBUTES_HIGH(SENSOR_TYPE, UNIT_MULTIPLIER, \
+ UPDATE_MULTIPLIER, UPDATE_INTERVAL) \
+ ( \
+ (((SENSOR_TYPE) << \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_POS) & \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_TYPE_MASK) | \
+ (((UNIT_MULTIPLIER) << \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_POS) & \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MASK) | \
+ (((UPDATE_MULTIPLIER) << \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_POS) & \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MASK) | \
+ (((UPDATE_INTERVAL) << \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_POS) & \
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK) \
+ )
+
+#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS 0
+#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS 16
+
+#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_MASK \
+ (UINT32_C(0xFFF) << SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS)
+#define SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_MASK \
+ (UINT32_C(0xFFFF) << SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS)
+
+#define SCMI_SENSOR_NUM_SENSOR_FLAGS(NUM_DESCS, NUM_REMAINING_DESCS) \
+ ( \
+ (((NUM_DESCS) << \
+ SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_POS) & \
+ SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_DESCS_MASK) | \
+ (((NUM_REMAINING_DESCS) << \
+ SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_POS) & \
+ SCMI_SENSOR_NUM_SENSOR_FLAGS_NUM_REMAINING_DESCS_MASK) \
+ )
+
+#define SCMI_SENSOR_NAME_LEN 16
+
+struct __attribute((packed)) scmi_sensor_desc {
+ uint32_t sensor_id;
+ uint32_t sensor_attributes_low;
+ uint32_t sensor_attributes_high;
+ char sensor_name[SCMI_SENSOR_NAME_LEN];
+};
+
+struct __attribute((packed)) scmi_sensor_protocol_description_get_a2p {
+ uint32_t desc_index;
+};
+
+struct __attribute((packed)) scmi_sensor_protocol_description_get_p2a {
+ int32_t status;
+ uint32_t num_sensor_flags;
+ struct scmi_sensor_desc sensor_desc[];
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* SCMI_SENSOR_H */
diff --git a/module/scmi_sensor/src/Makefile b/module/scmi_sensor/src/Makefile
new file mode 100644
index 00000000..a177d28e
--- /dev/null
+++ b/module/scmi_sensor/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI Sensor Protocol
+BS_LIB_SOURCES := mod_scmi_sensor.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_sensor/src/mod_scmi_sensor.c b/module/scmi_sensor/src/mod_scmi_sensor.c
new file mode 100644
index 00000000..0822563d
--- /dev/null
+++ b/module/scmi_sensor/src/mod_scmi_sensor.c
@@ -0,0 +1,462 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI sensor management protocol support.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_sensor.h>
+#include <mod_sensor.h>
+#include <mod_scmi.h>
+
+struct scmi_sensor_ctx {
+ unsigned int sensor_count;
+ const struct mod_scmi_from_protocol_api *scmi_api;
+ const struct mod_sensor_api *sensor_api;
+};
+
+static int scmi_sensor_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sensor_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sensor_protocol_msg_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sensor_protocol_desc_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sensor_reading_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables.
+ */
+static struct scmi_sensor_ctx scmi_sensor_ctx;
+
+static int (*handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] =
+ scmi_sensor_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] =
+ scmi_sensor_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_sensor_protocol_msg_attributes_handler,
+ [SCMI_SENSOR_DESCRIPTION_GET] =
+ scmi_sensor_protocol_desc_get_handler,
+ [SCMI_SENSOR_READING_GET] = scmi_sensor_reading_get_handler
+};
+
+static unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_SENSOR_DESCRIPTION_GET] =
+ sizeof(struct scmi_sensor_protocol_description_get_a2p),
+ [SCMI_SENSOR_READING_GET] =
+ sizeof(struct scmi_sensor_protocol_reading_get_a2p),
+};
+
+/*
+ * Sensor management protocol implementation
+ */
+static int scmi_sensor_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_SENSOR,
+ };
+
+ scmi_sensor_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_sensor_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = scmi_sensor_ctx.sensor_count,
+ .sensor_reg_len = 0, /* Unsupported */
+ };
+
+ scmi_sensor_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_protocol_msg_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ struct scmi_protocol_message_attributes_p2a return_values;
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p *)
+ payload;
+
+ if ((parameters->message_id < FWK_ARRAY_SIZE(handler_table)) &&
+ (handler_table[parameters->message_id] != NULL)) {
+ return_values = (struct scmi_protocol_message_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ /* All commands have an attributes value of 0 */
+ .attributes = 0,
+ };
+ } else
+ return_values.status = SCMI_NOT_FOUND;
+
+ scmi_sensor_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_protocol_desc_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ size_t payload_size;
+ size_t max_payload_size;
+ const struct scmi_sensor_protocol_description_get_a2p *parameters =
+ (const struct scmi_sensor_protocol_description_get_a2p *)payload;
+ struct scmi_sensor_desc desc = {};
+ unsigned int num_descs, desc_index, desc_index_max;
+ struct mod_sensor_info sensor_info;
+ struct scmi_sensor_protocol_description_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
+ fwk_id_t sensor_id;
+
+ payload_size = sizeof(return_values);
+
+ status = scmi_sensor_ctx.scmi_api->get_max_payload_size(service_id,
+ &max_payload_size);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (SCMI_SENSOR_DESCS_MAX(max_payload_size) == 0) {
+ /* Can't even fit one sensor description in the payload */
+ assert(false);
+ status = FWK_E_SIZE;
+ goto exit;
+ }
+
+ parameters =
+ (const struct scmi_sensor_protocol_description_get_a2p *)payload;
+ desc_index = parameters->desc_index;
+
+ if (desc_index >= scmi_sensor_ctx.sensor_count) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ num_descs = FWK_MIN(SCMI_SENSOR_DESCS_MAX(max_payload_size),
+ (scmi_sensor_ctx.sensor_count - desc_index));
+ desc_index_max = (desc_index + num_descs - 1);
+
+ for (; desc_index <= desc_index_max; ++desc_index,
+ payload_size += sizeof(desc)) {
+
+ desc = (struct scmi_sensor_desc) {
+ .sensor_id = desc_index,
+ .sensor_attributes_low = 0, /* None supported */
+ };
+
+ sensor_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_SENSOR, desc_index);
+ if (!fwk_module_is_valid_element_id(sensor_id)) {
+ /* domain_idx did not map to a sensor device */
+ assert(false);
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = scmi_sensor_ctx.sensor_api->get_info(sensor_id, &sensor_info);
+ if (status != FWK_SUCCESS) {
+ /* Unable to get sensor info */
+ assert(false);
+ goto exit;
+ }
+
+ if (sensor_info.type >= MOD_SENSOR_TYPE_COUNT) {
+ /* Invalid sensor type */
+ assert(false);
+ goto exit;
+ }
+
+ if ((sensor_info.unit_multiplier <
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MIN) ||
+ (sensor_info.unit_multiplier >
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UNIT_MULTIPLIER_MAX)) {
+
+ /* Sensor unit multiplier out of range */
+ assert(false);
+ goto exit;
+ }
+
+ if ((sensor_info.update_interval_multiplier <
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MIN) ||
+ (sensor_info.update_interval_multiplier >
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_MULTIPLIER_MAX)) {
+
+ /* Sensor update interval multiplier is out of range */
+ assert(false);
+ goto exit;
+ }
+
+ if (sensor_info.update_interval >=
+ SCMI_SENSOR_DESC_ATTRS_HIGH_SENSOR_UPDATE_INTERVAL_MASK) {
+
+ /* Update interval is too big to fit in it's mask */
+ assert(false);
+ goto exit;
+ }
+
+ desc.sensor_attributes_high =
+ SCMI_SENSOR_DESC_ATTRIBUTES_HIGH(sensor_info.type,
+ sensor_info.unit_multiplier,
+ (uint32_t)sensor_info.update_interval_multiplier,
+ (uint32_t)sensor_info.update_interval);
+
+ /*
+ * Copy sensor name into description struct. Copy n-1 chars to ensure a
+ * NULL terminator at the end. (struct has been zeroed out)
+ */
+ strncpy(desc.sensor_name, sensor_info.name,
+ sizeof(desc.sensor_name) - 1);
+
+ status = scmi_sensor_ctx.scmi_api->write_payload(service_id,
+ payload_size, &desc, sizeof(struct scmi_sensor_desc));
+ if (status != FWK_SUCCESS) {
+ /* Failed to write sensor description into message payload */
+ assert(false);
+ goto exit;
+ }
+ }
+
+ return_values = (struct scmi_sensor_protocol_description_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .num_sensor_flags = SCMI_SENSOR_NUM_SENSOR_FLAGS(num_descs,
+ (scmi_sensor_ctx.sensor_count - desc_index_max - 1))
+ };
+
+ status = scmi_sensor_ctx.scmi_api->write_payload(service_id, 0,
+ &return_values, sizeof(return_values));
+ if (status != FWK_SUCCESS)
+ return_values.status = SCMI_GENERIC_ERROR;
+
+exit:
+ scmi_sensor_ctx.scmi_api->respond(service_id,
+ (return_values.status == SCMI_SUCCESS) ?
+ NULL : &return_values.status,
+ (return_values.status == SCMI_SUCCESS) ?
+ payload_size : sizeof(return_values.status));
+
+ return status;
+}
+
+static int scmi_sensor_reading_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ const struct scmi_sensor_protocol_reading_get_a2p *parameters;
+ struct scmi_sensor_protocol_reading_get_p2a return_values;
+ uint64_t sensor_value;
+ uint32_t flags;
+ fwk_id_t sensor_id;
+ int status;
+
+ parameters = (const struct scmi_sensor_protocol_reading_get_a2p *)payload;
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ if (parameters->sensor_id >= scmi_sensor_ctx.sensor_count) {
+ /* Sensor does not exist */
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ /* Reject asynchronous read requests for now */
+ flags = parameters->flags;
+ if (flags & SCMI_SENSOR_PROTOCOL_READING_GET_ASYNC_FLAG_MASK) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ status = FWK_SUCCESS;
+ goto exit;
+ }
+
+ sensor_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_SENSOR,
+ parameters->sensor_id);
+
+ status = scmi_sensor_ctx.sensor_api->get_value(sensor_id, &sensor_value);
+ if (status == FWK_SUCCESS) {
+ return_values = (struct scmi_sensor_protocol_reading_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .sensor_value_low = (uint32_t)sensor_value,
+ .sensor_value_high = (uint32_t)(sensor_value >> 32),
+ };
+ } else if (status == FWK_E_PWRSTATE) {
+ /* The sensor is currently unpowered */
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_HARDWARE_ERROR;
+ } else {
+ /* Unable to read sensor */
+ assert(false);
+ }
+
+exit:
+ scmi_sensor_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI sensor module interface
+ */
+static int scmi_sensor_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_SENSOR;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_message_handler(fwk_id_t protocol_id,
+ fwk_id_t service_id,
+ const uint32_t *payload,
+ size_t payload_size,
+ unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Sensor management protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ /* Incorrect payload size or message is not supported */
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_sensor_ctx.scmi_api->respond(service_id, &return_value,
+ sizeof(return_value));
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_sensor_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_sensor_get_scmi_protocol_id,
+ .message_handler = scmi_sensor_message_handler
+};
+
+/*
+ * Framework interface
+ */
+static int scmi_sensor_init(fwk_id_t module_id,
+ unsigned int element_count,
+ const void *unused)
+{
+ if (element_count != 0) {
+ /* This module should not have any elements */
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+
+ scmi_sensor_ctx.sensor_count = fwk_module_get_element_count(
+ FWK_ID_MODULE(FWK_MODULE_IDX_SENSOR));
+ if (scmi_sensor_ctx.sensor_count == 0)
+ return FWK_E_SUPPORT;
+
+ /* SCMI protocol uses a 16 bit number to store the number of sensors.
+ * So expose no more than 0xFFFF number of sensors. */
+ if (scmi_sensor_ctx.sensor_count > UINT16_MAX)
+ scmi_sensor_ctx.sensor_count = UINT16_MAX;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI,
+ MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_sensor_ctx.scmi_api);
+ if (status != FWK_SUCCESS) {
+ /* Failed to bind to SCMI module */
+ assert(false);
+ return status;
+ }
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SENSOR),
+ FWK_ID_API(FWK_MODULE_IDX_SENSOR, 0),
+ &scmi_sensor_ctx.sensor_api);
+ if (status != FWK_SUCCESS) {
+ /* Failed to bind to sensor module */
+ assert(false);
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sensor_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_sensor_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_scmi_sensor = {
+ .name = "SCMI sensor management",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_sensor_init,
+ .bind = scmi_sensor_bind,
+ .process_bind_request = scmi_sensor_process_bind_request,
+};
+
+/* No elements, no module configuration data */
+struct fwk_module_config config_scmi_sensor = {};
diff --git a/module/scmi_system_power/include/internal/scmi_system_power.h b/module/scmi_system_power/include/internal/scmi_system_power.h
new file mode 100644
index 00000000..bcb5b278
--- /dev/null
+++ b/module/scmi_system_power/include/internal/scmi_system_power.h
@@ -0,0 +1,86 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SCMI_SYSTEM_POWER_H
+#define SCMI_SYSTEM_POWER_H
+
+#define SCMI_PROTOCOL_ID_SYS_POWER UINT32_C(0x12)
+#define SCMI_PROTOCOL_VERSION_SYS_POWER UINT32_C(0x10000)
+
+/*
+ * Identifiers of the SCMI System Power Management Protocol commands
+ */
+enum scmi_sys_power_command_id {
+ SCMI_SYS_POWER_STATE_SET = 0x003,
+ SCMI_SYS_POWER_STATE_GET = 0x004,
+ SCMI_SYS_POWER_STATE_NOTIFY = 0x005,
+};
+
+/*
+ * PROTOCOL_MESSAGE_ATTRIBUTES
+ */
+#define SYS_POWER_STATE_SET_ATTRIBUTES_WARM_RESET UINT32_C(0x80000000)
+#define SYS_POWER_STATE_SET_ATTRIBUTES_SUSPEND UINT32_C(0x40000000)
+
+/*
+ * SYSTEM_POWER_STATE_SET
+ */
+
+#define STATE_SET_FLAGS_MASK 0x1
+#define STATE_SET_FLAGS_GRACEFUL_REQUEST 0x1
+
+enum scmi_system_state {
+ SCMI_SYSTEM_STATE_SHUTDOWN,
+ SCMI_SYSTEM_STATE_COLD_RESET,
+ SCMI_SYSTEM_STATE_WARM_RESET,
+ SCMI_SYSTEM_STATE_POWER_UP,
+ SCMI_SYSTEM_STATE_SUSPEND,
+ SCMI_SYSTEM_STATE_MAX
+};
+
+struct __attribute((packed)) scmi_sys_power_state_set_a2p {
+ uint32_t flags;
+ uint32_t system_state;
+};
+
+struct __attribute((packed)) scmi_sys_power_state_set_p2a {
+ int32_t status;
+};
+
+/*
+ * SYSTEM_POWER_STATE_GET
+ */
+
+struct __attribute((packed)) scmi_sys_power_state_get_p2a {
+ int32_t status;
+ uint32_t system_state;
+};
+
+/*
+ * SYSTEM_POWER_STATE_NOTIFY
+ */
+
+#define STATE_NOTIFY_FLAGS_MASK 0x1
+
+struct __attribute((packed)) scmi_sys_power_state_notify_a2p {
+ uint32_t flags;
+};
+
+struct __attribute((packed)) scmi_sys_power_state_notify_p2a {
+ int32_t status;
+};
+
+/*
+ * SYSTEM_POWER_STATE_NOTIFIER
+ */
+
+struct __attribute((packed)) scmi_sys_power_state_notifier_p2a {
+ uint32_t agent_id;
+ uint32_t system_state;
+};
+
+#endif
diff --git a/module/scmi_system_power/include/mod_scmi_system_power.h b/module/scmi_system_power/include/mod_scmi_system_power.h
new file mode 100644
index 00000000..ff8157d9
--- /dev/null
+++ b/module/scmi_system_power/include/mod_scmi_system_power.h
@@ -0,0 +1,69 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_SCMI_SYSTEM_POWER_H
+#define MOD_SCMI_SYSTEM_POWER_H
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSCMI_SYS SCMI System Power Management Protocol
+ * @{
+ */
+
+/*!
+ * \brief SCMI system views.
+ */
+enum mod_scmi_system_view {
+ /*! OSPM view of the system */
+ MOD_SCMI_SYSTEM_VIEW_OSPM,
+
+ /*! Full view of the system */
+ MOD_SCMI_SYSTEM_VIEW_FULL,
+
+ MOD_SCMI_SYSTEM_VIEW_COUNT,
+};
+
+/*!
+ * \brief SCMI System Power Management Protocol configuration data.
+ */
+struct mod_scmi_system_power_config {
+ /*! System view */
+ enum mod_scmi_system_view system_view;
+
+ /*!
+ * \brief Identifier of the power domain to target for a system wake-up.
+ *
+ * \note This is only used with the OSPM view.
+ */
+ fwk_id_t wakeup_power_domain_id;
+
+ /*!
+ * \brief Composite state for a system wake-up.
+ *
+ * \note This is only used with the OSPM view.
+ */
+ uint32_t wakeup_composite_state;
+
+ /*!
+ * \brief System suspend state.
+ */
+ unsigned int system_suspend_state;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SCMI_SYSTEM_POWER_H */
diff --git a/module/scmi_system_power/src/Makefile b/module/scmi_system_power/src/Makefile
new file mode 100644
index 00000000..de13c175
--- /dev/null
+++ b/module/scmi_system_power/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SCMI System Power Protocol
+BS_LIB_SOURCES := mod_scmi_system_power.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/scmi_system_power/src/mod_scmi_system_power.c b/module/scmi_system_power/src/mod_scmi_system_power.c
new file mode 100644
index 00000000..752ef3d7
--- /dev/null
+++ b/module/scmi_system_power/src/mod_scmi_system_power.c
@@ -0,0 +1,443 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_system_power.h>
+#include <mod_power_domain.h>
+#include <mod_scmi.h>
+#include <mod_scmi_system_power.h>
+
+struct scmi_sys_power_ctx {
+ const struct mod_scmi_system_power_config *config;
+ const struct mod_scmi_from_protocol_api *scmi_api;
+ const struct mod_pd_restricted_api *pd_api;
+ fwk_id_t system_power_domain_id;
+};
+
+static int scmi_sys_power_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sys_power_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sys_power_msg_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sys_power_state_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_sys_power_state_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables
+ */
+static struct scmi_sys_power_ctx scmi_sys_power_ctx;
+
+static int (* const handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] = scmi_sys_power_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] = scmi_sys_power_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = scmi_sys_power_msg_attributes_handler,
+ [SCMI_SYS_POWER_STATE_SET] = scmi_sys_power_state_set_handler,
+ [SCMI_SYS_POWER_STATE_GET] = scmi_sys_power_state_get_handler,
+};
+
+static const unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_SYS_POWER_STATE_SET] =
+ sizeof(struct scmi_sys_power_state_set_a2p),
+ [SCMI_SYS_POWER_STATE_GET] = 0,
+};
+
+static enum mod_pd_system_shutdown system_state2system_shutdown[] = {
+ [SCMI_SYSTEM_STATE_SHUTDOWN] = MOD_PD_SYSTEM_SHUTDOWN,
+ [SCMI_SYSTEM_STATE_COLD_RESET] = MOD_PD_SYSTEM_COLD_RESET,
+ [SCMI_SYSTEM_STATE_WARM_RESET] = MOD_PD_SYSTEM_WARM_RESET,
+};
+
+static int system_state_get(enum scmi_system_state *system_state)
+{
+ int status;
+ unsigned int state;
+
+ status = scmi_sys_power_ctx.pd_api->get_state(
+ scmi_sys_power_ctx.system_power_domain_id, &state);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ switch (state) {
+ case MOD_PD_STATE_OFF:
+ *system_state = SCMI_SYSTEM_STATE_SHUTDOWN;
+ break;
+
+ case MOD_PD_STATE_ON:
+ *system_state = SCMI_SYSTEM_STATE_POWER_UP;
+ break;
+
+ default:
+ *system_state = SCMI_SYSTEM_STATE_SUSPEND;
+ }
+
+ return FWK_SUCCESS;
+};
+
+/*
+ * PROTOCOL_VERSION
+ */
+static int scmi_sys_power_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_SYS_POWER,
+ };
+
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return FWK_SUCCESS;
+}
+
+/*
+ * PROTOCOL_ATTRIBUTES
+ */
+static int scmi_sys_power_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return FWK_SUCCESS;
+}
+
+/*
+ * PROTOCOL_MESSAGE_ATTRIBUTES
+ */
+static int scmi_sys_power_msg_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values;
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p*)payload;
+ message_id = parameters->message_id;
+
+ if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
+ (handler_table[message_id] == NULL)) {
+
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ return_values = (struct scmi_protocol_message_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ if (message_id == SCMI_SYS_POWER_STATE_SET) {
+ return_values.attributes |= SYS_POWER_STATE_SET_ATTRIBUTES_SUSPEND |
+ SYS_POWER_STATE_SET_ATTRIBUTES_WARM_RESET;
+ }
+
+exit:
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
+ sizeof(return_values.status));
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * SYSTEM_POWER_STATE_SET
+ */
+static int scmi_sys_power_state_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status = FWK_SUCCESS;
+ const struct scmi_sys_power_state_set_a2p *parameters;
+ struct scmi_sys_power_state_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
+ unsigned int agent_id;
+ enum scmi_agent_type agent_type;
+ enum mod_pd_system_shutdown system_shutdown;
+ enum scmi_system_state system_state;
+
+ parameters = (const struct scmi_sys_power_state_set_a2p *)payload;
+
+ if (parameters->flags & (~STATE_SET_FLAGS_MASK)) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_sys_power_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if ((agent_type != SCMI_AGENT_TYPE_PSCI) &&
+ (agent_type != SCMI_AGENT_TYPE_MANAGEMENT)) {
+
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ /*
+ * Graceful request from MCP not supported yet as we need the
+ * notifications to do that.
+ */
+ if (parameters->flags & STATE_SET_FLAGS_GRACEFUL_REQUEST) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ switch (parameters->system_state) {
+ case SCMI_SYSTEM_STATE_SHUTDOWN:
+ case SCMI_SYSTEM_STATE_COLD_RESET:
+ case SCMI_SYSTEM_STATE_WARM_RESET:
+ system_shutdown =
+ system_state2system_shutdown[parameters->system_state];
+ status = scmi_sys_power_ctx.pd_api->system_shutdown(system_shutdown);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ break;
+
+ case SCMI_SYSTEM_STATE_SUSPEND:
+ status = scmi_sys_power_ctx.pd_api->system_suspend(
+ scmi_sys_power_ctx.config->system_suspend_state);
+ if (status != FWK_SUCCESS) {
+ if (status == FWK_E_STATE) {
+ status = FWK_SUCCESS;
+ return_values.status = SCMI_DENIED;
+ }
+ goto exit;
+ }
+ break;
+
+ case SCMI_SYSTEM_STATE_POWER_UP:
+ if ((agent_type != SCMI_AGENT_TYPE_MANAGEMENT) ||
+ (scmi_sys_power_ctx.config->system_view !=
+ MOD_SCMI_SYSTEM_VIEW_OSPM)) {
+
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = system_state_get(&system_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if ((system_state != SCMI_SYSTEM_STATE_SHUTDOWN) &&
+ (system_state != SCMI_SYSTEM_STATE_SUSPEND)) {
+
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ status = scmi_sys_power_ctx.pd_api->set_composite_state(
+ scmi_sys_power_ctx.config->wakeup_power_domain_id,
+ scmi_sys_power_ctx.config->wakeup_composite_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+ break;
+
+ default:
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ };
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
+ sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * SYSTEM_POWER_STATE_GET
+ */
+static int scmi_sys_power_state_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status = FWK_SUCCESS;
+ struct scmi_sys_power_state_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
+ enum scmi_system_state system_state;
+ unsigned int agent_id;
+ enum scmi_agent_type agent_type;
+
+ status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_sys_power_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_NOT_SUPPORTED;
+ if (scmi_sys_power_ctx.config->system_view == MOD_SCMI_SYSTEM_VIEW_FULL)
+ goto exit;
+ else {
+ if (agent_type == SCMI_AGENT_TYPE_PSCI)
+ goto exit;
+
+ status = system_state_get(&system_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values = (struct scmi_sys_power_state_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .system_state = system_state,
+ };
+ }
+
+exit:
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_values,
+ (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
+ sizeof(return_values.status));
+
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI system power module interface
+ */
+static int scmi_sys_power_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_SYS_POWER;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sys_power_handler(fwk_id_t protocol_id,
+ fwk_id_t service_id,
+ const uint32_t *payload,
+ size_t payload_size,
+ unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] System power protocol table sizes not consistent");
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+ assert(payload != NULL);
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ /* Incorrect payload size or message is not supported */
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_sys_power_ctx.scmi_api->respond(service_id, &return_value,
+ sizeof(return_value));
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_sys_power_mod_scmi_to_protocol = {
+ .get_scmi_protocol_id = scmi_sys_power_get_scmi_protocol_id,
+ .message_handler = scmi_sys_power_handler,
+};
+
+/*
+ * Framework handlers
+ */
+static int scmi_sys_power_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ scmi_sys_power_ctx.config = data;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sys_power_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ int pd_count;
+
+ if (round != 0)
+ return FWK_SUCCESS;
+
+ /* Bind to SCMI module */
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI,
+ MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_sys_power_ctx.scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Bind to POWER DOMAIN module */
+ status = fwk_module_bind(fwk_module_id_power_domain,
+ mod_pd_api_id_restricted, &scmi_sys_power_ctx.pd_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ pd_count = fwk_module_get_element_count(fwk_module_id_power_domain);
+ if (pd_count <= 0)
+ return FWK_E_SUPPORT;
+
+ scmi_sys_power_ctx.system_power_domain_id =
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, pd_count - 1);
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_sys_power_process_bind_request(fwk_id_t source_id,
+ fwk_id_t _target_id, fwk_id_t api_id, const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_sys_power_mod_scmi_to_protocol;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_scmi_system_power = {
+ .name = "SCMI System Power Management Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_sys_power_init,
+ .bind = scmi_sys_power_bind,
+ .process_bind_request = scmi_sys_power_process_bind_request,
+};
diff --git a/module/sds/include/mod_sds.h b/module/sds/include/mod_sds.h
new file mode 100644
index 00000000..cd75a396
--- /dev/null
+++ b/module/sds/include/mod_sds.h
@@ -0,0 +1,187 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_SDS_H
+#define MOD_SDS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSDS Shared Data Storage
+ *
+ * \details Provides a framework for the structured storage of data that is
+ * shared between the SCP Firmware and application processor firmware.
+ *
+ * @{
+ */
+
+/*
+ * Bit positions for the fields that make up a structure's unique,
+ * 32-bit identifier. This value consists of the 16-bit ID, an 8-bit major
+ * version number and an 8-bit minor version number.
+ */
+
+/*! Position of the identifier field. */
+#define MOD_SDS_ID_IDENTIFIER_POS 0
+/*! Position of the minor version field. */
+#define MOD_SDS_ID_VERSION_MINOR_POS 16
+/*! Position of the major version field. */
+#define MOD_SDS_ID_VERSION_MAJOR_POS 24
+
+/*
+ * Masks for the structure identifier fields.
+ */
+
+/*! Mask for the identifier field. */
+#define MOD_SDS_ID_IDENTIFIER_MASK 0x0000FFFF
+/*! Mask for the minor version field. */
+#define MOD_SDS_ID_VERSION_MINOR_MASK 0x00FF0000
+/*! Mask for the major version field. */
+#define MOD_SDS_ID_VERSION_MAJOR_MASK 0xFF000000
+
+/*!
+ * \brief Element descriptor that describes an SDS structure that will be
+ * automatically created during element initialization.
+ */
+struct mod_sds_structure_desc {
+ /*! Identifier of the structure to be created. */
+ uint32_t id;
+ /*! Size, in bytes, of the structure. */
+ size_t size;
+ /*!
+ * Payload of the structure. If not equal to NULL, as part of the
+ * initialization of the module's elements, the payload of the structure
+ * identified by 'id' is initalized/updated to the value pointed to by
+ * 'payload'.
+ */
+ const void *payload;
+ /*! Set the valid flag in the structure if true. */
+ bool finalize;
+};
+
+/*!
+ * \brief Module configuration.
+ */
+struct mod_sds_config {
+ /*! Base address of the region used for shared data storage */
+ uintptr_t region_base_address;
+
+ /*!
+ * Size, in bytes, of the SDS Memory Region. If the SDS module is
+ * initialized and a region already exists then the region will be
+ * resized to match the size given here. The existing region may be
+ * grown or shrunk, as required. In the latter case the new size must
+ * be sufficient to hold the existing contents and if it is not then
+ * the initialization process will fail.
+ */
+ size_t region_size;
+
+ /*! Identifier of the clock that this module depends on */
+ fwk_id_t clock_id;
+};
+
+/*!
+ * \brief Module interface.
+ */
+struct mod_sds_api {
+ /*!
+ * \brief Write data within a Shared Data Structure.
+ *
+ * \details Write data to a given offset within a Shared Data Structure. The
+ * SDS module ensures that the data being written is properly
+ * contained within the structure but it is the caller's responsibility
+ * to ensure that the correct field or fields are being updated.
+ *
+ * \note A single write may update a portion of a field, a complete
+ * field, or multiple fields, depending on the definition of the
+ * structure and the size of the data being written.
+ *
+ * \param structure_id The identifier of the Shared Data Structure into
+ * which data will be written.
+ *
+ * \param offset The offset, in bytes, of the field within the Shared Data
+ * Structure.
+ *
+ * \param data Pointer to the data that will be written into the Shared Data
+ * Structure's field.
+ *
+ * \param size Size, in bytes, of the data to be written.
+ *
+ * \retval FWK_SUCCESS Data was successfully written to the structure.
+ * \retval FWK_E_PARAM The data pointer parameter was NULL.
+ * \retval FWK_E_PARAM An invalid structure identifier was provided.
+ * \retval FWK_E_RANGE The field extends outside of the structure bounds.
+ */
+ int (*struct_write)(uint32_t structure_id, unsigned int offset,
+ const void *data, size_t size);
+
+ /*!
+ * \brief Read data from within a Shared Data Structure.
+ *
+ * \details Read data from a given offset within a Shared Data Structure.
+ * The SDS module ensures that the data being read is contained
+ * completely within the structure but it is the caller's
+ * responsibility to ensure that the correct field(s) are being read.
+ *
+ * A single read may return only a portion of a field, a complete
+ * field, or multiple fields, depending on the definition of the
+ * structure and the value of the size parameter.
+ *
+ * \note Reading from a structure that has not been finalized is permitted
+ * but discouraged.
+ *
+ * \param structure_id The identifier of the Shared Data Structure from
+ * which data will be read.
+ *
+ * \param offset The offset, in bytes, of the field within the Shared Data
+ * Structure.
+ *
+ * \param data Pointer to storage for the field data that will be read.
+ *
+ * \param size Size, in bytes, of the storage pointed to by the data
+ * parameter.
+ *
+ * \retval FWK_SUCCESS Data was successfully read from the structure.
+ * \retval FWK_E_PARAM The data pointer parameter was NULL.
+ * \retval FWK_E_PARAM An invalid structure identifier was provided.
+ * \retval FWK_E_RANGE The field extends outside of the structure bounds.
+ */
+ int (*struct_read)(uint32_t structure_id, unsigned int offset, void *data,
+ size_t size);
+
+ /*!
+ * \brief Mark a Shared Data Structure as valid and ready for use by
+ * application processor firmware.
+ *
+ * \param structure_id The identifier of the Shared Data Structure to
+ * finalize.
+ *
+ * \retval FWK_SUCCESS The structure was successfully finalized.
+ * \retval FWK_E_PARAM An invalid structure identifier was provided.
+ * \retval FWK_E_STATE The structure has already been finalized.
+ */
+ int (*struct_finalize)(uint32_t structure_id);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SDS_H */
diff --git a/module/sds/src/Makefile b/module/sds/src/Makefile
new file mode 100644
index 00000000..e813dc5b
--- /dev/null
+++ b/module/sds/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := "Shared Data Storage"
+BS_LIB_SOURCES = mod_sds.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/sds/src/mod_sds.c b/module/sds/src/mod_sds.c
new file mode 100644
index 00000000..98144557
--- /dev/null
+++ b/module/sds/src/mod_sds.c
@@ -0,0 +1,620 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_notification.h>
+#include <mod_clock.h>
+#include <mod_sds.h>
+
+/* Arbitrary, 16 bit value that indicates a valid SDS Memory Region */
+#define REGION_SIGNATURE 0xAA7A
+/* The minor version of the SDS schema supported by this implementation */
+#define SUPPORTED_VERSION_MINOR 0x0
+/* The major version of the SDS schema supported by this implementation */
+#define SUPPORTED_VERSION_MAJOR 0x1
+
+/* Minimum required byte alignment for fields within structures */
+#define MIN_FIELD_ALIGNMENT 4
+/* Minimum required byte alignment for structures within the region */
+#define MIN_STRUCT_ALIGNMENT 8
+/* Minimum structure size in bytes */
+#define MIN_STRUCT_SIZE 4
+
+/* Header containing Shared Data Structure metadata */
+struct structure_header {
+ /*
+ * Compound identifier value consisting of a linear structure ID, a major
+ * version, and a minor version. Together these uniquely identify the
+ * structure.
+ */
+ uint32_t id;
+
+ /*
+ * Flag indicating whether the structure's content is valid. Always false
+ * initially, the flag is set to true when the structure is finalized.
+ */
+ bool valid : 1;
+
+ /*
+ * Size, in bytes, of the structure. The size of the header is not included,
+ * only the structure content. If padding was used to maintain alignment
+ * during the structure's creation then the additional space used for the
+ * padding will be reflected in this value.
+ */
+ uint32_t size : 23;
+
+ /* Reserved */
+ uint32_t reserved : 8;
+};
+
+/* Region Descriptor describing the SDS Memory Region */
+struct region_descriptor {
+ /*
+ * Field used to determine the presence, or absence, of an SDS Memory Region
+ * and Region Descriptor. When the region is initialized the signature field
+ * is given a fixed value according to the REGION_SIGNATURE definition.
+ */
+ uint16_t signature;
+
+ /*
+ * The total number of structures, finalized or otherwise, within the SDS
+ * Memory Region.
+ */
+ uint16_t structure_count : 8;
+
+ /* The minor version component of the implemented SDS specification */
+ uint16_t version_minor : 4;
+
+ /* The major version component of the implemented SDS specification */
+ uint16_t version_major : 4;
+
+ /*
+ * The total size of the SDS Memory Region, in bytes. The value includes the
+ * size of the Region Descriptor because the descriptor is located within
+ * the region that it describes. This is in contrast to structure headers,
+ * which are considered to be prepended to the memory allocated for the
+ * structure content.
+ */
+ uint32_t region_size;
+};
+
+/* Module context structure*/
+struct sds_ctx {
+ /* Pointer to the module configuration. */
+ const struct mod_sds_config *module_config;
+
+ /* Pointer to the base of the SDS Memory Region. */
+ uint8_t *mem_base;
+
+ /* Size of the SDS Memory Region in bytes. */
+ unsigned int mem_size;
+
+ /* Remaining, unused memory in the SDS Memory Region, in bytes. */
+ unsigned int mem_free;
+
+ /* Pointer to the next free memory address in the SDS Memory Region. */
+ uint8_t *mem_next_free;
+
+ /* Pointer to the Region Descriptor structure at the memory region base. */
+ struct region_descriptor *region_desc;
+};
+
+/* Module context */
+static struct sds_ctx ctx;
+
+/*
+ * Static functions
+ */
+
+/*
+ * Perform some tests to determine whether any of the fields within a Structure
+ * Header contain obviously invalid data.
+ */
+static bool header_is_valid(struct structure_header *header)
+{
+ if (header->id == 0)
+ return false; /* Zero is not a valid identifier */
+
+ if ((header->id >> MOD_SDS_ID_VERSION_MAJOR_POS) == 0)
+ return false; /* 0 is not a valid major version */
+
+ if (header->size < FWK_ALIGN_NEXT(MIN_STRUCT_SIZE, MIN_STRUCT_ALIGNMENT))
+ return false; /* Padded structure size is less than the minimum */
+
+ if (header->size > (ctx.mem_size - sizeof(struct structure_header)))
+ /* Structure exceeds the capacity of the SDS Memory Region */
+ return false;
+
+ if ((header->size % MIN_STRUCT_ALIGNMENT) != 0)
+ return false; /* Structure does not meet alignment requirements */
+
+ return true;
+}
+
+static bool validate_structure_access(uint32_t structure_size, uint32_t offset,
+ size_t access_size)
+{
+ if ((offset >= structure_size) || (access_size > structure_size))
+ return FWK_E_PARAM;
+
+ if ((structure_size - offset) < access_size)
+ return FWK_E_PARAM;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Search the SDS Memory Region for a given structure ID and return a
+ * copy of the Structure Header that holds its information. Optionally, a
+ * uint8_t pointer pointer may be provided to retrieve a pointer to the base
+ * address of the structure content.
+ *
+ * The validity of the header is checked before its contents are returned. These
+ * tests check that the structure size is sane, and that the entire contents of
+ * the structure are contained within the SDS Memory Region.
+ *
+ * If the address of the structure header in the SDS Memory Region is needed
+ * (as opposed to a copy of the header contents) then this can be calculated by
+ * subtracting the size of the header from the structure base address obtained
+ * from this function.
+ *
+ * If a structure with the given ID is not present then FWK_E_PARAM is returned.
+ */
+static int get_structure_info(uint32_t structure_id,
+ struct structure_header *header,
+ uint8_t **structure_base)
+{
+ unsigned int struct_idx;
+ struct structure_header current_header;
+ uint32_t offset;
+
+ offset = sizeof(struct region_descriptor);
+
+ /* Iterate over structure headers to find one with a matching ID */
+ for (struct_idx = 0; struct_idx < ctx.region_desc->structure_count;
+ struct_idx++) {
+ current_header = *(struct structure_header *)(ctx.mem_base + offset);
+ if (!header_is_valid(&current_header))
+ return FWK_E_DATA;
+
+ if (current_header.id == structure_id) {
+ if (structure_base != NULL)
+ *structure_base = ((uint8_t *)(ctx.mem_base + offset)) +
+ sizeof(struct structure_header);
+
+ *header = current_header;
+ return FWK_SUCCESS;
+ }
+
+ offset += current_header.size;
+ offset += sizeof(struct structure_header);
+ if (offset >= ctx.mem_size)
+ return FWK_E_RANGE;
+ }
+
+ return FWK_E_PARAM;
+}
+
+/*
+ * Search the SDS Memory Region to determine if a structure with the given ID
+ * is present.
+ */
+static bool structure_exists(uint32_t structure_id)
+{
+ int status;
+ struct structure_header header;
+
+ status = get_structure_info(structure_id, &header, NULL);
+ return status == FWK_SUCCESS;
+}
+
+static int struct_alloc(uint32_t structure_id, size_t size)
+{
+ struct structure_header *header = NULL;
+ unsigned int padded_size;
+ unsigned int status;
+
+ if (size < MIN_STRUCT_SIZE)
+ return FWK_E_PARAM;
+
+ padded_size = FWK_ALIGN_NEXT(size, MIN_STRUCT_ALIGNMENT);
+
+ if (padded_size + sizeof(*header) > ctx.mem_free) {
+ status = FWK_E_NOMEM;
+ goto exit;
+ }
+
+ if (structure_exists(structure_id)) {
+ status = FWK_E_RANGE;
+ goto exit;
+ }
+
+ /* Create the Structure Header */
+ header = (struct structure_header *)ctx.mem_next_free;
+ header->id = structure_id;
+ header->size = padded_size;
+ header->valid = false;
+ ctx.mem_next_free += sizeof(*header);
+ ctx.mem_free -= sizeof(*header);
+
+ /* Zero the memory reserved for the structure, avoiding the header */
+ memset(ctx.mem_next_free, 0, padded_size);
+ ctx.mem_next_free += padded_size;
+ ctx.mem_free -= padded_size;
+
+ /* Increment the structure count within the region descriptor */
+ ctx.region_desc->structure_count++;
+
+ status = FWK_SUCCESS;
+
+exit:
+ return status;
+}
+
+/*
+ * Read an SDS Memory Region to determine its properties (through the Region
+ * Descriptor) such as its size limit, its schema version and the current number
+ * of structures stored within the region. Any Structure Headers that are
+ * found within the region will be checked for validity.
+ *
+ * Once the existing contents have been evaluated the memory region will be
+ * resized if the RAM Firmware has defined a different region size than the ROM
+ * Firmware. The resizing process only takes place if the new region size is
+ * not the same as the existing size. When resizing, the Region Descriptor's
+ * 'region size' field is modified but the contents of the region are not
+ * modified in any way.
+ *
+ * Finally, the total size of the structures and headers that are present
+ * is subtracted from the size of the memory region to determine the amount
+ * of free memory that remains.
+ */
+static int reinitialize_memory_region(void)
+{
+ unsigned int struct_idx;
+ uint32_t mem_used;
+ struct structure_header header;
+
+ if (ctx.region_desc->signature != REGION_SIGNATURE)
+ return FWK_E_DATA;
+
+ if (ctx.region_desc->version_major != SUPPORTED_VERSION_MAJOR)
+ return FWK_E_DATA;
+
+ mem_used = sizeof(struct region_descriptor);
+
+ for (struct_idx = 0; struct_idx < ctx.region_desc->structure_count;
+ struct_idx++) {
+ header = *(struct structure_header *)(ctx.mem_base + mem_used);
+
+ if (!header_is_valid(&header))
+ return FWK_E_DATA; /* Unexpected invalid header */
+
+ mem_used += header.size;
+ mem_used += sizeof(struct structure_header);
+ if (mem_used > ctx.region_desc->region_size)
+ return FWK_E_SIZE;
+ }
+
+ if (ctx.mem_size < mem_used)
+ return FWK_E_SIZE;
+
+ /*
+ * Update the SDS Memory Region size (if different between ROM and RAM) and
+ * the amount of free memory left in the SDS Memory Region.
+ */
+ if (ctx.region_desc->region_size != ctx.mem_size)
+ ctx.region_desc->region_size = ctx.mem_size;
+
+ ctx.mem_free = ctx.mem_size - mem_used;
+
+ /*
+ * Initialize the pointer that holds the next free memory location for a
+ * Shared Data Structure. This follows the last Shared Data Structure, or
+ * the Region Descriptor if there are no existing structures.
+ */
+ ctx.mem_next_free = ctx.mem_base + mem_used;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Initialize an empty SDS Memory Region so that it is ready for use.
+ */
+static int create_memory_region(void)
+{
+ unsigned int min_storage;
+
+ /*
+ * Check that the SDS Memory Region is large enough to support both the
+ * Region Descriptor and a single Shared Data Structure of the minimum
+ * permitted size.
+ */
+ min_storage = sizeof(struct region_descriptor) +
+ FWK_ALIGN_NEXT(MIN_STRUCT_SIZE, MIN_STRUCT_ALIGNMENT);
+ if (ctx.mem_size < min_storage)
+ return FWK_E_NOMEM;
+
+ ctx.mem_free = ctx.mem_size - sizeof(struct region_descriptor);
+
+ /*
+ * Update the Region Descriptor
+ */
+ ctx.region_desc->signature = REGION_SIGNATURE;
+ ctx.region_desc->structure_count = 0;
+ ctx.region_desc->version_major = SUPPORTED_VERSION_MAJOR;
+ ctx.region_desc->version_minor = SUPPORTED_VERSION_MINOR;
+ ctx.region_desc->region_size = ctx.mem_size;
+
+ /*
+ * Initialize the pointer that holds the next free memory location for
+ * placing a Structure Header. Initially this follows the Region Descriptor
+ * directly.
+ */
+ ctx.mem_next_free = ctx.mem_base + sizeof(struct region_descriptor);
+ assert(((uintptr_t)ctx.mem_next_free %
+ MIN_STRUCT_ALIGNMENT) == 0);
+
+ return FWK_SUCCESS;
+}
+
+static int struct_write(uint32_t structure_id, unsigned int offset,
+ const void *data, size_t size)
+{
+ int status;
+ uint8_t *structure_base;
+ struct structure_header header;
+
+ assert(data != NULL);
+ assert(size != 0);
+
+ /* Look up the Structure Header by its identifier */
+ status = get_structure_info(structure_id, &header, &structure_base);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /*
+ * Perform sanity checks on the field location and data size before invoking
+ * memcpy.
+ */
+ status = validate_structure_access(header.size, offset, size);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ memcpy(structure_base + offset, data, size);
+ return FWK_SUCCESS;
+}
+
+static int struct_finalize(uint32_t structure_id)
+{
+ int status;
+ uint8_t *structure_base;
+ struct structure_header header;
+ struct structure_header *header_mem;
+
+ /* Check that the structure being finalized exists */
+ status = get_structure_info(structure_id, &header, &structure_base);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Update the valid flag of the header within the SDS Memory Region */
+ header_mem = (struct structure_header *)(structure_base - sizeof(header));
+ header_mem->valid = true;
+
+ return FWK_SUCCESS;
+}
+
+static int struct_init(const struct mod_sds_structure_desc *struct_desc)
+{
+ int status = FWK_SUCCESS;
+
+ /* If the structure does not already exist, allocate it. */
+ if (!structure_exists(struct_desc->id)) {
+ status = struct_alloc(struct_desc->id, struct_desc->size);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ if (struct_desc->payload != NULL) {
+ status = struct_write(struct_desc->id, 0, struct_desc->payload,
+ struct_desc->size);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ /* Finalize the structure immediately if required */
+ if (struct_desc->finalize)
+ status = struct_finalize(struct_desc->id);
+
+ return status;
+}
+
+/*
+ * Module API
+ */
+
+static int sds_struct_write(uint32_t structure_id, unsigned int offset,
+ const void *data, size_t size)
+{
+ int status;
+
+ status = fwk_module_check_call(fwk_module_id_sds);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (data == NULL)
+ return FWK_E_PARAM;
+
+ if (size == 0)
+ return FWK_E_PARAM;
+
+ return struct_write(structure_id, offset, data, size);
+}
+
+static int sds_struct_read(uint32_t structure_id, unsigned int offset,
+ void *data, size_t size)
+{
+ int status;
+ uint8_t *structure_base;
+ struct structure_header header;
+
+ status = fwk_module_check_call(fwk_module_id_sds);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (data == NULL)
+ return FWK_E_PARAM;
+
+ if (size == 0)
+ return FWK_E_PARAM;
+
+ /* Check if a structure with this ID exists */
+ status = get_structure_info(structure_id, &header, &structure_base);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /*
+ * Perform sanity checks on the field location and data size before invoking
+ * memcpy.
+ */
+ status = validate_structure_access(header.size, offset, size);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ memcpy(data, structure_base + offset, size);
+ return FWK_SUCCESS;
+}
+
+static int sds_struct_finalize(uint32_t structure_id)
+{
+ int status;
+
+ status = fwk_module_check_call(fwk_module_id_sds);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return struct_finalize(structure_id);
+}
+
+static const struct mod_sds_api module_api = {
+ .struct_write = sds_struct_write,
+ .struct_read = sds_struct_read,
+ .struct_finalize = sds_struct_finalize,
+};
+
+/*
+ * Framework handlers
+ */
+
+static int sds_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ if (data == NULL)
+ return FWK_E_PARAM;
+
+ ctx.module_config = data;
+
+ assert((MIN_STRUCT_ALIGNMENT % MIN_FIELD_ALIGNMENT) == 0);
+
+ if (ctx.module_config->region_base_address == 0)
+ return FWK_E_PARAM;
+ if (((uintptr_t)ctx.module_config->region_base_address %
+ MIN_STRUCT_ALIGNMENT) > 0)
+ return FWK_E_PARAM;
+
+ ctx.mem_base = (uint8_t *)ctx.module_config->region_base_address;
+ ctx.mem_size = ctx.module_config->region_size;
+ ctx.region_desc = (struct region_descriptor *)ctx.mem_base;
+
+ return FWK_SUCCESS;
+}
+
+static int sds_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ return FWK_SUCCESS;
+}
+
+static int sds_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
+ fwk_id_t api_id, const void **api)
+{
+ if (!fwk_module_is_valid_module_id(requester_id))
+ return FWK_E_ACCESS;
+
+ *api = &module_api;
+ return FWK_SUCCESS;
+}
+
+static int sds_start(fwk_id_t id)
+{
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_MODULE))
+ return FWK_SUCCESS;
+
+ /* Register the module for clock state notifications */
+ return fwk_notification_subscribe(
+ mod_clock_notification_id_state_changed,
+ ctx.module_config->clock_id,
+ id);
+}
+
+static int sds_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ int status;
+ struct clock_notification_params *params;
+ int element_idx;
+ int element_count;
+ const struct mod_sds_structure_desc *struct_desc;
+
+ assert(fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed));
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_MODULE));
+
+ params = (struct clock_notification_params *)event->params;
+ if (params->new_state != MOD_CLOCK_STATE_RUNNING)
+ return FWK_SUCCESS;
+
+ /* Either reinitialize the memory region, or create it for the first time */
+ status = reinitialize_memory_region();
+ if (status != FWK_SUCCESS) {
+ status = create_memory_region();
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ element_count = fwk_module_get_element_count(event->target_id);
+ for (element_idx = 0; element_idx < element_count; ++element_idx) {
+ struct_desc = fwk_module_get_data(
+ fwk_id_build_element_id(event->target_id, element_idx));
+
+ status = struct_init(struct_desc);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+/* Module descriptor */
+const struct fwk_module module_sds = {
+ .name = "Shared Data Storage",
+ .type = FWK_MODULE_TYPE_SERVICE,
+ .api_count = 1,
+ .event_count = 0,
+ .init = sds_init,
+ .element_init = sds_element_init,
+ .process_bind_request = sds_process_bind_request,
+ .start = sds_start,
+ .process_notification = sds_process_notification
+};
diff --git a/module/sensor/include/mod_sensor.h b/module/sensor/include/mod_sensor.h
new file mode 100644
index 00000000..4420fed8
--- /dev/null
+++ b/module/sensor/include/mod_sensor.h
@@ -0,0 +1,234 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_SENSOR_H
+#define MOD_SENSOR_H
+
+#include <fwk_id.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModuleSensor Sensor
+ *
+ * \brief Module for reading hardware sensors.
+ *
+ * \details Module for interfacing with and reading various hardware sensors.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Sensor types as defined by SCMI
+ */
+enum mod_sensor_type {
+ MOD_SENSOR_TYPE_NONE = 0,
+ MOD_SENSOR_TYPE_UNSPECIFIED,
+ MOD_SENSOR_TYPE_DEGREES_C,
+ MOD_SENSOR_TYPE_DEGREES_F,
+ MOD_SENSOR_TYPE_DEGREES_K,
+ MOD_SENSOR_TYPE_VOLTS,
+ MOD_SENSOR_TYPE_AMPS,
+ MOD_SENSOR_TYPE_WATTS,
+ MOD_SENSOR_TYPE_JOULES,
+ MOD_SENSOR_TYPE_COULOMBS,
+ MOD_SENSOR_TYPE_VA,
+ MOD_SENSOR_TYPE_NITS,
+ MOD_SENSOR_TYPE_LUMENS,
+ MOD_SENSOR_TYPE_LUX,
+ MOD_SENSOR_TYPE_CANDELAS,
+ MOD_SENSOR_TYPE_KPA,
+ MOD_SENSOR_TYPE_PSI,
+ MOD_SENSOR_TYPE_NEWTONS,
+ MOD_SENSOR_TYPE_CFM,
+ MOD_SENSOR_TYPE_RPM,
+ MOD_SENSOR_TYPE_HERTZ,
+ MOD_SENSOR_TYPE_SECONDS,
+ MOD_SENSOR_TYPE_MINUTES,
+ MOD_SENSOR_TYPE_HOURS,
+ MOD_SENSOR_TYPE_DAYS,
+ MOD_SENSOR_TYPE_WEEKS,
+ MOD_SENSOR_TYPE_MILS,
+ MOD_SENSOR_TYPE_INCHES,
+ MOD_SENSOR_TYPE_FEET,
+ MOD_SENSOR_TYPE_CUBIC_INCHES,
+ MOD_SENSOR_TYPE_CUBIC_FEET,
+ MOD_SENSOR_TYPE_METERS,
+ MOD_SENSOR_TYPE_CUBIC_CENTIMETERS,
+ MOD_SENSOR_TYPE_CUBIC_METERS,
+ MOD_SENSOR_TYPE_LITRES,
+ MOD_SENSOR_TYPE_FLUID_OUNCES,
+ MOD_SENSOR_TYPE_RADIANS,
+ MOD_SENSOR_TYPE_STERADIANS,
+ MOD_SENSOR_TYPE_REVOLUTIONS,
+ MOD_SENSOR_TYPE_CYCLES,
+ MOD_SENSOR_TYPE_GRAVITIES,
+ MOD_SENSOR_TYPE_OUNCES,
+ MOD_SENSOR_TYPE_POUNDS,
+ MOD_SENSOR_TYPE_FOOT_POUNDS,
+ MOD_SENSOR_TYPE_OUNCE_INCHES,
+ MOD_SENSOR_TYPE_GAUSS,
+ MOD_SENSOR_TYPE_GILBERTS,
+ MOD_SENSOR_TYPE_HENRIES,
+ MOD_SENSOR_TYPE_FARADS,
+ MOD_SENSOR_TYPE_OHMS,
+ MOD_SENSOR_TYPE_SIEMENS,
+ MOD_SENSOR_TYPE_MOLES,
+ MOD_SENSOR_TYPE_BECQUERELS,
+ MOD_SENSOR_TYPE_PPM,
+ MOD_SENSOR_TYPE_DECIBELS,
+ MOD_SENSOR_TYPE_DBA,
+ MOD_SENSOR_TYPE_DBC,
+ MOD_SENSOR_TYPE_GRAYS,
+ MOD_SENSOR_TYPE_SIEVERTS,
+ MOD_SENSOR_TYPE_COLOR_TEMP_DEGREES_K,
+ MOD_SENSOR_TYPE_BITS,
+ MOD_SENSOR_TYPE_BYTES,
+ MOD_SENSOR_TYPE_WORDS,
+ MOD_SENSOR_TYPE_DWORDS,
+ MOD_SENSOR_TYPE_QWORDS,
+ MOD_SENSOR_TYPE_PERCENTAGE,
+ MOD_SENSOR_TYPE_PASCALS,
+ MOD_SENSOR_TYPE_COUNTS,
+ MOD_SENSOR_TYPE_GRAMS,
+ MOD_SENSOR_TYPE_NEWTON_METERS,
+ MOD_SENSOR_TYPE_HITS,
+ MOD_SENSOR_TYPE_MISSES,
+ MOD_SENSOR_TYPE_RETRIES,
+ MOD_SENSOR_TYPE_OVERRUNS,
+ MOD_SENSOR_TYPE_UNDERRUNS,
+ MOD_SENSOR_TYPE_COLLISIONS,
+ MOD_SENSOR_TYPE_PACKETS,
+ MOD_SENSOR_TYPE_MESSAGES,
+ MOD_SENSOR_TYPE_CHARACTERS,
+ MOD_SENSOR_TYPE_ERRORS,
+ MOD_SENSOR_TYPE_CORRECTED_ERRORS,
+ MOD_SENSOR_TYPE_UNCORRECTABLE_ERRORS,
+ MOD_SENSOR_TYPE_SQUARE_MILS,
+ MOD_SENSOR_TYPE_SQUARE_INCHES,
+ MOD_SENSOR_TYPE_SQUARE_FEET,
+ MOD_SENSOR_TYPE_SQUARE_CENTIMETERS,
+ MOD_SENSOR_TYPE_SQUARE_METERS,
+ MOD_SENSOR_TYPE_OEM_UNIT = 0xFF,
+ MOD_SENSOR_TYPE_COUNT
+};
+
+/*!
+ * \brief Structure containing all sensor information
+ *
+ * \details Sensor information structure used to configure the sensor and serve
+ * SCMI requests.
+ */
+struct mod_sensor_info {
+ /*! SCMI sensor type */
+ enum mod_sensor_type type;
+
+ /*! Pointer to NULL-terminated name string */
+ const char *name;
+
+ /*! Time (in seconds) between sensor updates. Set this field to 0 to
+ * indicate that the sensor does not have a minimum update interval. This
+ * field is used with \ref update_interval_multiplier to calculate the
+ * actual update_interval.
+ */
+ unsigned int update_interval;
+
+ /*!
+ * Power-of-10 multiplier for \ref update_interval \n\n
+ * This is used to calculate the actual interval time:\n
+ * actual = \ref update_interval x10^(\ref update_interval_multiplier)\n
+ */
+ int update_interval_multiplier;
+
+ /*!
+ * Power-of-10 multiplier applied to the unit (specified by \ref type)\n\n
+ * Used like this: unit x10^(\ref unit_multiplier)
+ */
+ int unit_multiplier;
+};
+
+/*!
+ * \brief Sensor device configuration
+ *
+ * \details Configuration structure for individual sensors
+ */
+struct mod_sensor_dev_config {
+ /*! Module or element id of the driver */
+ fwk_id_t driver_id;
+
+ /*! Sensor information */
+ struct mod_sensor_info *info;
+};
+
+/*!
+ * \brief Sensor driver API
+ *
+ * \details Api used by this module to interface with the driver
+ */
+struct mod_sensor_driver_api {
+ /*!
+ * \brief Read sensor value
+ *
+ * \details Synchronously read current sensor value
+ *
+ * \param id Specific sensor device id
+ * \param value Pointer to storage for the sensor value
+ *
+ * \retval FWK_SUCCESS Value was read successfully
+ *
+ * One of the other specific error codes described by the module.
+ */
+ int (*get_value)(fwk_id_t id, uint64_t *value);
+};
+
+/*!
+ * \brief Sensor API
+ */
+struct mod_sensor_api {
+ /*!
+ * \brief Read sensor value
+ *
+ * \details Synchronously read current sensor value
+ *
+ * \param id Specific sensor device id
+ * \param value Pointer to storage for the sensor value
+ *
+ * \retval FWK_SUCCESS Operation succeeded
+ *
+ * One of the other specific error codes described by the module.
+ */
+ int (*get_value)(fwk_id_t id, uint64_t *value);
+
+ /*!
+ * \brief Get sensor information
+ *
+ * \details Get a pointer to the sensor_info structure of a specific sensor
+ *
+ * \param id Specific sensor device id
+ * \param value Pointer to storage for the info struct
+ *
+ * \retval FWK_SUCCESS Operation succeeded
+ *
+ * One of the other specific error codes described by the module.
+ */
+ int (*get_info)(fwk_id_t id, struct mod_sensor_info *info);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SENSOR_H */
diff --git a/module/sensor/src/Makefile b/module/sensor/src/Makefile
new file mode 100644
index 00000000..c6eeef7f
--- /dev/null
+++ b/module/sensor/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Sensor
+BS_LIB_SOURCES := mod_sensor.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/sensor/src/mod_sensor.c b/module/sensor/src/mod_sensor.c
new file mode 100644
index 00000000..26697438
--- /dev/null
+++ b/module/sensor/src/mod_sensor.c
@@ -0,0 +1,182 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_sensor.h>
+
+struct sensor_dev_ctx {
+ struct mod_sensor_dev_config *config;
+ struct mod_sensor_driver_api *driver_api;
+};
+
+static struct sensor_dev_ctx *ctx_table;
+
+static int get_ctx_if_valid_call(fwk_id_t id,
+ void *data,
+ struct sensor_dev_ctx **ctx)
+{
+ int status;
+
+ if (data == NULL) {
+ /* Invalid pointer */
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ status = fwk_module_check_call(id);
+ if (status != FWK_SUCCESS) {
+ /* Module is in invalid state or ID is invalid */
+ assert(false);
+ return status;
+ }
+
+ *ctx = ctx_table + fwk_id_get_element_idx(id);
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Module API
+ */
+static int get_value(fwk_id_t id, uint64_t *value)
+{
+ int status;
+ struct sensor_dev_ctx *ctx;
+
+ status = get_ctx_if_valid_call(id, value, &ctx);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return ctx->driver_api->get_value(ctx->config->driver_id, value);
+}
+
+static int get_info(fwk_id_t id, struct mod_sensor_info *info)
+{
+ int status;
+ struct sensor_dev_ctx *ctx;
+
+ status = get_ctx_if_valid_call(id, info, &ctx);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *info = *ctx->config->info;
+
+ return FWK_SUCCESS;
+}
+
+static struct mod_sensor_api sensor_api = {
+ .get_value = get_value,
+ .get_info = get_info,
+};
+
+/*
+ * Framework handlers
+ */
+static int sensor_init(fwk_id_t module_id,
+ unsigned int element_count,
+ const void *unused)
+{
+ ctx_table = fwk_mm_alloc(element_count, sizeof(ctx_table[0]));
+
+ if (ctx_table == NULL) {
+ /* Unable to allocate device context memory */
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int sensor_dev_init(fwk_id_t element_id,
+ unsigned int unused,
+ const void *data)
+{
+ struct sensor_dev_ctx *ctx;
+ struct mod_sensor_dev_config *config;
+
+ ctx = ctx_table + fwk_id_get_element_idx(element_id);
+ config = (struct mod_sensor_dev_config*)data;
+
+ /* Validate config */
+ if ((config->info == NULL) ||
+ (config->info->type >= MOD_SENSOR_TYPE_COUNT)) {
+
+ /* Invalid config */
+ assert(false);
+ return FWK_E_DATA;
+ }
+
+ /* Link info->name to framework element name */
+ config->info->name = fwk_module_get_name(element_id);
+
+ ctx->config = config;
+
+ return FWK_SUCCESS;
+}
+
+static int sensor_bind(fwk_id_t id, unsigned int round)
+{
+ struct sensor_dev_ctx *ctx;
+ int status;
+ struct mod_sensor_driver_api *driver;
+
+ if ((round > 0) || fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ /*
+ * Only bind in first round of calls
+ * Nothing to do for module
+ */
+ return FWK_SUCCESS;
+ }
+
+ ctx = ctx_table + fwk_id_get_element_idx(id);
+
+ /* Bind to driver */
+ status = fwk_module_bind(ctx->config->driver_id,
+ FWK_ID_API(fwk_id_get_module_idx(ctx->config->driver_id), 0),
+ &driver);
+ if (status != FWK_SUCCESS) {
+ /* Unable to bind to driver */
+ assert(false);
+ return status;
+ }
+
+ if (driver->get_value == NULL) {
+ /* Incomplete driver API */
+ assert(false);
+ return FWK_E_DATA;
+ }
+
+ ctx->driver_api = driver;
+
+ return FWK_SUCCESS;
+}
+
+static int sensor_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_type,
+ const void **api)
+{
+ *api = &sensor_api;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_sensor = {
+ .name = "SENSOR",
+ .api_count = 1,
+ .event_count = 0,
+ .type = FWK_MODULE_TYPE_HAL,
+ .init = sensor_init,
+ .element_init = sensor_dev_init,
+ .bind = sensor_bind,
+ .process_bind_request = sensor_process_bind_request,
+};
diff --git a/module/smt/include/internal/smt.h b/module/smt/include/internal/smt.h
new file mode 100644
index 00000000..2582d26a
--- /dev/null
+++ b/module/smt/include/internal/smt.h
@@ -0,0 +1,41 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SMT_H
+#define SMT_H
+
+struct __attribute((packed)) mod_smt_memory {
+ uint32_t reserved0;
+ uint32_t status;
+ uint64_t reserved1;
+ uint32_t flags;
+ uint32_t length; /* message_header + payload */
+ uint32_t message_header;
+ uint32_t payload[];
+};
+
+#define MOD_SMT_MAX_CHANNELS 8
+
+#define MOD_SMT_MAILBOX_STATUS_FREE_POS 0
+#define MOD_SMT_MAILBOX_STATUS_FREE_MASK \
+ (UINT32_C(0x1) << MOD_SMT_MAILBOX_STATUS_FREE_POS)
+
+#define MOD_SMT_MAILBOX_STATUS_ERROR_POS 1
+#define MOD_SMT_MAILBOX_STATUS_ERROR_MASK \
+ (UINT32_C(0x1) << MOD_SMT_MAILBOX_STATUS_ERROR_POS)
+
+#define MOD_SMT_MAILBOX_FLAGS_IENABLED_POS 0
+#define MOD_SMT_MAILBOX_FLAGS_IENABLED_MASK \
+ (UINT32_C(0x1) << MOD_SMT_MAILBOX_FLAGS_IENABLED_POS)
+
+#define MOD_SMT_MIN_PAYLOAD_SIZE \
+ sizeof(((struct mod_smt_memory *)NULL)->payload[0])
+
+#define MOD_SMT_MIN_MAILBOX_SIZE \
+ (sizeof(struct mod_smt_memory) + MOD_SMT_MIN_PAYLOAD_SIZE)
+
+#endif /* SMT_H */
diff --git a/module/smt/include/mod_smt.h b/module/smt/include/mod_smt.h
new file mode 100644
index 00000000..700aa5d5
--- /dev/null
+++ b/module/smt/include/mod_smt.h
@@ -0,0 +1,123 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_SMT_H
+#define MOD_SMT_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_id.h>
+
+/*!
+ * \name Channel policies
+ *
+ * \details These policies define attributes that affect how the channel is
+ * treated by the SMT component.
+ *
+ * @{
+ */
+
+/*! No policies */
+#define MOD_SMT_POLICY_NONE ((uint32_t)0)
+
+/*! This channel is secure */
+#define MOD_SMT_POLICY_SECURE ((uint32_t)(1 << 0))
+
+/*! The mailbox for this channel requires initialization */
+#define MOD_SMT_POLICY_INIT_MAILBOX ((uint32_t)(1 << 1))
+
+/*!
+ * @}
+ */
+
+/*!
+ * \brief Channel type
+ *
+ * \details Defines the role of an entity in a channel
+ */
+enum mod_smt_channel_type {
+ /*! Master channel */
+ MOD_SMT_CHANNEL_TYPE_MASTER,
+
+ /*! Slave channel */
+ MOD_SMT_CHANNEL_TYPE_SLAVE,
+
+ /*! Channel type count */
+ MOD_SMT_CHANNEL_TYPE_COUNT,
+};
+
+/*!
+ * \brief Channel config.
+ */
+struct mod_smt_channel_config {
+ /*! Channel role (slave or master) */
+ enum mod_smt_channel_type type;
+
+ /*! Channel policies */
+ uint32_t policies;
+
+ /*! Shared mailbox address */
+ uintptr_t mailbox_address;
+
+ /*! Shared mailbox size in bytes */
+ size_t mailbox_size;
+
+ /*! Identifier of the driver */
+ fwk_id_t driver_id;
+
+ /*! Identifier of the driver API to bind to */
+ fwk_id_t driver_api_id;
+
+ /*! Identifier of the power domain that this channel depends on */
+ fwk_id_t pd_source_id;
+};
+
+/*!
+ * \brief Driver API
+ */
+struct mod_smt_driver_api {
+ /*!
+ * \brief Raise an interrupt on the receiver
+ *
+ * \param device_id Device identifier
+ *
+ * \retval FWK_SUCCESS The operation succeeded
+ * \retval FWK_E_PARAM The device_id parameter is invalid
+ * \return One of the standard error codes for implementation-defined
+ * errors
+ */
+ int (*raise_interrupt)(fwk_id_t device_id);
+};
+
+/*!
+ * \brief Driver input API (Implemented by SMT)
+ *
+ * \details Interface used for driver -> SMT communication.
+ */
+struct mod_smt_driver_input_api {
+ /*!
+ * \brief Signal an incoming message in the mailbox
+ *
+ * \param device_id Channel identifier
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard error codes for implementation-defined
+ * errors.
+ */
+ int (*signal_message)(fwk_id_t channel_id);
+};
+
+/*!
+ * \brief Type of the interfaces exposed by the power domain module.
+ */
+enum mod_smt_api_idx {
+ MOD_SMT_API_IDX_DRIVER_INPUT,
+ MOD_SMT_API_IDX_SCMI_TRANSPORT,
+ MOD_SMT_API_IDX_COUNT,
+};
+
+#endif /* MOD_SMT_H */
diff --git a/module/smt/src/Makefile b/module/smt/src/Makefile
new file mode 100644
index 00000000..4d9d6b95
--- /dev/null
+++ b/module/smt/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SMT
+BS_LIB_SOURCES = mod_smt.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/smt/src/mod_smt.c b/module/smt/src/mod_smt.c
new file mode 100644
index 00000000..e57c830c
--- /dev/null
+++ b/module/smt/src/mod_smt.c
@@ -0,0 +1,563 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_notification.h>
+#include <mod_log.h>
+#include <mod_power_domain.h>
+#include <mod_scmi.h>
+#include <mod_smt.h>
+#include <internal/smt.h>
+
+struct smt_channel_ctx {
+ /* Channel identifier */
+ fwk_id_t id;
+
+ /* Channel configuration data */
+ struct mod_smt_channel_config *config;
+
+ /* Channel read and write cache memory areas */
+ struct mod_smt_memory *in, *out;
+
+ /* Message processing in progrees flag */
+ volatile bool locked;
+
+ /* Maximum payload size of the channel */
+ size_t max_payload_size;
+
+ /* Driver entity identifier */
+ fwk_id_t driver_id;
+
+ /* SCMI module service bound to the channel */
+ fwk_id_t scmi_service_id;
+
+ /* Driver API */
+ struct mod_smt_driver_api *driver_api;
+
+ /* SCMI service API */
+ struct mod_scmi_from_transport_api *scmi_api;
+};
+
+struct smt_ctx {
+ /* Log module API */
+ struct mod_log_api *log_api;
+
+ /* Table of channel contexts */
+ struct smt_channel_ctx *channel_ctx_table;
+
+ /* Number of channels */
+ unsigned int channel_count;
+};
+
+static struct smt_ctx smt_ctx;
+
+/*
+ * SCMI Transport API
+ */
+static int smt_get_secure(fwk_id_t channel_id, bool *secure)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ if (secure == NULL) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ *secure = channel_ctx->config->policies & MOD_SMT_POLICY_SECURE;
+
+ return FWK_SUCCESS;
+}
+
+static int smt_get_max_payload_size(fwk_id_t channel_id, size_t *size)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ if (size == NULL) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ *size = channel_ctx->max_payload_size;
+
+ return FWK_SUCCESS;
+}
+
+static int smt_get_message_header(fwk_id_t channel_id, uint32_t *header)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ if (header == NULL) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ if (!channel_ctx->locked)
+ return FWK_E_ACCESS;
+
+ *header = channel_ctx->in->message_header;
+
+ return FWK_SUCCESS;
+}
+
+static int smt_get_payload(fwk_id_t channel_id,
+ const void **payload,
+ size_t *size)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ if (payload == NULL) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ if (!channel_ctx->locked)
+ return FWK_E_ACCESS;
+
+ *payload = channel_ctx->in->payload;
+
+ if (size != NULL) {
+ *size = channel_ctx->in->length -
+ sizeof(channel_ctx->in->message_header);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int smt_write_payload(fwk_id_t channel_id,
+ size_t offset,
+ const void *payload,
+ size_t size)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ if ((payload == NULL) ||
+ (offset > channel_ctx->max_payload_size) ||
+ (size > channel_ctx->max_payload_size) ||
+ ((offset + size) > channel_ctx->max_payload_size)) {
+
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ if (!channel_ctx->locked)
+ return FWK_E_ACCESS;
+
+ memcpy(((uint8_t*)channel_ctx->out->payload) + offset, payload, size);
+
+ return FWK_SUCCESS;
+}
+
+static int smt_respond(fwk_id_t channel_id, const void *payload, size_t size)
+{
+ struct smt_channel_ctx *channel_ctx;
+ struct mod_smt_memory *memory;
+ int status;
+ (void)status;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS) {
+ assert(false);
+ return status;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+ memory = ((struct mod_smt_memory*)channel_ctx->config->mailbox_address);
+
+ /* Copy the header from the write buffer */
+ *memory = *channel_ctx->out;
+
+ /* Copy the payload from either the write buffer or the payload parameter */
+ memcpy(memory->payload,
+ (payload == NULL ? channel_ctx->out->payload : payload),
+ size);
+
+ /*
+ * NOTE: Disable interrupts for a brief period to ensure interrupts are not
+ * erroneously accepted in between unlocking the context, and setting
+ * the mailbox free bit. The agent should not interrupt during this
+ * period anyway, but this guard is included to protect against a
+ * misbehaving agent.
+ */
+ fwk_interrupt_global_disable();
+
+ channel_ctx->locked = false;
+
+ memory->length = sizeof(memory->message_header) + size;
+ memory->status |= MOD_SMT_MAILBOX_STATUS_FREE_MASK;
+
+ fwk_interrupt_global_enable();
+
+ if (memory->flags & MOD_SMT_MAILBOX_FLAGS_IENABLED_MASK)
+ channel_ctx->driver_api->raise_interrupt(channel_ctx->driver_id);
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_scmi_to_transport_api smt_mod_scmi_to_transport_api = {
+ .get_secure = smt_get_secure,
+ .get_max_payload_size = smt_get_max_payload_size,
+ .get_message_header = smt_get_message_header,
+ .get_payload = smt_get_payload,
+ .write_payload = smt_write_payload,
+ .respond = smt_respond,
+};
+
+/*
+ * Driver handler API
+ */
+static int smt_slave_handler(struct smt_channel_ctx *channel_ctx)
+{
+ struct mod_smt_memory *memory, *in, *out;
+ size_t payload_size;
+ int status;
+
+ /* Check if we are already processing */
+ if (channel_ctx->locked)
+ return FWK_E_STATE;
+
+ memory = ((struct mod_smt_memory*)channel_ctx->config->mailbox_address);
+ in = channel_ctx->in;
+ out = channel_ctx->out;
+
+ /* Check we have ownership of the mailbox */
+ if (memory->status & MOD_SMT_MAILBOX_STATUS_FREE_MASK)
+ return FWK_E_STATE;
+
+ /* Commit to sending a response */
+ channel_ctx->locked = true;
+
+ /* Mirror mailbox contents in read and write buffers (Payload not copied) */
+ *in = *memory;
+ *out = *memory;
+
+ /* Ensure error bit is not set */
+ out->status &= ~MOD_SMT_MAILBOX_STATUS_ERROR_MASK;
+
+ /*
+ * Verify:
+ * 1. The length is at least as large as the message header
+ * 2. The length, minus the size of the message header, is less than or
+ * equal to the maximum payload size
+ *
+ * Note: the payload size is permitted to be of size zero.
+ */
+ if ((in->length < sizeof(in->message_header)) ||
+ ((in->length - sizeof(in->message_header))
+ > channel_ctx->max_payload_size)) {
+
+ out->status |= MOD_SMT_MAILBOX_STATUS_ERROR_MASK;
+ return smt_respond(channel_ctx->id, &(int32_t){SCMI_PROTOCOL_ERROR},
+ sizeof(int32_t));
+ }
+
+ /* Copy payload from shared memory to read buffer */
+ payload_size = in->length - sizeof(in->message_header);
+ memcpy(in->payload, memory->payload, payload_size);
+
+ /* Let SCMI handle the message */
+ status =
+ channel_ctx->scmi_api->signal_message(channel_ctx->scmi_service_id);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ return FWK_SUCCESS;
+}
+
+static int smt_signal_message(fwk_id_t channel_id)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ status = fwk_module_check_call(channel_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+
+ switch (channel_ctx->config->type) {
+ case MOD_SMT_CHANNEL_TYPE_MASTER:
+ /* Not supported yet */
+ assert(false);
+ break;
+ case MOD_SMT_CHANNEL_TYPE_SLAVE:
+ return smt_slave_handler(channel_ctx);
+ break;
+ default:
+ /* Invalid config */
+ assert(false);
+ break;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_smt_driver_input_api driver_input_api = {
+ .signal_message = smt_signal_message,
+};
+
+/*
+ * Framework API
+ */
+static int smt_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ smt_ctx.channel_ctx_table = fwk_mm_calloc(element_count,
+ sizeof(smt_ctx.channel_ctx_table[0]));
+ if (smt_ctx.channel_ctx_table == NULL) {
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+ smt_ctx.channel_count = element_count;
+
+ return FWK_SUCCESS;
+}
+
+static int smt_channel_init(fwk_id_t channel_id, unsigned int unused,
+ const void *data)
+{
+ struct smt_channel_ctx *channel_ctx;
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
+ channel_ctx->config = (struct mod_smt_channel_config*)data;
+
+ /* Validate channel config */
+ if ((channel_ctx->config->type >= MOD_SMT_CHANNEL_TYPE_COUNT) ||
+ (channel_ctx->config->mailbox_address == 0) ||
+ (channel_ctx->config->mailbox_size == 0)) {
+ assert(false);
+ return FWK_E_DATA;
+ }
+
+ channel_ctx->id = channel_id;
+ channel_ctx->in = fwk_mm_alloc(1, channel_ctx->config->mailbox_size);
+ channel_ctx->out = fwk_mm_alloc(1, channel_ctx->config->mailbox_size);
+
+ if ((channel_ctx->in == NULL) || (channel_ctx->out == NULL))
+ return FWK_E_NOMEM;
+
+ channel_ctx->max_payload_size = channel_ctx->config->mailbox_size -
+ sizeof(struct mod_smt_memory);
+
+ /* Check memory allocations */
+ if ((channel_ctx->in == NULL) || (channel_ctx->out == NULL)) {
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int smt_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct smt_channel_ctx *channel_ctx;
+
+ if (round == 0) {
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ return fwk_module_bind(fwk_module_id_log,
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &smt_ctx.log_api);
+ }
+
+ channel_ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
+ status = fwk_module_bind(channel_ctx->config->driver_id,
+ channel_ctx->config->driver_api_id,
+ &channel_ctx->driver_api);
+ if (status != FWK_SUCCESS)
+ return status;
+ channel_ctx->driver_id = channel_ctx->config->driver_id;
+ }
+
+ if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
+ channel_ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
+ status = fwk_module_bind(channel_ctx->scmi_service_id,
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_TRANSPORT),
+ &channel_ctx->scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int smt_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ struct smt_channel_ctx *channel_ctx;
+
+ /* Only bind to a channel (not the whole module) */
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
+ /* Tried to bind to something other than a specific channel */
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(target_id)];
+
+ switch (fwk_id_get_api_idx(api_id)) {
+ case MOD_SMT_API_IDX_DRIVER_INPUT:
+ /* Driver input API */
+
+ /*
+ * Make sure that the element that is trying to bind to us is the
+ * same element that we previously bound to.
+ *
+ * NOTE: We bound to an element but a sub-element should be binding
+ * back to us. This means we cannot use fwk_id_is_equal() because
+ * the ids have different types. For now we compare the indicies
+ * manually.
+ */
+ if (fwk_id_get_module_idx(channel_ctx->driver_id) ==
+ fwk_id_get_module_idx(source_id) &&
+ fwk_id_get_element_idx(channel_ctx->driver_id) ==
+ fwk_id_get_element_idx(source_id)) {
+
+ /* Ids are equal */
+ *api = &driver_input_api;
+ } else {
+ /* A module that we did not bind to is trying to bind to us */
+ assert(false);
+ return FWK_E_ACCESS;
+ }
+ break;
+
+ case MOD_SMT_API_IDX_SCMI_TRANSPORT:
+ /* SCMI transport API */
+ *api = &smt_mod_scmi_to_transport_api;
+ channel_ctx->scmi_service_id = source_id;
+ break;
+
+ default:
+ /* Invalid API */
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int smt_start(fwk_id_t id)
+{
+ struct smt_channel_ctx *ctx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
+
+ /* Register for power domain state transition notifications */
+ return fwk_notification_subscribe(
+ mod_pd_notification_id_power_state_transition,
+ ctx->config->pd_source_id,
+ id);
+}
+
+static int smt_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ struct mod_pd_power_state_transition_notification_params *params;
+ struct smt_channel_ctx *channel_ctx;
+
+ assert(fwk_id_is_equal(event->id,
+ mod_pd_notification_id_power_state_transition));
+ assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
+
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ event->params;
+
+ if (params->state != MOD_PD_STATE_ON)
+ return FWK_SUCCESS;
+
+ channel_ctx =
+ &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(event->target_id)];
+
+ if (channel_ctx->config->policies & MOD_SMT_POLICY_INIT_MAILBOX) {
+ /* Initialize mailbox */
+ *((struct mod_smt_memory *)channel_ctx->config->mailbox_address) =
+ (struct mod_smt_memory) {
+ .status = (1 << MOD_SMT_MAILBOX_STATUS_FREE_POS)
+ };
+ }
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_smt = {
+ .name = "smt",
+ .type = FWK_MODULE_TYPE_SERVICE,
+ .api_count = MOD_SMT_API_IDX_COUNT,
+ .init = smt_init,
+ .element_init = smt_channel_init,
+ .bind = smt_bind,
+ .start = smt_start,
+ .process_bind_request = smt_process_bind_request,
+ .process_notification = smt_process_notification,
+};
diff --git a/module/system_pll/include/mod_system_pll.h b/module/system_pll/include/mod_system_pll.h
new file mode 100644
index 00000000..0806e385
--- /dev/null
+++ b/module/system_pll/include/mod_system_pll.h
@@ -0,0 +1,93 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_SYSTEM_PLL_H
+#define MOD_SYSTEM_PLL_H
+
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_macros.h>
+
+/*!
+ * \ingroup GroupModules Modules
+ * \defgroup GroupSystemPll System PLL Driver
+ *
+ * \details A driver for system PLL devices.
+ *
+ * @{
+ */
+
+/*! The slowest rate at which the PLL hardware can operate. */
+#define MOD_SYSTEM_PLL_MIN_RATE (50UL * FWK_MHZ)
+
+/*! The fastest rate at which the PLL hardware can operate. */
+#define MOD_SYSTEM_PLL_MAX_RATE (4UL * FWK_GHZ)
+
+/*! The maximum precision that can be used when setting the PLL rate. */
+#define MOD_SYSTEM_PLL_MIN_INTERVAL (1UL * FWK_KHZ)
+
+/*! Indexes of APIs that the module offers for binding. */
+enum mod_system_pll_api_types {
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT,
+ MOD_SYSTEM_PLL_API_COUNT,
+};
+
+/*!
+ * \brief PLL device configuration.
+ */
+struct mod_system_pll_dev_config {
+ /*! Pointer to the PLL's control register. */
+ volatile uint32_t * const control_reg;
+
+ /*! Pointer to the PLL's status register, if any. */
+ volatile uint32_t * const status_reg;
+
+ /*!
+ * Mask for the bit within the status register that indicates whether the
+ * PLL has locked at the programmed rate.
+ */
+ const uint8_t lock_flag_mask;
+
+ /*! The initial rate the PLL is set to during initialization. */
+ const uint64_t initial_rate;
+
+ /*!
+ * The slowest rate the PLL can be set to. This may be different from the
+ * hardware-imposed limit).
+ */
+ const uint64_t min_rate;
+
+ /*!
+ * The fastest rate the PLL can be set to. This may be different from the
+ * hardware-imposed limit).
+ */
+ const uint64_t max_rate;
+
+ /*!
+ * The maximum precision that can be used when setting the PLL rate. This
+ * may be different from the hardware-imposed limit).
+ */
+ const uint64_t min_step;
+
+ /*!
+ * If \c true, the driver will not attempt to set a default frequency, or to
+ * otherwise configure the PLL during the pre-runtime phase. The PLL is
+ * expected to be initialized later in response to a notification or other
+ * event.
+ */
+ const bool defer_initialization;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SYSTEM_PLL_H */
diff --git a/module/system_pll/src/Makefile b/module/system_pll/src/Makefile
new file mode 100644
index 00000000..5f5e4f07
--- /dev/null
+++ b/module/system_pll/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SYSTEM PLL
+BS_LIB_SOURCES := mod_system_pll.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/system_pll/src/mod_system_pll.c b/module/system_pll/src/mod_system_pll.c
new file mode 100644
index 00000000..edc30c2f
--- /dev/null
+++ b/module/system_pll/src/mod_system_pll.c
@@ -0,0 +1,336 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <mod_clock.h>
+#include <mod_system_pll.h>
+#include <mod_power_domain.h>
+
+/* Device context */
+struct system_pll_dev_ctx {
+ bool initialized;
+ uint64_t current_rate;
+ enum mod_clock_state current_state;
+ const struct mod_system_pll_dev_config *config;
+};
+
+/* Module context */
+struct system_pll_ctx {
+ struct system_pll_dev_ctx *dev_ctx_table;
+ unsigned int dev_count;
+};
+
+static struct system_pll_ctx module_ctx;
+
+/*
+ * Static helper functions
+ */
+
+/*
+ * Given a frequency (Hz), return the period (picoseconds) of a half cycle.
+ *
+ * Note: For performance reasons, 32-bit math is used for the conversion. This
+ * may cause a loss of precision if the given frequency is not a multiple
+ * of 1 KHz.
+ */
+static unsigned int freq_to_half_cycle_ps(unsigned int freq_hz)
+{
+ unsigned int freq_khz;
+
+ /* Check if the given frequency is a multiple of 1 KHz */
+ if (freq_hz % MOD_SYSTEM_PLL_MIN_INTERVAL != 0)
+ return FWK_E_PARAM;
+
+ freq_khz = freq_hz / FWK_KHZ;
+ if (freq_khz == 0)
+ return 0;
+
+ /*
+ * Legend:
+ * s = seconds
+ * P = Half cycle period in picoseconds
+ * Fh = Frequency in hertz
+ * Fk = Frequency in kilohertz
+ *
+ * Starting from "Period = Time / Frequency"
+ * General equation for half cycle in picoseconds:
+ * P = ((1s / Fh ) / 2) * 10^12
+ * To avoid decimal calculations, re-arrange and simplify equation:
+ * P = 10^12 / 2 * Fh
+ * To avoid dividend overflowing a 32-bit storage, dividend and divisor can
+ * be divided by 10^3:
+ * P = (10^12 / 10^3) / (2 * Fh / 10^3)
+ * Given Fk = Fh / 10^3, the equation can be further simplified as:
+ * P = 10^9 / 2 * Fk
+ * P = 5*10^8 / Fk
+ */
+ return 500000000UL / freq_khz;
+}
+
+/*
+ * Clock driver API functions
+ */
+
+static int system_pll_set_rate(fwk_id_t dev_id, uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ uint64_t rounded_rate;
+ uint64_t rounded_rate_alt;
+ unsigned int picoseconds;
+ struct system_pll_dev_ctx *ctx;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ /* If the given rate is not attainable as-is then round as requested */
+ if ((rate % ctx->config->min_step) > 0) {
+ switch (round_mode) {
+ case MOD_CLOCK_ROUND_MODE_NONE:
+ return FWK_E_RANGE;
+ case MOD_CLOCK_ROUND_MODE_NEAREST:
+ rounded_rate = FWK_ALIGN_PREVIOUS(rate, ctx->config->min_step);
+ rounded_rate_alt = FWK_ALIGN_NEXT(rate, ctx->config->min_step);
+
+ /* Select the rounded rate that is closest to the given rate */
+ if ((rate - rounded_rate) > (rounded_rate_alt - rate))
+ rounded_rate = rounded_rate_alt;
+ break;
+ case MOD_CLOCK_ROUND_MODE_DOWN:
+ rounded_rate = FWK_ALIGN_PREVIOUS(rate, ctx->config->min_step);
+ break;
+ case MOD_CLOCK_ROUND_MODE_UP:
+ rounded_rate = FWK_ALIGN_NEXT(rate, ctx->config->min_step);
+ break;
+ default:
+ return FWK_E_SUPPORT;
+ }
+ } else
+ rounded_rate = rate;
+
+ if (rounded_rate < ctx->config->min_rate)
+ return FWK_E_RANGE;
+ if (rounded_rate > ctx->config->max_rate)
+ return FWK_E_RANGE;
+
+ picoseconds = freq_to_half_cycle_ps(rounded_rate);
+
+ if (picoseconds == 0)
+ return FWK_E_RANGE;
+
+ *ctx->config->control_reg = picoseconds;
+
+ if (ctx->config->status_reg != NULL) {
+ /* Wait until the PLL has locked */
+ while ((*ctx->config->status_reg & ctx->config->lock_flag_mask) == 0)
+ continue;
+ }
+
+ ctx->current_rate = rounded_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int system_pll_get_rate(fwk_id_t dev_id, uint64_t *rate)
+{
+ struct system_pll_dev_ctx *ctx;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *rate = ctx->current_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int system_pll_get_rate_from_index(fwk_id_t dev_id,
+ unsigned int rate_index,
+ uint64_t *rate)
+{
+ /* PLLs have a continuous range of rates and are not indexed */
+ return FWK_E_SUPPORT;
+}
+
+static int system_pll_set_state(fwk_id_t dev_id, enum mod_clock_state state)
+{
+ if (state == MOD_CLOCK_STATE_RUNNING)
+ return FWK_SUCCESS;
+
+ /* PLLs can only be stopped by a parent power domain state change. */
+ return FWK_E_SUPPORT;
+}
+
+static int system_pll_get_state(fwk_id_t dev_id, enum mod_clock_state *state)
+{
+ struct system_pll_dev_ctx *ctx;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+ if (state == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *state = ctx->current_state;
+
+ return FWK_SUCCESS;
+}
+
+static int system_pll_get_range(fwk_id_t dev_id, struct mod_clock_range *range)
+{
+ struct system_pll_dev_ctx *ctx;
+
+ if (!fwk_module_is_valid_element_id(dev_id))
+ return FWK_E_PARAM;
+ if (range == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ range->rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS;
+ range->min = ctx->config->min_rate;
+ range->max = ctx->config->max_rate;
+ range->step = ctx->config->min_step;
+
+ return FWK_SUCCESS;
+}
+
+static int system_pll_power_state_change(
+ fwk_id_t dev_id,
+ unsigned int state)
+{
+ int status;
+ uint64_t rate;
+ struct system_pll_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (state != MOD_PD_STATE_ON)
+ return FWK_SUCCESS;
+
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+
+ if (ctx->initialized) {
+ /* Restore the previous rate */
+ rate = ctx->current_rate;
+ } else {
+ /* Initialize the PLL to its default rate */
+ ctx->initialized = true;
+ rate = ctx->config->initial_rate;
+ }
+
+ return system_pll_set_rate(dev_id, rate, MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int system_pll_power_state_pending_change(
+ fwk_id_t dev_id,
+ unsigned int current_state,
+ unsigned int next_state)
+{
+ int status;
+ struct system_pll_dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (next_state == MOD_PD_STATE_OFF) {
+ /* Just mark the PLL as stopped */
+ ctx->current_state = MOD_CLOCK_STATE_STOPPED;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_clock_drv_api api_system_pll = {
+ .set_rate = system_pll_set_rate,
+ .get_rate = system_pll_get_rate,
+ .get_rate_from_index = system_pll_get_rate_from_index,
+ .set_state = system_pll_set_state,
+ .get_state = system_pll_get_state,
+ .get_range = system_pll_get_range,
+ .process_power_transition = system_pll_power_state_change,
+ .process_pending_power_transition = system_pll_power_state_pending_change,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int system_pll_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ module_ctx.dev_count = element_count;
+
+ if (element_count == 0)
+ return FWK_SUCCESS;
+
+ module_ctx.dev_ctx_table = fwk_mm_calloc(element_count,
+ sizeof(struct system_pll_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int system_pll_element_init(fwk_id_t element_id, unsigned int unused,
+ const void *data)
+{
+ struct system_pll_dev_ctx *ctx;
+ const struct mod_system_pll_dev_config *dev_config = data;
+
+ if (!fwk_module_is_valid_element_id(element_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+
+ ctx->config = dev_config;
+
+ if (ctx->config->defer_initialization)
+ return FWK_SUCCESS;
+
+ ctx->initialized = true;
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ return system_pll_set_rate(element_id, ctx->config->initial_rate,
+ MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int system_pll_process_bind_request(fwk_id_t requester_id, fwk_id_t id,
+ fwk_id_t api_type, const void **api)
+{
+ *api = &api_system_pll;
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_system_pll = {
+ .name = "SYSTEM PLL Driver",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_SYSTEM_PLL_API_COUNT,
+ .event_count = 0,
+ .init = system_pll_init,
+ .element_init = system_pll_element_init,
+ .process_bind_request = system_pll_process_bind_request,
+};
diff --git a/module/system_power/include/mod_system_power.h b/module/system_power/include/mod_system_power.h
new file mode 100644
index 00000000..c2251e33
--- /dev/null
+++ b/module/system_power/include/mod_system_power.h
@@ -0,0 +1,126 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Power Support
+ */
+
+#ifndef MOD_SYSTEM_POWER_H
+#define MOD_SYSTEM_POWER_H
+
+#include <fwk_id.h>
+#include <mod_power_domain.h>
+
+/*!
+ * \ingroup GroupModules Modules
+ * \defgroup GroupSystemPower System Power Support
+ *
+ * @{
+ */
+
+/*! Additional system_power power states */
+enum mod_system_power_power_states {
+ MOD_SYSTEM_POWER_POWER_STATE_SLEEP0 = MOD_PD_STATE_COUNT,
+ MOD_SYSTEM_POWER_POWER_STATE_SLEEP1,
+ MOD_SYSTEM_POWER_POWER_STATE_COUNT
+};
+
+/*! Extended PPU configuration */
+struct mod_system_power_ext_ppu_config {
+ /*! PPU identifier */
+ fwk_id_t ppu_id;
+
+ /*! API identifier */
+ fwk_id_t api_id;
+};
+
+/*! Module configuration */
+struct mod_system_power_config {
+ /*! SoC wakeup IRQ number */
+ unsigned int soc_wakeup_irq;
+
+ /*! System 0 PPU element ID */
+ fwk_id_t ppu_sys0_id;
+
+ /*! System 1 PPU element ID */
+ fwk_id_t ppu_sys1_id;
+
+ /*! System PPUs API ID */
+ fwk_id_t ppu_sys_api_id;
+
+ /*! Number of extended PPUs */
+ size_t ext_ppus_count;
+
+ /*!
+ * \brief Pointer to array of extended PPU configurations.
+ *
+ * \details These PPUs will be powered on automatically with the rest of the
+ * system.
+ */
+ const struct mod_system_power_ext_ppu_config *ext_ppus;
+
+ /*! System shutdown driver identifier */
+ fwk_id_t driver_id;
+
+ /*! System shutdown driver API identifier */
+ fwk_id_t driver_api_id;
+};
+
+/*!
+ * \brief Driver interface.
+ */
+struct mod_system_power_driver_api {
+ /*!
+ * \brief Pointer to the system shutdown function.
+ *
+ * \note This function is \b mandatory. In case of a successful call the
+ * function does not return.
+ *
+ * \param system_shutdown Type of system shutdown.
+ *
+ * \retval One of the driver-defined error code.
+ */
+ int (*system_shutdown)(enum mod_pd_system_shutdown system_shutdown);
+};
+
+/*!
+ * \defgroup GroupSystemPowerIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_system_power_api_idx {
+ /*! API index for the power domain driver API */
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER,
+
+ /*! API index for the power domain driver input API */
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER_INPUT,
+
+ /*! Number of exposed APIs */
+ MOD_SYSTEM_POWER_API_COUNT
+};
+
+/*! Identifier of the power domain driver API */
+static const fwk_id_t mod_system_power_api_id_pd_driver =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER,
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER);
+
+/*! Identifier of the power domain driver input API */
+static const fwk_id_t mod_system_power_api_id_pd_driver_input =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER,
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER_INPUT);
+
+/*!
+ * \}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SYSTEM_POWER_H */
diff --git a/module/system_power/src/Makefile b/module/system_power/src/Makefile
new file mode 100644
index 00000000..45a6acb6
--- /dev/null
+++ b/module/system_power/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SYSTEM POWER
+BS_LIB_SOURCES = mod_system_power.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/system_power/src/mod_system_power.c b/module/system_power/src/mod_system_power.c
new file mode 100644
index 00000000..acd49f42
--- /dev/null
+++ b/module/system_power/src/mod_system_power.c
@@ -0,0 +1,365 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * System Power Support.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_log.h>
+#include <mod_system_power.h>
+#include <mod_power_domain.h>
+
+/* Module context */
+struct system_power_ctx {
+ /* Log API pointer */
+ const struct mod_log_api *log_api;
+
+ /* SYS0 power domain driver API pointer */
+ const struct mod_pd_driver_api *sys0_api;
+
+ /* SYS1 power domain driver API pointer*/
+ const struct mod_pd_driver_api *sys1_api;
+
+ /* Pointer to array of extended PPU power domain driver APIs */
+ const struct mod_pd_driver_api *ext_ppu_apis;
+
+ /* Power domain module restricted API pointer */
+ const struct mod_pd_restricted_api *mod_pd_restricted_api;
+
+ /* Power domain module driver input API pointer */
+ const struct mod_pd_driver_input_api *mod_pd_driver_input_api;
+
+ /* Driver API pointer */
+ const struct mod_system_power_driver_api *driver_api;
+
+ /* Power domain module identifier of the system power domain */
+ fwk_id_t mod_pd_system_id;
+
+ /* Current system-level power state */
+ unsigned int state;
+
+ /* Pointer to module config */
+ const struct mod_system_power_config *config;
+};
+
+struct system_power_isr {
+ unsigned int interrupt;
+ void (*handler)(void);
+};
+
+static struct system_power_ctx system_power_ctx;
+
+static void ext_ppus_set_state(enum mod_pd_state state)
+{
+ unsigned int i;
+
+ for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
+ system_power_ctx.ext_ppu_apis[i].set_state(
+ system_power_ctx.config->ext_ppus[i].ppu_id,
+ state);
+ }
+}
+
+/*
+ * Functions fulfilling the Power Domain module's driver API
+ */
+
+static int system_power_set_state(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ switch (state) {
+ case MOD_PD_STATE_ON:
+ fwk_interrupt_disable(system_power_ctx.config->soc_wakeup_irq);
+
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_ON);
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_ON);
+
+ ext_ppus_set_state(MOD_PD_STATE_ON);
+
+ break;
+
+ case MOD_SYSTEM_POWER_POWER_STATE_SLEEP0:
+ ext_ppus_set_state(MOD_PD_STATE_OFF);
+
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_OFF);
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_ON);
+
+ fwk_interrupt_enable(system_power_ctx.config->soc_wakeup_irq);
+
+ break;
+
+ case MOD_PD_STATE_OFF:
+ fwk_interrupt_disable(system_power_ctx.config->soc_wakeup_irq);
+ ext_ppus_set_state(MOD_PD_STATE_OFF);
+
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_OFF);
+ system_power_ctx.sys0_api->set_state(
+ system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_OFF);
+
+ break;
+
+ default:
+ return FWK_E_SUPPORT;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int system_power_get_state(fwk_id_t pd_id, unsigned int *state)
+{
+ int status;
+
+ status = fwk_module_check_call(pd_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *state = system_power_ctx.state;
+
+ return FWK_SUCCESS;
+}
+
+static int system_power_reset(fwk_id_t pd_id)
+{
+ return FWK_E_SUPPORT;
+}
+
+static int system_power_shutdown(fwk_id_t pd_id,
+ enum mod_pd_system_shutdown system_shutdown)
+{
+ int status;
+
+ status = system_power_set_state(pd_id, MOD_PD_STATE_OFF);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return system_power_ctx.driver_api->system_shutdown(system_shutdown);
+}
+
+static void soc_wakeup_handler(void)
+{
+ int status;
+ fwk_id_t pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, 0);
+ uint32_t state = MOD_PD_COMPOSITE_STATE(MOD_PD_LEVEL_2,
+ 0,
+ MOD_PD_STATE_ON,
+ MOD_PD_STATE_ON,
+ MOD_PD_STATE_ON);
+
+ status =
+ system_power_ctx.mod_pd_restricted_api->set_composite_state_async(
+ pd_id, false, state);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+}
+
+static const struct mod_pd_driver_api system_power_power_domain_driver_api = {
+ .set_state = system_power_set_state,
+ .get_state = system_power_get_state,
+ .reset = system_power_reset,
+ .shutdown = system_power_shutdown
+};
+
+/*
+ * Functions fulfilling the Power Domain module's driver input API
+ */
+
+static int system_power_report_power_state_transition(fwk_id_t module_id,
+ unsigned int state)
+{
+ int status;
+ unsigned int sys0_state, sys1_state;
+
+ system_power_ctx.sys0_api->get_state(system_power_ctx.config->ppu_sys0_id,
+ &sys0_state);
+ system_power_ctx.sys1_api->get_state(system_power_ctx.config->ppu_sys1_id,
+ &sys1_state);
+
+ if ((sys0_state == MOD_PD_STATE_ON) && (sys1_state == MOD_PD_STATE_ON))
+ system_power_ctx.state = MOD_PD_STATE_ON;
+ else if ((sys0_state == MOD_PD_STATE_OFF) &&
+ (sys1_state == MOD_PD_STATE_ON))
+ system_power_ctx.state = MOD_SYSTEM_POWER_POWER_STATE_SLEEP0;
+ else
+ system_power_ctx.state = MOD_PD_STATE_OFF;
+
+ status =
+ system_power_ctx.mod_pd_driver_input_api->report_power_state_transition(
+ system_power_ctx.mod_pd_system_id, system_power_ctx.state);
+ assert(status == FWK_SUCCESS);
+ (void)status;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_pd_driver_input_api
+ system_power_power_domain_driver_input_api = {
+ .report_power_state_transition = system_power_report_power_state_transition
+};
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int system_power_mod_init(fwk_id_t module_id,
+ unsigned int unused,
+ const void *data)
+{
+ assert(data != NULL);
+
+ system_power_ctx.config = data;
+ system_power_ctx.mod_pd_system_id = FWK_ID_NONE;
+
+ if (system_power_ctx.config->ext_ppus_count > 0) {
+ system_power_ctx.ext_ppu_apis = fwk_mm_calloc(
+ system_power_ctx.config->ext_ppus_count,
+ sizeof(system_power_ctx.ext_ppu_apis[0]));
+ if (system_power_ctx.ext_ppu_apis == NULL)
+ return FWK_E_NOMEM;
+ }
+
+ if (system_power_ctx.config->soc_wakeup_irq != FWK_INTERRUPT_NONE) {
+ return fwk_interrupt_set_isr(system_power_ctx.config->soc_wakeup_irq,
+ soc_wakeup_handler);
+ } else
+ return FWK_SUCCESS;
+}
+
+static int system_power_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ unsigned int i;
+
+ if (round == 1) {
+ /*
+ * During the first round of binding, the power domain module should
+ * have bound to the power domain driver API provided by the present
+ * module. Bind back to the power domain driver input API provided by
+ * the system_power_ctx.mod_pd_system_id power domain module element to
+ * report power state transitions of the system power domains.
+ */
+ return fwk_module_bind(system_power_ctx.mod_pd_system_id,
+ mod_pd_api_id_driver_input,
+ &system_power_ctx.mod_pd_driver_input_api);
+ }
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &system_power_ctx.log_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(system_power_ctx.config->ppu_sys0_id,
+ system_power_ctx.config->ppu_sys_api_id,
+ &system_power_ctx.sys0_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(system_power_ctx.config->ppu_sys1_id,
+ system_power_ctx.config->ppu_sys_api_id,
+ &system_power_ctx.sys1_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
+ status = fwk_module_bind(
+ system_power_ctx.config->ext_ppus[i].ppu_id,
+ system_power_ctx.config->ext_ppus[i].api_id,
+ &system_power_ctx.ext_ppu_apis[i]);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ status = fwk_module_bind(system_power_ctx.config->driver_id,
+ system_power_ctx.config->driver_api_id, &system_power_ctx.driver_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = fwk_module_bind(fwk_module_id_power_domain,
+ mod_pd_api_id_restricted,
+ &system_power_ctx.mod_pd_restricted_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return FWK_SUCCESS;
+}
+
+static int system_power_process_bind_request(fwk_id_t requester_id,
+ fwk_id_t pd_id, fwk_id_t api_id,
+ const void **api)
+{
+ if (fwk_id_is_equal(api_id, mod_system_power_api_id_pd_driver)) {
+
+ if (!fwk_id_is_equal(fwk_id_build_module_id(requester_id),
+ fwk_module_id_power_domain))
+ return FWK_E_ACCESS;
+
+ *api = &system_power_power_domain_driver_api;
+ system_power_ctx.mod_pd_system_id = requester_id;
+ } else {
+ if (!fwk_id_is_equal(requester_id,
+ system_power_ctx.config->ppu_sys0_id) &&
+ !fwk_id_is_equal(requester_id,
+ system_power_ctx.config->ppu_sys1_id))
+ return FWK_E_ACCESS;
+ *api = &system_power_power_domain_driver_input_api;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int system_power_start(fwk_id_t id)
+{
+ int status;
+ unsigned int state;
+
+ status = system_power_ctx.sys1_api->get_state
+ (system_power_ctx.config->ppu_sys1_id, &state);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (state == MOD_PD_STATE_OFF) {
+ system_power_ctx.state = MOD_PD_STATE_OFF;
+ return FWK_SUCCESS;
+ }
+
+ status = system_power_ctx.sys0_api->get_state
+ (system_power_ctx.config->ppu_sys0_id, &state);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ system_power_ctx.state = (state == MOD_PD_STATE_ON) ?
+ MOD_PD_STATE_ON :
+ MOD_SYSTEM_POWER_POWER_STATE_SLEEP0;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_system_power = {
+ .name = "SYSTEM_POWER",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_SYSTEM_POWER_API_COUNT,
+ .init = system_power_mod_init,
+ .bind = system_power_bind,
+ .start = system_power_start,
+ .process_bind_request = system_power_process_bind_request,
+};
diff --git a/module/timer/include/mod_timer.h b/module/timer/include/mod_timer.h
new file mode 100644
index 00000000..7bf3567a
--- /dev/null
+++ b/module/timer/include/mod_timer.h
@@ -0,0 +1,294 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Timer HAL
+ */
+
+#ifndef MOD_TIMER_H
+#define MOD_TIMER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+/*!
+ * \addtogroup GroupModules Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModuleTimer Timer HAL
+ *
+ * \brief Hardware Abstraction Layer for Timers.
+ *
+ * \details Provides functionality for setting timer events, tracking elapsed
+ * time, and synchronously delaying execution.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Timer module API indicies
+ */
+enum mod_timer_api_idx {
+ /*! Timer API index */
+ MOD_TIMER_API_IDX_TIMER,
+
+ /*! Alarm API index */
+ MOD_TIMER_API_IDX_ALARM,
+
+ /*! Number of APIs */
+ MOD_TIMER_API_COUNT,
+};
+
+/*!
+ * \brief Timer API ID
+ */
+#define MOD_TIMER_API_ID_TIMER FWK_ID_API(FWK_MODULE_IDX_TIMER, \
+ MOD_TIMER_API_IDX_TIMER)
+
+/*!
+ * \brief Alarm API ID
+ */
+#define MOD_TIMER_API_ID_ALARM FWK_ID_API(FWK_MODULE_IDX_TIMER, \
+ MOD_TIMER_API_IDX_ALARM)
+
+/*!
+ * \brief Alarm type.
+ */
+enum mod_timer_alarm_type {
+ /*! Alarm that will trigger once */
+ MOD_TIMER_ALARM_TYPE_ONCE,
+
+ /*! Alarm that will trigger at regular intervals */
+ MOD_TIMER_ALARM_TYPE_PERIODIC,
+
+ /*! Number of alarm types */
+ MOD_TIMER_ALARM_TYPE_COUNT,
+};
+
+/*!
+ * \brief Timer device descriptor
+ */
+struct mod_timer_dev_config {
+ /*! Element identifier for the device's associated driver */
+ fwk_id_t id;
+
+ /*! Timer device IRQ number */
+ unsigned int timer_irq;
+};
+
+/*!
+ * \brief Timer driver interface.
+ */
+struct mod_timer_driver_api {
+ /*! Name of the driver. */
+ const char *name;
+
+ /*! Enable timer events */
+ int (*enable)(fwk_id_t dev_id);
+
+ /*! Disable timer events */
+ int (*disable)(fwk_id_t dev_id);
+
+ /*! Set timer event for a specified timestamp */
+ int (*set_timer)(fwk_id_t dev_id, uint64_t timestamp);
+
+ /*! Get remaining time until the next pending timer event is due to fire */
+ int (*get_timer)(fwk_id_t dev_id, uint64_t *timestamp);
+
+ /*! Get current counter value */
+ int (*get_counter)(fwk_id_t dev_id, uint64_t *value);
+
+ /*! Get counter frequency */
+ int (*get_frequency)(fwk_id_t dev_id, uint32_t *value);
+};
+
+/*!
+ * \brief Timer HAL interface
+ */
+struct mod_timer_api {
+ /*!
+ * \brief Get the frequency of a given timer.
+ *
+ * \details Get the frequency in Hertz (Hz) that a timer is running at.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param frequency Pointer to storage for the timer frequency.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM The frequency pointer was invalid.
+ * \retval One of the other specific error codes described by the framework.
+ */
+ int (*get_frequency)(fwk_id_t dev_id, uint32_t *frequency);
+
+ /*!
+ * \brief Get a counter timestamp that represents a given time period in
+ * microseconds (µS).
+ *
+ * \note The value of the resulting timestamp is only valid for the given
+ * device, since other timer devices may operate at different rates.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param microseconds Period, in microseconds.
+ * \param timestamp Pointer to storage for the resulting counter timestamp.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM The timestamp pointer was invalid.
+ * \retval One of the other specific error codes described by the framework.
+ */
+ int (*time_to_timestamp)(fwk_id_t dev_id,
+ uint32_t microseconds,
+ uint64_t *timestamp);
+
+ /*!
+ * \brief Get the current counter value of a given timer.
+ *
+ * \details Directly returns the counter value of the timer at the present
+ * moment.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param counter Pointer to storage for the counter value.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM The counter pointer was invalid.
+ * \retval One of the other specific error codes described by the framework.
+ */
+ int (*get_counter)(fwk_id_t dev_id, uint64_t *counter);
+
+ /*!
+ * \brief Delay execution by synchronously waiting for a specified amount
+ * of time.
+ *
+ * \details Blocks the calling thread for the specified amount of time.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param microseconds The amount of time, given in microseconds, to delay.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval One of the other specific error codes described by the module.
+ */
+ int (*delay)(fwk_id_t dev_id, uint32_t microseconds);
+
+ /*!
+ * \brief Delay execution, waiting until a given condition is true or until
+ * a given timeout period has been exceeded, whichever occurs first.
+ *
+ * \note The calling thread is blocked until either condition has been met.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param microseconds Maximum amount of time, in microseconds, to wait for
+ * the given condition to be met.
+ * \param cond Pointer to the function that evaluates the condition and
+ * which returns a boolean value indicating if it has been met or not.
+ * The condition function is called repeatedly until it returns true,
+ * or until the timeout period has elapsed.
+ * \param data Pointer passed to the condition function when it is called.
+ *
+ * \retval FWK_SUCCESS The condition was met before the timeout period
+ * elapsed.
+ * \retval FWK_E_TIMEOUT The timeout period elapsed before the condition was
+ * met.
+ * \retval One of the other specific error codes described by the module.
+ */
+ int (*wait)(fwk_id_t dev_id,
+ uint32_t microseconds,
+ bool (*cond)(void*),
+ void *data);
+
+ /*!
+ * \brief Get the time difference, expressed in timer ticks, between the
+ * current timer counter value and the given timestamp. This represents
+ * the remaining number of ticks until the given timestamp is reached.
+ *
+ * \note If the given timestamp is in the past then the remaining_ticks is
+ * set to zero.
+ *
+ * \param dev_id Element identifier that identifies the timer device.
+ * \param timestamp Timestamp to compare to the current timer value.
+ * \param remaining_ticks Pointer to storage for the remaining number of
+ * ticks before the timer value reaches the given timestamp.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM The remaining_ticks pointer was invalid.
+ * \retval One of the other specific error codes described by the module.
+ *
+ * \note remaining_ticks is also a timestamp.
+ */
+ int (*remaining)(fwk_id_t dev_id,
+ uint64_t timestamp,
+ uint64_t *remaining_ticks);
+};
+
+/*!
+ * \brief Alarm interface
+ */
+struct mod_timer_alarm_api {
+ /*!
+ * \brief Start an alarm so it will trigger after a specified time.
+ *
+ * \details When an alarm is triggered, an event with identifier
+ * \p event_id will be sent to the entity that is bound to the alarm.
+ * The first word of the event's parameter will be set to \p param.
+ *
+ * If the alarm is periodic, it will automatically be started again
+ * with the same time delay after it triggers.
+ *
+ * An alarm can be started multiple times without being stopped. In this
+ * case, internally, the alarm will be stopped then started again with
+ * the new configuration.
+ *
+ * \param alarm_id Sub-element identifier of the alarm.
+ * \param event_id Identifier of the event the caller is expecting.
+ * \param milliseconds The time delay, given in milliseconds, until the
+ * alarm should trigger.
+ * \param type \ref MOD_TIMER_ALARM_TYPE_ONCE or
+ * \ref MOD_TIMER_ALARM_TYPE_PERIODIC.
+ * \param param Word-size parameter for the event.
+ *
+ * \pre \p alarm_id must be a valid sub-element alarm identifier that has
+ * previously been bound to.
+ *
+ * \retval FWK_SUCCESS The alarm was started.
+ */
+ int (*start)(fwk_id_t alarm_id,
+ unsigned int milliseconds,
+ enum mod_timer_alarm_type type,
+ fwk_id_t event_id,
+ uintptr_t param);
+
+ /*!
+ * \brief Stop a previously started alarm.
+ *
+ * \details Stop an alarm that was previously started. This will prevent the
+ * alarm from triggering. This does not undo the binding of the alarm
+ * and it can be started again afterwards.
+ *
+ * Any pending alarm events associated with the alarm are not cancelled
+ * or removed when the alarm is stopped.
+ *
+ * \param alarm_id Sub-element identifier of the alarm item.
+ *
+ * \pre \p alarm_id must be a valid sub-element alarm identifier that has
+ * previously been bound to.
+ *
+ * \retval FWK_SUCCESS The alarm was stopped.
+ * \retval FWK_E_STATE The alarm was already stopped.
+ */
+ int (*stop)(fwk_id_t alarm_id);
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_TIMER_H */
diff --git a/module/timer/src/Makefile b/module/timer/src/Makefile
new file mode 100644
index 00000000..a89d8a96
--- /dev/null
+++ b/module/timer/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Timer
+BS_LIB_SOURCES = mod_timer.c
+
+include $(BS_DIR)/lib.mk
diff --git a/module/timer/src/mod_timer.c b/module/timer/src/mod_timer.c
new file mode 100644
index 00000000..2b61cd26
--- /dev/null
+++ b/module/timer/src/mod_timer.c
@@ -0,0 +1,653 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Implementation of Timer module
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_event.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_list.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_thread.h>
+#include <mod_log.h>
+#include <mod_timer.h>
+#include <fwk_module_idx.h>
+
+/* Timer device context (element) */
+struct dev_ctx {
+ /* Pointer to the device's configuration */
+ const struct mod_timer_dev_config *config;
+ /* Pointer to an API provided by the driver that controls the device */
+ struct mod_timer_driver_api *driver;
+ /* Identifier of the driver that controls the device */
+ fwk_id_t driver_dev_id;
+ /* Storage for all alarms */
+ struct alarm_ctx *alarm_pool;
+ /* Queue of active alarms */
+ struct fwk_dlist alarms_active;
+};
+
+/* Alarm item context (sub-element) */
+struct alarm_ctx {
+ /* List node */
+ struct fwk_dlist_node node;
+ /* Time between starting this alarm and it triggering */
+ uint32_t microseconds;
+ /* Timestamp of the time this alarm will trigger */
+ uint64_t timestamp;
+ /* Identifier of the entity to send the alarm event to */
+ fwk_id_t listener;
+ /* Identifier of the event the listener is expecting to receive */
+ fwk_id_t event_id;
+ /* Parameter of the event */
+ uintptr_t param;
+ /* Flag indicating if this alarm if periodic */
+ bool periodic;
+ /* Flag indicating if this alarm is in the active queue */
+ bool started;
+ /* Flag indicating if this alarm has been bound to */
+ bool bound;
+};
+
+/* Table of timer device context structures */
+static struct dev_ctx *ctx_table;
+
+/* Log API */
+static const struct mod_log_api *log_api;
+
+/*
+ * Forward declarations
+ */
+
+static void timer_isr(uintptr_t ctx_ptr);
+
+/*
+ * Internal functions
+ */
+
+static int _time_to_timestamp(struct dev_ctx *ctx,
+ uint32_t microseconds,
+ uint64_t *timestamp)
+{
+ int status;
+ uint32_t frequency;
+
+ assert(ctx != NULL);
+ assert(timestamp != NULL);
+
+ status = ctx->driver->get_frequency(ctx->driver_dev_id, &frequency);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *timestamp = ((uint64_t)frequency * microseconds) / 1000000;
+
+ return FWK_SUCCESS;
+}
+
+static int _timestamp_from_now(struct dev_ctx *ctx,
+ uint32_t microseconds,
+ uint64_t *timestamp)
+{
+ int status;
+ uint64_t counter;
+
+ assert(ctx != NULL);
+ assert(timestamp != NULL);
+
+ status = _time_to_timestamp(ctx, microseconds, timestamp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *timestamp += counter;
+
+ return FWK_SUCCESS;
+}
+
+static void _configure_timer_with_next_alarm(struct dev_ctx *ctx)
+{
+ struct alarm_ctx *alarm_head;
+ uint64_t counter = 0;
+ int status;
+
+ assert(ctx != NULL);
+
+ alarm_head = (struct alarm_ctx *)fwk_list_head(&ctx->alarms_active);
+ if (alarm_head != NULL) {
+ /*
+ * If an alarm's period is very small, the timer device could be
+ * configured to interrupt on a timestamp that is "in the past" by the
+ * time interrupts are enabled. In this case, the interrupt will not be
+ * generated due to a model bug. This code can be deleted once this bug
+ * has been fixed.
+ *
+ * If this alarm occurs very soon, process it immediately to avoid
+ * potentially missing the interrupt and waiting forever.
+ */
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if ((status == FWK_SUCCESS) &&
+ (counter + 2000 >= alarm_head->timestamp))
+ timer_isr((uintptr_t)ctx);
+
+ /* Configure timer device */
+ ctx->driver->set_timer(ctx->driver_dev_id, alarm_head->timestamp);
+ ctx->driver->enable(ctx->driver_dev_id);
+ }
+}
+
+static void _insert_alarm_ctx_into_active_queue(struct dev_ctx *ctx,
+ struct alarm_ctx *alarm_new)
+{
+ struct fwk_dlist_node *alarm_node;
+ struct alarm_ctx *alarm;
+
+ assert(ctx != NULL);
+ assert(alarm_new != NULL);
+
+ /*
+ * Search though the active queue to find the correct place to insert the
+ * new alarm item
+ */
+ alarm_node = fwk_list_head(&ctx->alarms_active);
+ alarm = FWK_LIST_GET(alarm_node, struct alarm_ctx, node);
+
+ while ((alarm_node != NULL) && (alarm_new->timestamp > alarm->timestamp)) {
+ alarm_node = fwk_list_next(&ctx->alarms_active, alarm_node);
+ alarm = FWK_LIST_GET(alarm_node, struct alarm_ctx, node);
+ }
+
+ /* Insert alarm_new just BEFORE the alarm that was found */
+ fwk_list_insert(&ctx->alarms_active,
+ &(alarm_new->node),
+ alarm_node);
+
+ alarm_new->started = true;
+}
+
+
+/*
+ * Functions fulfilling the timer API
+ */
+
+static int get_frequency(fwk_id_t dev_id, uint32_t *frequency)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ if (frequency == NULL)
+ return FWK_E_PARAM;
+
+ return ctx->driver->get_frequency(ctx->driver_dev_id, frequency);
+}
+
+static int time_to_timestamp(fwk_id_t dev_id,
+ uint32_t microseconds,
+ uint64_t *timestamp)
+{
+ int status;
+ struct dev_ctx *ctx;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (timestamp == NULL)
+ return FWK_E_PARAM;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ return _time_to_timestamp(ctx, microseconds, timestamp);
+}
+
+static int get_counter(fwk_id_t dev_id, uint64_t *counter)
+{
+ struct dev_ctx *ctx;
+ int status;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ if (counter == NULL)
+ return FWK_E_PARAM;
+
+ /* Read counter */
+ return ctx->driver->get_counter(ctx->driver_dev_id, counter);
+}
+
+static int delay(fwk_id_t dev_id, uint32_t microseconds)
+{
+ int status;
+ struct dev_ctx *ctx;
+ uint64_t counter, counter_limit;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ status = _timestamp_from_now(ctx, microseconds, &counter_limit);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ do {
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if (status != FWK_SUCCESS)
+ return status;
+ } while (counter < counter_limit);
+
+ return FWK_SUCCESS;
+}
+
+static int wait(fwk_id_t dev_id,
+ uint32_t microseconds,
+ bool (*cond)(void*),
+ void *data)
+{
+ struct dev_ctx *ctx;
+ int status;
+ uint64_t counter, counter_limit;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ status = _timestamp_from_now(ctx, microseconds, &counter_limit);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ while (true) {
+
+ if (cond(data))
+ return FWK_SUCCESS;
+
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if (status != FWK_SUCCESS)
+ return FWK_E_DEVICE;
+
+ /*
+ * If the time to wait is over, check condition one last time.
+ */
+ if (counter > counter_limit) {
+ if (cond(data))
+ return FWK_SUCCESS;
+ else
+ return FWK_E_TIMEOUT;
+ }
+ }
+}
+
+static int remaining(fwk_id_t dev_id,
+ uint64_t timestamp,
+ uint64_t *remaining_ticks)
+{
+ struct dev_ctx *ctx;
+ int status;
+ uint64_t counter;
+
+ status = fwk_module_check_call(dev_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(dev_id)];
+
+ if (remaining_ticks == NULL)
+ return FWK_E_PARAM;
+
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (timestamp <= counter)
+ *remaining_ticks = 0;
+ else
+ *remaining_ticks = timestamp - counter;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_timer_api timer_api = {
+ .get_frequency = get_frequency,
+ .time_to_timestamp = time_to_timestamp,
+ .get_counter = get_counter,
+ .delay = delay,
+ .wait = wait,
+ .remaining = remaining,
+};
+
+/*
+ * Functions fulfilling the alarm API
+ */
+
+static int alarm_stop(fwk_id_t alarm_id)
+{
+ int status;
+ struct dev_ctx *ctx;
+ struct alarm_ctx *alarm;
+
+ assert(fwk_module_is_valid_sub_element_id(alarm_id));
+
+ status = fwk_module_check_call(alarm_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(alarm_id)];
+ alarm = &ctx->alarm_pool[fwk_id_get_sub_element_idx(alarm_id)];
+
+ if (!alarm->started)
+ return FWK_E_STATE;
+
+ /* Disable timer interrupts to work with the active queue */
+ ctx->driver->disable(ctx->driver_dev_id);
+
+ fwk_list_remove(&ctx->alarms_active, (struct fwk_dlist_node *)alarm);
+ alarm->started = false;
+
+ _configure_timer_with_next_alarm(ctx);
+
+ return FWK_SUCCESS;
+}
+
+static int alarm_start(fwk_id_t alarm_id,
+ unsigned int milliseconds,
+ enum mod_timer_alarm_type type,
+ fwk_id_t event_id,
+ uintptr_t param)
+{
+ int status;
+ struct dev_ctx *ctx;
+ struct alarm_ctx *alarm;
+
+ assert(fwk_module_is_valid_sub_element_id(alarm_id));
+
+ status = fwk_module_check_call(alarm_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx = ctx_table + fwk_id_get_element_idx(alarm_id);
+ alarm = &ctx->alarm_pool[fwk_id_get_sub_element_idx(alarm_id)];
+
+ if (alarm->started)
+ alarm_stop(alarm_id);
+
+ /* Cap to ensure value will not overflow when stored as microseconds */
+ milliseconds = FWK_MIN(milliseconds, UINT32_MAX / 1000);
+
+ /* Populate alarm item */
+ alarm->event_id = event_id;
+ alarm->param = param;
+ alarm->periodic =
+ (type == MOD_TIMER_ALARM_TYPE_PERIODIC ? true : false);
+ alarm->microseconds = milliseconds * 1000;
+ status = _timestamp_from_now(ctx,
+ alarm->microseconds,
+ &alarm->timestamp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Disable timer interrupts to work with the active queue */
+ ctx->driver->disable(ctx->driver_dev_id);
+
+ _insert_alarm_ctx_into_active_queue(ctx, alarm);
+
+ _configure_timer_with_next_alarm(ctx);
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_timer_alarm_api alarm_api = {
+ .start = alarm_start,
+ .stop = alarm_stop,
+};
+
+static void timer_isr(uintptr_t ctx_ptr)
+{
+ int status;
+ struct alarm_ctx *alarm;
+ struct alarm_ctx *alarm_head;
+ struct dev_ctx *ctx = (struct dev_ctx *)ctx_ptr;
+ uint64_t timestamp = 0;
+ uint64_t counter = 0;
+ struct fwk_event event;
+
+ assert(ctx != NULL);
+
+ /* Disable timer interrupts to work with the active queue */
+ ctx->driver->disable(ctx->driver_dev_id);
+ fwk_interrupt_clear_pending(ctx->config->timer_irq);
+
+process_alarm:
+ alarm = (struct alarm_ctx *)fwk_list_pop_head(&ctx->alarms_active);
+
+ if (alarm == NULL) {
+ /* Timer interrupt triggered without any alarm in the active queue */
+ assert(false);
+ return;
+ }
+
+ alarm->started = false;
+
+ event = (struct fwk_event) {
+ .source_id = fwk_module_id_timer,
+ .target_id = alarm->listener,
+ .id = alarm->event_id,
+ };
+ *(uintptr_t *)event.params = alarm->param; /* Word-size parameter */
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS)
+ log_api->log(MOD_LOG_GROUP_WARNING,
+ "[Timer] Warning: Alarm was triggered but event could not "
+ "be pushed. Error code: %i\n", status);
+
+ if (alarm->periodic) {
+ /* Put this alarm back into the active queue */
+ status = _time_to_timestamp(ctx, alarm->microseconds, &timestamp);
+
+ if (status == FWK_SUCCESS) {
+ alarm->timestamp += timestamp;
+ _insert_alarm_ctx_into_active_queue(ctx, alarm);
+ } else
+ log_api->log(MOD_LOG_GROUP_ERROR,
+ "[Timer] Error: Periodic alarm could not be added "
+ "back into queue.\n");
+ }
+
+ alarm_head = FWK_LIST_GET(fwk_list_head(&ctx->alarms_active),
+ struct alarm_ctx,
+ node);
+ if (alarm_head != NULL) {
+ /*
+ * If successive alarm item timestamps are very close together, the
+ * timer device could be configured to interrupt on a timestamp that is
+ * "in the past". In this case, the interrupt will not be generated due
+ * to a model bug. This code can be deleted once this bug has been
+ * fixed.
+ *
+ * If the next alarm occurs very soon, process it immidiately to avoid
+ * potentially missing the interrupt and waiting forever.
+ */
+ status = ctx->driver->get_counter(ctx->driver_dev_id, &counter);
+ if ((status == FWK_SUCCESS) &&
+ (counter + 2000 >= alarm_head->timestamp))
+ goto process_alarm;
+
+ ctx->driver->set_timer(ctx->driver_dev_id, alarm_head->timestamp);
+ ctx->driver->enable(ctx->driver_dev_id);
+ }
+}
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int timer_init(fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ ctx_table = fwk_mm_calloc(element_count, sizeof(struct dev_ctx));
+
+ if (ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ return FWK_SUCCESS;
+}
+
+static int timer_device_init(fwk_id_t element_id, unsigned int alarm_count,
+ const void *data)
+{
+ struct dev_ctx *ctx;
+
+ assert(data != NULL);
+
+ ctx = ctx_table + fwk_id_get_element_idx(element_id);
+ ctx->config = data;
+
+ if (alarm_count > 0) {
+ ctx->alarm_pool = fwk_mm_calloc(alarm_count, sizeof(struct alarm_ctx));
+ if (ctx->alarm_pool == NULL) {
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int timer_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct dev_ctx *ctx;
+ struct mod_timer_driver_api *driver;
+ unsigned int driver_module_idx;
+
+ /* Nothing to do after the initial round. */
+ if (round > 0)
+ return FWK_SUCCESS;
+
+ /* Bind to log module */
+ if (fwk_module_is_valid_module_id(id)) {
+ return fwk_module_bind(fwk_module_id_log,
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0),
+ &log_api);
+ }
+
+ ctx = ctx_table + fwk_id_get_element_idx(id);
+ ctx->driver_dev_id = ctx->config->id;
+
+ /* Bind to the driver API for the current device */
+ driver_module_idx = fwk_id_get_module_idx(ctx->driver_dev_id);
+ status = fwk_module_bind(ctx->driver_dev_id,
+ FWK_ID_API(driver_module_idx, 0),
+ &driver);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ /* Check that the driver API is completely fulfilled */
+ if (driver->enable == NULL ||
+ driver->disable == NULL ||
+ driver->get_counter == NULL ||
+ driver->get_frequency == NULL)
+ return FWK_E_DEVICE;
+
+ ctx->driver = driver;
+
+ return FWK_SUCCESS;
+}
+
+static int timer_process_bind_request(fwk_id_t requester_id,
+ fwk_id_t id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ struct dev_ctx *ctx;
+ struct alarm_ctx *alarm_ctx;
+
+ if (fwk_id_is_equal(api_id, MOD_TIMER_API_ID_TIMER)) {
+ if (!fwk_module_is_valid_element_id(id)) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ *api = &timer_api;
+ return FWK_SUCCESS;
+ }
+
+ /* Alarm API requested */
+
+ if (!fwk_module_is_valid_sub_element_id(id)) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ ctx = ctx_table + fwk_id_get_element_idx(id);
+ alarm_ctx = &ctx->alarm_pool[fwk_id_get_sub_element_idx(id)];
+
+ if (alarm_ctx->bound) {
+ assert(false);
+ return FWK_E_STATE;
+ }
+
+ alarm_ctx->bound = true;
+ alarm_ctx->listener = requester_id;
+
+ *api = &alarm_api;
+ return FWK_SUCCESS;
+}
+
+static int timer_start(fwk_id_t id)
+{
+ struct dev_ctx *ctx;
+
+ if (!fwk_module_is_valid_element_id(id))
+ return FWK_SUCCESS;
+
+ ctx = ctx_table + fwk_id_get_element_idx(id);
+
+ fwk_list_init(&ctx->alarms_active);
+
+ fwk_interrupt_set_isr_param(ctx->config->timer_irq,
+ timer_isr,
+ (uintptr_t)ctx);
+ fwk_interrupt_enable(ctx->config->timer_irq);
+
+ return FWK_SUCCESS;
+}
+
+/* Module descriptor */
+const struct fwk_module module_timer = {
+ .name = "Timer HAL",
+ .api_count = MOD_TIMER_API_COUNT,
+ .type = FWK_MODULE_TYPE_HAL,
+ .init = timer_init,
+ .element_init = timer_device_init,
+ .bind = timer_bind,
+ .process_bind_request = timer_process_bind_request,
+ .start = timer_start,
+};
diff --git a/product/sgm775/include/fmw_cmsis.h b/product/sgm775/include/fmw_cmsis.h
new file mode 100644
index 00000000..8cf22ddd
--- /dev/null
+++ b/product/sgm775/include/fmw_cmsis.h
@@ -0,0 +1,30 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FMW_CMSIS_H
+#define FMW_CMSIS_H
+
+#define __CHECK_DEVICE_DEFINES
+#define __CM3_REV 0x0201
+#define __MPU_PRESENT 1
+#define __NVIC_PRIO_BITS 3
+#define __Vendor_SysTickConfig 0
+
+typedef enum IRQn {
+ NonMaskableInt_IRQn = -14,
+ MemoryManagement_IRQn = -12,
+ BusFault_IRQn = -11,
+ UsageFault_IRQn = -10,
+ SVCall_IRQn = -5,
+ DebugMonitor_IRQn = -4,
+ PendSV_IRQn = -2,
+ SysTick_IRQn = -1,
+} IRQn_Type;
+
+#include <core_cm3.h>
+
+#endif /* FMW_CMSIS_H */
diff --git a/product/sgm775/include/sgm775_core.h b/product/sgm775/include/sgm775_core.h
new file mode 100644
index 00000000..afa59509
--- /dev/null
+++ b/product/sgm775/include/sgm775_core.h
@@ -0,0 +1,15 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_CORE_H
+#define SGM775_CORE_H
+
+#define SGM775_CORE_PER_CLUSTER_MAX 8
+
+unsigned int sgm775_core_get_count(void);
+
+#endif /* SGM775_CORE_H */
diff --git a/product/sgm775/include/sgm775_irq.h b/product/sgm775/include/sgm775_irq.h
new file mode 100644
index 00000000..fbbe92e7
--- /dev/null
+++ b/product/sgm775/include/sgm775_irq.h
@@ -0,0 +1,178 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_IRQ_H
+#define SGM775_IRQ_H
+
+#include <fwk_interrupt.h>
+
+#define WDOG_IRQ FWK_INTERRUPT_NMI /* SCP Watchdog (SP805) */
+
+enum sgm775_irq {
+ TIM32KHZ_IRQ = 0, /* 32KHz Physical Timer */
+ CDBG_PWR_UP_REQ_IRQ = 1, /* Coresight Debug Power Request */
+ CSYS_PWR_UP_REQ_IRQ = 2, /* Coresight System Power Request */
+ CDBG_RST_REQ_IRQ = 3, /* Coresight Debug Reset Request */
+ GIC_EXT_WAKEUP_IRQ = 4, /* External GIC Wakeup Request */
+ RESERVED5_IRQ = 5, /* Reserved */
+ RESERVED6_IRQ = 6, /* Reserved */
+ RESERVED7_IRQ = 7, /* Reserved */
+ RESERVED8_IRQ = 8, /* Reserved */
+ RESERVED9_IRQ = 9, /* Reserved */
+ RESERVED10_IRQ = 10, /* Reserved */
+ RESERVED11_IRQ = 11, /* Reserved */
+ RESERVED12_IRQ = 12, /* Reserved */
+ RESERVED13_IRQ = 13, /* Reserved */
+ RESERVED14_IRQ = 14, /* Reserved */
+ RESERVED15_IRQ = 15, /* Reserved */
+ SOC_WAKEUP0_IRQ = 16, /* SoC Expansion Wakeup */
+ SOC_WAKEUP1_IRQ = 17, /* SoC Expansion Wakeup */
+ SOC_WAKEUP2_IRQ = 18, /* SoC Expansion Wakeup */
+ SOC_WAKEUP3_IRQ = 19, /* SoC Expansion Wakeup */
+ SOC_WAKEUP4_IRQ = 20, /* SoC Expansion Wakeup */
+ SOC_WAKEUP5_IRQ = 21, /* SoC Expansion Wakeup */
+ SOC_WAKEUP6_IRQ = 22, /* SoC Expansion Wakeup */
+ SOC_WAKEUP7_IRQ = 23, /* SoC Expansion Wakeup */
+ SOC_WAKEUP8_IRQ = 24, /* SoC Expansion Wakeup */
+ SOC_WAKEUP9_IRQ = 25, /* SoC Expansion Wakeup */
+ SOC_WAKEUP10_IRQ = 26, /* SoC Expansion Wakeup */
+ SOC_WAKEUP11_IRQ = 27, /* SoC Expansion Wakeup */
+ SOC_WAKEUP12_IRQ = 28, /* SoC Expansion Wakeup */
+ SOC_WAKEUP13_IRQ = 29, /* SoC Expansion Wakeup */
+ SOC_WAKEUP14_IRQ = 30, /* SoC Expansion Wakeup */
+ SOC_WAKEUP15_IRQ = 31, /* SoC Expansion Wakeup */
+ PPU_SCP_IRQ = 32, /* SCP Power Policy Unit */
+ TIMREFCLK_IRQ = 33, /* REFCLK Physical Timer */
+ MHU_HIGH_PRIO_IRQ = 34, /* MHU High Priority */
+ MHU_LOW_PRIO_IRQ = 35, /* MHU Low Priority */
+ MHU_SECURE_IRQ = 36, /* MHU Secure */
+ CTI_TRIGGER0_IRQ = 37, /* SCP CTI Trigger */
+ CTI_TRIGGER1_IRQ = 38, /* SCP CTI Trigger */
+ GIC_ERROR_ECC_IRQ = 39, /* GIC Error (ECC Fatal) */
+ GIC_ERROR_AXIM_IRQ = 40, /* GIC Error (AXIM) */
+ DMC_RESERVED0_IRQ = 41, /* DMC, Reserved */
+ DMC_0_ERROR_ECC_IRQ = 42, /* DMC0 Combined ECC Error */
+ DMC_0_ERROR_ACCESS_IRQ = 43, /* DMC0 Combined Misc Access Error */
+ DMC_RESERVED1_IRQ = 44, /* DMC, Reserved */
+ DMC_RESERVED2_IRQ = 45, /* DMC, Reserved */
+ DMC_1_ERROR_ECC_IRQ = 46, /* DMC1 Combined ECC Error */
+ DMC_1_ERROR_ACCESS_IRQ = 47, /* DMC1 Combined Misc Access Error */
+ DMC_RESERVED3_IRQ = 48, /* DMC, Reserved */
+ DMC_RESERVED4_IRQ = 49, /* DMC, Reserved */
+ DMC_2_ERROR_ECC_IRQ = 50, /* DMC2 Combined ECC Error */
+ DMC_2_ERROR_ACCESS_IRQ = 51, /* DMC2 Combined Misc Access Error */
+ DMC_RESERVED5_IRQ = 52, /* DMC, Reserved */
+ DMC_RESERVED6_IRQ = 53, /* DMC, Reserved */
+ DMC_3_ERROR_ECC_IRQ = 54, /* DMC3 Combined ECC Error */
+ DMC_3_ERROR_ACCESS_IRQ = 55, /* DMC3 Combined Misc Access Error */
+ DMC_RESERVED7_IRQ = 56, /* DMC, Reserved */
+ RESERVED57_IRQ = 57, /* Reserved */
+ RESERVED58_IRQ = 58, /* Reserved */
+ RESERVED59_IRQ = 59, /* Reserved */
+ RESERVED60_IRQ = 60, /* Reserved */
+ RESERVED61_IRQ = 61, /* Reserved */
+ RESERVED62_IRQ = 62, /* Reserved */
+ RESERVED63_IRQ = 63, /* Reserved */
+ PPU_CLUS0CORE0_IRQ = 64, /* Cluster 0 Core 0 Power Policy Unit */
+ PPU_CLUS0CORE1_IRQ = 65, /* Cluster 0 Core 1 Power Policy Unit */
+ PPU_CLUS0CORE2_IRQ = 66, /* Cluster 0 Core 2 Power Policy Unit */
+ PPU_CLUS0CORE3_IRQ = 67, /* Cluster 0 Core 3 Power Policy Unit */
+ PPU_CLUS0_IRQ = 68, /* Cluster 0 Power Policy Unit */
+ PPU_CLUS1CORE0_IRQ = 69, /* Cluster 1 Core 0 Power Policy Unit */
+ PPU_CLUS1CORE1_IRQ = 70, /* Cluster 1 Core 1 Power Policy Unit */
+ PPU_CLUS1CORE2_IRQ = 71, /* Cluster 1 Core 2 Power Policy Unit */
+ PPU_CLUS1CORE3_IRQ = 72, /* Cluster 1 Core 3 Power Policy Unit */
+ PPU_CLUS1_IRQ = 73, /* Cluster 1 Power Policy Unit */
+ PPU_SYS0_IRQ = 74, /* System Power Policy Unit 0 */
+ PPU_SYS1_IRQ = 75, /* System Power Policy Unit 1 */
+ PPU_GPU_IRQ = 76, /* GPU Power Policy Unit */
+ PPU_VPU_IRQ = 77, /* Video Power Policy Unit */
+ RESERVED78_IRQ = 78, /* Reserved */
+ PPU_DPU0_IRQ = 79, /* Display Power Policy Unit 0 */
+ PPU_DPU1_IRQ = 80, /* Display Power Policy Unit 1 */
+ PPU_DEBUG_IRQ = 81, /* DBGSYS Power Policy Unit */
+ RESERVED82_IRQ = 82, /* Reserved */
+ RESERVED83_IRQ = 83, /* Reserved */
+ RESERVED84_IRQ = 84, /* Reserved */
+ RESERVED85_IRQ = 85, /* Reserved */
+ RESERVED86_IRQ = 86, /* Reserved */
+ RESERVED87_IRQ = 87, /* Reserved */
+ RESERVED88_IRQ = 88, /* Reserved */
+ RESERVED89_IRQ = 89, /* Reserved */
+ PPU_CLUS0CORE4_IRQ = 90, /* Cluster 0 Core 4 Power Policy Unit */
+ PPU_CLUS0CORE5_IRQ = 91, /* Cluster 0 Core 5 Power Policy Unit */
+ PPU_CLUS0CORE6_IRQ = 92, /* Cluster 0 Core 6 Power Policy Unit */
+ PPU_CLUS0CORE7_IRQ = 93, /* Cluster 0 Core 7 Power Policy Unit */
+ PPU_CLUS1CORE4_IRQ = 94, /* Cluster 1 Core 4 Power Policy Unit */
+ PPU_CLUS1CORE5_IRQ = 95, /* Cluster 1 Core 5 Power Policy Unit */
+ PPU_CLUS1CORE6_IRQ = 96, /* Cluster 1 Core 6 Power Policy Unit */
+ PPU_CLUS1CORE7_IRQ = 97, /* Cluster 1 Core 7 Power Policy Unit */
+ PLL_CLUS0_LOCK_IRQ = 98, /* Cluster 0 CPU PLL Lock */
+ PLL_CLUS1_LOCK_IRQ = 99, /* Cluster 1 CPU PLL Lock */
+ PLL_GPU_LOCK_IRQ = 100, /* GPU PLL Lock */
+ PLL_VPU_LOCK_IRQ = 101, /* Video PLL Lock */
+ PLL_SYS_LOCK_IRQ = 102, /* System PLL Lock */
+ PLL_DPU_LOCK_IRQ = 103, /* Display PLL Lock */
+ PLL_CLUS0CORE0_IRQ = 104, /* Cluster 0 PLL0 Lock */
+ PLL_CLUS0CORE1_IRQ = 105, /* Cluster 0 PLL1 Lock */
+ PLL_CLUS0CORE2_IRQ = 106, /* Cluster 0 PLL2 Lock */
+ PLL_CLUS0CORE3_IRQ = 107, /* Cluster 0 PLL3 Lock */
+ PLL_CLUS0CORE4_IRQ = 108, /* Cluster 0 PLL4 Lock */
+ PLL_CLUS0CORE5_IRQ = 109, /* Cluster 0 PLL5 Lock */
+ PLL_CLUS0CORE6_IRQ = 110, /* Cluster 0 PLL6 Lock */
+ PLL_CLUS0CORE7_IRQ = 111, /* Cluster 0 PLL7 Lock */
+ PLL_CLUS1CORE0_IRQ = 112, /* Cluster 1 PLL0 Lock */
+ PLL_CLUS1CORE1_IRQ = 113, /* Cluster 1 PLL1 Lock */
+ PLL_CLUS1CORE2_IRQ = 114, /* Cluster 1 PLL2 Lock */
+ PLL_CLUS1CORE3_IRQ = 115, /* Cluster 1 PLL3 Lock */
+ PLL_CLUS1CORE4_IRQ = 116, /* Cluster 1 PLL4 Lock */
+ PLL_CLUS1CORE5_IRQ = 117, /* Cluster 1 PLL5 Lock */
+ PLL_CLUS1CORE6_IRQ = 118, /* Cluster 1 PLL6 Lock */
+ PLL_CLUS1CORE7_IRQ = 119, /* Cluster 1 PLL7 Lock */
+ RESERVED120_IRQ = 120, /* Reserved */
+ RESERVED121_IRQ = 121, /* Reserved */
+ RESERVED122_IRQ = 122, /* Reserved */
+ RESERVED123_IRQ = 123, /* Reserved */
+ RESERVED124_IRQ = 124, /* Reserved */
+ RESERVED125_IRQ = 125, /* Reserved */
+ RESERVED126_IRQ = 126, /* Reserved */
+ RESERVED127_IRQ = 127, /* Reserved */
+ SCP_EXT_INTR0_IRQ = 128, /* SCP Customer Extension */
+ SCP_EXT_INTR1_IRQ = 129, /* SCP Customer Extension */
+ SCP_EXT_INTR2_IRQ = 130, /* SCP Customer Extension */
+ SCP_EXT_INTR3_IRQ = 131, /* SCP Customer Extension */
+ SCP_EXT_INTR4_IRQ = 132, /* SCP Customer Extension */
+ SCP_EXT_INTR5_IRQ = 133, /* SCP Customer Extension */
+ SCP_EXT_INTR6_IRQ = 134, /* SCP Customer Extension */
+ SCP_EXT_INTR7_IRQ = 135, /* SCP Customer Extension */
+ SCP_EXT_INTR8_IRQ = 136, /* SCP Customer Extension */
+ SCP_EXT_INTR9_IRQ = 137, /* SCP Customer Extension */
+ SCP_EXT_INTR10_IRQ = 138, /* SCP Customer Extension */
+ SCP_EXT_INTR11_IRQ = 139, /* SCP Customer Extension */
+ SCP_EXT_INTR12_IRQ = 140, /* SCP Customer Extension */
+ SCP_EXT_INTR13_IRQ = 141, /* SCP Customer Extension */
+ SCP_EXT_INTR14_IRQ = 142, /* SCP Customer Extension */
+ SCP_EXT_INTR15_IRQ = 143, /* SCP Customer Extension */
+ SCP_EXT_INTR16_IRQ = 144, /* SCP Customer Extension */
+ SCP_EXT_INTR17_IRQ = 145, /* SCP Customer Extension */
+ SCP_EXT_INTR18_IRQ = 146, /* SCP Customer Extension */
+ SCP_EXT_INTR19_IRQ = 147, /* SCP Customer Extension */
+ SCP_EXT_INTR20_IRQ = 148, /* SCP Customer Extension */
+ SCP_EXT_INTR21_IRQ = 149, /* SCP Customer Extension */
+ SCP_EXT_INTR22_IRQ = 150, /* SCP Customer Extension */
+ SCP_EXT_INTR23_IRQ = 151, /* SCP Customer Extension */
+ SCP_EXT_INTR24_IRQ = 152, /* SCP Customer Extension */
+ SCP_EXT_INTR25_IRQ = 153, /* SCP Customer Extension */
+ SCP_EXT_INTR26_IRQ = 154, /* SCP Customer Extension */
+ SCP_EXT_INTR27_IRQ = 155, /* SCP Customer Extension */
+ SCP_EXT_INTR28_IRQ = 156, /* SCP Customer Extension */
+ SCP_EXT_INTR29_IRQ = 157, /* SCP Customer Extension */
+ SCP_EXT_INTR30_IRQ = 158, /* SCP Customer Extension */
+ SCP_EXT_INTR31_IRQ = 159, /* SCP Customer Extension */
+};
+
+#endif /* SGM775_IRQ_H */
diff --git a/product/sgm775/include/sgm775_mhu.h b/product/sgm775/include/sgm775_mhu.h
new file mode 100644
index 00000000..8cdc4dd0
--- /dev/null
+++ b/product/sgm775/include/sgm775_mhu.h
@@ -0,0 +1,21 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * MHU module device indexes.
+ */
+
+#ifndef SGM775_MHU_H
+#define SGM775_MHU_H
+
+enum sgm775_mhu_device_idx {
+ SGM775_MHU_DEVICE_IDX_S,
+ SGM775_MHU_DEVICE_IDX_NS_H,
+ SGM775_MHU_DEVICE_IDX_NS_L,
+ SGM775_MHU_DEVICE_IDX_COUNT
+};
+
+#endif /* SGM775_MHU_H */
diff --git a/product/sgm775/include/sgm775_mmap.h b/product/sgm775/include/sgm775_mmap.h
new file mode 100644
index 00000000..16626760
--- /dev/null
+++ b/product/sgm775/include/sgm775_mmap.h
@@ -0,0 +1,101 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_MMAP_H
+#define SGM775_MMAP_H
+
+#include <stdint.h>
+
+/*
+ * Top-level base addresses
+ */
+#define EXPANSION0_BASE UINT32_C(0x40000000)
+#define PERIPHERAL_BASE UINT32_C(0x44000000)
+#define POWER_PERIPHERAL_BASE UINT32_C(0x50000000)
+#define SYS0_BASE UINT32_C(0x60000000)
+#define SYS1_BASE UINT32_C(0xA0000000)
+#define PPB_INTERNAL_BASE UINT32_C(0xE0000000)
+#define PPB_EXTERNAL_BASE UINT32_C(0xE0040000)
+#define EXPANSION1_BASE UINT32_C(0xE0100000)
+
+/*
+ * Peripherals
+ */
+#define REFCLK_CNTCTL_BASE (PERIPHERAL_BASE + 0x0000)
+#define REFCLK_CNTBASE0_BASE (PERIPHERAL_BASE + 0x1000)
+#define WDOG_BASE (PERIPHERAL_BASE + 0x6000)
+#define S32K_CNTCONTROL_BASE (PERIPHERAL_BASE + 0x7000)
+#define S32K_CNTCTL_BASE (PERIPHERAL_BASE + 0x8000)
+#define S32K_CNTBASE0_BASE (PERIPHERAL_BASE + 0x9000)
+#define CS_CNTCONTROL_BASE (PERIPHERAL_BASE + 0xA000)
+
+/*
+ * Power control peripherals
+ */
+#define PIK_SCP_BASE (POWER_PERIPHERAL_BASE + 0x00000)
+#define PIK_DEBUG_BASE (POWER_PERIPHERAL_BASE + 0x20000)
+#define SENSOR_DEBUG_BASE (POWER_PERIPHERAL_BASE + 0x30000)
+#define PIK_SYSTEM_BASE (POWER_PERIPHERAL_BASE + 0x40000)
+#define SENSOR_SYSTEM_BASE (POWER_PERIPHERAL_BASE + 0x50000)
+#define PIK_CLUS0_BASE (POWER_PERIPHERAL_BASE + 0x60000)
+#define SENSOR_CLUS0_BASE (POWER_PERIPHERAL_BASE + 0x70000)
+#define PIK_CLUS1_BASE (POWER_PERIPHERAL_BASE + 0x80000)
+#define SENSOR_CLUS1_BASE (POWER_PERIPHERAL_BASE + 0x90000)
+#define PIK_GPU_BASE (POWER_PERIPHERAL_BASE + 0xA0000)
+#define SENSOR_GPU_BASE (POWER_PERIPHERAL_BASE + 0xB0000)
+#define PIK_VPU_BASE (POWER_PERIPHERAL_BASE + 0xC0000)
+#define SENSOR_VPU_BASE (POWER_PERIPHERAL_BASE + 0xD0000)
+#define PIK_DPU_BASE (POWER_PERIPHERAL_BASE + 0xE0000)
+#define SENSOR_DPU_BASE (POWER_PERIPHERAL_BASE + 0xF0000)
+
+/*
+ * PPU base address
+ */
+#define PPU_SCP_BASE (PIK_SCP_BASE + 0x1000)
+#define PPU_SYS0_BASE (PIK_SYSTEM_BASE + 0x1000)
+#define PPU_SYS1_BASE (PIK_SYSTEM_BASE + 0x2000)
+#define PPU_DEBUG_BASE (PIK_DEBUG_BASE + 0x1000)
+#define PPU_CLUS0CORE0_BASE (PIK_CLUS0_BASE + 0x2000)
+#define PPU_CLUS0CORE1_BASE (PIK_CLUS0_BASE + 0x3000)
+#define PPU_CLUS0CORE2_BASE (PIK_CLUS0_BASE + 0x4000)
+#define PPU_CLUS0CORE3_BASE (PIK_CLUS0_BASE + 0x5000)
+#define PPU_CLUS0CORE4_BASE (PIK_CLUS0_BASE + 0x6000)
+#define PPU_CLUS0CORE5_BASE (PIK_CLUS0_BASE + 0x7000)
+#define PPU_CLUS0CORE6_BASE (PIK_CLUS0_BASE + 0x8000)
+#define PPU_CLUS0CORE7_BASE (PIK_CLUS0_BASE + 0x9000)
+#define PPU_CLUS0_BASE (PIK_CLUS0_BASE + 0x1000)
+#define PPU_CLUS1CORE0_BASE (PIK_CLUS1_BASE + 0x2000)
+#define PPU_CLUS1CORE1_BASE (PIK_CLUS1_BASE + 0x3000)
+#define PPU_CLUS1CORE2_BASE (PIK_CLUS1_BASE + 0x4000)
+#define PPU_CLUS1CORE3_BASE (PIK_CLUS1_BASE + 0x5000)
+#define PPU_CLUS1_BASE (PIK_CLUS1_BASE + 0x1000)
+#define PPU_GPU_BASE (PIK_GPU_BASE + 0x1000)
+#define PPU_VPU_BASE (PIK_VPU_BASE + 0x1000)
+#define PPU_DPU0_BASE (PIK_DPU_BASE + 0x2000)
+#define PPU_DPU1_BASE (PIK_DPU_BASE + 0x3000)
+
+/*
+ * System access port 1
+ */
+#define TRUSTED_RAM_BASE (SYS1_BASE + 0x04000000)
+#define NONTRUSTED_RAM_BASE (SYS1_BASE + 0x06000000)
+#define SSC_BASE (SYS1_BASE + 0x2A420000)
+#define REFCLK_CNTCONTROL_BASE (SYS1_BASE + 0x2A430000)
+#define MHU_BASE (SYS1_BASE + 0x2B1F0000)
+
+/*
+ * Base addresses of MHU devices
+ */
+
+#define MHU_SCP_INTR_L_BASE (MHU_BASE)
+#define MHU_SCP_INTR_H_BASE (MHU_BASE + 0x0020)
+#define MHU_CPU_INTR_L_BASE (MHU_BASE + 0x0100)
+#define MHU_CPU_INTR_H_BASE (MHU_BASE + 0x0120)
+#define MHU_SCP_INTR_S_BASE (MHU_BASE + 0x0200)
+#define MHU_CPU_INTR_S_BASE (MHU_BASE + 0x0300)
+
+#endif /* SGM775_MMAP_H */
diff --git a/product/sgm775/include/sgm775_mmap_scp.h b/product/sgm775/include/sgm775_mmap_scp.h
new file mode 100644
index 00000000..a4ceb5b9
--- /dev/null
+++ b/product/sgm775/include/sgm775_mmap_scp.h
@@ -0,0 +1,19 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCP ROM and RAM memory bases. These definitions are kept isolated without
+ * the UINT32_C() or UL decorators allowing them to be used in the linker
+ * script.
+ */
+
+#ifndef SGM775_MMAP_SCP_H
+#define SGM775_MMAP_SCP_H
+
+#define SCP_ROM_BASE 0x00000000
+#define SCP_RAM_BASE 0x10000000
+
+#endif /* SGM775_MMAP_SCP_H */
diff --git a/product/sgm775/include/sgm775_pik.h b/product/sgm775/include/sgm775_pik.h
new file mode 100644
index 00000000..7d0ac12b
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik.h
@@ -0,0 +1,29 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_H
+#define SGM775_PIK_H
+
+#include <sgm775_mmap.h>
+#include <sgm775_pik_cpu.h>
+#include <sgm775_pik_debug.h>
+#include <sgm775_pik_dpu.h>
+#include <sgm775_pik_gpu.h>
+#include <sgm775_pik_scp.h>
+#include <sgm775_pik_system.h>
+#include <sgm775_pik_vpu.h>
+
+#define PIK_CLUS0 ((struct pik_cpu_reg_v8_2 *) PIK_CLUS0_BASE)
+#define PIK_CLUS1 ((struct pik_cpu_reg_v8_2 *) PIK_CLUS1_BASE)
+#define PIK_DEBUG ((struct pik_debug_reg *) PIK_DEBUG_BASE)
+#define PIK_DPU ((struct pik_dpu_reg *) PIK_DPU_BASE)
+#define PIK_GPU ((struct pik_gpu_reg *) PIK_GPU_BASE)
+#define PIK_SCP ((struct pik_scp_reg *) PIK_SCP_BASE)
+#define PIK_SYSTEM ((struct pik_system_reg *) PIK_SYSTEM_BASE)
+#define PIK_VPU ((struct pik_vpu_reg *) PIK_VPU_BASE)
+
+#endif /* SGM775_PIK_H */
diff --git a/product/sgm775/include/sgm775_pik_cpu.h b/product/sgm775/include/sgm775_pik_cpu.h
new file mode 100644
index 00000000..5c7cc646
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_cpu.h
@@ -0,0 +1,145 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_CPU_H
+#define SGM775_PIK_CPU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+#define PE_COUNT_MAX 16
+
+/*!
+ * \brief CPU PIK V8 register definitions
+ */
+struct pik_cpu_reg_v8 {
+ FWK_RW uint32_t STATIC_CONFIG;
+ uint8_t RESERVED0[0x10 - 0x4];
+ FWK_RW uint32_t RVBARADDR0_LW;
+ FWK_RW uint32_t RVBARADDR0_UP;
+ FWK_RW uint32_t RVBARADDR1_LW;
+ FWK_RW uint32_t RVBARADDR1_UP;
+ FWK_RW uint32_t RVBARADDR2_LW;
+ FWK_RW uint32_t RVBARADDR2_UP;
+ FWK_RW uint32_t RVBARADDR3_LW;
+ FWK_RW uint32_t RVBARADDR3_UP;
+ FWK_RW uint32_t CLUSTER_CONFIG;
+ uint8_t RESERVED1[0x200 - 0x34];
+ FWK_R uint32_t DBG_RST_STATU;
+ FWK_RW uint32_t DBG_RST_SET;
+ FWK_RW uint32_t DBG_RST_CLR;
+ uint8_t RESERVED2[0x400 - 0x20C];
+ FWK_RW uint32_t CPUACTIVE_CTRL;
+ uint8_t RESERVED3[0x800 - 0x404];
+ FWK_RW uint32_t PPUCLK_CTRL;
+ FWK_RW uint32_t PPUCLK_DIV1;
+ FWK_RW uint32_t PPUCLK_DIV2;
+ FWK_R uint32_t RESERVED4;
+ FWK_RW uint32_t CPUCLK_CTRL;
+ FWK_RW uint32_t CPUCLK_DIV1;
+ FWK_RW uint32_t CPUCLK_DIV2;
+ FWK_R uint32_t RESERVED5;
+ FWK_RW uint32_t PCLKDBG_CTRL;
+ uint8_t RESERVED6[0x830 - 0x824];
+ FWK_RW uint32_t ATCLKDBG_CTRL;
+ uint8_t RESERVED7[0x840 - 0x834];
+ FWK_RW uint32_t ACLKCPU_CTRL;
+ uint8_t RESERVED8[0xA00 - 0x844];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_RW uint32_t CLKFORCE_SET;
+ FWK_RW uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED9[0xFC0 - 0xA0C];
+ FWK_R uint32_t PIK_CONFIG;
+ uint8_t RESERVED10[0xFD0 - 0xFC4];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+/*!
+ * \brief PE Static Configuration register definitions
+ */
+struct static_config {
+ FWK_RW uint32_t STATIC_CONFIG;
+ FWK_RW uint32_t RVBARADDR_LW;
+ FWK_RW uint32_t RVBARADDR_UP;
+ uint32_t RESERVED;
+};
+
+/*!
+ * \brief AP cores clock control register definitions
+ */
+struct ap_clk_ctrl {
+ FWK_RW uint32_t CORECLK_CTRL;
+ FWK_RW uint32_t CORECLK_DIV;
+ uint32_t RESERVED;
+ FWK_RW uint32_t CORECLK_MOD;
+};
+
+/*!
+ * \brief CPU PIK V8.2 register definitions
+ */
+struct pik_cpu_reg_v8_2 {
+ FWK_RW uint32_t CLUSTER_CONFIG;
+ uint8_t RESERVED0[0x10 - 0x4];
+ struct static_config STATIC_CONFIG[PE_COUNT_MAX];
+ uint8_t RESERVED1[0x800 - 0x110];
+ FWK_RW uint32_t PPUCLK_CTRL;
+ FWK_RW uint32_t PPUCLK_DIV1;
+ uint8_t RESERVED2[0x810 - 0x808];
+ FWK_RW uint32_t PCLK_CTRL;
+ uint8_t RESERVED3[0x820 - 0x814];
+ FWK_RW uint32_t ATCLK_CTRL;
+ uint8_t RESERVED4[0x830 - 0x824];
+ FWK_RW uint32_t GICCLK_CTRL;
+ uint8_t RESERVED5[0x840 - 0x834];
+ FWK_RW uint32_t AMBACLK_CTRL;
+ uint8_t RESERVED6[0x850 - 0x844];
+ FWK_RW uint32_t CLUSCLK_CTRL;
+ FWK_RW uint32_t CLUSCLK_DIV1;
+ uint8_t RESERVED7[0x860 - 0x858];
+ struct ap_clk_ctrl AP_CLK_CTRL[8];
+ uint8_t RESERVED8[0xA00 - 0x8E0];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_RW uint32_t CLKFORCE_SET;
+ FWK_RW uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED9[0xFB8 - 0xA0C];
+ FWK_R uint32_t CAP2;
+ FWK_R uint32_t CAP;
+ FWK_R uint32_t PIK_CONFIG;
+ uint8_t RESERVED10[0xFD0 - 0xFC4];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#define PIK_CPU_V8_2_CAP_CLUSSYNC UINT32_C(0x00000001)
+#define PIK_CPU_V8_2_CAP_CORESYNC(CORE) ((uint32_t)(1 << ((CORE) + 1)))
+#define PIK_CPU_V8_2_CAP_PE_MASK UINT32_C(0xF0000000)
+#define PIK_CPU_V8_2_CAP_PE_POS 28
+
+#define PIK_CPU_V8_2_PIK_CONFIG_NO_OF_PPU UINT32_C(0x0000000F)
+
+#endif /* SGM775_PIK_CPU_H */
diff --git a/product/sgm775/include/sgm775_pik_debug.h b/product/sgm775/include/sgm775_pik_debug.h
new file mode 100644
index 00000000..3a7f5fee
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_debug.h
@@ -0,0 +1,49 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_DEBUG_H
+#define SGM775_PIK_DEBUG_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief Debug register definitions
+ */
+struct pik_debug_reg {
+ FWK_RW uint32_t DEBUG_CONTROL;
+ FWK_R uint32_t DEBUG_STATUS;
+ uint32_t RESERVED0[2];
+ FWK_R uint32_t APP_DAP_TARGET_ID;
+ FWK_R uint32_t SCP_DAP_TARGET_ID;
+ FWK_R uint32_t DAP_INSTANCE_ID;
+ uint8_t RESERVED1[0x810 - 0x1C];
+ FWK_RW uint32_t TRACECLK_CTRL;
+ FWK_RW uint32_t TRACECLK_DIV1;
+ uint32_t RESERVED2[2];
+ FWK_RW uint32_t PCLKDBG_CTRL;
+ uint32_t RESERVED3[3];
+ FWK_RW uint32_t ATCLK_CTRL;
+ FWK_RW uint32_t ATCLK_DIV1;
+ FWK_R uint8_t RESERVED4[0xFC0 - 0x838];
+ FWK_R uint32_t PIK_CONFIG;
+ uint32_t RESERVED5[3];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#endif /* SGM775_PIK_DEBUG_H */
diff --git a/product/sgm775/include/sgm775_pik_dpu.h b/product/sgm775/include/sgm775_pik_dpu.h
new file mode 100644
index 00000000..e9c5292f
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_dpu.h
@@ -0,0 +1,51 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_DPU_H
+#define SGM775_PIK_DPU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief DPU PIK register definitions
+ */
+struct pik_dpu_reg {
+ FWK_R uint8_t RESERVED0[0x810];
+ FWK_RW uint32_t M0CLK_CTRL;
+ FWK_RW uint32_t M0CLK_DIV1;
+ FWK_RW uint32_t M0CLK_DIV2;
+ uint32_t RESERVED1;
+ FWK_RW uint32_t M1CLK_CTRL;
+ FWK_RW uint32_t M1CLK_DIV1;
+ FWK_RW uint32_t M1CLK_DIV2;
+ uint32_t RESERVED2;
+ FWK_RW uint32_t ACLKDP_CTRL;
+ FWK_RW uint32_t ACLKDP_DIV1;
+ FWK_RW uint32_t ACLKDP_DIV2;
+ uint8_t RESERVED3[0xA00 - 0x83C];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_W uint32_t CLKFORCE_SET;
+ FWK_W uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED4[0xFC0 - 0xA0C];
+ FWK_RW uint32_t PWR_CTRL_CONFIG;
+ uint8_t RESERVED5[0xFD0 - 0xFC4];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#endif /* SGM775_PIK_DPU_H */
diff --git a/product/sgm775/include/sgm775_pik_gpu.h b/product/sgm775/include/sgm775_pik_gpu.h
new file mode 100644
index 00000000..c92a3e94
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_gpu.h
@@ -0,0 +1,45 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_GPU_H
+#define SGM775_PIK_GPU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief GPU PIK register definitions
+ */
+struct pik_gpu_reg {
+ uint8_t RESERVED0[0x810];
+ FWK_RW uint32_t GPUCLK_CTRL;
+ FWK_RW uint32_t GPUCLK_DIV1;
+ FWK_RW uint32_t GPUCLK_DIV2;
+ FWK_R uint32_t RESERVED1;
+ FWK_RW uint32_t ACLKGPU_CTRL;
+ uint8_t RESERVED2[0xA00 - 0x824];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_W uint32_t CLKFORCE_SET;
+ FWK_W uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED3[0xFC0 - 0xA0C];
+ FWK_RW uint32_t PWR_CTRL_CONFIG;
+ uint8_t RESERVED4[0xFD0 - 0xFC4];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#endif /* SGM775_PIK_GPU_H */
diff --git a/product/sgm775/include/sgm775_pik_scp.h b/product/sgm775/include/sgm775_pik_scp.h
new file mode 100644
index 00000000..dce3a371
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_scp.h
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_SCP_H
+#define SGM775_PIK_SCP_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief SCP PIK register definitions
+ */
+struct pik_scp_reg {
+ uint32_t RESERVED0[4];
+ FWK_RW uint32_t RESET_SYNDROME;
+ FWK_RW uint32_t WIC_CTRL;
+ FWK_R uint32_t WIC_STATUS;
+ uint8_t RESERVED1[0xA00 - 0x1C];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_RW uint32_t CLKFORCE_SET;
+ FWK_RW uint32_t CLKFORCE_CLR;
+ uint32_t RESERVED2;
+ FWK_R uint32_t PLL_STATUS0;
+ FWK_R uint32_t PLL_STATUS1;
+ uint8_t RESERVED3[0xFC0 - 0xA18];
+ FWK_R uint32_t PIK_CONFIG;
+ uint32_t RESERVED4[3];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#define PLL_STATUS0_REFCLK UINT32_C(0x00000001)
+#define PLL_STATUS0_BCPUPLLLOCK UINT32_C(0x00000002)
+#define PLL_STATUS0_LCPUPLLLOCK UINT32_C(0x00000004)
+#define PLL_STATUS0_GPUPLLLOCK UINT32_C(0x00000008)
+#define PLL_STATUS0_VIDEOPLLLOCK UINT32_C(0x00000010)
+#define PLL_STATUS0_SYSPLLLOCK UINT32_C(0x00000020)
+#define PLL_STATUS0_DISPLAYPLLLOCK UINT32_C(0x00000040)
+
+#define PLL_STATUS1_CPUPLLLOCK(CPU, PLL) \
+ ((uint32_t)((1 << (PLL)) << ((CPU) * 8)))
+
+#define RESET_SYNDROME_PORESET UINT32_C(0x01)
+#define RESET_SYNDROME_WDOGRESET_SCP UINT32_C(0x02)
+#define RESET_SYNDROME_WDOGRESET_SYS UINT32_C(0x04)
+#define RESET_SYNDROME_SYSRESETREQ UINT32_C(0x08)
+#define RESET_SYNDROME_SCPM3LOCKUP UINT32_C(0x10)
+
+#endif /* SGM775_PIK_SCP_H */
diff --git a/product/sgm775/include/sgm775_pik_system.h b/product/sgm775/include/sgm775_pik_system.h
new file mode 100644
index 00000000..3e3dccdb
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_system.h
@@ -0,0 +1,69 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_SYSTEM_H
+#define SGM775_PIK_SYSTEM_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief System PIK register definitions
+ */
+struct pik_system_reg {
+ uint8_t RESERVED0[0x800];
+ FWK_RW uint32_t PPUCLK_CTRL;
+ FWK_RW uint32_t PPUCLK_DIV1;
+ uint32_t RESERVED1[2];
+ FWK_RW uint32_t ACLKNCI_CTRL;
+ FWK_RW uint32_t ACLKNCI_DIV1;
+ uint32_t RESERVED2[2];
+ FWK_RW uint32_t ACLKCCI_CTRL;
+ FWK_RW uint32_t ACLKCCI_DIV1;
+ uint32_t RESERVED3[6];
+ FWK_RW uint32_t TCUCLK_CTRL;
+ FWK_RW uint32_t TCUCLK_DIV1;
+ uint32_t RESERVED4[2];
+ FWK_RW uint32_t GICCLK_CTRL;
+ FWK_RW uint32_t GICCLK_DIV1;
+ uint32_t RESERVED5[2];
+ FWK_RW uint32_t PCLKSCP_CTRL;
+ FWK_RW uint32_t PCLKSCP_DIV1;
+ uint32_t RESERVED6[2];
+ FWK_RW uint32_t SYSPERCLK_CTRL;
+ FWK_RW uint32_t SYSPERCLK_DIV1;
+ uint32_t RESERVED7[6];
+ FWK_RW uint32_t FCMCLK_CTRL;
+ FWK_RW uint32_t FCMCLK_DIV1;
+ uint8_t RESERVED8[0xA00 - 0x898];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_RW uint32_t CLKFORCE_SET;
+ FWK_RW uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED9[0xFBC - 0xA0C];
+ FWK_R uint32_t CAP;
+ FWK_R uint32_t PIK_CONFIG;
+ uint32_t RESERVED10[3];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#define CAP_GICCLK_GATING_SUPPORT UINT32_C(0x00000001)
+#define CAP_TCUCLK_SUPPORT UINT32_C(0x00000002)
+#define CAP_ELA_SUPPORT UINT32_C(0x00000004)
+#define CAP_FCMCLK_SUPPORT UINT32_C(0x00000008)
+
+#endif /* SGM775_PIK_SYSTEM_H */
diff --git a/product/sgm775/include/sgm775_pik_vpu.h b/product/sgm775/include/sgm775_pik_vpu.h
new file mode 100644
index 00000000..a2742bb9
--- /dev/null
+++ b/product/sgm775/include/sgm775_pik_vpu.h
@@ -0,0 +1,43 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_PIK_VPU_H
+#define SGM775_PIK_VPU_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief VPU PIK register definitions
+ */
+struct pik_vpu_reg {
+ uint8_t RESERVED0[0x810];
+ FWK_RW uint32_t VIDEOCLK_CTRL;
+ FWK_RW uint32_t VIDEOCLK_DIV1;
+ FWK_RW uint32_t VIDEOCLK_DIV2;
+ uint8_t RESERVED1[0xA00 - 0x81C];
+ FWK_R uint32_t CLKFORCE_STATUS;
+ FWK_W uint32_t CLKFORCE_SET;
+ FWK_W uint32_t CLKFORCE_CLR;
+ uint8_t RESERVED2[0xFC0 - 0xA0C];
+ FWK_RW uint32_t PWR_CTRL_CONFIG;
+ uint8_t RESERVED3[0xFD0 - 0xFC4];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t PID5;
+ FWK_R uint32_t PID6;
+ FWK_R uint32_t PID7;
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t ID0;
+ FWK_R uint32_t ID1;
+ FWK_R uint32_t ID2;
+ FWK_R uint32_t ID3;
+};
+
+#endif /* SGM775_PIK_VPU_H */
diff --git a/product/sgm775/include/sgm775_scmi.h b/product/sgm775/include/sgm775_scmi.h
new file mode 100644
index 00000000..a920c09f
--- /dev/null
+++ b/product/sgm775/include/sgm775_scmi.h
@@ -0,0 +1,29 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Definitions for SCMI and SMT module configurations.
+ */
+
+#ifndef SGM775_SCMI_H
+#define SGM775_SCMI_H
+
+/* SCMI agent identifiers */
+enum sgm775_scmi_agent_id {
+ /* 0 is reserved for the platform */
+ SCMI_AGENT_ID_OSPM = 1,
+ SCMI_AGENT_ID_PSCI,
+ SCMI_AGENT_ID_COUNT,
+};
+
+/* SCMI service indexes */
+enum sgm775_scmi_service_idx {
+ SGM775_SCMI_SERVICE_IDX_PSCI,
+ SGM775_SCMI_SERVICE_IDX_OSPM_0,
+ SGM775_SCMI_SERVICE_IDX_OSPM_1,
+ SGM775_SCMI_SERVICE_IDX_COUNT,
+};
+#endif /* SGM775_SCMI_H */
diff --git a/product/sgm775/include/sgm775_sds.h b/product/sgm775/include/sgm775_sds.h
new file mode 100644
index 00000000..943cfa7f
--- /dev/null
+++ b/product/sgm775/include/sgm775_sds.h
@@ -0,0 +1,149 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_SDS_H
+#define SGM775_SDS_H
+
+#include <mod_sds.h>
+
+/*
+ * Structure identifiers.
+ */
+enum sgm775_sds_struct_id {
+ SGM775_SDS_CPU_INFO = 1 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_ROM_VERSION = 2 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_RAM_VERSION = 3 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_PLATFORM_ID = 4 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_RESET_SYNDROME = 5 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_FEATURE_AVAILABILITY = 6 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_CPU_BOOTCTR = 7 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_CPU_FLAGS = 8 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+ SGM775_SDS_BOOTLOADER = 9 | (1 << MOD_SDS_ID_VERSION_MAJOR_POS),
+};
+
+/*
+ * Structure sizes.
+ */
+#define SGM775_SDS_CPU_INFO_SIZE 4
+#define SGM775_SDS_ROM_VERSION_SIZE 4
+#define SGM775_SDS_RAM_VERSION_SIZE 4
+#define SGM775_SDS_PLATFORM_ID_SIZE 8
+#define SGM775_SDS_RESET_SYNDROME_SIZE 4
+#define SGM775_SDS_FEATURE_AVAILABILITY_SIZE 4
+#define SGM775_SDS_CPU_BOOTCTR_SIZE 8
+#define SGM775_SDS_CPU_FLAGS_SIZE 8
+#define SGM775_SDS_BOOTLOADER_SIZE 12
+
+/*
+ * Field masks and offsets for the SGM775_SDS_AP_CPU_INFO structure.
+ */
+#define SGM775_SDS_CPU_INFO_PRIMARY_MASK 0xFFFFFFFF
+#define SGM775_SDS_CPU_INFO_PRIMARY_POS 0
+
+/*
+ * Structure, field masks and offsets for the SGM775_SDS_PLATFORM_ID structure.
+ */
+
+struct sgm775_sds_platid {
+ uint32_t platform_identifier;
+ uint32_t platform_type_identifier;
+};
+
+#define SGM775_SDS_PLATID_PARTNO_MASK 0xFFF
+#define SGM775_SDS_PLATID_DESIGNER_MASK 0xFF000
+#define SGM775_SDS_PLATID_REV_MINOR_MASK 0xF00000
+#define SGM775_SDS_PLATID_REV_MAJOR_MASK 0xF000000
+#define SGM775_SDS_PLATID_CONFIG_MASK 0xF0000000
+#define SGM775_SDS_PLATID_TYPE_MASK 0xF
+
+#define SGM775_SDS_PLATID_PARTNO_POS 0
+#define SGM775_SDS_PLATID_DESIGNER_POS 12
+#define SGM775_SDS_PLATID_REV_MINOR_POS 20
+#define SGM775_SDS_PLATID_REV_MAJOR_POS 24
+#define SGM775_SDS_PLATID_CONFIG_POS 28
+
+#define SGM775_SDS_PLATID_TYPE_POS 0
+/*
+ * Field masks and offsets for the SGM775_SDS_RESET_SYNDROME structure.
+ */
+#define SGM775_SDS_RESET_SYNDROME_POR_MASK 0x1
+#define SGM775_SDS_RESET_SYNDROME_WDOGSCP_MASK 0x2
+#define SGM775_SDS_RESET_SYNDROME_WDOGAP_MASK 0x4
+#define SGM775_SDS_RESET_SYNDROME_SYSRESET_MASK 0x8
+#define SGM775_SDS_RESET_SYNDROME_M3LOCKUP_MASK 0x10
+
+#define SGM775_SDS_RESET_SYNDROME_POR_POS 0
+#define SGM775_SDS_RESET_SYNDROME_WDOGSCP_POS 1
+#define SGM775_SDS_RESET_SYNDROME_WDOGAP_POS 2
+#define SGM775_SDS_RESET_SYNDROME_SYSRESET_POS 3
+#define SGM775_SDS_RESET_SYNDROME_M3LOCKUP_POS 4
+
+/*
+ * Field masks and offsets for the SGM775_SDS_FEATURE_AVAILABILITY structure.
+ */
+#define SGM775_SDS_FEATURE_FIRMWARE_MASK 0x1
+#define SGM775_SDS_FEATURE_DMC_MASK 0x2
+#define SGM775_SDS_FEATURE_MESSAGING_MASK 0x4
+
+#define SGM775_SDS_FEATURE_FIRMWARE_POS 0
+#define SGM775_SDS_FEATURE_DMC_POS 1
+#define SGM775_SDS_FEATURE_MESSAGING_POS 2
+
+/*
+ * Field masks and offsets for the SGM775_SDS_CPU_BOOTCTR structure.
+ */
+#define SGM775_SDS_CPU_BOOTCTR_CPU0_MASK 0xFF
+#define SGM775_SDS_CPU_BOOTCTR_CPU1_MASK 0xFF00
+#define SGM775_SDS_CPU_BOOTCTR_CPU2_MASK 0xFF0000
+#define SGM775_SDS_CPU_BOOTCTR_CPU3_MASK 0xFF000000
+#define SGM775_SDS_CPU_BOOTCTR_CPU4_MASK 0xFF
+#define SGM775_SDS_CPU_BOOTCTR_CPU5_MASK 0xFF00
+#define SGM775_SDS_CPU_BOOTCTR_CPU6_MASK 0xFF0000
+#define SGM775_SDS_CPU_BOOTCTR_CPU7_MASK 0xFF000000
+
+
+#define SGM775_SDS_CPU_BOOTCTR_CPU0_POS 0
+#define SGM775_SDS_CPU_BOOTCTR_CPU1_POS 8
+#define SGM775_SDS_CPU_BOOTCTR_CPU2_POS 16
+#define SGM775_SDS_CPU_BOOTCTR_CPU3_POS 24
+#define SGM775_SDS_CPU_BOOTCTR_CPU4_POS 0
+#define SGM775_SDS_CPU_BOOTCTR_CPU5_POS 8
+#define SGM775_SDS_CPU_BOOTCTR_CPU6_POS 16
+#define SGM775_SDS_CPU_BOOTCTR_CPU7_POS 24
+
+/*
+ * Field masks and offsets for the SGM775_SDS_CPU_FLAGS structure.
+ */
+#define SGM775_SDS_CPU_FLAGS_CPU0_WFI_MASK 0x1
+#define SGM775_SDS_CPU_FLAGS_CPU1_WFI_MASK 0x100
+#define SGM775_SDS_CPU_FLAGS_CPU2_WFI_MASK 0x10000
+#define SGM775_SDS_CPU_FLAGS_CPU3_WFI_MASK 0x1000000
+#define SGM775_SDS_CPU_FLAGS_CPU4_WFI_MASK 0x1
+#define SGM775_SDS_CPU_FLAGS_CPU5_WFI_MASK 0x100
+#define SGM775_SDS_CPU_FLAGS_CPU6_WFI_MASK 0x10000
+#define SGM775_SDS_CPU_FLAGS_CPU7_WFI_MASK 0x1000000
+
+#define SGM775_SDS_CPU_FLAGS_CPU0_WFI_POS 0
+#define SGM775_SDS_CPU_FLAGS_CPU1_WFI_POS 8
+#define SGM775_SDS_CPU_FLAGS_CPU2_WFI_POS 16
+#define SGM775_SDS_CPU_FLAGS_CPU3_WFI_POS 24
+#define SGM775_SDS_CPU_FLAGS_CPU4_WFI_POS 0
+#define SGM775_SDS_CPU_FLAGS_CPU5_WFI_POS 8
+#define SGM775_SDS_CPU_FLAGS_CPU6_WFI_POS 16
+#define SGM775_SDS_CPU_FLAGS_CPU7_WFI_POS 24
+/*
+ * Field masks and offsets for the SGM775_SDS_BOOTLOADER structure.
+ */
+#define SGM775_SDS_BOOTLOADER_VALID_MASK 0x1
+#define SGM775_SDS_BOOTLOADER_OFFSET_MASK 0xFFFFFFFF
+#define SGM775_SDS_BOOTLOADER_SIZE_MASK 0xFFFFFFFF
+
+#define SGM775_SDS_BOOTLOADER_VALID_POS 0
+#define SGM775_SDS_BOOTLOADER_OFFSET_POS 0
+#define SGM775_SDS_BOOTLOADER_SIZE_POS 0
+
+#endif /* SGM775_SDS_H */
diff --git a/product/sgm775/include/sgm775_ssc.h b/product/sgm775/include/sgm775_ssc.h
new file mode 100644
index 00000000..9b514a11
--- /dev/null
+++ b/product/sgm775/include/sgm775_ssc.h
@@ -0,0 +1,42 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SGM775_SSC_H
+#define SGM775_SSC_H
+
+#include <stdint.h>
+#include <fwk_macros.h>
+
+/*!
+ * \brief System Security Control (SSC) register definitions
+ */
+struct ssc_reg {
+ FWK_R uint32_t RESERVED1[4];
+ FWK_R uint32_t SSC_DBGCFG_STAT;
+ FWK_W uint32_t SSC_DBGCFG_SET;
+ FWK_W uint32_t SSC_DBGCFG_CLR;
+ FWK_R uint32_t RESERVED2[2];
+ FWK_RW uint32_t SSC_SWDHOD;
+ FWK_RW uint32_t SSC_AUXDBGCFG;
+ FWK_R uint32_t RESERVED3;
+ FWK_RW uint32_t SSC_GPRETN;
+ FWK_R uint32_t RESERVED4[3];
+ FWK_R uint32_t SSC_VERSION;
+ FWK_R uint32_t RESERVED5[995];
+ FWK_R uint32_t PID4;
+ FWK_R uint32_t RESERVED6[3];
+ FWK_R uint32_t PID0;
+ FWK_R uint32_t PID1;
+ FWK_R uint32_t PID2;
+ FWK_R uint32_t PID3;
+ FWK_R uint32_t COMPID0;
+ FWK_R uint32_t COMPID1;
+ FWK_R uint32_t COMPID2;
+ FWK_R uint32_t COMPID3;
+};
+
+#endif /* SGM775_SSC_H */
diff --git a/product/sgm775/include/software_mmap.h b/product/sgm775/include/software_mmap.h
new file mode 100644
index 00000000..e0ce772f
--- /dev/null
+++ b/product/sgm775/include/software_mmap.h
@@ -0,0 +1,128 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Software defined memory map shared between SCP and AP cores.
+ */
+
+#ifndef SOFTWARE_MMAP_H
+#define SOFTWARE_MMAP_H
+
+#include <fwk_macros.h>
+#include <system_mmap.h>
+
+/*
+ * The 4KiB AP/SCP Shared memory at the base of Trusted SRAM is used for several
+ * purposes. These are: the Shared Data Storage (SDS) Memory Region, the SCMI
+ * secure payload areas, and the context area for Application Processor
+ * firmware.
+ *
+ * Shared Data Storage (SDS) Memory Region: Used for structured storage of data
+ * that is shared between SCP Firmware and Application Processor firmware. The
+ * SDS Memory Region occupies the area between the context region base and
+ * the SCMI Secure Payload base.
+ *
+ * SCMI Secure Payload Areas: Storage for SCMI message contents in both the
+ * Agent->Platform and Platform->Agent directions.
+ *
+ * Application Processor Context Area: The usage of this area is defined by the
+ * firmware running on the Application Processors. The SCP Firmware must zero
+ * this memory before releasing any Application Processors. This area must
+ * always be located in the top 64 bytes of the 4KiB reserved region.
+ *
+ * +-----------------------+ 4096
+ * | |
+ * 64B | AP Context Area |
+ * | |
+ * +-----------------------+
+ * | |
+ * 256B | Unused |
+ * | |
+ * +-----------------------+
+ * | |
+ * | SCMI Sec. Payload |
+ * 128B | Platform to Agent |
+ * | |
+ * +-----------------------+
+ * | |
+ * 128B | SCMI Sec. Payload |
+ * | Agent to Platform |
+ * | |
+ * +-----------------------+
+ * | |
+ * 3520B | SDS Memory Region |
+ * | |
+ * +-----------------------+ 0
+ */
+
+/* Secure shared memory at the base of Trusted SRAM */
+#define SHARED_SECURE_BASE (TRUSTED_RAM_BASE)
+#define SHARED_SECURE_SIZE (4 * FWK_KIB)
+
+/* SDS Memory Region */
+#define SDS_MEM_BASE (SHARED_SECURE_BASE)
+#define SDS_MEM_SIZE (3520)
+
+/* AP Context Area */
+#define AP_CONTEXT_BASE (SHARED_SECURE_BASE + SHARED_SECURE_SIZE - \
+ AP_CONTEXT_SIZE)
+#define AP_CONTEXT_SIZE (64)
+
+/* SCMI Secure Payload Areas */
+#define SCMI_PAYLOAD_SIZE (128)
+#define SCMI_PAYLOAD_S_A2P_BASE (SDS_MEM_BASE + SDS_MEM_SIZE)
+#define SCMI_PAYLOAD_S_P2A_BASE (SCMI_PAYLOAD_S_A2P_BASE + SCMI_PAYLOAD_SIZE)
+
+/*
+ * The 4KiB AP/SCP Shared memory at the base of Non-trusted SRAM is used for the
+ * SCMI non-secure payload areas.
+ *
+ * Two SCMI non-Secure Payload Areas: Storage for SCMI message contents in both
+ * the Agent->Platform and Platform->Agent directions.
+ *
+ * +-----------------------+ 4096
+ * 3584B | Unused |
+ * +-----------------------+
+ * | |
+ * | Non-Sec. Channel 1 |
+ * | SCMI non-Sec. Payload |
+ * 128B | Platform to Agent |
+ * | |
+ * +-----------------------+
+ * | |
+ * | Non-Sec. Channel 1 |
+ * 128B | SCMI non-Sec. Payload |
+ * | Agent to Platform |
+ * | |
+ * +-----------------------+
+ * | |
+ * | Non-Sec. Channel 0 |
+ * | SCMI non-Sec. Payload |
+ * 128B | Platform to Agent |
+ * | |
+ * +-----------------------+
+ * | |
+ * | Non-Sec. Channel 0 |
+ * 128B | SCMI non-Sec. Payload |
+ * | Agent to Platform |
+ * | |
+ * +-----------------------+ 0
+ */
+
+/* Non-secure shared memory at the base of Non-trusted SRAM */
+#define SHARED_NONSECURE_BASE (NONTRUSTED_RAM_BASE)
+#define SHARED_NONSECURE_SIZE (4 * FWK_KIB)
+
+/* SCMI Non-Secure Payload Areas */
+#define SCMI_PAYLOAD0_NS_A2P_BASE (SHARED_NONSECURE_BASE)
+#define SCMI_PAYLOAD0_NS_P2A_BASE (SCMI_PAYLOAD0_NS_A2P_BASE + \
+ SCMI_PAYLOAD_SIZE)
+#define SCMI_PAYLOAD1_NS_A2P_BASE (SCMI_PAYLOAD0_NS_P2A_BASE + \
+ SCMI_PAYLOAD_SIZE)
+#define SCMI_PAYLOAD1_NS_P2A_BASE (SCMI_PAYLOAD1_NS_A2P_BASE + \
+ SCMI_PAYLOAD_SIZE)
+
+#endif /* SOFTWARE_MMAP_H */
diff --git a/product/sgm775/include/system_clock.h b/product/sgm775/include/system_clock.h
new file mode 100644
index 00000000..c3f5a76c
--- /dev/null
+++ b/product/sgm775/include/system_clock.h
@@ -0,0 +1,28 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SYSTEM_CLOCK_H
+#define SYSTEM_CLOCK_H
+
+#include <fwk_macros.h>
+
+/*!
+ * \brief Calculates the necessary divider for obtaining a target frequency
+ * from a given clock.
+ *
+ * \param CLOCK_RATE The tick rate of the clock to be divided.
+ *
+ * \param TARGET_FREQ The target frequency to be obtained by the division.
+ *
+ * \return The divider needed to obtain TARGET_FREQ from CLOCK_RATE.
+ */
+#define DIV_FROM_CLOCK(CLOCK_RATE, TARGET_FREQ) ((CLOCK_RATE) / (TARGET_FREQ))
+
+#define CLOCK_RATE_REFCLK (50UL * FWK_MHZ)
+#define CLOCK_RATE_SYSPLLCLK (2000UL * FWK_MHZ)
+
+#endif /* SYSTEM_CLOCK_H */
diff --git a/product/sgm775/include/system_mmap.h b/product/sgm775/include/system_mmap.h
new file mode 100644
index 00000000..697f7811
--- /dev/null
+++ b/product/sgm775/include/system_mmap.h
@@ -0,0 +1,61 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SYSTEM_MMAP_H
+#define SYSTEM_MMAP_H
+
+#include <sgm775_mmap.h>
+
+#define DMC_EXTERNAL0 (SYS0_BASE + 0x3FBE0000)
+#define DMC_EXTERNAL1 (SYS0_BASE + 0x3FBF0000)
+#define DMC_EXTERNAL2 (SYS0_BASE + 0x3FC00000)
+#define DMC_EXTERNAL3 (SYS0_BASE + 0x3FC10000)
+
+#define BOARD_UART1_BASE (SYS0_BASE + 0x3FF70000)
+#define PLAT_BASE (SYS0_BASE + 0x3FFE0000)
+
+#define PLL_GPU (PLAT_BASE + 0x00000008)
+#define PLL_SYSTEM (PLAT_BASE + 0x0000000C)
+#define PLL_VIDEO (PLAT_BASE + 0x00000010)
+#define PLL_DISPLAY (PLAT_BASE + 0x00000014)
+
+#define PIX0_CONTROL (PLAT_BASE + 0x00000018)
+#define PIX1_CONTROL (PLAT_BASE + 0x0000001C)
+
+#define SWCLKTCK_CONTROL (PLAT_BASE + 0x00000020)
+#define SENSOR_SOC_TEMP (PLAT_BASE + 0x00000080)
+#define PLATFORM_ID (PLAT_BASE + 0x000000E0)
+
+#define PLL_CLUS0_0 (PLAT_BASE + 0x00000100)
+#define PLL_CLUS0_1 (PLAT_BASE + 0x00000104)
+#define PLL_CLUS0_2 (PLAT_BASE + 0x00000108)
+#define PLL_CLUS0_3 (PLAT_BASE + 0x0000010C)
+#define PLL_CLUS0_4 (PLAT_BASE + 0x00000110)
+#define PLL_CLUS0_5 (PLAT_BASE + 0x00000114)
+#define PLL_CLUS0_6 (PLAT_BASE + 0x00000118)
+#define PLL_CLUS0_7 (PLAT_BASE + 0x0000011C)
+
+#define DDR_PHY0 (SYS0_BASE + 0x3FB60000)
+#define DDR_PHY1 (SYS0_BASE + 0x3FB70000)
+#define DDR_PHY2 (SYS0_BASE + 0x3FB80000)
+#define DDR_PHY3 (SYS0_BASE + 0x3FB90000)
+
+#define GPV_CCI_GPU1 (SYS1_BASE + 0x2A004000)
+#define GPV_CCI_GPU0 (SYS1_BASE + 0x2A005000)
+#define GPV_CCI_LITTLE (SYS1_BASE + 0x2A006000)
+#define GPV_CCI_BIG (SYS1_BASE + 0x2A007000)
+#define GPV_VPU (SYS1_BASE + 0x2A243000)
+#define GPV_DPU0 (SYS1_BASE + 0x2A244000)
+#define GPV_DPU1 (SYS1_BASE + 0x2A245000)
+
+#define DMC_INTERNAL0 (SYS1_BASE + 0x2A500000)
+#define DMC_INTERNAL1 (SYS1_BASE + 0x2A540000)
+#define DMC_INTERNAL2 (SYS1_BASE + 0x2A580000)
+#define DMC_INTERNAL3 (SYS1_BASE + 0x2A5C0000)
+
+
+#endif /* SYSTEM_MMAP_H */
diff --git a/product/sgm775/include/system_mmap_scp.h b/product/sgm775/include/system_mmap_scp.h
new file mode 100644
index 00000000..097e4316
--- /dev/null
+++ b/product/sgm775/include/system_mmap_scp.h
@@ -0,0 +1,21 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCP ROM and RAM memory sizes. These definitions are kept isolated without
+ * the UINT32_C() or UL decorators allowing them to be used in the linker
+ * script.
+ */
+
+#ifndef SYSTEM_MMAP_SCP_H
+#define SYSTEM_MMAP_SCP_H
+
+#include <sgm775_mmap_scp.h>
+
+#define SCP_ROM_SIZE (64 * 1024)
+#define SCP_RAM_SIZE (128 * 1024)
+
+#endif /* SYSTEM_MMAP_SCP_H */
diff --git a/product/sgm775/module/sgm775_system/include/mod_sgm775_system.h b/product/sgm775/module/sgm775_system/include/mod_sgm775_system.h
new file mode 100644
index 00000000..c3cfb498
--- /dev/null
+++ b/product/sgm775/module/sgm775_system/include/mod_sgm775_system.h
@@ -0,0 +1,44 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SGM775 System Support
+ */
+
+#ifndef MOD_SGM775_SYSTEM_H
+#define MOD_SGM775_SYSTEM_H
+
+/*!
+ * \addtogroup GroupSGM775Module SGM775 Product Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupSGM775System SGM775 System Support
+ *
+ * @{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_sgm775_system_api_idx {
+ /*! API index for the driver interface of the SYSTEM POWER module */
+ MOD_SGM775_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER,
+
+ /*! Number of defined APIs */
+ MOD_SGM775_SYSTEM_API_COUNT
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_SGM775_SYSTEM_H */
diff --git a/product/sgm775/module/sgm775_system/src/Makefile b/product/sgm775/module/sgm775_system/src/Makefile
new file mode 100644
index 00000000..77ad18c1
--- /dev/null
+++ b/product/sgm775/module/sgm775_system/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := SGM775 SYSTEM
+BS_LIB_SOURCES = mod_sgm775_system.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/sgm775/module/sgm775_system/src/mod_sgm775_system.c b/product/sgm775/module/sgm775_system/src/mod_sgm775_system.c
new file mode 100644
index 00000000..dc1459e4
--- /dev/null
+++ b/product/sgm775/module/sgm775_system/src/mod_sgm775_system.c
@@ -0,0 +1,59 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SGM775 System Support.
+ */
+
+#include <fmw_cmsis.h>
+#include <fwk_module.h>
+#include <mod_system_power.h>
+#include <mod_sgm775_system.h>
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int sgm775_system_shutdown(enum mod_pd_system_shutdown system_shutdown)
+{
+ NVIC_SystemReset();
+
+ return FWK_E_DEVICE;
+}
+
+static const struct mod_system_power_driver_api
+ sgm775_system_system_power_driver_api = {
+ .system_shutdown = sgm775_system_shutdown
+};
+
+/*
+ * Functions fulfilling the framework's module interface
+ */
+
+static int sgm775_system_init(fwk_id_t module_id, unsigned int unused,
+ const void *unused2)
+{
+ return FWK_SUCCESS;
+}
+
+static int sgm775_system_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id, const void **api)
+{
+ *api = &sgm775_system_system_power_driver_api;
+
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_sgm775_system = {
+ .name = "SGM775_SYSTEM",
+ .api_count = MOD_SGM775_SYSTEM_API_COUNT,
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .init = sgm775_system_init,
+ .process_bind_request = sgm775_system_process_bind_request,
+};
+
+/* No elements, no module configuration data */
+struct fwk_module_config config_sgm775_system = {};
diff --git a/product/sgm775/product.mk b/product/sgm775/product.mk
new file mode 100644
index 00000000..0572bdc3
--- /dev/null
+++ b/product/sgm775/product.mk
@@ -0,0 +1,10 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_PRODUCT_NAME := sgm775
+BS_FIRMWARE_LIST := scp_romfw \
+ scp_ramfw
diff --git a/product/sgm775/scp_ramfw/RTX_Config.h b/product/sgm775/scp_ramfw/RTX_Config.h
new file mode 100644
index 00000000..25621f84
--- /dev/null
+++ b/product/sgm775/scp_ramfw/RTX_Config.h
@@ -0,0 +1,56 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * RTX2 v5 configuration file.
+ * The file must be called RTX_Config.h as it is included by the an RTX
+ * file in order to create a object file containing the configuration.
+ */
+
+#ifndef RTX_CONFIG_H_
+#define RTX_CONFIG_H_
+
+/* System */
+#define OS_DYNAMIC_MEM_SIZE 0
+#define OS_TICK_FREQ 1000 /* Hz */
+#define OS_ROBIN_ENABLE 0
+#define OS_ROBIN_TIMEOUT 0
+#define OS_ISR_FIFO_QUEUE 16
+
+/* Thread */
+#define OS_THREAD_OBJ_MEM 0
+#define OS_THREAD_NUM 1
+#define OS_THREAD_DEF_STACK_NUM 0
+#define OS_THREAD_USER_STACK_SIZE 0
+#define OS_STACK_SIZE 200
+#define OS_IDLE_THREAD_STACK_SIZE 200
+#define OS_STACK_CHECK 1
+#define OS_STACK_WATERMARK 0
+#define OS_PRIVILEGE_MODE 1
+
+/* Timer */
+#define OS_TIMER_OBJ_MEM 0
+#define OS_TIMER_NUM 1
+#define OS_TIMER_THREAD_PRIO 40
+#define OS_TIMER_THREAD_STACK_SIZE 200
+#define OS_TIMER_CB_QUEUE 4
+
+/* Event flags */
+#define OS_EVFLAGS_OBJ_MEM 0
+#define OS_EVFLAGS_NUM 1
+
+#define OS_MUTEX_OBJ_MEM 0
+#define OS_MUTEX_NUM 1
+#define OS_SEMAPHORE_OBJ_MEM 0
+#define OS_SEMAPHORE_NUM 1
+#define OS_MEMPOOL_OBJ_MEM 0
+#define OS_MEMPOOL_NUM 1
+#define OS_MEMPOOL_DATA_SIZE 0
+#define OS_MSGQUEUE_OBJ_MEM 0
+#define OS_MSGQUEUE_NUM 1
+#define OS_MSGQUEUE_DATA_SIZE 0
+
+#endif /* RTX_CONFIG_H_ */
diff --git a/product/sgm775/scp_ramfw/clock_devices.h b/product/sgm775/scp_ramfw/clock_devices.h
new file mode 100644
index 00000000..c1eed2ef
--- /dev/null
+++ b/product/sgm775/scp_ramfw/clock_devices.h
@@ -0,0 +1,26 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CLOCK_DEVICES_H
+#define CLOCK_DEVICES_H
+
+/*!
+ * \brief Clock device indexes.
+ */
+enum clock_dev_idx {
+ CLOCK_DEV_IDX_BIG,
+ CLOCK_DEV_IDX_LITTLE,
+ CLOCK_DEV_IDX_GPU,
+ CLOCK_DEV_IDX_VPU,
+ CLOCK_DEV_IDX_DPU,
+ CLOCK_DEV_IDX_PIXEL_0,
+ CLOCK_DEV_IDX_PIXEL_1,
+ CLOCK_DEV_IDX_FCMCLK,
+ CLOCK_DEV_IDX_COUNT
+};
+
+#endif /* CLOCK_DEVICES_H */
diff --git a/product/sgm775/scp_ramfw/config_clock.c b/product/sgm775/scp_ramfw/config_clock.c
new file mode 100644
index 00000000..3728fb79
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_clock.c
@@ -0,0 +1,119 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_css_clock.h>
+#include <mod_system_pll.h>
+#include <mod_pik_clock.h>
+#include <mod_power_domain.h>
+#include <config_power_domain.h>
+#include <clock_devices.h>
+#include <sgm775_core.h>
+
+static struct fwk_element clock_dev_desc_table[] = {
+ [CLOCK_DEV_IDX_BIG] = {
+ .name = "CPU_GROUP_BIG",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 0),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_LITTLE] = {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 1),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_GPU] = {
+ .name = "GPU",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 2),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_VPU] = {
+ .name = "VPU",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 3),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_DPU] = {
+ .name = "DPU",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 4),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_PIXEL_0] = {
+ .name = "PIXEL_0",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 5),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ }),
+ },
+ [CLOCK_DEV_IDX_PIXEL_1] = {
+ .name = "PIXEL_1",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 6),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ }),
+ },
+ [CLOCK_DEV_IDX_FCMCLK] = {
+ .name = "FCMCLK",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 12),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ }),
+ },
+ [CLOCK_DEV_IDX_COUNT] = { }, /* Termination description. */
+};
+
+static const struct fwk_element *clock_get_dev_desc_table(fwk_id_t module_id)
+{
+ unsigned int i;
+ unsigned int core_count;
+ struct mod_clock_dev_config *dev_config;
+
+ core_count = sgm775_core_get_count();
+
+ /* Configure all clocks to respond to changes in SYSTOP power state */
+ for (i = 0; i < CLOCK_DEV_IDX_COUNT; i++) {
+ dev_config =
+ (struct mod_clock_dev_config *)clock_dev_desc_table[i].data;
+ dev_config->pd_source_id = FWK_ID_ELEMENT(
+ FWK_MODULE_IDX_POWER_DOMAIN,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_COUNT + core_count);
+ }
+
+ return clock_dev_desc_table;
+}
+
+struct fwk_module_config config_clock = {
+ .get_element_table = clock_get_dev_desc_table,
+ .data = &((struct mod_clock_config) {
+ .pd_transition_notification_id = FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION),
+ .pd_pre_transition_notification_id = FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION),
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_css_clock.c b/product/sgm775/scp_ramfw/config_css_clock.c
new file mode 100644
index 00000000..aa83f2c3
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_css_clock.c
@@ -0,0 +1,302 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_css_clock.h>
+#include <mod_system_pll.h>
+#include <mod_pik_clock.h>
+#include <sgm775_pik.h>
+
+static const struct mod_css_clock_rate rate_table_cpu_group_big[] = {
+ {
+ /* Super Underdrive */
+ .rate = 1313 * FWK_MHZ,
+ .pll_rate = 1313 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Underdrive */
+ .rate = 1531 * FWK_MHZ,
+ .pll_rate = 1531 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Nominal */
+ .rate = 1750 * FWK_MHZ,
+ .pll_rate = 1750 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Overdrive */
+ .rate = 2100 * FWK_MHZ,
+ .pll_rate = 2100 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Super Overdrive */
+ .rate = 2450 * FWK_MHZ,
+ .pll_rate = 2450 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+};
+
+static const struct mod_css_clock_rate rate_table_cpu_group_little[] = {
+ {
+ /* Super Underdrive */
+ .rate = 665 * FWK_MHZ,
+ .pll_rate = 665 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Underdrive */
+ .rate = 998 * FWK_MHZ,
+ .pll_rate = 998 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Nominal */
+ .rate = 1330 * FWK_MHZ,
+ .pll_rate = 1330 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Overdrive */
+ .rate = 1463 * FWK_MHZ,
+ .pll_rate = 1463 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+ {
+ /* Super Overdrive */
+ .rate = 1596 * FWK_MHZ,
+ .pll_rate = 1596 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+};
+
+static const struct mod_css_clock_rate rate_table_gpu[] = {
+ {
+ .rate = 450 * FWK_MHZ,
+ .pll_rate = 450 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+ {
+ .rate = 487500 * FWK_KHZ,
+ .pll_rate = 487500 * FWK_KHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+ {
+ .rate = 525 * FWK_MHZ,
+ .pll_rate = 525 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+ {
+ .rate = 562500 * FWK_KHZ,
+ .pll_rate = 562500 * FWK_KHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+ {
+ /* Nominal */
+ .rate = 600 * FWK_MHZ,
+ .pll_rate = 600 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+};
+
+static const struct mod_css_clock_rate rate_table_vpu[] = {
+ {
+ /* Nominal */
+ .rate = 600 * FWK_MHZ,
+ .pll_rate = 600 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+};
+
+static const fwk_id_t member_table_cpu_group_big[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 4),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 5),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 6),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 7),
+};
+
+static const fwk_id_t member_table_cpu_group_little[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 0),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 1),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 2),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 3),
+};
+
+static const fwk_id_t member_table_gpu[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 8),
+};
+
+static const fwk_id_t member_table_vpu[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 9),
+};
+
+static const fwk_id_t member_table_dpu[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 11),
+};
+
+static const struct fwk_element css_clock_element_table[] = {
+ {
+ .name = "CPU_GROUP_BIG",
+ .data = &((struct mod_css_clock_dev_config) {
+ .clock_type = MOD_CSS_CLOCK_TYPE_INDEXED,
+ .rate_table = rate_table_cpu_group_big,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_group_big),
+ .clock_switching_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 1),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_cpu_group_big,
+ .member_count = FWK_ARRAY_SIZE(member_table_cpu_group_big),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 1750 * FWK_MHZ,
+ .modulation_supported = true,
+ }),
+ },
+ {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &((struct mod_css_clock_dev_config) {
+ .clock_type = MOD_CSS_CLOCK_TYPE_INDEXED,
+ .rate_table = rate_table_cpu_group_little,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_group_little),
+ .clock_switching_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 0),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_cpu_group_little,
+ .member_count = FWK_ARRAY_SIZE(member_table_cpu_group_little),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 1330 * FWK_MHZ,
+ .modulation_supported = true,
+ }),
+ },
+ {
+ .name = "GPU",
+ .data = &((struct mod_css_clock_dev_config) {
+ .clock_type = MOD_CSS_CLOCK_TYPE_INDEXED,
+ .rate_table = rate_table_gpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_gpu),
+ .clock_switching_source = MOD_PIK_CLOCK_GPUCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 2),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_gpu,
+ .member_count = FWK_ARRAY_SIZE(member_table_gpu),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 600 * FWK_MHZ,
+ .modulation_supported = false,
+ }),
+ },
+ {
+ .name = "VPU",
+ .data = &((struct mod_css_clock_dev_config) {
+ .clock_type = MOD_CSS_CLOCK_TYPE_INDEXED,
+ .rate_table = rate_table_vpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_vpu),
+ .clock_switching_source = MOD_PIK_CLOCK_VPUCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 4),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_vpu,
+ .member_count = FWK_ARRAY_SIZE(member_table_vpu),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 600 * FWK_MHZ,
+ .modulation_supported = false,
+ }),
+ },
+ {
+ .name = "DPU",
+ .data = &((struct mod_css_clock_dev_config) {
+ .clock_type = MOD_CSS_CLOCK_TYPE_NON_INDEXED,
+ .clock_default_source = MOD_PIK_CLOCK_DPUCLK_SOURCE_DISPLAYPLLCLK,
+ .clock_switching_source = MOD_PIK_CLOCK_DPUCLK_SOURCE_PIXELCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 3),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_dpu,
+ .member_count = FWK_ARRAY_SIZE(member_table_dpu),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 260 * FWK_MHZ,
+ .modulation_supported = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *css_clock_get_element_table
+ (fwk_id_t module_id)
+{
+ return css_clock_element_table;
+}
+
+struct fwk_module_config config_css_clock = {
+ .get_element_table = css_clock_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_ddr_phy500.c b/product/sgm775/scp_ramfw/config_ddr_phy500.c
new file mode 100644
index 00000000..ad66c744
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_ddr_phy500.c
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <mod_ddr_phy500.h>
+#include <system_mmap.h>
+
+/* Default configuration values for DDR PHY500 devices. */
+static const struct mod_ddr_phy500_reg ddr_reg_val = {
+ .T_CTRL_DELAY = 0x00000000,
+ .READ_DELAY = 0x00000003,
+ .T_CTRL_UPD_MIN = 0x00000000,
+ .DELAY_SEL = 0x0000000A,
+ .CAPTURE_MASK = 0x0000001f,
+ .T_RDDATA_EN = 0x00001C00,
+ .T_RDLAT = 0x00000016,
+ .T_WRLAT = 0x01000000,
+ .DFI_WR_PREMBL = 0x00000002,
+ .LP_ACK = 0x00641300,
+};
+
+/* Table of DDR PHY500 element descriptions. */
+static struct fwk_element ddr_phy500_element_table[] = {
+ [0] = { .name = "DDR_PHY500-0",
+ .data = &((struct mod_ddr_phy500_element_config) {
+ .ddr = DDR_PHY0,
+ }),
+ },
+ [1] = { .name = "DDR_PHY500-1",
+ .data = &((struct mod_ddr_phy500_element_config) {
+ .ddr = DDR_PHY1,
+ }),
+ },
+ [2] = { .name = "DDR_PHY500-2",
+ .data = &((struct mod_ddr_phy500_element_config) {
+ .ddr = DDR_PHY2,
+ }),
+ },
+ [3] = { .name = "DDR_PHY500-3",
+ .data = &((struct mod_ddr_phy500_element_config) {
+ .ddr = DDR_PHY3,
+ }),
+ },
+ [4] = { }, /* Termination description. */
+};
+
+static const struct fwk_element *ddr_phy500_get_element_table
+ (fwk_id_t module_id)
+{
+ return ddr_phy500_element_table;
+}
+
+/* Configuration of the DDR PHY500 module. */
+struct fwk_module_config config_ddr_phy500 = {
+ .get_element_table = ddr_phy500_get_element_table,
+ .data = &((struct mod_ddr_phy500_module_config) {
+ .ddr_reg_val = &ddr_reg_val,
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_dmc500.c b/product/sgm775/scp_ramfw/config_dmc500.c
new file mode 100644
index 00000000..567c787b
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_dmc500.c
@@ -0,0 +1,221 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_dmc500.h>
+#include <system_mmap.h>
+
+#define COL_BITS 1
+#define BANK_BITS 0
+#define RANK_BITS 1
+#define BANK_GROUP 0
+#define ROW_BITS 4
+#define MEM_TYPE 3
+#define MEM_BURST 2
+#define DEVICE_WIDTH 2
+#define ADDR_SHUTTER 2
+
+static const struct mod_dmc500_reg reg_val = {
+ .ADDRESS_CONTROL = ADDRESS_CONTROL_VAL(RANK_BITS,
+ BANK_BITS,
+ ROW_BITS,
+ COL_BITS),
+ .RANK_REMAP_CONTROL = 0x00000000,
+ .MEMORY_TYPE = MEMORY_TYPE_VAL(BANK_GROUP,
+ DEVICE_WIDTH,
+ MEM_TYPE),
+ .FORMAT_CONTROL = FORMAT_CONTROL_VAL(MEM_BURST),
+ .DECODE_CONTROL = 0x00000011,
+ .FEATURE_CONTROL = 0x00000000,
+ .ODT_WR_CONTROL_31_00 = 0x00000000,
+ .ODT_RD_CONTROL_31_00 = 0x00000000,
+ .ODT_TIMING = 0x10001000,
+ .T_REFI = 0x0000030b,
+ .T_RFC = 0x000340d0,
+ .T_RDPDEN = 0x0000002e,
+ .T_RCD = 0x0000001d,
+ .T_RAS = 0x80000044,
+ .T_RP = 0x0000221d,
+ .T_RRD = 0x00001010,
+ .T_ACT_WINDOW = 0x00000040,
+ .T_RTR = 0x000c0808,
+ .T_RTW = 0x001f1f1f,
+ .T_RTP = 0x0000000C,
+ .T_WR = 0x00000035,
+ .T_WTR = 0x00082929,
+ .T_WTW = 0x000b0808,
+ .T_XTMW = 0x00000020,
+ .T_CLOCK_CONTROL = 0x1119030d,
+ .T_EP = 0x0000000C,
+ .T_XP = 0x000c000c,
+ .T_ESR = 0x00000019,
+ .T_XSR = 0x00e100e1,
+ .ADDRESS_MAP = ADDRESS_MAP_VAL(ADDR_SHUTTER),
+ .SI0_SI_INTERRUPT_CONTROL = 0x00000000,
+ .SI0_PMU_REQ_CONTROL = 0x00000B1A,
+ .SI0_PMU_REQ_ATTRIBUTE_MASK_0 = 0xB0562AA1,
+ .SI0_PMU_REQ_ATTRIBUTE_MATCH_0 = 0xD0FB6716,
+ .SI0_PMU_REQ_ATTRIBUTE_MASK_1 = 0x7FC24C15,
+ .SI0_PMU_REQ_ATTRIBUTE_MATCH_1 = 0xF7A9B2AC,
+ .SI0_PMU_REQ_ATTRIBUTE_MASK_2 = 0xDD35FA69,
+ .SI0_PMU_REQ_ATTRIBUTE_MATCH_2 = 0x3555A8F5,
+ .SI0_PMU_REQ_ATTRIBUTE_MASK_3 = 0xDE382B10,
+ .SI0_PMU_REQ_ATTRIBUTE_MATCH_3 = 0x3484B32C,
+ .SI0_THRESHOLD_CONTROL = 0x80000801,
+ .SI1_SI_INTERRUPT_CONTROL = 0x00000000,
+ .SI1_PMU_REQ_CONTROL = 0x00000B1A,
+ .SI1_PMU_REQ_ATTRIBUTE_MASK_0 = 0xB0562AA1,
+ .SI1_PMU_REQ_ATTRIBUTE_MATCH_0 = 0xD0FB6716,
+ .SI1_PMU_REQ_ATTRIBUTE_MASK_1 = 0x7FC24C15,
+ .SI1_PMU_REQ_ATTRIBUTE_MATCH_1 = 0xF7A9B2AC,
+ .SI1_PMU_REQ_ATTRIBUTE_MASK_2 = 0xDD35FA69,
+ .SI1_PMU_REQ_ATTRIBUTE_MATCH_2 = 0x3555A8F5,
+ .SI1_PMU_REQ_ATTRIBUTE_MASK_3 = 0xDE382B10,
+ .SI1_PMU_REQ_ATTRIBUTE_MATCH_3 = 0x3484B32C,
+ .SI1_THRESHOLD_CONTROL = 0x80000801,
+ .QUEUE_THRESHOLD_CONTROL_31_00 = 0xDEF8D550,
+ .QUEUE_THRESHOLD_CONTROL_63_32 = 0xB038362F,
+ .DCB_INTERRUPT_CONTROL = 0x00000000,
+ .PMU_DCB_CONTROL = 0x00000800,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_0 = 0xFD98CF7D,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_0 = 0x9F276EB5,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_1 = 0x40B1FC24,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_1 = 0x04BBF4FA,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MASK_2 = 0x8089B0AF,
+ .PMU_DATA_CONTROL_BLOCK_ATTRIBUTE_MATCH_2 = 0x7D26E0BE,
+ .PMU_TAG_ENTRIES_ATTRIBUTE_MASK = 0x000000CE,
+ .PMU_TAG_ENTRIES_ATTRIBUTE_MATCH = 0x00000056,
+ .QE_INTERRUPT_CONTROL = 0x00000000,
+ .RANK_TURNAROUND_CONTROL = 0x8909020F,
+ .HIT_TURNAROUND_CONTROL = 0x37B8222C,
+ .QOS_CLASS_CONTROL = 0x00000D50,
+ .ESCALATION_CONTROL = 0x000D0C00,
+ .QV_CONTROL_31_00 = 0xED2626B0,
+ .QV_CONTROL_63_32 = 0x4159BE97,
+ .RT_CONTROL_31_00 = 0xE8DC790A,
+ .RT_CONTROL_63_32 = 0x9441A291,
+ .TIMEOUT_CONTROL = 0x00000003,
+ .WRITE_PRIORITY_CONTROL_31_00 = 0x81268C40,
+ .WRITE_PRIORITY_CONTROL_63_32 = 0x15F20D15,
+ .DIR_TURNAROUND_CONTROL = 0x06060403,
+ .HIT_PREDICTION_CONTROL = 0x00020705,
+ .REFRESH_PRIORITY = 0x00000204,
+ .MC_UPDATE_CONTROL = 0x0000ff00,
+ .PHY_UPDATE_CONTROL = 0x15A3925F,
+ .PHY_MASTER_CONTROL = 0x6875AF9A,
+ .LOW_POWER_CONTROL = 0x000E0801,
+ .PMU_QE_CONTROL = 0x00000C0D,
+ .PMU_QE_MUX = 0x05670023,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MASK_0 = 0x000000F1,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MATCH_0 = 0x00000662,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MASK_1 = 0x000000DD,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MATCH_1 = 0x00000097,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MASK_2 = 0x0000001A,
+ .PMU_QOS_ENGINE_ATTRIBUTE_MATCH_2 = 0x00000755,
+ .PMU_QUEUED_ENTRIES_ATTRIBUTE_MASK = 0xAD625ED5,
+ .PMU_QUEUED_ENTRIES_ATTRIBUTE_MATCH = 0x853C65BB,
+ .MI_INTERRUPT_CONTROL = 0x00000000,
+ .POWER_DOWN_CONTROL = 0x00000005,
+ .REFRESH_CONTROL = 0x00000000,
+ .PMU_MI_CONTROL = 0x00000100,
+ .PMU_MEMORY_IF_ATTRIBUTE_MASK_0 = 0x0032BB0E,
+ .PMU_MEMORY_IF_ATTRIBUTE_MATCH_0 = 0x0033F5AB,
+ .PMU_MEMORY_IF_ATTRIBUTE_MASK_1 = 0x00296B28,
+ .PMU_MEMORY_IF_ATTRIBUTE_MATCH_1 = 0x002C67BF,
+ .PMU_BANK_STATES_ATTRIBUTE_MASK = 0x00000005,
+ .PMU_BANK_STATES_ATTRIBUTE_MATCH = 0x00000019,
+ .PMU_RANK_STATES_ATTRIBUTE_MASK = 0x0000001B,
+ .PMU_RANK_STATES_ATTRIBUTE_MATCH = 0x00000020,
+ .CFG_INTERRUPT_CONTROL = 0x00000000,
+ .T_RDDATA_EN = 0x00000001,
+ .T_PHYRDLAT = 0x0000003f,
+ .T_PHYWRLAT = 0x010f170e,
+ .ERR_RAMECC_CTLR = 0x00000000,
+ .PHY_POWER_CONTROL = 0x0000012a,
+ .T_PHY_TRAIN = 0x00f8000a,
+ .PHYUPD_INIT = 0x00000000,
+ .REFRESH_ENABLE = 0x00000001,
+ .MI_STATE_CONTROL = 0,
+ .QUEUE_STATE_CONTROL = 0,
+ .SI0_SI_STATE_CONTROL = 0,
+ .SI1_SI_STATE_CONTROL = 0,
+};
+
+/* Table of DMC500 elements descriptions. */
+static struct fwk_element dmc500_element_table[] = {
+ [0] = { .name = "DMC500-0",
+ .data = &((struct mod_dmc500_element_config) {
+ .dmc = DMC_INTERNAL0,
+ .ddr_phy_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DDR_PHY500, 0),
+ }),
+ },
+ [1] = { .name = "DMC500-1",
+ .data = &((struct mod_dmc500_element_config) {
+ .dmc = DMC_INTERNAL1,
+ .ddr_phy_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DDR_PHY500, 1),
+ }),
+ },
+ [2] = { .name = "DMC500-2",
+ .data = &((struct mod_dmc500_element_config) {
+ .dmc = DMC_INTERNAL2,
+ .ddr_phy_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DDR_PHY500, 2),
+ }),
+ },
+ [3] = { .name = "DMC500-3",
+ .data = &((struct mod_dmc500_element_config) {
+ .dmc = DMC_INTERNAL3,
+ .ddr_phy_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DDR_PHY500, 3),
+ }),
+ },
+ [4] = { }, /* Termination description. */
+};
+
+static const struct fwk_element *dmc500_get_element_table(fwk_id_t module_id)
+{
+ return dmc500_element_table;
+}
+
+static void direct_ddr_cmd(struct mod_dmc500_reg *dmc)
+{
+ dmc->DIRECT_CMD_SETTINGS = 0x00C80000;
+ dmc->DIRECT_CMD = 0x00000000;
+ dmc->DIRECT_CLK_DISABLE = 0x00280003;
+ dmc->CLK_STATUS_OVERRIDE = 0x00000003;
+ dmc->DIRECT_CMD_SETTINGS = 0x01F40003;
+ dmc->DIRECT_CMD = 0x00800080;
+ dmc->RANK_STATUS_OVERRIDE = 0x30000003;
+ dmc->DIRECT_CMD_SETTINGS = 0x04b00003;
+ dmc->DIRECT_CMD = 0x00800FE0;
+ dmc->DIRECT_CMD_SETTINGS = 0x00500003;
+ dmc->DIRECT_CMD = 0x008011E0;
+ dmc->DIRECT_CMD_SETTINGS = 0x00140003;
+ dmc->DIRECT_CMD = 0x06d601c6;
+ dmc->DIRECT_CMD_SETTINGS = 0x01000003;
+ dmc->DIRECT_CMD = 0x00f60dc6;
+ dmc->DIRECT_CMD_SETTINGS = 0x00140003;
+ dmc->DIRECT_CMD = 0x31d603c6;
+ dmc->DIRECT_CMD_SETTINGS = 0x00140003;
+ dmc->DIRECT_CMD = 0x16f601c6;
+ dmc->DIRECT_CMD_SETTINGS = 0x00140003;
+ dmc->DIRECT_CMD = 0x2dd602c6;
+ dmc->DIRECT_CMD_SETTINGS = 0x02000003;
+ dmc->DIRECT_CMD = 0x00d60de6;
+}
+
+/* Configuration of the DMC500 module. */
+struct fwk_module_config config_dmc500 = {
+ .get_element_table = dmc500_get_element_table,
+ .data = &((struct mod_dmc500_module_config) {
+ .timer_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0),
+ .ddr_phy_module_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_DDR_PHY500),
+ .ddr_phy_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_DDR_PHY500, 0),
+ .reg_val = &reg_val,
+ .direct_ddr_cmd = direct_ddr_cmd,
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_dvfs.c b/product/sgm775/scp_ramfw/config_dvfs.c
new file mode 100644
index 00000000..43c7ab97
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_dvfs.c
@@ -0,0 +1,129 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <config_dvfs.h>
+#include <mod_dvfs.h>
+
+static const struct mod_dvfs_domain_config cpu_group_little = {
+ .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU, 0),
+ .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 1),
+ .latency = 1200,
+ .sustained_idx = 2,
+ .opps = (struct mod_dvfs_opp[]) {
+ {
+ .frequency = 665 * FWK_MHZ,
+ .voltage = 100,
+ },
+ {
+ .frequency = 998 * FWK_MHZ,
+ .voltage = 200,
+ },
+ {
+ .frequency = 1330 * FWK_MHZ,
+ .voltage = 300,
+ },
+ {
+ .frequency = 1463 * FWK_MHZ,
+ .voltage = 400,
+ },
+ {
+ .frequency = 1596 * FWK_MHZ,
+ .voltage = 500,
+ },
+ { }
+ }
+};
+
+static const struct mod_dvfs_domain_config cpu_group_big = {
+ .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU, 1),
+ .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 0),
+ .latency = 1200,
+ .sustained_idx = 2,
+ .opps = (struct mod_dvfs_opp[]) {
+ {
+ .frequency = 1313 * FWK_MHZ,
+ .voltage = 100,
+ },
+ {
+ .frequency = 1531 * FWK_MHZ,
+ .voltage = 200,
+ },
+ {
+ .frequency = 1750 * FWK_MHZ,
+ .voltage = 300,
+ },
+ {
+ .frequency = 2100 * FWK_MHZ,
+ .voltage = 400,
+ },
+ {
+ .frequency = 2450 * FWK_MHZ,
+ .voltage = 500,
+ },
+ { }
+ }
+};
+
+static const struct mod_dvfs_domain_config gpu = {
+ .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU, 2),
+ .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 2),
+ .latency = 1200,
+ .sustained_idx = 4,
+ .opps = (struct mod_dvfs_opp[]) {
+ {
+ .frequency = 450 * FWK_MHZ,
+ .voltage = 100,
+ },
+ {
+ .frequency = 487500 * FWK_KHZ,
+ .voltage = 200,
+ },
+ {
+ .frequency = 525 * FWK_MHZ,
+ .voltage = 300,
+ },
+ {
+ .frequency = 562500 * FWK_KHZ,
+ .voltage = 400,
+ },
+ {
+ .frequency = 600 * FWK_MHZ,
+ .voltage = 500,
+ },
+ { }
+ }
+};
+
+static const struct fwk_element element_table[] = {
+ [DVFS_ELEMENT_IDX_LITTLE] = {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &cpu_group_little,
+ },
+ [DVFS_ELEMENT_IDX_BIG] = {
+ .name = "CPU_GROUP_BIG",
+ .data = &cpu_group_big,
+ },
+ [DVFS_ELEMENT_IDX_GPU] = {
+ .name = "GPU",
+ .data = &gpu,
+ },
+ { }
+};
+
+static const struct fwk_element *dvfs_get_element_table(fwk_id_t module_id)
+{
+ return element_table;
+}
+
+struct fwk_module_config config_dvfs = {
+ .get_element_table = dvfs_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_dvfs.h b/product/sgm775/scp_ramfw/config_dvfs.h
new file mode 100644
index 00000000..365cc1cd
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_dvfs.h
@@ -0,0 +1,18 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_DVFS_H
+#define CONFIG_DVFS_H
+
+enum dvfs_element_idx {
+ DVFS_ELEMENT_IDX_LITTLE,
+ DVFS_ELEMENT_IDX_BIG,
+ DVFS_ELEMENT_IDX_GPU,
+ DVFS_ELEMENT_IDX_COUNT
+};
+
+#endif /* CONFIG_DVFS_H */
diff --git a/product/sgm775/scp_ramfw/config_log.c b/product/sgm775/scp_ramfw/config_log.c
new file mode 100644
index 00000000..938ca39b
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_log.c
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_banner.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_log.h>
+#include <mod_pl011.h>
+#include <system_mmap.h>
+#include <clock_devices.h>
+
+/*
+ * PL011 module
+ */
+static const struct fwk_element pl011_element_table[] = {
+ [0] = {
+ .name = "board-uart1",
+ .data = &((struct mod_pl011_device_config) {
+ .reg_base = BOARD_UART1_BASE,
+ .baud_rate_bps = 115200,
+ .clock_rate_hz = 24 * FWK_MHZ,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_FCMCLK),
+ }),
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *get_pl011_table(fwk_id_t module_id)
+{
+ return pl011_element_table;
+}
+
+struct fwk_module_config config_pl011 = {
+ .get_element_table = get_pl011_table,
+};
+
+/*
+ * Log module
+ */
+static const struct mod_log_config log_data = {
+ .device_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_PL011, 0),
+ .api_id = FWK_ID_API(FWK_MODULE_IDX_PL011, 0),
+ .log_groups = MOD_LOG_GROUP_ERROR |
+ MOD_LOG_GROUP_INFO |
+ MOD_LOG_GROUP_WARNING |
+ MOD_LOG_GROUP_DEBUG,
+ .banner = FWK_BANNER_SCP
+ FWK_BANNER_RAM_FIRMWARE
+ BUILD_VERSION_DESCRIBE_STRING "\n",
+};
+
+struct fwk_module_config config_log = {
+ .get_element_table = NULL,
+ .data = &log_data,
+};
diff --git a/product/sgm775/scp_ramfw/config_mhu.c b/product/sgm775/scp_ramfw/config_mhu.c
new file mode 100644
index 00000000..b6f89940
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_mhu.c
@@ -0,0 +1,54 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <mod_mhu.h>
+#include <sgm775_irq.h>
+#include <sgm775_mhu.h>
+#include <sgm775_mmap.h>
+
+static const struct fwk_element mhu_element_table[] = {
+ [SGM775_MHU_DEVICE_IDX_S] = {
+ .name = "MHU_S",
+ .sub_element_count = 1,
+ .data = &((struct mod_mhu_device_config) {
+ .irq = MHU_SECURE_IRQ,
+ .in = MHU_CPU_INTR_S_BASE,
+ .out = MHU_SCP_INTR_S_BASE,
+ })
+ },
+ [SGM775_MHU_DEVICE_IDX_NS_H] = {
+ .name = "MHU_NS_H",
+ .sub_element_count = 1,
+ .data = &((struct mod_mhu_device_config) {
+ .irq = MHU_HIGH_PRIO_IRQ,
+ .in = MHU_CPU_INTR_H_BASE,
+ .out = MHU_SCP_INTR_H_BASE,
+ })
+ },
+ [SGM775_MHU_DEVICE_IDX_NS_L] = {
+ .name = "MHU_NS_L",
+ .sub_element_count = 1,
+ .data = &((struct mod_mhu_device_config) {
+ .irq = MHU_LOW_PRIO_IRQ,
+ .in = MHU_CPU_INTR_L_BASE,
+ .out = MHU_SCP_INTR_L_BASE,
+ })
+ },
+ [SGM775_MHU_DEVICE_IDX_COUNT] = {},
+};
+
+static const struct fwk_element *mhu_get_element_table(fwk_id_t module_id)
+{
+ return mhu_element_table;
+}
+
+struct fwk_module_config config_mhu = {
+ .get_element_table = mhu_get_element_table,
+};
diff --git a/product/sgm775/scp_ramfw/config_mock_psu.c b/product/sgm775/scp_ramfw/config_mock_psu.c
new file mode 100644
index 00000000..366f2023
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_mock_psu.c
@@ -0,0 +1,52 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <mod_mock_psu.h>
+
+static const struct fwk_element element_table[] = {
+ {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &(const struct mod_mock_psu_device_config) {
+ .default_enabled = true,
+ .default_voltage = 100,
+ },
+ },
+ {
+ .name = "CPU_GROUP_BIG",
+ .data = &(const struct mod_mock_psu_device_config) {
+ .default_enabled = true,
+ .default_voltage = 100,
+ },
+ },
+ {
+ .name = "GPU",
+ .data = &(const struct mod_mock_psu_device_config) {
+ .default_enabled = true,
+ .default_voltage = 100,
+ },
+ },
+ {
+ .name = "VPU",
+ .data = &(const struct mod_mock_psu_device_config) {
+ .default_enabled = true,
+ .default_voltage = 100,
+ },
+ },
+ { }
+};
+
+static const struct fwk_element *get_element_table(fwk_id_t module_id)
+{
+ return element_table;
+}
+
+struct fwk_module_config config_mock_psu = {
+ .get_element_table = get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_pik_clock.c b/product/sgm775/scp_ramfw/config_pik_clock.c
new file mode 100644
index 00000000..06c37de7
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_pik_clock.c
@@ -0,0 +1,295 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_pik_clock.h>
+#include <sgm775_pik.h>
+#include <system_clock.h>
+
+/*
+ * Rate lookup tables
+ */
+
+static struct mod_pik_clock_rate rate_table_cpu_a55[] = {
+ {
+ .rate = 1330 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static struct mod_pik_clock_rate rate_table_cpu_a75[] = {
+ {
+ .rate = 1750 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static struct mod_pik_clock_rate rate_table_gpu[] = {
+ {
+ .rate = 600 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via GPU PLL */
+ },
+};
+
+static struct mod_pik_clock_rate rate_table_vpu[] = {
+ {
+ .rate = 600 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_VPUCLK_SOURCE_VIDEOPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via VPU PLL */
+ },
+};
+
+static struct mod_pik_clock_rate rate_table_dpu[] = {
+ {
+ .rate = 260 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_DPUCLK_SOURCE_DISPLAYPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via display PLL */
+ },
+};
+
+static struct mod_pik_clock_rate rate_table_aclkdp[] = {
+ {
+ .rate = (CLOCK_RATE_SYSPLLCLK / 3),
+ .source = MOD_PIK_CLOCK_ACLKDPU_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = 3,
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_sys_fcmclk[] = {
+ {
+ .rate = CLOCK_RATE_SYSPLLCLK,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = 1,
+ },
+};
+
+static const struct fwk_element pik_clock_element_table[] = {
+ /*
+ * A55 CPUS
+ */
+ {
+ .name = "CLUS0_CPU0",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU1",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU2",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU3",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * A75 CPUS
+ */
+ {
+ .name = "CLUS0_CPU4",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU5",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU6",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU7",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * GPU
+ */
+ {
+ .name = "GPU",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .control_reg = &PIK_GPU->GPUCLK_CTRL,
+ .divsys_reg = &PIK_GPU->GPUCLK_DIV1,
+ .divext_reg = &PIK_GPU->GPUCLK_DIV2,
+ .rate_table = rate_table_gpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_gpu),
+ .initial_rate = 600 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * VPU
+ */
+ {
+ .name = "VPU",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .control_reg = &PIK_VPU->VIDEOCLK_CTRL,
+ .divsys_reg = &PIK_VPU->VIDEOCLK_DIV1,
+ .divext_reg = &PIK_VPU->VIDEOCLK_DIV2,
+ .rate_table = rate_table_vpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_vpu),
+ .initial_rate = 600 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * DPU
+ */
+ {
+ .name = "ACLKDP",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .control_reg = &PIK_DPU->ACLKDP_CTRL,
+ .divsys_reg = &PIK_DPU->ACLKDP_DIV1,
+ .divext_reg = &PIK_DPU->ACLKDP_DIV2,
+ .rate_table = rate_table_aclkdp,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_aclkdp),
+ .initial_rate = (CLOCK_RATE_SYSPLLCLK / 3),
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "DPU_M0",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .control_reg = &PIK_DPU->M0CLK_CTRL,
+ .divsys_reg = &PIK_DPU->M0CLK_DIV1,
+ .divext_reg = &PIK_DPU->M0CLK_DIV2,
+ .rate_table = rate_table_dpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_dpu),
+ .initial_rate = 260 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * FCM Clock
+ */
+ {
+ .name = "FCMCLK",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->FCMCLK_CTRL,
+ .divsys_reg = &PIK_SYSTEM->FCMCLK_DIV1,
+ .rate_table = rate_table_sys_fcmclk,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_fcmclk),
+ .initial_rate = CLOCK_RATE_SYSPLLCLK,
+ .defer_initialization = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *pik_clock_get_element_table
+ (fwk_id_t module_id)
+{
+ return pik_clock_element_table;
+}
+
+struct fwk_module_config config_pik_clock = {
+ .get_element_table = pik_clock_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_power_domain.c b/product/sgm775/scp_ramfw/config_power_domain.c
new file mode 100644
index 00000000..ea674d40
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_power_domain.c
@@ -0,0 +1,258 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <config_power_domain.h>
+#include <config_ppu_v0.h>
+#include <mod_system_power.h>
+#include <mod_power_domain.h>
+#include <mod_ppu_v1.h>
+#include <sgm775_core.h>
+
+static const char *core_pd_name_table[SGM775_CORE_PER_CLUSTER_MAX] = {
+ "CLUS0CORE0", "CLUS0CORE1", "CLUS0CORE2", "CLUS0CORE3",
+ "CLUS0CORE4", "CLUS0CORE5", "CLUS0CORE6", "CLUS0CORE7",
+};
+
+/* Mask of the allowed states for the systop power domain */
+static const uint32_t systop_allowed_state_mask_table[] = {
+ [0] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK |
+ (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP0) |
+ (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP1)
+};
+
+/*
+ * Mask of the allowed states for the top level power domains
+ * (but the cluster power domains) depending on the system states.
+ */
+static const uint32_t toplevel_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK
+};
+
+/*
+ * Mask of the allowed states for the cluster power domain depending on the
+ * system states.
+ */
+static const uint32_t cluster_pd_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK |
+ MOD_PD_STATE_SLEEP_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK
+};
+
+/* Mask of the allowed states for a core depending on the cluster states. */
+static const uint32_t core_pd_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK |
+ MOD_PD_STATE_SLEEP_MASK,
+ [MOD_PD_STATE_SLEEP] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_SLEEP_MASK,
+};
+
+/* Power module specific configuration data (none) */
+static const struct mod_power_domain_config sgm775_power_domain_config = { };
+
+static struct fwk_element sgm775_power_domain_static_element_table[] = {
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_CLUSTER0] = {
+ .name = "CLUS0",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_CLUSTER,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_CLUSTER0,
+ 0),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V1,
+ MOD_PPU_V1_API_IDX_POWER_DOMAIN_DRIVER),
+ .allowed_state_mask_table = cluster_pd_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(cluster_pd_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DBGTOP] = {
+ .name = "DBGTOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_DEVICE_DEBUG,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DBGTOP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0, PPU_V0_ELEMENT_IDX_DBGTOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU0TOP] = {
+ .name = "DPU0TOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU0TOP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0, PPU_V0_ELEMENT_IDX_DPU0TOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU1TOP] = {
+ .name = "DPU1TOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU1TOP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0, PPU_V0_ELEMENT_IDX_DPU1TOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_GPUTOP] = {
+ .name = "GPUTOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_GPUTOP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0, PPU_V0_ELEMENT_IDX_GPUTOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_VPUTOP] = {
+ .name = "VPUTOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_1,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_VPUTOP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0, PPU_V0_ELEMENT_IDX_VPUTOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table)
+ }),
+ },
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_COUNT] = {
+ .name = "SYSTOP",
+ .data = &((struct mod_power_domain_element_config) {
+ .attributes.pd_type = MOD_PD_TYPE_SYSTEM,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2, 0, 0, 0, 0),
+ .driver_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SYSTEM_POWER),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER,
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER),
+ .allowed_state_mask_table = systop_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(systop_allowed_state_mask_table)
+ }),
+ },
+};
+
+
+/*
+ * Function definitions with internal linkage
+ */
+static const struct fwk_element *sgm775_power_domain_get_element_table
+ (fwk_id_t module_id)
+{
+ struct fwk_element *element_table, *element;
+ struct mod_power_domain_element_config *pd_config_table, *pd_config;
+ unsigned int core_idx;
+
+ element_table = fwk_mm_calloc(
+ sgm775_core_get_count()
+ + FWK_ARRAY_SIZE(sgm775_power_domain_static_element_table)
+ + 1, /* Terminator */
+ sizeof(struct fwk_element));
+ if (element_table == NULL)
+ return NULL;
+
+ pd_config_table = fwk_mm_calloc(sgm775_core_get_count(),
+ sizeof(struct mod_power_domain_element_config));
+ if (pd_config_table == NULL)
+ return NULL;
+
+ for (core_idx = 0; core_idx < sgm775_core_get_count(); core_idx++) {
+ element = &element_table[core_idx];
+ pd_config = &pd_config_table[core_idx];
+
+ element->name = core_pd_name_table[core_idx];
+ element->data = pd_config;
+
+ pd_config->attributes.pd_type = MOD_PD_TYPE_CORE,
+ pd_config->tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_0,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_CLUSTER0,
+ core_idx),
+ pd_config->driver_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_PPU_V1, core_idx),
+ pd_config->api_id = FWK_ID_API(
+ FWK_MODULE_IDX_PPU_V1, MOD_PPU_V1_API_IDX_POWER_DOMAIN_DRIVER),
+ pd_config->allowed_state_mask_table = core_pd_allowed_state_mask_table,
+ pd_config->allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(core_pd_allowed_state_mask_table);
+ }
+
+ pd_config = (struct mod_power_domain_element_config *)
+ sgm775_power_domain_static_element_table
+ [CONFIG_POWER_DOMAIN_SYSTOP_CHILD_CLUSTER0]
+ .data;
+ pd_config->driver_id =
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PPU_V1, sgm775_core_get_count());
+
+ memcpy(element_table + sgm775_core_get_count(),
+ sgm775_power_domain_static_element_table,
+ sizeof(sgm775_power_domain_static_element_table));
+
+ return element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_power_domain = {
+ .get_element_table = sgm775_power_domain_get_element_table,
+ .data = &sgm775_power_domain_config,
+};
diff --git a/product/sgm775/scp_ramfw/config_power_domain.h b/product/sgm775/scp_ramfw/config_power_domain.h
new file mode 100644
index 00000000..10a40a47
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_power_domain.h
@@ -0,0 +1,21 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_POWER_DOMAIN_H
+#define CONFIG_POWER_DOMAIN_H
+
+enum systop_child_index {
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_CLUSTER0,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DBGTOP,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU0TOP,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_DPU1TOP,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_GPUTOP,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_VPUTOP,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_COUNT
+};
+
+#endif /* CONFIG_POWER_DOMAIN_H */
diff --git a/product/sgm775/scp_ramfw/config_ppu_v0.c b/product/sgm775/scp_ramfw/config_ppu_v0.c
new file mode 100644
index 00000000..9c65b589
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_ppu_v0.c
@@ -0,0 +1,92 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <mod_ppu_v0.h>
+#include <config_ppu_v0.h>
+#include <sgm775_irq.h>
+#include <sgm775_mmap.h>
+
+static struct fwk_element sgm775_ppu_v0_element_table[] = {
+ [PPU_V0_ELEMENT_IDX_DBGTOP] = {
+ .name = "DBGTOP",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_DEVICE_DEBUG,
+ .ppu.reg_base = PPU_DEBUG_BASE,
+ .ppu.irq = PPU_DEBUG_IRQ
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_DPU0TOP] = {
+ .name = "DPU0TOP",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .ppu.reg_base = PPU_DPU0_BASE,
+ .ppu.irq = PPU_DPU0_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_DPU1TOP] = {
+ .name = "DPU1TOP",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .ppu.reg_base = PPU_DPU1_BASE,
+ .ppu.irq = PPU_DPU1_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_GPUTOP] = {
+ .name = "GPUTOP",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .ppu.reg_base = PPU_GPU_BASE,
+ .ppu.irq = PPU_GPU_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_VPUTOP] = {
+ .name = "VPUTOP",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .ppu.reg_base = PPU_VPU_BASE,
+ .ppu.irq = PPU_VPU_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_SYS0] = {
+ .name = "SYS0",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_SYSTEM,
+ .ppu.reg_base = PPU_SYS0_BASE,
+ .ppu.irq = PPU_SYS0_IRQ
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_SYS1] = {
+ .name = "SYS1",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_SYSTEM,
+ .ppu.reg_base = PPU_SYS1_BASE,
+ .ppu.irq = PPU_SYS1_IRQ
+ }),
+ },
+ [PPU_V0_ELEMENT_IDX_COUNT] = { }, /* Termination entry */
+};
+
+
+static const struct fwk_element *sgm775_ppu_v0_get_element_table
+ (fwk_id_t module_id)
+{
+ return sgm775_ppu_v0_element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_ppu_v0 = {
+ .get_element_table = sgm775_ppu_v0_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_ppu_v0.h b/product/sgm775/scp_ramfw/config_ppu_v0.h
new file mode 100644
index 00000000..80260051
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_ppu_v0.h
@@ -0,0 +1,22 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_PPU_V0_H
+#define CONFIG_PPU_V0_H
+
+enum ppu_v0_element_idx {
+ PPU_V0_ELEMENT_IDX_DBGTOP,
+ PPU_V0_ELEMENT_IDX_DPU0TOP,
+ PPU_V0_ELEMENT_IDX_DPU1TOP,
+ PPU_V0_ELEMENT_IDX_GPUTOP,
+ PPU_V0_ELEMENT_IDX_VPUTOP,
+ PPU_V0_ELEMENT_IDX_SYS0,
+ PPU_V0_ELEMENT_IDX_SYS1,
+ PPU_V0_ELEMENT_IDX_COUNT
+};
+
+#endif /* CONFIG_PPU_V0_H */
diff --git a/product/sgm775/scp_ramfw/config_ppu_v1.c b/product/sgm775/scp_ramfw/config_ppu_v1.c
new file mode 100644
index 00000000..3f90ccdb
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_ppu_v1.c
@@ -0,0 +1,105 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_element.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_power_domain.h>
+#include <mod_ppu_v1.h>
+#include <sgm775_core.h>
+#include <sgm775_irq.h>
+#include <sgm775_mmap.h>
+#include <config_power_domain.h>
+
+static const char *core_pd_name_table[SGM775_CORE_PER_CLUSTER_MAX] = {
+ "CLUS0CORE0", "CLUS0CORE1", "CLUS0CORE2", "CLUS0CORE3",
+ "CLUS0CORE4", "CLUS0CORE5", "CLUS0CORE6", "CLUS0CORE7",
+};
+
+static uintptr_t core_pd_ppu_base_table[] = {
+ PPU_CLUS0CORE0_BASE, PPU_CLUS0CORE1_BASE, PPU_CLUS0CORE2_BASE,
+ PPU_CLUS0CORE3_BASE, PPU_CLUS0CORE4_BASE, PPU_CLUS0CORE5_BASE,
+ PPU_CLUS0CORE6_BASE, PPU_CLUS0CORE7_BASE
+};
+
+static unsigned int core_pd_ppu_irq_table[] = {
+ PPU_CLUS0CORE0_IRQ, PPU_CLUS0CORE1_IRQ, PPU_CLUS0CORE2_IRQ,
+ PPU_CLUS0CORE3_IRQ, PPU_CLUS0CORE4_IRQ, PPU_CLUS0CORE5_IRQ,
+ PPU_CLUS0CORE6_IRQ, PPU_CLUS0CORE7_IRQ
+};
+
+struct mod_ppu_v1_config sgm775_ppu_v1_notification_config = {
+ .pd_notification_id = FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION),
+};
+
+static const struct fwk_element *sgm775_ppu_v1_get_element_table
+ (fwk_id_t module_id)
+{
+ struct fwk_element *element_table, *element;
+ struct mod_ppu_v1_pd_config *pd_config_table, *pd_config;
+ unsigned int core_idx;
+
+ /*
+ * Allocate element descriptors based on:
+ * Number of cores
+ * +1 cluster descriptor
+ * +1 terminator descriptor
+ */
+ element_table = fwk_mm_calloc(sgm775_core_get_count() + 2,
+ sizeof(struct fwk_element));
+ if (element_table == NULL)
+ return NULL;
+
+ pd_config_table = fwk_mm_calloc(sgm775_core_get_count() + 1,
+ sizeof(struct mod_ppu_v1_pd_config));
+ if (pd_config_table == NULL)
+ return NULL;
+
+ for (core_idx = 0; core_idx < sgm775_core_get_count(); core_idx++) {
+ element = &element_table[core_idx];
+ pd_config = &pd_config_table[core_idx];
+
+ element->name = core_pd_name_table[core_idx];
+ element->data = pd_config;
+
+ pd_config->pd_type = MOD_PD_TYPE_CORE;
+ pd_config->ppu.reg_base = core_pd_ppu_base_table[core_idx];
+ pd_config->ppu.irq = core_pd_ppu_irq_table[core_idx];
+ pd_config->cluster_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_PPU_V1,
+ sgm775_core_get_count());
+ pd_config->observer_id = FWK_ID_NONE;
+ }
+
+ element = &element_table[sgm775_core_get_count()];
+ pd_config = &pd_config_table[sgm775_core_get_count()];
+
+ element->name = "CLUS0";
+ element->data = pd_config;
+
+ pd_config->pd_type = MOD_PD_TYPE_CLUSTER;
+ pd_config->ppu.reg_base = PPU_CLUS0_BASE;
+ pd_config->ppu.irq = PPU_CLUS0_IRQ;
+ pd_config->observer_id = FWK_ID_NONE;
+
+ sgm775_ppu_v1_notification_config.pd_source_id = FWK_ID_ELEMENT(
+ FWK_MODULE_IDX_POWER_DOMAIN,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_COUNT + sgm775_core_get_count());
+
+ return element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_ppu_v1 = {
+ .get_element_table = sgm775_ppu_v1_get_element_table,
+ .data = &sgm775_ppu_v1_notification_config,
+};
diff --git a/product/sgm775/scp_ramfw/config_psu.c b/product/sgm775/scp_ramfw/config_psu.c
new file mode 100644
index 00000000..80af3a16
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_psu.c
@@ -0,0 +1,58 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_mock_psu.h>
+#include <mod_psu.h>
+
+static const struct fwk_element element_table[] = {
+ {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &(const struct mod_psu_device_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_MOCK_PSU, 0),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_PSU,
+ MOD_MOCK_PSU_API_IDX_PSU_DRIVER)
+ },
+ },
+ {
+ .name = "CPU_GROUP_BIG",
+ .data = &(const struct mod_psu_device_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_MOCK_PSU, 1),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_PSU,
+ MOD_MOCK_PSU_API_IDX_PSU_DRIVER)
+ },
+ },
+ {
+ .name = "GPU",
+ .data = &(const struct mod_psu_device_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_MOCK_PSU, 2),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_PSU,
+ MOD_MOCK_PSU_API_IDX_PSU_DRIVER)
+ },
+ },
+ {
+ .name = "VPU",
+ .data = &(const struct mod_psu_device_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_MOCK_PSU, 3),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MOCK_PSU,
+ MOD_MOCK_PSU_API_IDX_PSU_DRIVER)
+ },
+ },
+ { }
+};
+
+static const struct fwk_element *psu_get_element_table(fwk_id_t module_id)
+{
+ return element_table;
+}
+
+struct fwk_module_config config_psu = {
+ .get_element_table = psu_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_scmi.c b/product/sgm775/scp_ramfw/config_scmi.c
new file mode 100644
index 00000000..a79bdab1
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_scmi.c
@@ -0,0 +1,77 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <sgm775_scmi.h>
+#include <mod_scmi.h>
+#include <internal/scmi.h>
+#include <mod_smt.h>
+
+static const struct fwk_element service_table[] = {
+ [SGM775_SCMI_SERVICE_IDX_PSCI] = {
+ .name = "SERVICE0",
+ .data = &((struct mod_scmi_service_config) {
+ .transport_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SMT,
+ SGM775_SCMI_SERVICE_IDX_PSCI),
+ .transport_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SMT,
+ MOD_SMT_API_IDX_SCMI_TRANSPORT),
+ .scmi_agent_id = SCMI_AGENT_ID_PSCI,
+ }),
+ },
+ [SGM775_SCMI_SERVICE_IDX_OSPM_0] = {
+ .name = "SERVICE1",
+ .data = &((struct mod_scmi_service_config) {
+ .transport_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SMT,
+ SGM775_SCMI_SERVICE_IDX_OSPM_0),
+ .transport_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SMT,
+ MOD_SMT_API_IDX_SCMI_TRANSPORT),
+ .scmi_agent_id = SCMI_AGENT_ID_OSPM,
+ }),
+ },
+ [SGM775_SCMI_SERVICE_IDX_OSPM_1] = {
+ .name = "SERVICE2",
+ .data = &((struct mod_scmi_service_config) {
+ .transport_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SMT,
+ SGM775_SCMI_SERVICE_IDX_OSPM_1),
+ .transport_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SMT,
+ MOD_SMT_API_IDX_SCMI_TRANSPORT),
+ .scmi_agent_id = SCMI_AGENT_ID_OSPM,
+ }),
+ },
+ [SGM775_SCMI_SERVICE_IDX_COUNT] = {}
+};
+
+static const struct fwk_element *get_service_table(fwk_id_t module_id)
+{
+ return service_table;
+}
+
+static const struct mod_scmi_agent agent_table[] = {
+ [SCMI_AGENT_ID_OSPM] = {
+ .type = SCMI_AGENT_TYPE_OSPM,
+ .name = "OSPM",
+ },
+ [SCMI_AGENT_ID_PSCI] = {
+ .type = SCMI_AGENT_TYPE_PSCI,
+ .name = "PSCI",
+ },
+};
+
+struct fwk_module_config config_scmi = {
+ .get_element_table = get_service_table,
+ .data = &((struct mod_scmi_config) {
+ .protocol_count_max = 9,
+ .agent_count = FWK_ARRAY_SIZE(agent_table) - 1,
+ .agent_table = agent_table,
+ .vendor_identifier = "arm",
+ .sub_vendor_identifier = "arm",
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_scmi_apcore.c b/product/sgm775/scp_ramfw/config_scmi_apcore.c
new file mode 100644
index 00000000..689ddaf7
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_scmi_apcore.c
@@ -0,0 +1,31 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_scmi_apcore.h>
+#include <sgm775_core.h>
+#include <sgm775_pik.h>
+
+static const struct mod_scmi_apcore_reset_register_group
+ reset_reg_group_table[] = {
+ {
+ .base_register =
+ (uintptr_t)&PIK_CLUS0->STATIC_CONFIG[0].RVBARADDR_LW,
+ .register_count = SGM775_CORE_PER_CLUSTER_MAX,
+ },
+ };
+
+const struct fwk_module_config config_scmi_apcore = {
+ .data = &((struct mod_scmi_apcore_config){
+ .reset_register_width = MOD_SCMI_APCORE_REG_WIDTH_64,
+ .reset_register_group_count =
+ FWK_ARRAY_SIZE(reset_reg_group_table),
+ .reset_register_group_table = &reset_reg_group_table[0],
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_scmi_clock.c b/product/sgm775/scp_ramfw/config_scmi_clock.c
new file mode 100644
index 00000000..254c9733
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_scmi_clock.c
@@ -0,0 +1,70 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_scmi_clock.h>
+#include <sgm775_scmi.h>
+#include <clock_devices.h>
+
+static const struct mod_scmi_clock_device agent_device_table_ospm[] = {
+ {
+ /* VPU */
+ .element_id =
+ FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VPU),
+ .permissions = MOD_SCMI_CLOCK_PERM_ATTRIBUTES |
+ MOD_SCMI_CLOCK_PERM_DESCRIBE_RATES |
+ MOD_SCMI_CLOCK_PERM_GET_RATE |
+ MOD_SCMI_CLOCK_PERM_SET_RATE,
+ },
+ {
+ /* DPU */
+ .element_id =
+ FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DPU),
+ .permissions = MOD_SCMI_CLOCK_PERM_ATTRIBUTES |
+ MOD_SCMI_CLOCK_PERM_DESCRIBE_RATES |
+ MOD_SCMI_CLOCK_PERM_GET_RATE |
+ MOD_SCMI_CLOCK_PERM_SET_RATE,
+ },
+ {
+ /* PIXEL_0 */
+ .element_id =
+ FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_PIXEL_0),
+ .permissions = MOD_SCMI_CLOCK_PERM_ATTRIBUTES |
+ MOD_SCMI_CLOCK_PERM_DESCRIBE_RATES |
+ MOD_SCMI_CLOCK_PERM_GET_RATE |
+ MOD_SCMI_CLOCK_PERM_SET_RATE,
+ },
+ {
+ /* PIXEL_1 */
+ .element_id =
+ FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_PIXEL_1),
+ .permissions = MOD_SCMI_CLOCK_PERM_ATTRIBUTES |
+ MOD_SCMI_CLOCK_PERM_DESCRIBE_RATES |
+ MOD_SCMI_CLOCK_PERM_GET_RATE |
+ MOD_SCMI_CLOCK_PERM_SET_RATE,
+ },
+};
+
+static const struct mod_scmi_clock_agent agent_table[SCMI_AGENT_ID_COUNT] = {
+ [SCMI_AGENT_ID_PSCI] = { /* No access */ },
+ [SCMI_AGENT_ID_OSPM] = {
+ .device_table = agent_device_table_ospm,
+ .device_count = FWK_ARRAY_SIZE(agent_device_table_ospm),
+ },
+};
+
+struct fwk_module_config config_scmi_clock = {
+ .data = &((struct mod_scmi_clock_config) {
+ .max_pending_transactions = 0,
+ .agent_table = agent_table,
+ .agent_count = FWK_ARRAY_SIZE(agent_table),
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_scmi_perf.c b/product/sgm775/scp_ramfw/config_scmi_perf.c
new file mode 100644
index 00000000..0197ae8d
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_scmi_perf.c
@@ -0,0 +1,44 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <config_dvfs.h>
+#include <sgm775_scmi.h>
+#include <mod_scmi_perf.h>
+
+static const struct mod_scmi_perf_domain_config domains[] = {
+ [DVFS_ELEMENT_IDX_LITTLE] = {
+ .permissions = &(uint32_t[]) {
+ [SCMI_AGENT_ID_OSPM] = MOD_SCMI_PERF_PERMS_SET_LEVEL |
+ MOD_SCMI_PERF_PERMS_SET_LIMITS,
+ [SCMI_AGENT_ID_PSCI] = MOD_SCMI_PERF_PERMS_NONE,
+ }
+ },
+ [DVFS_ELEMENT_IDX_BIG] = {
+ .permissions = &(uint32_t[]) {
+ [SCMI_AGENT_ID_OSPM] = MOD_SCMI_PERF_PERMS_SET_LEVEL |
+ MOD_SCMI_PERF_PERMS_SET_LIMITS,
+ [SCMI_AGENT_ID_PSCI] = MOD_SCMI_PERF_PERMS_NONE,
+ }
+ },
+ [DVFS_ELEMENT_IDX_GPU] = {
+ .permissions = &(uint32_t[]) {
+ [SCMI_AGENT_ID_OSPM] = MOD_SCMI_PERF_PERMS_SET_LEVEL |
+ MOD_SCMI_PERF_PERMS_SET_LIMITS,
+ [SCMI_AGENT_ID_PSCI] = MOD_SCMI_PERF_PERMS_NONE,
+ }
+ },
+};
+
+struct fwk_module_config config_scmi_perf = {
+ .get_element_table = NULL,
+ .data = &((struct mod_scmi_perf_config) {
+ .domains = &domains,
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_scmi_system_power.c b/product/sgm775/scp_ramfw/config_scmi_system_power.c
new file mode 100644
index 00000000..5bbc074c
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_scmi_system_power.c
@@ -0,0 +1,19 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_module.h>
+#include <mod_scmi_system_power.h>
+#include <mod_system_power.h>
+
+struct fwk_module_config config_scmi_system_power = {
+ .get_element_table = NULL,
+ .data = &((struct mod_scmi_system_power_config) {
+ .system_view = MOD_SCMI_SYSTEM_VIEW_FULL,
+ .system_suspend_state = MOD_SYSTEM_POWER_POWER_STATE_SLEEP0
+ }),
+};
diff --git a/product/sgm775/scp_ramfw/config_sds.c b/product/sgm775/scp_ramfw/config_sds.c
new file mode 100644
index 00000000..400e56fc
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_sds.c
@@ -0,0 +1,64 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_sds.h>
+#include <sgm775_mmap.h>
+#include <sgm775_sds.h>
+#include <clock_devices.h>
+
+static const uint32_t feature_flags = SGM775_SDS_FEATURE_FIRMWARE_MASK;
+static const uint32_t version_packed = FWK_BUILD_VERSION;
+
+const struct mod_sds_config sds_module_config = {
+ .region_base_address = TRUSTED_RAM_BASE,
+ .region_size = 3520,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_FCMCLK),
+};
+
+static const struct fwk_element sds_element_table[] = {
+ {
+ .name = "RAM Version",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_RAM_VERSION,
+ .size = SGM775_SDS_RAM_VERSION_SIZE,
+ .payload = &version_packed,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "Feature Availability",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_FEATURE_AVAILABILITY,
+ .size = sizeof(feature_flags),
+ .payload = &feature_flags,
+ .finalize = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *sds_get_element_table(fwk_id_t module_id)
+{
+ static_assert(BUILD_VERSION_MAJOR < UINT8_MAX, "Invalid version size");
+ static_assert(BUILD_VERSION_MINOR < UINT8_MAX, "Invalid version size");
+ static_assert(BUILD_VERSION_PATCH < UINT16_MAX, "Invalid version size");
+
+ return sds_element_table;
+}
+
+struct fwk_module_config config_sds = {
+ .get_element_table = sds_get_element_table,
+ .data = &sds_module_config,
+};
diff --git a/product/sgm775/scp_ramfw/config_sensor.c b/product/sgm775/scp_ramfw/config_sensor.c
new file mode 100644
index 00000000..439d3c22
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_sensor.c
@@ -0,0 +1,74 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_reg_sensor.h>
+#include <mod_sensor.h>
+#include <system_mmap.h>
+
+enum REG_SENSOR_DEVICES {
+ REG_SENSOR_DEV_SOC_TEMP,
+ REG_SENSOR_DEV_COUNT,
+};
+
+/*
+ * Register Sensor driver config
+ */
+static const struct fwk_element reg_sensor_element_table[] = {
+ [REG_SENSOR_DEV_SOC_TEMP] = {
+ .name = "Soc Temperature",
+ .data = &((struct mod_reg_sensor_dev_config) {
+ .reg = (uintptr_t)(SENSOR_SOC_TEMP),
+ }),
+ },
+ [REG_SENSOR_DEV_COUNT] = {},
+};
+
+static const struct fwk_element *get_reg_sensor_element_table(fwk_id_t id)
+{
+ return reg_sensor_element_table;
+}
+
+struct fwk_module_config config_reg_sensor = {
+ .get_element_table = get_reg_sensor_element_table,
+};
+
+
+/*
+ * Sensor module config
+ */
+static const struct mod_sensor_dev_config soctemp_config = {
+ .driver_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_REG_SENSOR,
+ REG_SENSOR_DEV_SOC_TEMP),
+ .info = &((struct mod_sensor_info) {
+ .type = MOD_SENSOR_TYPE_DEGREES_C,
+ .update_interval = 0,
+ .update_interval_multiplier = 0,
+ .unit_multiplier = 0,
+ }),
+};
+
+static const struct fwk_element sensor_element_table[] = {
+ [0] = {
+ .name = "Soc Temperature",
+ .data = &soctemp_config,
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *get_sensor_element_table(fwk_id_t module_id)
+{
+ return sensor_element_table;
+}
+
+struct fwk_module_config config_sensor = {
+ .get_element_table = get_sensor_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_smt.c b/product/sgm775/scp_ramfw/config_smt.c
new file mode 100644
index 00000000..ca506885
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_smt.c
@@ -0,0 +1,78 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_smt.h>
+#include <sgm775_core.h>
+#include <sgm775_mhu.h>
+#include <sgm775_scmi.h>
+#include <config_power_domain.h>
+#include <clock_devices.h>
+#include <software_mmap.h>
+
+static const struct fwk_element smt_element_table[] = {
+ [SGM775_SCMI_SERVICE_IDX_PSCI] = {
+ .name = "PSCI",
+ .data = &((struct mod_smt_channel_config) {
+ .type = MOD_SMT_CHANNEL_TYPE_SLAVE,
+ .policies = MOD_SMT_POLICY_INIT_MAILBOX | MOD_SMT_POLICY_SECURE,
+ .mailbox_address = (uintptr_t)SCMI_PAYLOAD_S_A2P_BASE,
+ .mailbox_size = SCMI_PAYLOAD_SIZE,
+ .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_MHU,
+ SGM775_MHU_DEVICE_IDX_S, 0),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MHU, 0),
+ })
+ },
+ [SGM775_SCMI_SERVICE_IDX_OSPM_0] = {
+ .name = "OSPM0",
+ .data = &((struct mod_smt_channel_config) {
+ .type = MOD_SMT_CHANNEL_TYPE_SLAVE,
+ .policies = MOD_SMT_POLICY_INIT_MAILBOX,
+ .mailbox_address = (uintptr_t)SCMI_PAYLOAD0_NS_A2P_BASE,
+ .mailbox_size = SCMI_PAYLOAD_SIZE,
+ .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_MHU,
+ SGM775_MHU_DEVICE_IDX_NS_L, 0),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MHU, 0),
+ })
+ },
+ [SGM775_SCMI_SERVICE_IDX_OSPM_1] = {
+ .name = "OSPM1",
+ .data = &((struct mod_smt_channel_config) {
+ .type = MOD_SMT_CHANNEL_TYPE_SLAVE,
+ .policies = MOD_SMT_POLICY_INIT_MAILBOX,
+ .mailbox_address = (uintptr_t)SCMI_PAYLOAD1_NS_A2P_BASE,
+ .mailbox_size = SCMI_PAYLOAD_SIZE,
+ .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_MHU,
+ SGM775_MHU_DEVICE_IDX_NS_H, 0),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_MHU, 0),
+ })
+ },
+ [SGM775_SCMI_SERVICE_IDX_COUNT] = {},
+};
+
+static const struct fwk_element *smt_get_element_table(fwk_id_t module_id)
+{
+ unsigned int idx;
+ struct mod_smt_channel_config *config;
+
+ for (idx = 0; idx < SGM775_SCMI_SERVICE_IDX_COUNT; idx++) {
+ config = (struct mod_smt_channel_config *)(smt_element_table[idx].data);
+ config->pd_source_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN,
+ CONFIG_POWER_DOMAIN_SYSTOP_CHILD_COUNT + sgm775_core_get_count());
+ }
+
+ return smt_element_table;
+}
+
+struct fwk_module_config config_smt = {
+ .get_element_table = smt_get_element_table,
+};
diff --git a/product/sgm775/scp_ramfw/config_system_pll.c b/product/sgm775/scp_ramfw/config_system_pll.c
new file mode 100644
index 00000000..a66543e3
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_system_pll.c
@@ -0,0 +1,118 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_system_pll.h>
+#include <sgm775_pik.h>
+#include <system_mmap.h>
+
+static const struct fwk_element system_pll_element_table[] = {
+ {
+ .name = "CPU_PLL_0",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_CLUS0_0,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS1,
+ .lock_flag_mask = PLL_STATUS1_CPUPLLLOCK(0, 0),
+ .initial_rate = 1330 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CPU_PLL_1",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_CLUS0_1,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS1,
+ .lock_flag_mask = PLL_STATUS1_CPUPLLLOCK(0, 1),
+ .initial_rate = 1750 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "GPU_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_GPU,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS0,
+ .lock_flag_mask = PLL_STATUS0_GPUPLLLOCK,
+ .initial_rate = 600 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "DPU_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_DISPLAY,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS0,
+ .lock_flag_mask = PLL_STATUS0_DISPLAYPLLLOCK,
+ .initial_rate = 260 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "VPU_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_VIDEO,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS0,
+ .lock_flag_mask = PLL_STATUS0_VIDEOPLLLOCK,
+ .initial_rate = 600 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "PIX0_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PIX0_CONTROL,
+ .status_reg = NULL,
+ .initial_rate = 594 * FWK_MHZ,
+ .min_rate = 12500 * FWK_KHZ,
+ .max_rate = 594 * FWK_MHZ,
+ .min_step = 250 * FWK_KHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "PIX1_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PIX1_CONTROL,
+ .status_reg = NULL,
+ .initial_rate = 594 * FWK_MHZ,
+ .min_rate = 12500 * FWK_KHZ,
+ .max_rate = 594 * FWK_MHZ,
+ .min_step = 250 * FWK_KHZ,
+ .defer_initialization = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *system_pll_get_element_table
+ (fwk_id_t module_id)
+{
+ return system_pll_element_table;
+}
+
+struct fwk_module_config config_system_pll = {
+ .get_element_table = system_pll_get_element_table,
+ .data = NULL,
+};
diff --git a/product/sgm775/scp_ramfw/config_system_power.c b/product/sgm775/scp_ramfw/config_system_power.c
new file mode 100644
index 00000000..37498086
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_system_power.c
@@ -0,0 +1,62 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <sgm775_irq.h>
+#include <config_ppu_v0.h>
+#include <mod_system_power.h>
+#include <mod_sgm775_system.h>
+
+/*
+ * The DPU/GPU/VPU PPUs in this list are there as a temporary workaround, until
+ * Linux supports handling these domains on its own.
+ */
+static const struct mod_system_power_ext_ppu_config ext_ppus[] = {
+ {
+ .ppu_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_DPU0TOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ },
+ {
+ .ppu_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_DPU1TOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ },
+ {
+ .ppu_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_GPUTOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ },
+ {
+ .ppu_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_VPUTOP),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ },
+};
+
+const struct fwk_module_config config_system_power = {
+ .data = &((struct mod_system_power_config) {
+ .soc_wakeup_irq = SOC_WAKEUP0_IRQ,
+ .ppu_sys0_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_SYS0),
+ .ppu_sys1_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PPU_V0,
+ PPU_V0_ELEMENT_IDX_SYS1),
+ .ppu_sys_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PPU_V0, 0),
+ .ext_ppus = ext_ppus,
+ .ext_ppus_count = FWK_ARRAY_SIZE(ext_ppus),
+ .driver_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SGM775_SYSTEM),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SGM775_SYSTEM,
+ MOD_SGM775_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER)
+ })
+};
diff --git a/product/sgm775/scp_ramfw/config_timer.c b/product/sgm775/scp_ramfw/config_timer.c
new file mode 100644
index 00000000..a4e1c8ea
--- /dev/null
+++ b/product/sgm775/scp_ramfw/config_timer.c
@@ -0,0 +1,71 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_gtimer.h>
+#include <mod_timer.h>
+#include <sgm775_mmap.h>
+#include <sgm775_irq.h>
+#include <clock_devices.h>
+#include <system_clock.h>
+
+/*
+ * Generic timer driver config
+ */
+static const struct fwk_element gtimer_dev_table[] = {
+ [0] = {
+ .name = "REFCLK",
+ .data = &((struct mod_gtimer_dev_config) {
+ .hw_timer = REFCLK_CNTBASE0_BASE,
+ .hw_counter = REFCLK_CNTCTL_BASE,
+ .control = REFCLK_CNTCONTROL_BASE,
+ .frequency = CLOCK_RATE_REFCLK,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_FCMCLK),
+ })
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *gtimer_get_dev_table(fwk_id_t module_id)
+{
+ return gtimer_dev_table;
+};
+
+struct fwk_module_config config_gtimer = {
+ .get_element_table = gtimer_get_dev_table,
+};
+
+/*
+ * Timer HAL config
+ */
+static const struct mod_timer_dev_config refclk_config = {
+ .id = FWK_ID_ELEMENT(FWK_MODULE_IDX_GTIMER, 0),
+ .timer_irq = TIMREFCLK_IRQ,
+};
+
+static const struct fwk_element timer_dev_table[] = {
+ [0] = {
+ .name = "REFCLK",
+ .data = &refclk_config,
+ .sub_element_count = 8, /* Number of alarms */
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *timer_get_dev_table(fwk_id_t module_id)
+{
+ return timer_dev_table;
+}
+
+struct fwk_module_config config_timer = {
+ .get_element_table = timer_get_dev_table,
+};
diff --git a/product/sgm775/scp_ramfw/firmware.mk b/product/sgm775/scp_ramfw/firmware.mk
new file mode 100644
index 00000000..ed810a8f
--- /dev/null
+++ b/product/sgm775/scp_ramfw/firmware.mk
@@ -0,0 +1,72 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_FIRMWARE_CPU := cortex-m3
+BS_FIRMWARE_HAS_MULTITHREADING := yes
+BS_FIRMWARE_HAS_NOTIFICATION := yes
+
+BS_FIRMWARE_MODULES := \
+ pl011 \
+ log \
+ gtimer \
+ timer \
+ ddr_phy500 \
+ dmc500 \
+ pik_clock \
+ clock \
+ system_pll \
+ css_clock \
+ ppu_v0 \
+ ppu_v1 \
+ system_power \
+ sgm775_system \
+ power_domain \
+ reg_sensor \
+ sensor \
+ dvfs \
+ psu \
+ mock_psu \
+ mhu \
+ smt \
+ scmi \
+ scmi_power_domain \
+ scmi_clock \
+ scmi_perf \
+ scmi_sensor \
+ scmi_system_power \
+ scmi_apcore \
+ sds
+
+BS_FIRMWARE_SOURCES := \
+ rtx_config.c \
+ sgm775_core.c \
+ config_log.c \
+ config_timer.c \
+ config_ddr_phy500.c \
+ config_dmc500.c \
+ config_sds.c \
+ config_pik_clock.c \
+ config_clock.c \
+ config_system_pll.c \
+ config_css_clock.c \
+ config_ppu_v0.c \
+ config_ppu_v1.c \
+ config_power_domain.c \
+ config_sensor.c \
+ config_dvfs.c \
+ config_psu.c \
+ config_mock_psu.c \
+ config_mhu.c \
+ config_smt.c \
+ config_scmi.c \
+ config_scmi_clock.c \
+ config_scmi_perf.c \
+ config_scmi_system_power.c \
+ config_scmi_apcore.c \
+ config_system_power.c
+
+include $(BS_DIR)/firmware.mk
diff --git a/product/sgm775/scp_ramfw/fmw_memory.ld.S b/product/sgm775/scp_ramfw/fmw_memory.ld.S
new file mode 100644
index 00000000..481a8c7a
--- /dev/null
+++ b/product/sgm775/scp_ramfw/fmw_memory.ld.S
@@ -0,0 +1,21 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FMW_MEMORY_LD_S
+#define FMW_MEMORY_LD_S
+
+#include <system_mmap_scp.h>
+
+#define FIRMWARE_MEM_MODE FWK_MEM_MODE_SINGLE_REGION
+
+/* RAM */
+#define FIRMWARE_MEM0_BASE SCP_RAM_BASE
+#define FIRMWARE_MEM0_SIZE SCP_RAM_SIZE
+
+#define FIRMWARE_STACK_SIZE (1 * 1024)
+
+#endif /* FMW_MEMORY_LD_S */
diff --git a/product/sgm775/scp_ramfw/rtx_config.c b/product/sgm775/scp_ramfw/rtx_config.c
new file mode 100644
index 00000000..c3c10bfe
--- /dev/null
+++ b/product/sgm775/scp_ramfw/rtx_config.c
@@ -0,0 +1,79 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <cmsis_compiler.h>
+#include <rtx_os.h>
+#include <system_clock.h>
+
+#include <rtx_lib.c>
+
+/*
+ * Required by RTX to configure the SysTick timer.
+ */
+uint32_t SystemCoreClock = CLOCK_RATE_REFCLK;
+
+/*
+ * Idle thread
+ */
+__NO_RETURN void osRtxIdleThread(void *argument)
+{
+ (void)argument;
+
+ while (true)
+ __WFI();
+}
+
+/*
+ * OS error handler
+ */
+uint32_t osRtxErrorNotify(uint32_t code, void *object_id)
+{
+ (void)object_id;
+
+ switch (code) {
+ case osRtxErrorStackUnderflow:
+ /*
+ * Stack underflow detected for thread
+ * thread_id=object_id
+ */
+ break;
+
+ case osRtxErrorISRQueueOverflow:
+ /*
+ * ISR Queue overflow detected when inserting object
+ * object_id
+ */
+ break;
+
+ case osRtxErrorTimerQueueOverflow:
+ /*
+ * User Timer Callback Queue overflow detected for timer
+ * timer_id=object_id
+ */
+ break;
+
+ case osRtxErrorClibSpace:
+ /*
+ * Standard C/C++ library libspace not available:
+ * increase OS_THREAD_LIBSPACE_NUM
+ */
+ break;
+
+ case osRtxErrorClibMutex:
+ /*
+ * Standard C/C++ library mutex initialization failed
+ */
+ break;
+
+ default:
+ break;
+ }
+
+ osRtxIdleThread(object_id);
+}
diff --git a/product/sgm775/scp_romfw/clock_devices.h b/product/sgm775/scp_romfw/clock_devices.h
new file mode 100644
index 00000000..31fe8b1c
--- /dev/null
+++ b/product/sgm775/scp_romfw/clock_devices.h
@@ -0,0 +1,28 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CLOCK_DEVICES_H
+#define CLOCK_DEVICES_H
+
+/*!
+ * \brief Clock device indexes.
+ */
+enum clock_dev_idx {
+ CLOCK_DEV_IDX_BIG,
+ CLOCK_DEV_IDX_LITTLE,
+ CLOCK_DEV_IDX_GPU,
+ CLOCK_DEV_IDX_SYS_ACLKNCI,
+ CLOCK_DEV_IDX_SYS_FCMCLK,
+ CLOCK_DEV_IDX_SYS_GICCLK,
+ CLOCK_DEV_IDX_SYS_PCLKSCP,
+ CLOCK_DEV_IDX_SYS_SYSPERCLK,
+ CLOCK_DEV_IDX_PLL_SWTCLKTCK,
+ CLOCK_DEV_IDX_PLL_SYSTEM,
+ CLOCK_DEV_IDX_COUNT
+};
+
+#endif /* CLOCK_DEVICES_H */
diff --git a/product/sgm775/scp_romfw/config_bootloader.c b/product/sgm775/scp_romfw/config_bootloader.c
new file mode 100644
index 00000000..39db37e2
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_bootloader.c
@@ -0,0 +1,26 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_bootloader.h>
+#include <system_mmap.h>
+#include <system_mmap_scp.h>
+#include <sgm775_sds.h>
+
+static const struct mod_bootloader_config bootloader_module_config = {
+ .source_base = TRUSTED_RAM_BASE,
+ .source_size = 256 * 1024,
+ .destination_base = SCP_RAM_BASE,
+ .destination_size = SCP_RAM_SIZE,
+ .sds_struct_id = SGM775_SDS_BOOTLOADER,
+};
+
+struct fwk_module_config config_bootloader = {
+ .data = &bootloader_module_config,
+};
diff --git a/product/sgm775/scp_romfw/config_clock.c b/product/sgm775/scp_romfw/config_clock.c
new file mode 100644
index 00000000..c6bd916e
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_clock.c
@@ -0,0 +1,126 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_css_clock.h>
+#include <mod_system_pll.h>
+#include <mod_msys_rom.h>
+#include <mod_pik_clock.h>
+#include <clock_devices.h>
+
+static const struct fwk_element clock_dev_desc_table[] = {
+ [CLOCK_DEV_IDX_BIG] = {
+ .name = "CPU_GROUP_BIG",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 0),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_LITTLE] = {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 1),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_GPU] = {
+ .name = "GPU",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CSS_CLOCK, 2),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CSS_CLOCK,
+ MOD_CSS_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_SYS_ACLKNCI] = {
+ .name = "SYS_ACLKNCI",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 0),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_SYS_FCMCLK] = {
+ .name = "SYS_FCMCLK",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 1),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_SYS_GICCLK] = {
+ .name = "SYS_GICCLK",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 2),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_SYS_PCLKSCP] = {
+ .name = "SYS_PCLKSCP",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 3),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_SYS_SYSPERCLK] = {
+ .name = "SYS_SYSPERCLK",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PIK_CLOCK, 4),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CLOCK),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_PLL_SWTCLKTCK] = {
+ .name = "PLL_SWTCLKTCK",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 3),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_PLL_SYSTEM] = {
+ .name = "PLL_SYSTEM",
+ .data = &((struct mod_clock_dev_config) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 4),
+ .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ }),
+ },
+ [CLOCK_DEV_IDX_COUNT] = { }, /* Termination description. */
+};
+
+static const struct fwk_element *clock_get_dev_desc_table(fwk_id_t module_id)
+{
+ return clock_dev_desc_table;
+}
+
+const struct fwk_module_config config_clock = {
+ .get_element_table = clock_get_dev_desc_table,
+ .data = &((struct mod_clock_config) {
+ .pd_transition_notification_id = FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_MSYS_ROM,
+ MOD_MSYS_ROM_NOTIFICATION_IDX_POWER_SYSTOP),
+ .pd_pre_transition_notification_id = FWK_ID_NONE_INIT,
+ }),
+};
diff --git a/product/sgm775/scp_romfw/config_css_clock.c b/product/sgm775/scp_romfw/config_css_clock.c
new file mode 100644
index 00000000..763cf874
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_css_clock.c
@@ -0,0 +1,134 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_css_clock.h>
+#include <mod_system_pll.h>
+#include <mod_pik_clock.h>
+
+static const struct mod_css_clock_rate rate_table_cpu_group_big[] = {
+ {
+ .rate = 1750 * FWK_MHZ,
+ .pll_rate = 1750 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+};
+
+static const struct mod_css_clock_rate rate_table_cpu_group_little[] = {
+ {
+ .rate = 1330 * FWK_MHZ,
+ .pll_rate = 1330 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ .clock_mod_numerator = 1,
+ .clock_mod_denominator = 1,
+ },
+};
+
+static const struct mod_css_clock_rate rate_table_gpu[] = {
+ {
+ .rate = 600 * FWK_MHZ,
+ .pll_rate = 600 * FWK_MHZ,
+ .clock_source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .clock_div_type = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .clock_div = 1,
+ },
+};
+
+static const fwk_id_t member_table_cpu_group_big[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 9),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 10),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 11),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 12),
+};
+
+static const fwk_id_t member_table_cpu_group_little[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 5),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 6),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 7),
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 8),
+};
+
+static const fwk_id_t member_table_gpu[] = {
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_PIK_CLOCK, 13),
+};
+
+static const struct fwk_element css_clock_element_table[] = {
+ {
+ .name = "CPU_GROUP_BIG",
+ .data = &((struct mod_css_clock_dev_config) {
+ .rate_table = rate_table_cpu_group_big,
+ .rate_count = sizeof(rate_table_cpu_group_big) /
+ sizeof(struct mod_css_clock_rate),
+ .clock_switching_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 1),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_cpu_group_big,
+ .member_count = FWK_ARRAY_SIZE(member_table_cpu_group_big),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 1750 * FWK_MHZ,
+ .modulation_supported = true,
+ }),
+ },
+ {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &((struct mod_css_clock_dev_config) {
+ .rate_table = rate_table_cpu_group_little,
+ .rate_count = sizeof(rate_table_cpu_group_little) /
+ sizeof(struct mod_css_clock_rate),
+ .clock_switching_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 0),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_cpu_group_little,
+ .member_count = FWK_ARRAY_SIZE(member_table_cpu_group_little),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 1330 * FWK_MHZ,
+ .modulation_supported = true,
+ }),
+ },
+ {
+ .name = "GPU",
+ .data = &((struct mod_css_clock_dev_config) {
+ .rate_table = rate_table_gpu,
+ .rate_count = sizeof(rate_table_gpu) /
+ sizeof(struct mod_css_clock_rate),
+ .clock_switching_source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_SYSREFCLK,
+ .pll_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SYSTEM_PLL, 2),
+ .pll_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_PLL,
+ MOD_SYSTEM_PLL_API_TYPE_DEFAULT),
+ .member_table = member_table_gpu,
+ .member_count = FWK_ARRAY_SIZE(member_table_gpu),
+ .member_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PIK_CLOCK,
+ MOD_PIK_CLOCK_API_TYPE_CSS),
+ .initial_rate = 600 * FWK_MHZ,
+ .modulation_supported = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *css_clock_get_element_table
+ (fwk_id_t module_id)
+{
+ return css_clock_element_table;
+}
+
+struct fwk_module_config config_css_clock = {
+ .get_element_table = css_clock_get_element_table,
+};
diff --git a/product/sgm775/scp_romfw/config_log.c b/product/sgm775/scp_romfw/config_log.c
new file mode 100644
index 00000000..9fe5259b
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_log.c
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_banner.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_log.h>
+#include <mod_pl011.h>
+#include <system_mmap.h>
+#include <clock_devices.h>
+
+/*
+ * PL011 module
+ */
+static const struct fwk_element pl011_element_desc_table[] = {
+ [0] = {
+ .name = "board-uart1",
+ .data = &((struct mod_pl011_device_config) {
+ .reg_base = BOARD_UART1_BASE,
+ .baud_rate_bps = 115200,
+ .clock_rate_hz = 24 * FWK_MHZ,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_SYS_FCMCLK),
+ }),
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *get_pl011_table(fwk_id_t module_id)
+{
+ return pl011_element_desc_table;
+}
+
+struct fwk_module_config config_pl011 = {
+ .get_element_table = get_pl011_table,
+};
+
+/*
+ * Log module
+ */
+static const struct mod_log_config log_data = {
+ .device_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_PL011, 0),
+ .api_id = FWK_ID_API(FWK_MODULE_IDX_PL011, 0),
+ .log_groups = MOD_LOG_GROUP_ERROR |
+ MOD_LOG_GROUP_INFO |
+ MOD_LOG_GROUP_WARNING |
+ MOD_LOG_GROUP_DEBUG,
+ .banner = FWK_BANNER_SCP
+ FWK_BANNER_ROM_FIRMWARE
+ BUILD_VERSION_DESCRIBE_STRING "\n",
+};
+
+struct fwk_module_config config_log = {
+ .get_element_table = NULL,
+ .data = &log_data,
+};
diff --git a/product/sgm775/scp_romfw/config_msys_rom.c b/product/sgm775/scp_romfw/config_msys_rom.c
new file mode 100644
index 00000000..12bb5b35
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_msys_rom.c
@@ -0,0 +1,24 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_msys_rom.h>
+#include <software_mmap.h>
+#include <sgm775_mmap_scp.h>
+#include <sgm775_core.h>
+
+const struct fwk_module_config config_msys_rom = {
+ .data = &((struct msys_rom_config) {
+ .ap_context_base = AP_CONTEXT_BASE,
+ .ap_context_size = AP_CONTEXT_SIZE,
+ .ramfw_base = SCP_RAM_BASE,
+ .id_primary_cluster = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PPU_V1, 0),
+ .id_primary_core = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PPU_V1, 1),
+ })
+};
diff --git a/product/sgm775/scp_romfw/config_pik_clock.c b/product/sgm775/scp_romfw/config_pik_clock.c
new file mode 100644
index 00000000..8059865f
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_pik_clock.c
@@ -0,0 +1,306 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_pik_clock.h>
+#include <sgm775_pik.h>
+#include <system_clock.h>
+
+/*
+ * Rate lookup tables.
+ */
+
+static const struct mod_pik_clock_rate rate_table_sys_fcmclk[] = {
+ {
+ .rate = CLOCK_RATE_SYSPLLCLK,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = 1,
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_sys_aclknci[] = {
+ {
+ .rate = 666 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = DIV_FROM_CLOCK(CLOCK_RATE_SYSPLLCLK, 666 * FWK_MHZ),
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_sys_gicclk[] = {
+ {
+ .rate = 666 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = DIV_FROM_CLOCK(CLOCK_RATE_SYSPLLCLK, 666 * FWK_MHZ),
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_sys_pclkscp[] = {
+ {
+ .rate = 125 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = DIV_FROM_CLOCK(CLOCK_RATE_SYSPLLCLK, 125 * FWK_MHZ),
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_sys_sysperclk[] = {
+ {
+ .rate = 125 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_SYSPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_SYS,
+ .divider = DIV_FROM_CLOCK(CLOCK_RATE_SYSPLLCLK, 125 * FWK_MHZ),
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_cpu_a55[] = {
+ {
+ .rate = 1330 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_cpu_a75[] = {
+ {
+ .rate = 1750 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_CLUSCLK_SOURCE_PLL1,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static const struct mod_pik_clock_rate rate_table_gpu[] = {
+ {
+ .rate = 600 * FWK_MHZ,
+ .source = MOD_PIK_CLOCK_MSCLOCK_SOURCE_PRIVPLLCLK,
+ .divider_reg = MOD_PIK_CLOCK_MSCLOCK_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via GPU PLL */
+ },
+};
+
+static const struct fwk_element pik_clock_element_table[] = {
+ /*
+ * System Clocks
+ */
+ {
+ .name = "SYS_ACLKNCI",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->ACLKNCI_CTRL,
+ .divsys_reg = &PIK_SYSTEM->ACLKNCI_DIV1,
+ .rate_table = rate_table_sys_aclknci,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_aclknci),
+ .initial_rate = 666 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "SYS_FCMCLK",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->FCMCLK_CTRL,
+ .divsys_reg = &PIK_SYSTEM->FCMCLK_DIV1,
+ .rate_table = rate_table_sys_fcmclk,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_fcmclk),
+ .initial_rate = CLOCK_RATE_SYSPLLCLK,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "SYS_GICCLK",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->GICCLK_CTRL,
+ .divsys_reg = &PIK_SYSTEM->GICCLK_DIV1,
+ .rate_table = rate_table_sys_gicclk,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_gicclk),
+ .initial_rate = 666 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "SYS_PCLKSCP",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->PCLKSCP_CTRL,
+ .divsys_reg = &PIK_SYSTEM->PCLKSCP_DIV1,
+ .rate_table = rate_table_sys_pclkscp,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_pclkscp),
+ .initial_rate = 125 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "SYS_SYSPERCLK",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = false,
+ .control_reg = &PIK_SYSTEM->SYSPERCLK_CTRL,
+ .divsys_reg = &PIK_SYSTEM->SYSPERCLK_DIV1,
+ .rate_table = rate_table_sys_sysperclk,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_sys_sysperclk),
+ .initial_rate = 125 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ /*
+ * A55 CPUS
+ */
+ {
+ .name = "CLUS0_CPU0",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[0].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU1",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[1].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU2",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[2].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU3",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[3].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a55,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a55),
+ .initial_rate = 1330 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ /*
+ * A75 CPUS
+ */
+ {
+ .name = "CLUS0_CPU4",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[4].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU5",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[5].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU6",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[6].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ {
+ .name = "CLUS0_CPU7",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_CLUSTER,
+ .is_group_member = true,
+ .control_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_CTRL,
+ .divext_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_DIV,
+ .modulator_reg = &PIK_CLUS0->AP_CLK_CTRL[7].CORECLK_MOD,
+ .rate_table = rate_table_cpu_a75,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a75),
+ .initial_rate = 1750 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ /*
+ * GPU
+ */
+ {
+ .name = "GPU",
+ .data = &((struct mod_pik_clock_dev_config) {
+ .type = MOD_PIK_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .control_reg = &PIK_GPU->GPUCLK_CTRL,
+ .divsys_reg = &PIK_GPU->GPUCLK_DIV1,
+ .divext_reg = &PIK_GPU->GPUCLK_DIV2,
+ .rate_table = rate_table_gpu,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_gpu),
+ .initial_rate = 600 * FWK_MHZ,
+ .defer_initialization = true,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *pik_clock_get_element_table
+ (fwk_id_t module_id)
+{
+ return pik_clock_element_table;
+}
+
+struct fwk_module_config config_pik_clock = {
+ .get_element_table = pik_clock_get_element_table,
+};
diff --git a/product/sgm775/scp_romfw/config_ppu_v0.c b/product/sgm775/scp_romfw/config_ppu_v0.c
new file mode 100644
index 00000000..59051a41
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_ppu_v0.c
@@ -0,0 +1,48 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_module.h>
+#include <mod_ppu_v0.h>
+#include <sgm775_irq.h>
+#include <sgm775_mmap.h>
+
+static struct fwk_element sgm775_ppu_v0_element_table[] = {
+ {
+ .name = "SYS0",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_SYSTEM,
+ .ppu.reg_base = PPU_SYS0_BASE,
+ .ppu.irq = PPU_SYS0_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ {
+ .name = "SYS1",
+ .data = &((struct mod_ppu_v0_pd_config) {
+ .pd_type = MOD_PD_TYPE_SYSTEM,
+ .ppu.reg_base = PPU_SYS1_BASE,
+ .ppu.irq = PPU_SYS1_IRQ,
+ .default_power_on = true,
+ }),
+ },
+ {}, /* Termination entry */
+};
+
+
+static const struct fwk_element *sgm775_ppu_v0_get_element_table(
+ fwk_id_t module_id)
+{
+ return sgm775_ppu_v0_element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_ppu_v0 = {
+ .get_element_table = sgm775_ppu_v0_get_element_table,
+};
diff --git a/product/sgm775/scp_romfw/config_ppu_v1.c b/product/sgm775/scp_romfw/config_ppu_v1.c
new file mode 100644
index 00000000..c1e1e954
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_ppu_v1.c
@@ -0,0 +1,59 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <fwk_element.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_msys_rom.h>
+#include <mod_ppu_v1.h>
+#include <sgm775_irq.h>
+#include <sgm775_mmap.h>
+
+static struct fwk_element sgm775_ppu_v1_element_table[] = {
+ {
+ .name = "CLUS0",
+ .data = &((struct mod_ppu_v1_pd_config) {
+ .pd_type = MOD_PD_TYPE_CLUSTER,
+ .ppu.reg_base = PPU_CLUS0_BASE,
+ .ppu.irq = FWK_INTERRUPT_NONE,
+ .observer_id = FWK_ID_NONE_INIT,
+ }),
+ },
+ {
+ .name = "CORE0",
+ .data = &((struct mod_ppu_v1_pd_config) {
+ .pd_type = MOD_PD_TYPE_CORE,
+ .ppu.reg_base = PPU_CLUS0CORE0_BASE,
+ .ppu.irq = FWK_INTERRUPT_NONE,
+ .cluster_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PPU_V1, 0),
+ .observer_id = FWK_ID_NONE_INIT,
+ }),
+ },
+ {}, /* Termination entry */
+};
+
+static const struct fwk_element *sgm775_ppu_v1_get_element_table(
+ fwk_id_t module_id)
+{
+ return sgm775_ppu_v1_element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_ppu_v1 = {
+ .get_element_table = sgm775_ppu_v1_get_element_table,
+ .data = &(struct mod_ppu_v1_config) {
+ .pd_notification_id = FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_MSYS_ROM,
+ MOD_MSYS_ROM_NOTIFICATION_IDX_POWER_SYSTOP),
+ .pd_source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_MSYS_ROM),
+ },
+};
diff --git a/product/sgm775/scp_romfw/config_sds.c b/product/sgm775/scp_romfw/config_sds.c
new file mode 100644
index 00000000..fb8f5e30
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_sds.c
@@ -0,0 +1,122 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_sds.h>
+#include <sgm775_mmap.h>
+#include <sgm775_sds.h>
+#include <sgm775_ssc.h>
+#include <sgm775_pik.h>
+#include <system_mmap.h>
+#include <clock_devices.h>
+
+static const struct mod_sds_config sds_module_config = {
+ .region_base_address = TRUSTED_RAM_BASE,
+ .region_size = 3520,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_SYS_FCMCLK),
+};
+static const uint32_t version_packed = FWK_BUILD_VERSION;
+static struct sgm775_sds_platid platid;
+
+static const struct fwk_element sds_element_table[] = {
+ {
+ .name = "CPU Info",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_CPU_INFO,
+ .size = SGM775_SDS_CPU_INFO_SIZE,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "ROM Version",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_ROM_VERSION,
+ .size = SGM775_SDS_ROM_VERSION_SIZE,
+ .payload = &version_packed,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "Platform ID",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_PLATFORM_ID,
+ .size = SGM775_SDS_PLATFORM_ID_SIZE,
+ .payload = &platid,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "Reset Syndrome",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_RESET_SYNDROME,
+ .size = SGM775_SDS_RESET_SYNDROME_SIZE,
+ .payload = (void *)(&PIK_SCP->RESET_SYNDROME),
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "Bootloader",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_BOOTLOADER,
+ .size = SGM775_SDS_BOOTLOADER_SIZE,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "Features",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_FEATURE_AVAILABILITY,
+ .size = SGM775_SDS_FEATURE_AVAILABILITY_SIZE,
+ .finalize = true,
+ }),
+ },
+#ifdef MODE_DEBUG
+ {
+ .name = "Boot Counters",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_CPU_BOOTCTR,
+ .size = SGM775_SDS_CPU_BOOTCTR_SIZE,
+ .finalize = true,
+ }),
+ },
+ {
+ .name = "CPU Flags",
+ .data = &((struct mod_sds_structure_desc) {
+ .id = SGM775_SDS_CPU_FLAGS,
+ .size = SGM775_SDS_CPU_FLAGS_SIZE,
+ .finalize = true,
+ }),
+ },
+#endif
+ {}, /* Termination description. */
+};
+
+static const struct fwk_element *sds_get_element_table(fwk_id_t module_id)
+{
+ struct ssc_reg *ssc_regs = ((struct ssc_reg *)(SSC_BASE));
+
+ static_assert(BUILD_VERSION_MAJOR < UINT8_MAX, "Invalid version size");
+ static_assert(BUILD_VERSION_MINOR < UINT8_MAX, "Invalid version size");
+ static_assert(BUILD_VERSION_PATCH < UINT16_MAX, "Invalid version size");
+
+ platid.platform_identifier = ssc_regs->SSC_VERSION;
+ platid.platform_type_identifier = *((uint32_t*)PLATFORM_ID);
+
+ return sds_element_table;
+}
+
+struct fwk_module_config config_sds = {
+ .get_element_table = sds_get_element_table,
+ .data = &sds_module_config,
+};
diff --git a/product/sgm775/scp_romfw/config_system_pll.c b/product/sgm775/scp_romfw/config_system_pll.c
new file mode 100644
index 00000000..a709b140
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_system_pll.c
@@ -0,0 +1,91 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <mod_system_pll.h>
+#include <sgm775_pik.h>
+#include <system_mmap.h>
+
+static const struct fwk_element system_pll_element_table[] = {
+ {
+ .name = "CPU_PLL_0",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_CLUS0_0,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS1,
+ .lock_flag_mask = PLL_STATUS1_CPUPLLLOCK(0, 0),
+ .initial_rate = 1330 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "CPU_PLL_1",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_CLUS0_1,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS1,
+ .lock_flag_mask = PLL_STATUS1_CPUPLLLOCK(0, 1),
+ .initial_rate = 1750 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "GPU_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_GPU,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS0,
+ .lock_flag_mask = PLL_STATUS0_GPUPLLLOCK,
+ .initial_rate = 100 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "SWTCLKTCK_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)SWCLKTCK_CONTROL,
+ .status_reg = NULL,
+ .initial_rate = 100 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "SYS_PLL",
+ .data = &((struct mod_system_pll_dev_config) {
+ .control_reg = (void *)PLL_SYSTEM,
+ .status_reg = (void *)&PIK_SCP->PLL_STATUS0,
+ .lock_flag_mask = PLL_STATUS0_SYSPLLLOCK,
+ .initial_rate = 2000 * FWK_MHZ,
+ .min_rate = MOD_SYSTEM_PLL_MIN_RATE,
+ .max_rate = MOD_SYSTEM_PLL_MAX_RATE,
+ .min_step = MOD_SYSTEM_PLL_MIN_INTERVAL,
+ .defer_initialization = false,
+ }),
+ },
+ { }, /* Termination description. */
+};
+
+static const struct fwk_element *system_pll_get_element_table
+ (fwk_id_t module_id)
+{
+ return system_pll_element_table;
+}
+
+struct fwk_module_config config_system_pll = {
+ .get_element_table = system_pll_get_element_table,
+};
diff --git a/product/sgm775/scp_romfw/config_timer.c b/product/sgm775/scp_romfw/config_timer.c
new file mode 100644
index 00000000..e58bbf1a
--- /dev/null
+++ b/product/sgm775/scp_romfw/config_timer.c
@@ -0,0 +1,68 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <fwk_id.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <mod_clock.h>
+#include <mod_gtimer.h>
+#include <mod_timer.h>
+#include <sgm775_mmap.h>
+#include <clock_devices.h>
+#include <system_clock.h>
+
+/*
+ * Generic timer driver config
+ */
+static const struct fwk_element gtimer_dev_table[] = {
+ [0] = {
+ .name = "REFCLK",
+ .data = &((struct mod_gtimer_dev_config) {
+ .hw_timer = REFCLK_CNTBASE0_BASE,
+ .hw_counter = REFCLK_CNTCTL_BASE,
+ .control = REFCLK_CNTCONTROL_BASE,
+ .frequency = CLOCK_RATE_REFCLK,
+ .clock_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_CLOCK,
+ CLOCK_DEV_IDX_SYS_FCMCLK),
+ })
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *gtimer_get_dev_table(fwk_id_t module_id)
+{
+ return gtimer_dev_table;
+};
+
+struct fwk_module_config config_gtimer = {
+ .get_element_table = gtimer_get_dev_table,
+};
+
+/*
+ * Timer HAL config
+ */
+static const struct mod_timer_dev_config refclk_config = {
+ .id = FWK_ID_ELEMENT(FWK_MODULE_IDX_GTIMER, 0),
+};
+
+static const struct fwk_element timer_dev_table[] = {
+ [0] = {
+ .name = "REFCLK",
+ .data = &refclk_config,
+ },
+ [1] = {},
+};
+
+static const struct fwk_element *timer_get_dev_table(fwk_id_t module_id)
+{
+ return timer_dev_table;
+}
+
+struct fwk_module_config config_timer = {
+ .get_element_table = timer_get_dev_table,
+};
diff --git a/product/sgm775/scp_romfw/firmware.mk b/product/sgm775/scp_romfw/firmware.mk
new file mode 100644
index 00000000..1250577e
--- /dev/null
+++ b/product/sgm775/scp_romfw/firmware.mk
@@ -0,0 +1,38 @@
+#
+# Arm SCP/MCP Software
+# Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_FIRMWARE_CPU := cortex-m3
+BS_FIRMWARE_HAS_MULTITHREADING := no
+BS_FIRMWARE_HAS_NOTIFICATION := yes
+BS_FIRMWARE_MODULE_HEADERS_ONLY := timer \
+ power_domain
+BS_FIRMWARE_MODULES := ppu_v0 \
+ ppu_v1 \
+ pl011 \
+ log \
+ gtimer \
+ msys_rom \
+ bootloader \
+ system_pll \
+ css_clock \
+ pik_clock \
+ clock \
+ sds
+BS_FIRMWARE_SOURCES := config_log.c \
+ config_timer.c \
+ config_msys_rom.c \
+ config_sds.c \
+ config_bootloader.c \
+ config_system_pll.c \
+ config_css_clock.c \
+ config_pik_clock.c \
+ config_clock.c \
+ sgm775_core.c \
+ config_ppu_v0.c \
+ config_ppu_v1.c
+
+include $(BS_DIR)/firmware.mk
diff --git a/product/sgm775/scp_romfw/fmw_memory.ld.S b/product/sgm775/scp_romfw/fmw_memory.ld.S
new file mode 100644
index 00000000..23b89ab4
--- /dev/null
+++ b/product/sgm775/scp_romfw/fmw_memory.ld.S
@@ -0,0 +1,36 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * ROM firmware memory layout for the linker script.
+ */
+
+#ifndef FMW_MEMORY_LD_S
+#define FMW_MEMORY_LD_S
+
+#include <system_mmap_scp.h>
+
+#define FIRMWARE_MEM_MODE FWK_MEM_MODE_DUAL_REGION_RELOCATION
+
+/*
+ * ROM memory
+ */
+#define FIRMWARE_MEM0_SIZE SCP_ROM_SIZE
+#define FIRMWARE_MEM0_BASE SCP_ROM_BASE
+
+/*
+ * RAM memory (16 KiB block at the top of the RAM)
+ *
+ * Note: The sections that must go into the RAM memory (i.e. stack, heap, bss
+ * and data) are placed at the end of the RAM memory to avoid being overwritten
+ * by the bootloader when loading the RAM firmware image.
+ */
+#define FIRMWARE_MEM1_SIZE (16 * 1024)
+#define FIRMWARE_MEM1_BASE (SCP_RAM_BASE + SCP_RAM_SIZE - FIRMWARE_MEM1_SIZE)
+
+#define FIRMWARE_STACK_SIZE (1 * 1024)
+
+#endif /* FMW_MEMORY_LD_S */
diff --git a/product/sgm775/src/sgm775_core.c b/product/sgm775/src/sgm775_core.c
new file mode 100644
index 00000000..750f18a0
--- /dev/null
+++ b/product/sgm775/src/sgm775_core.c
@@ -0,0 +1,14 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sgm775_pik.h>
+#include <sgm775_core.h>
+
+unsigned int sgm775_core_get_count(void)
+{
+ return (PIK_CLUS0->PIK_CONFIG & PIK_CPU_V8_2_PIK_CONFIG_NO_OF_PPU) - 1;
+}
diff --git a/tools/ci.py b/tools/ci.py
index 415d0b30..c0e276b1 100755
--- a/tools/ci.py
+++ b/tools/ci.py
@@ -69,6 +69,12 @@ def main():
result = subprocess.call(cmd, shell=True)
results.append(('Product host build', result))
+ banner('Test building sgm775 product')
+
+ cmd = 'CROSS_COMPILE=arm-none-eabi- PRODUCT=sgm775 make'
+ result = subprocess.call(cmd, shell=True)
+ results.append(('Product sgm775 build', result))
+
banner('Tests summary')
total_success = 0