/** @file Arm SCMI Info Library. Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
Arm Functional Fixed Hardware Specification: - https://developer.arm.com/documentation/den0048/latest/ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include /** 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; }