summaryrefslogtreecommitdiffstats
path: root/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c')
-rw-r--r--ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c b/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c
new file mode 100644
index 0000000000..64d2afab72
--- /dev/null
+++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c
@@ -0,0 +1,418 @@
+/** @file
+
+ Copyright (c) 2017-2018, Arm Limited. All rights reserved.
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ System Control and Management Interface V1.0
+ http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/
+ DEN0056A_System_Control_and_Management_Interface.pdf
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/ArmScmiClockProtocol.h>
+
+#include "ArmScmiClockProtocolPrivate.h"
+#include "ScmiPrivate.h"
+
+/** Convert to 64 bit value from two 32 bit words.
+
+ @param[in] Low Lower 32 bits.
+ @param[in] High Higher 32 bits.
+
+ @retval UINT64 64 bit value.
+**/
+STATIC
+UINT64
+ConvertTo64Bit (
+ IN UINT32 Low,
+ IN UINT32 High
+ )
+{
+ return (Low | ((UINT64)High << 32));
+}
+
+/** Return version of the clock management protocol supported by SCP firmware.
+
+ @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
+
+ @param[out] Version Version of the supported SCMI Clock management protocol.
+
+ @retval EFI_SUCCESS The version is returned.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockGetVersion (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ OUT UINT32 *Version
+ )
+{
+ return ScmiGetProtocolVersion (SCMI_PROTOCOL_ID_CLOCK, Version);
+}
+
+/** Return total number of clock devices supported by the clock management
+ protocol.
+
+ @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
+
+ @param[out] TotalClocks Total number of clocks supported.
+
+ @retval EFI_SUCCESS Total number of clocks supported is returned.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockGetTotalClocks (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ OUT UINT32 *TotalClocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 *ReturnValues;
+
+ Status = ScmiGetProtocolAttributes (SCMI_PROTOCOL_ID_CLOCK, &ReturnValues);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *TotalClocks = SCMI_CLOCK_PROTOCOL_TOTAL_CLKS (ReturnValues[0]);
+
+ return EFI_SUCCESS;
+}
+
+/** Return attributes of a clock device.
+
+ @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
+ @param[in] ClockId Identifier for the clock device.
+
+ @param[out] Enabled If TRUE, the clock device is enabled.
+ @param[out] ClockAsciiName A NULL terminated ASCII string with the clock
+ name, of up to 16 bytes.
+
+ @retval EFI_SUCCESS Clock device attributes are returned.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockGetClockAttributes (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ IN UINT32 ClockId,
+ OUT BOOLEAN *Enabled,
+ OUT CHAR8 *ClockAsciiName
+ )
+{
+ EFI_STATUS Status;
+
+ UINT32 *MessageParams;
+ CLOCK_ATTRIBUTES *ClockAttributes;
+ SCMI_COMMAND Cmd;
+ UINT32 PayloadLength;
+
+ Status = ScmiCommandGetPayload (&MessageParams);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *MessageParams = ClockId;
+
+ Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
+ Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_ATTRIBUTES;
+
+ PayloadLength = sizeof (ClockId);
+
+ Status = ScmiCommandExecute (
+ &Cmd,
+ &PayloadLength,
+ (UINT32**)&ClockAttributes
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ // TRUE if bit 0 of ClockAttributes->Attributes is set.
+ *Enabled = CLOCK_ENABLED (ClockAttributes->Attributes);
+
+ AsciiStrCpyS (
+ ClockAsciiName,
+ SCMI_MAX_STR_LEN,
+ (CONST CHAR8*)ClockAttributes->ClockName
+ );
+
+ return EFI_SUCCESS;
+}
+
+/** Return list of rates supported by a given clock device.
+
+ @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance.
+ @param[in] ClockId Identifier for the clock device.
+
+ @param[out] Format SCMI_CLOCK_RATE_FORMAT_DISCRETE: Clock device
+ supports range of clock rates which are non-linear.
+
+ SCMI_CLOCK_RATE_FORMAT_LINEAR: Clock device supports
+ range of linear clock rates from Min to Max in steps.
+
+ @param[out] TotalRates Total number of rates.
+
+ @param[in,out] RateArraySize Size of the RateArray.
+
+ @param[out] RateArray List of clock rates.
+
+ @retval EFI_SUCCESS List of clock rates is returned.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result.
+ It has been updated to the size needed.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockDescribeRates (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ IN UINT32 ClockId,
+ OUT SCMI_CLOCK_RATE_FORMAT *Format,
+ OUT UINT32 *TotalRates,
+ IN OUT UINT32 *RateArraySize,
+ OUT SCMI_CLOCK_RATE *RateArray
+ )
+{
+ EFI_STATUS Status;
+
+ UINT32 PayloadLength;
+ SCMI_COMMAND Cmd;
+ UINT32 *MessageParams;
+ CLOCK_DESCRIBE_RATES *DescribeRates;
+ CLOCK_RATE_DWORD *Rate;
+
+ UINT32 RequiredArraySize = 0;
+ UINT32 RateIndex = 0;
+ UINT32 RateNo;
+ UINT32 RateOffset;
+
+ *TotalRates = 0;
+
+ Status = ScmiCommandGetPayload (&MessageParams);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
+ Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_DESCRIBE_RATES;
+
+ *MessageParams++ = ClockId;
+
+ do {
+
+ *MessageParams = RateIndex;
+
+ // Set Payload length, note PayloadLength is a IN/OUT parameter.
+ PayloadLength = sizeof (ClockId) + sizeof (RateIndex);
+
+ // Execute and wait for response on a SCMI channel.
+ Status = ScmiCommandExecute (
+ &Cmd,
+ &PayloadLength,
+ (UINT32**)&DescribeRates
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (*TotalRates == 0) {
+ // In the first iteration we will get number of returned rates and number
+ // of remaining rates. With this information calculate required size
+ // for rate array. If provided RateArraySize is less, return an
+ // error.
+
+ *Format = RATE_FORMAT (DescribeRates->NumRatesFlags);
+
+ *TotalRates = NUM_RATES (DescribeRates->NumRatesFlags)
+ + NUM_REMAIN_RATES (DescribeRates->NumRatesFlags);
+
+ if (*Format == SCMI_CLOCK_RATE_FORMAT_DISCRETE) {
+ RequiredArraySize = (*TotalRates) * sizeof (UINT64);
+ } else {
+ // We need to return triplet of 64 bit value for each rate
+ RequiredArraySize = (*TotalRates) * 3 * sizeof (UINT64);
+ }
+
+ if (RequiredArraySize > (*RateArraySize)) {
+ *RateArraySize = RequiredArraySize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ RateOffset = 0;
+
+ if (*Format == SCMI_CLOCK_RATE_FORMAT_DISCRETE) {
+ for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) {
+ Rate = &DescribeRates->Rates[RateOffset++];
+ // Non-linear discrete rates.
+ RateArray[RateIndex++].Rate = ConvertTo64Bit (Rate->Low, Rate->High);
+ }
+ } else {
+ for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) {
+ // Linear clock rates from minimum to maximum in steps
+ // Minimum clock rate.
+ Rate = &DescribeRates->Rates[RateOffset++];
+ RateArray[RateIndex].Min = ConvertTo64Bit (Rate->Low, Rate->High);
+
+ Rate = &DescribeRates->Rates[RateOffset++];
+ // Maximum clock rate.
+ RateArray[RateIndex].Max = ConvertTo64Bit (Rate->Low, Rate->High);
+
+ Rate = &DescribeRates->Rates[RateOffset++];
+ // Step.
+ RateArray[RateIndex++].Step = ConvertTo64Bit (Rate->Low, Rate->High);
+ }
+ }
+ } while (NUM_REMAIN_RATES (DescribeRates->NumRatesFlags) != 0);
+
+ // Update RateArraySize with RequiredArraySize.
+ *RateArraySize = RequiredArraySize;
+
+ return EFI_SUCCESS;
+}
+
+/** Get clock rate.
+
+ @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
+ @param[in] ClockId Identifier for the clock device.
+
+ @param[out] Rate Clock rate.
+
+ @retval EFI_SUCCESS Clock rate is returned.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockRateGet (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ IN UINT32 ClockId,
+ OUT UINT64 *Rate
+ )
+{
+ EFI_STATUS Status;
+
+ UINT32 *MessageParams;
+ CLOCK_RATE_DWORD *ClockRate;
+ SCMI_COMMAND Cmd;
+
+ UINT32 PayloadLength;
+
+ Status = ScmiCommandGetPayload (&MessageParams);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Fill arguments for clock protocol command.
+ *MessageParams = ClockId;
+
+ Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
+ Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_RATE_GET;
+
+ PayloadLength = sizeof (ClockId);
+
+ // Execute and wait for response on a SCMI channel.
+ Status = ScmiCommandExecute (
+ &Cmd,
+ &PayloadLength,
+ (UINT32**)&ClockRate
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Rate = ConvertTo64Bit (ClockRate->Low, ClockRate->High);
+
+ return EFI_SUCCESS;
+}
+
+/** Set clock rate.
+
+ @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
+ @param[in] ClockId Identifier for the clock device.
+ @param[in] Rate Clock rate.
+
+ @retval EFI_SUCCESS Clock rate set success.
+ @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
+ @retval !(EFI_SUCCESS) Other errors.
+**/
+STATIC
+EFI_STATUS
+ClockRateSet (
+ IN SCMI_CLOCK_PROTOCOL *This,
+ IN UINT32 ClockId,
+ IN UINT64 Rate
+ )
+{
+ EFI_STATUS Status;
+ CLOCK_RATE_SET_ATTRIBUTES *ClockRateSetAttributes;
+ SCMI_COMMAND Cmd;
+ UINT32 PayloadLength;
+
+ Status = ScmiCommandGetPayload ((UINT32**)&ClockRateSetAttributes);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Fill arguments for clock protocol command.
+ ClockRateSetAttributes->ClockId = ClockId;
+ ClockRateSetAttributes->Flags = CLOCK_SET_DEFAULT_FLAGS;
+ ClockRateSetAttributes->Rate.Low = (UINT32)Rate;
+ ClockRateSetAttributes->Rate.High = (UINT32)(Rate >> 32);
+
+ Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
+ Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_RATE_SET;
+
+ PayloadLength = sizeof (CLOCK_RATE_SET_ATTRIBUTES);
+
+ // Execute and wait for response on a SCMI channel.
+ Status = ScmiCommandExecute (
+ &Cmd,
+ &PayloadLength,
+ NULL
+ );
+
+ return Status;
+}
+
+// Instance of the SCMI clock management protocol.
+STATIC CONST SCMI_CLOCK_PROTOCOL ScmiClockProtocol = {
+ ClockGetVersion,
+ ClockGetTotalClocks,
+ ClockGetClockAttributes,
+ ClockDescribeRates,
+ ClockRateGet,
+ ClockRateSet
+ };
+
+/** Initialize clock management protocol and install protocol on a given handle.
+
+ @param[in] Handle Handle to install clock management protocol.
+
+ @retval EFI_SUCCESS Clock protocol interface installed successfully.
+**/
+EFI_STATUS
+ScmiClockProtocolInit (
+ IN EFI_HANDLE* Handle
+ )
+{
+ return gBS->InstallMultipleProtocolInterfaces (
+ Handle,
+ &gArmScmiClockProtocolGuid,
+ &ScmiClockProtocol,
+ NULL
+ );
+}