summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c')
-rw-r--r--DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c b/DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c
new file mode 100644
index 0000000000..da5bc18957
--- /dev/null
+++ b/DynamicTablesPkg/Library/DynamicTablesScmiInfoLib/DynamicTablesScmiInfoLib.c
@@ -0,0 +1,297 @@
+/** @file
+ Arm SCMI Info Library.
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.<BR>
+
+ Arm Functional Fixed Hardware Specification:
+ - https://developer.arm.com/documentation/den0048/latest/
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/AcpiLib.h>
+#include <Library/DynamicTablesScmiInfoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/ArmScmi.h>
+#include <Protocol/ArmScmiPerformanceProtocol.h>
+
+/** Arm FFH registers
+
+ Cf. Arm Functional Fixed Hardware Specification
+ s3.2 Performance management and Collaborative Processor Performance Control
+*/
+#define ARM_FFH_DELIVERED_PERF_COUNTER_REGISTER 0x0
+#define ARM_FFH_REFERENCE_PERF_COUNTER_REGISTER 0x1
+
+/// Arm SCMI performance protocol.
+STATIC SCMI_PERFORMANCE_PROTOCOL *ScmiPerfProtocol;
+
+/** Arm SCMI Info Library constructor.
+
+ @param ImageHandle Image of the loaded driver.
+ @param SystemTable Pointer to the System Table.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_DEVICE_ERROR Device error.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not Found
+ @retval EFI_TIMEOUT Timeout.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+DynamicTablesScmiInfoLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Version;
+
+ Status = gBS->LocateProtocol (
+ &gArmScmiPerformanceProtocolGuid,
+ NULL,
+ (VOID **)&ScmiPerfProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = ScmiPerfProtocol->GetVersion (ScmiPerfProtocol, &Version);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // FastChannels were added in SCMI v2.0 spec.
+ if (Version < PERFORMANCE_PROTOCOL_VERSION_V2) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "DynamicTablesScmiInfoLib requires SCMI version > 2.0\n"
+ ));
+ return EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+/** Get the OPPs/performance states of a power domain.
+
+ This function is a wrapper around the SCMI PERFORMANCE_DESCRIBE_LEVELS
+ command. The list of discrete performance states is returned in a buffer
+ that must be freed by the caller.
+
+ @param[in] DomainId Identifier for the performance domain.
+ @param[out] LevelArray If success, pointer to the list of list of
+ performance state. This memory must be freed by
+ the caller.
+ @param[out] LevelArrayCount If success, contains the number of states in
+ LevelArray.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_DEVICE_ERROR Device error.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_TIMEOUT Time out.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DynamicTablesScmiInfoDescribeLevels (
+ IN UINT32 DomainId,
+ OUT SCMI_PERFORMANCE_LEVEL **LevelArray,
+ OUT UINT32 *LevelArrayCount
+ )
+{
+ EFI_STATUS Status;
+ SCMI_PERFORMANCE_LEVEL *Array;
+ UINT32 Count;
+ UINT32 Size;
+
+ if ((ScmiPerfProtocol == NULL) ||
+ (LevelArray == NULL) ||
+ (LevelArrayCount == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // First call to get the number of levels.
+ Size = 0;
+ Status = ScmiPerfProtocol->DescribeLevels (
+ ScmiPerfProtocol,
+ DomainId,
+ &Count,
+ &Size,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ // EFI_SUCCESS is not a valid option.
+ if (Status == EFI_SUCCESS) {
+ return EFI_INVALID_PARAMETER;
+ } else {
+ return Status;
+ }
+ }
+
+ Array = AllocateZeroPool (Size);
+ if (Array == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Second call to get the descriptions of the levels.
+ Status = ScmiPerfProtocol->DescribeLevels (
+ ScmiPerfProtocol,
+ DomainId,
+ &Count,
+ &Size,
+ Array
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *LevelArray = Array;
+ *LevelArrayCount = Count;
+
+ return Status;
+}
+
+/** Populate a AML_CPC_INFO object based on SCMI information.
+
+ @param[in] DomainId Identifier for the performance domain.
+ @param[out] CpcInfo If success, this structure was populated from
+ information queried to the SCP.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_DEVICE_ERROR Device error.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_TIMEOUT Time out.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+DynamicTablesScmiInfoGetFastChannel (
+ IN UINT32 DomainId,
+ OUT AML_CPC_INFO *CpcInfo
+ )
+{
+ EFI_STATUS Status;
+ SCMI_PERFORMANCE_FASTCHANNEL FcLevelGet;
+ SCMI_PERFORMANCE_FASTCHANNEL FcLimitsSet;
+ SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES DomainAttributes;
+
+ SCMI_PERFORMANCE_LEVEL *LevelArray;
+ UINT32 LevelCount;
+
+ UINT64 FcLevelGetAddr;
+ UINT64 FcLimitsMaxSetAddr;
+ UINT64 FcLimitsMinSetAddr;
+
+ if ((ScmiPerfProtocol == NULL) ||
+ (CpcInfo == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = ScmiPerfProtocol->DescribeFastchannel (
+ ScmiPerfProtocol,
+ DomainId,
+ ScmiMessageIdPerformanceLevelSet,
+ &FcLevelGet
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = ScmiPerfProtocol->DescribeFastchannel (
+ ScmiPerfProtocol,
+ DomainId,
+ ScmiMessageIdPerformanceLimitsSet,
+ &FcLimitsSet
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = ScmiPerfProtocol->GetDomainAttributes (
+ ScmiPerfProtocol,
+ DomainId,
+ &DomainAttributes
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = DynamicTablesScmiInfoDescribeLevels (DomainId, &LevelArray, &LevelCount);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ /* Do some safety checks.
+ Only support FastChannels (and not doorbells) as this is
+ the only mechanism supported by SCP.
+ FcLimits[Get|Set] require 2 UINT32 values (max, then min) and
+ FcLimits[Get|Set] require 1 UINT32 value (level).
+ */
+ if ((FcLevelGet.ChanSize != sizeof (UINT32)) ||
+ ((FcLevelGet.Attributes & SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ==
+ SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ||
+ (FcLimitsSet.ChanSize != 2 * sizeof (UINT32)) ||
+ ((FcLimitsSet.Attributes & SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ==
+ SCMI_PERF_FC_ATTRIB_HAS_DOORBELL))
+ {
+ Status = EFI_INVALID_PARAMETER;
+ goto exit_handler;
+ }
+
+ FcLevelGetAddr = ((UINT64)FcLevelGet.ChanAddrHigh << 32) |
+ FcLevelGet.ChanAddrLow;
+ FcLimitsMaxSetAddr = ((UINT64)FcLimitsSet.ChanAddrHigh << 32) |
+ FcLimitsSet.ChanAddrLow;
+ FcLimitsMinSetAddr = FcLimitsMaxSetAddr + 0x4;
+
+ CpcInfo->Revision = EFI_ACPI_6_5_AML_CPC_REVISION;
+ CpcInfo->HighestPerformanceInteger = LevelArray[LevelCount - 1].Level;
+ CpcInfo->NominalPerformanceInteger = DomainAttributes.SustainedPerfLevel;
+ CpcInfo->LowestNonlinearPerformanceInteger = LevelArray[0].Level;
+ CpcInfo->LowestPerformanceInteger = LevelArray[0].Level;
+
+ CpcInfo->DesiredPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
+ CpcInfo->DesiredPerformanceRegister.RegisterBitWidth = 32;
+ CpcInfo->DesiredPerformanceRegister.RegisterBitOffset = 0;
+ CpcInfo->DesiredPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
+ CpcInfo->DesiredPerformanceRegister.Address = FcLevelGetAddr;
+
+ CpcInfo->MinimumPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
+ CpcInfo->MinimumPerformanceRegister.RegisterBitWidth = 32;
+ CpcInfo->MinimumPerformanceRegister.RegisterBitOffset = 0;
+ CpcInfo->MinimumPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
+ CpcInfo->MinimumPerformanceRegister.Address = FcLimitsMinSetAddr;
+
+ CpcInfo->MaximumPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
+ CpcInfo->MaximumPerformanceRegister.RegisterBitWidth = 32;
+ CpcInfo->MaximumPerformanceRegister.RegisterBitOffset = 0;
+ CpcInfo->MaximumPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
+ CpcInfo->MaximumPerformanceRegister.Address = FcLimitsMaxSetAddr;
+
+ CpcInfo->ReferencePerformanceCounterRegister.AddressSpaceId = EFI_ACPI_6_5_FUNCTIONAL_FIXED_HARDWARE;
+ CpcInfo->ReferencePerformanceCounterRegister.RegisterBitWidth = 0x40;
+ CpcInfo->ReferencePerformanceCounterRegister.RegisterBitOffset = 0;
+ CpcInfo->ReferencePerformanceCounterRegister.AccessSize = ARM_FFH_REFERENCE_PERF_COUNTER_REGISTER;
+ CpcInfo->ReferencePerformanceCounterRegister.Address = 0x4;
+
+ CpcInfo->DeliveredPerformanceCounterRegister.AddressSpaceId = EFI_ACPI_6_5_FUNCTIONAL_FIXED_HARDWARE;
+ CpcInfo->DeliveredPerformanceCounterRegister.RegisterBitWidth = 0x40;
+ CpcInfo->DeliveredPerformanceCounterRegister.RegisterBitOffset = 0;
+ CpcInfo->DeliveredPerformanceCounterRegister.AccessSize = ARM_FFH_DELIVERED_PERF_COUNTER_REGISTER;
+ CpcInfo->DeliveredPerformanceCounterRegister.Address = 0x4;
+
+ // SCMI should advertise performance values on a unified scale. So frequency
+ // values are not available. LowestFrequencyInteger and
+ // NominalFrequencyInteger are populated in the ConfigurationManager.
+
+exit_handler:
+ FreePool (LevelArray);
+ return Status;
+}