/** @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 
#include 
#include 
#include 
#include "ArmScmiBaseProtocolPrivate.h"
#include "ScmiPrivate.h"
/** Return version of the Base protocol supported by SCP firmware.
  @param[in]  This     A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] Version  Version of the supported SCMI Base protocol.
  @retval EFI_SUCCESS       The version of the protocol is returned.
  @retval EFI_DEVICE_ERROR  SCP returns an SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
STATIC
EFI_STATUS
BaseGetVersion (
  IN  SCMI_BASE_PROTOCOL  *This,
  OUT UINT32              *Version
  )
{
  return ScmiGetProtocolVersion (ScmiProtocolIdBase, Version);
}
/** Return total number of SCMI protocols supported by the SCP firmware.
  @param[in]  This           A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] TotalProtocols Total number of SCMI protocols supported.
  @retval EFI_SUCCESS       Total number of protocols supported are returned.
  @retval EFI_DEVICE_ERROR  SCP returns a SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
STATIC
EFI_STATUS
BaseGetTotalProtocols (
  IN  SCMI_BASE_PROTOCOL  *This,
  OUT UINT32              *TotalProtocols
  )
{
  EFI_STATUS  Status;
  UINT32      *ReturnValues;
  Status = ScmiGetProtocolAttributes (ScmiProtocolIdBase, &ReturnValues);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  *TotalProtocols = SCMI_TOTAL_PROTOCOLS (ReturnValues[0]);
  return EFI_SUCCESS;
}
/** Common function which returns vendor details.
  @param[in] MessageId       ScmiMessageIdBaseDiscoverVendor
                             OR
                             ScmiMessageIdBaseDiscoverSubVendor
  @param[out] VendorIdentifier ASCII name of the vendor/subvendor.
  @retval EFI_SUCCESS       VendorIdentifier is returned.
  @retval EFI_DEVICE_ERROR  SCP returns an SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
STATIC
EFI_STATUS
BaseDiscoverVendorDetails (
  IN  SCMI_MESSAGE_ID_BASE  MessageId,
  OUT UINT8                 VendorIdentifier[SCMI_MAX_STR_LEN]
  )
{
  EFI_STATUS    Status;
  UINT32        *ReturnValues;
  SCMI_COMMAND  Cmd;
  UINT32        PayloadLength;
  Cmd.ProtocolId = ScmiProtocolIdBase;
  Cmd.MessageId  = MessageId;
  PayloadLength = 0;
  Status = ScmiCommandExecute (
             &Cmd,
             &PayloadLength,
             &ReturnValues
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  AsciiStrCpyS (
    (CHAR8 *)VendorIdentifier,
    SCMI_MAX_STR_LEN,
    (CONST CHAR8 *)ReturnValues
    );
  return EFI_SUCCESS;
}
/** Return vendor name.
  @param[in] This           A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] VendorIdentifier Null terminated ASCII string of up to
                               16 bytes with a vendor name.
  @retval EFI_SUCCESS       VendorIdentifier is returned.
  @retval EFI_DEVICE_ERROR  SCP returns a SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
STATIC
EFI_STATUS
BaseDiscoverVendor (
  IN  SCMI_BASE_PROTOCOL  *This,
  OUT UINT8               VendorIdentifier[SCMI_MAX_STR_LEN]
  )
{
  return BaseDiscoverVendorDetails (
           ScmiMessageIdBaseDiscoverVendor,
           VendorIdentifier
           );
}
/** Return sub vendor name.
  @param[in]  This           A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] VendorIdentifier Null terminated ASCII string of up to
                               16 bytes with a sub vendor name.
  @retval EFI_SUCCESS       VendorIdentifier is returned.
  @retval EFI_DEVICE_ERROR  SCP returns a SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
EFI_STATUS
BaseDiscoverSubVendor (
  IN  SCMI_BASE_PROTOCOL  *This,
  OUT UINT8               VendorIdentifier[SCMI_MAX_STR_LEN]
  )
{
  return BaseDiscoverVendorDetails (
           ScmiMessageIdBaseDiscoverSubVendor,
           VendorIdentifier
           );
}
/** Return implementation version.
  @param[in] This           A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] ImplementationVersion Vendor specific implementation version.
  @retval EFI_SUCCESS       Implementation version is returned.
  @retval EFI_DEVICE_ERROR  SCP returns a SCMI error.
  @retval !(EFI_SUCCESS)    Other errors.
**/
STATIC
EFI_STATUS
BaseDiscoverImplVersion (
  IN  SCMI_BASE_PROTOCOL  *This,
  OUT UINT32              *ImplementationVersion
  )
{
  EFI_STATUS    Status;
  UINT32        *ReturnValues;
  SCMI_COMMAND  Cmd;
  UINT32        PayloadLength;
  Cmd.ProtocolId = ScmiProtocolIdBase;
  Cmd.MessageId  = ScmiMessageIdBaseDiscoverImplementationVersion;
  PayloadLength = 0;
  Status = ScmiCommandExecute (
             &Cmd,
             &PayloadLength,
             &ReturnValues
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  *ImplementationVersion = ReturnValues[0];
  return EFI_SUCCESS;
}
/** Return list of protocols.
  @param[in]  This           A Pointer to SCMI_BASE_PROTOCOL Instance.
  @param[out] ProtocolListSize  Size of the ProtocolList.
  @param[out] ProtocolList   Protocol list.
  @retval EFI_SUCCESS          List of protocols is returned.
  @retval EFI_BUFFER_TOO_SMALL ProtocolListSize is too small for the result.
                                It has been updated to the size needed.
  @retval EFI_DEVICE_ERROR     SCP returns a SCMI error.
  @retval !(EFI_SUCCESS)       Other errors.
**/
STATIC
EFI_STATUS
BaseDiscoverListProtocols (
  IN     SCMI_BASE_PROTOCOL  *This,
  IN OUT UINT32              *ProtocolListSize,
  OUT    UINT8               *ProtocolList
  )
{
  EFI_STATUS          Status;
  UINT32              TotalProtocols;
  UINT32              *MessageParams;
  BASE_DISCOVER_LIST  *DiscoverList;
  UINT32              Skip;
  UINT32              Index;
  SCMI_COMMAND        Cmd;
  UINT32              PayloadLength;
  UINT32              RequiredSize;
  Status = BaseGetTotalProtocols (This, &TotalProtocols);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = ScmiCommandGetPayload (&MessageParams);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  RequiredSize = sizeof (UINT8) * TotalProtocols;
  if (*ProtocolListSize < RequiredSize) {
    *ProtocolListSize = RequiredSize;
    return EFI_BUFFER_TOO_SMALL;
  }
  Cmd.ProtocolId = ScmiProtocolIdBase;
  Cmd.MessageId  = ScmiMessageIdBaseDiscoverListProtocols;
  Skip = 0;
  while (Skip < TotalProtocols) {
    *MessageParams = Skip;
    // Note PayloadLength is a IN/OUT parameter.
    PayloadLength = sizeof (Skip);
    Status = ScmiCommandExecute (
               &Cmd,
               &PayloadLength,
               (UINT32 **)&DiscoverList
               );
    if (EFI_ERROR (Status)) {
      return Status;
    }
    for (Index = 0; Index < DiscoverList->NumProtocols; Index++) {
      ProtocolList[Skip++] = DiscoverList->Protocols[Index];
    }
  }
  *ProtocolListSize = RequiredSize;
  return EFI_SUCCESS;
}
// Instance of the SCMI Base protocol.
STATIC CONST SCMI_BASE_PROTOCOL  BaseProtocol = {
  BaseGetVersion,
  BaseGetTotalProtocols,
  BaseDiscoverVendor,
  BaseDiscoverSubVendor,
  BaseDiscoverImplVersion,
  BaseDiscoverListProtocols
};
/** Initialize Base protocol and install protocol on a given handle.
   @param[in] Handle              Handle to install Base protocol.
   @retval EFI_SUCCESS            Base protocol interface installed
                                  successfully.
**/
EFI_STATUS
ScmiBaseProtocolInit (
  IN OUT EFI_HANDLE  *Handle
  )
{
  return gBS->InstallMultipleProtocolInterfaces (
                Handle,
                &gArmScmiBaseProtocolGuid,
                &BaseProtocol,
                NULL
                );
}