CloverBootloader/ArmPkg/Drivers/ArmScmiDxe/Scmi.c

255 lines
6.8 KiB
C

/** @file
Copyright (c) 2017-2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
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/ArmMtlLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include "ScmiPrivate.h"
// Arbitrary timeout value 20ms.
#define RESPONSE_TIMEOUT 20000
/** Return a pointer to the message payload.
@param[out] Payload Holds pointer to the message payload.
@retval EFI_SUCCESS Payload holds a valid message payload pointer.
@retval EFI_TIMEOUT Time out error if MTL channel is busy.
@retval EFI_UNSUPPORTED If MTL channel is unsupported.
**/
EFI_STATUS
ScmiCommandGetPayload (
OUT UINT32 **Payload
)
{
EFI_STATUS Status;
MTL_CHANNEL *Channel;
// Get handle to the Channel.
Status = MtlGetChannel (MTL_CHANNEL_TYPE_LOW, &Channel);
if (EFI_ERROR (Status)) {
return Status;
}
// Payload will not be populated until channel is free.
Status = MtlWaitUntilChannelFree (Channel, RESPONSE_TIMEOUT);
if (EFI_ERROR (Status)) {
return Status;
}
// Get the address of the payload.
*Payload = MtlGetChannelPayload (Channel);
return EFI_SUCCESS;
}
/** Execute a SCMI command and receive a response.
This function uses a MTL channel to transfer message to SCP
and waits for a response.
@param[in] Command Pointer to the SCMI command (Protocol ID
and Message ID)
@param[in,out] PayloadLength SCMI command message length.
@param[out] OPTIONAL ReturnValues Pointer to SCMI response.
@retval OUT EFI_SUCCESS Command sent and message received successfully.
@retval OUT EFI_UNSUPPORTED Channel not supported.
@retval OUT EFI_TIMEOUT Timeout on the channel.
@retval OUT EFI_DEVICE_ERROR Channel not ready.
@retval OUT EFI_DEVICE_ERROR Message Header corrupted.
@retval OUT EFI_DEVICE_ERROR SCMI error.
**/
EFI_STATUS
ScmiCommandExecute (
IN SCMI_COMMAND *Command,
IN OUT UINT32 *PayloadLength,
OUT UINT32 **ReturnValues OPTIONAL
)
{
EFI_STATUS Status;
SCMI_MESSAGE_RESPONSE *Response;
UINT32 MessageHeader;
UINT32 ResponseHeader;
MTL_CHANNEL *Channel;
ASSERT (PayloadLength != NULL);
Status = MtlGetChannel (MTL_CHANNEL_TYPE_LOW, &Channel);
if (EFI_ERROR (Status)) {
return Status;
}
// Fill in message header.
MessageHeader = SCMI_MESSAGE_HEADER (
Command->MessageId,
ScmiMessageTypeCommand,
Command->ProtocolId
);
// Send payload using MTL channel.
Status = MtlSendMessage (
Channel,
MessageHeader,
*PayloadLength
);
if (EFI_ERROR (Status)) {
return Status;
}
// Wait for the response on the channel.
Status = MtlReceiveMessage (Channel, &ResponseHeader, PayloadLength);
if (EFI_ERROR (Status)) {
return Status;
}
// SCMI must return MessageHeader unmodified.
if (MessageHeader != ResponseHeader) {
ASSERT (FALSE);
return EFI_DEVICE_ERROR;
}
Response = (SCMI_MESSAGE_RESPONSE *)MtlGetChannelPayload (Channel);
if (Response->Status != ScmiSuccess) {
DEBUG ((
DEBUG_ERROR,
"SCMI error: ProtocolId = 0x%x, MessageId = 0x%x, error = %d\n",
Command->ProtocolId,
Command->MessageId,
Response->Status
));
ASSERT (FALSE);
return EFI_DEVICE_ERROR;
}
if (ReturnValues != NULL) {
*ReturnValues = Response->ReturnValues;
}
return EFI_SUCCESS;
}
/** Internal common function useful for common protocol discovery messages.
@param[in] ProtocolId Protocol Id of the protocol.
@param[in] MessageId Message Id of the message.
@param[out] ReturnValues SCMI response return values.
@retval EFI_SUCCESS Success with valid return values.
@retval EFI_DEVICE_ERROR SCMI error.
@retval !(EFI_SUCCESS) Other errors.
**/
STATIC
EFI_STATUS
ScmiProtocolDiscoveryCommon (
IN SCMI_PROTOCOL_ID ProtocolId,
IN SCMI_MESSAGE_ID MessageId,
OUT UINT32 **ReturnValues
)
{
SCMI_COMMAND Command;
UINT32 PayloadLength;
PayloadLength = 0;
Command.ProtocolId = ProtocolId;
Command.MessageId = MessageId;
return ScmiCommandExecute (
&Command,
&PayloadLength,
ReturnValues
);
}
/** Return protocol version from SCP for a given protocol ID.
@param[in] Protocol ID Protocol ID.
@param[out] Version Pointer to version of the protocol.
@retval EFI_SUCCESS Version holds a valid version received
from the SCP.
@retval EFI_DEVICE_ERROR SCMI error.
@retval !(EFI_SUCCESS) Other errors.
**/
EFI_STATUS
ScmiGetProtocolVersion (
IN SCMI_PROTOCOL_ID ProtocolId,
OUT UINT32 *Version
)
{
EFI_STATUS Status;
UINT32 *ProtocolVersion;
Status = ScmiProtocolDiscoveryCommon (
ProtocolId,
ScmiMessageIdProtocolVersion,
(UINT32 **)&ProtocolVersion
);
if (EFI_ERROR (Status)) {
return Status;
}
*Version = *ProtocolVersion;
return EFI_SUCCESS;
}
/** Return protocol attributes from SCP for a given protocol ID.
@param[in] Protocol ID Protocol ID.
@param[out] ReturnValues Pointer to attributes of the protocol.
@retval EFI_SUCCESS ReturnValues points to protocol attributes.
@retval EFI_DEVICE_ERROR SCMI error.
@retval !(EFI_SUCCESS) Other errors.
**/
EFI_STATUS
ScmiGetProtocolAttributes (
IN SCMI_PROTOCOL_ID ProtocolId,
OUT UINT32 **ReturnValues
)
{
return ScmiProtocolDiscoveryCommon (
ProtocolId,
ScmiMessageIdProtocolAttributes,
ReturnValues
);
}
/** Return protocol message attributes from SCP for a given protocol ID.
@param[in] Protocol ID Protocol ID.
@param[out] Attributes Pointer to attributes of the protocol.
@retval EFI_SUCCESS ReturnValues points to protocol message attributes.
@retval EFI_DEVICE_ERROR SCMI error.
@retval !(EFI_SUCCESS) Other errors.
**/
EFI_STATUS
ScmiGetProtocolMessageAttributes (
IN SCMI_PROTOCOL_ID ProtocolId,
OUT UINT32 **ReturnValues
)
{
return ScmiProtocolDiscoveryCommon (
ProtocolId,
ScmiMessageIdProtocolMessageAttributes,
ReturnValues
);
}