summaryrefslogtreecommitdiff
path: root/Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c')
-rw-r--r--Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c b/Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c
new file mode 100644
index 0000000000..ee1efe1574
--- /dev/null
+++ b/Platform/ARM/JunoPkg/Library/ArmJunoMtlLib/ArmJunoMtlLib.c
@@ -0,0 +1,198 @@
+/** @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 <Uefi/UefiBaseType.h>
+#include <Uefi/UefiSpec.h>
+#include <Library/ArmLib.h>
+#include <Library/ArmMtlLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "ArmJunoMtlPrivateLib.h"
+
+// Each channel has a shared mailbox and a doorbell register.
+STATIC CONST MTL_CHANNEL Channels[NUM_CHANNELS] = {
+ // Low priority channel.
+ {
+ MTL_CHANNEL_TYPE_LOW,
+ (MTL_MAILBOX*)(MTL_MAILBOX_BASE),
+ DOORBELL_LOW
+ },
+ // High priority channel
+ {
+ MTL_CHANNEL_TYPE_HIGH,
+ (MTL_MAILBOX*)(MTL_MAILBOX_BASE + MTL_MAILBOX_HIGH_PRIORITY_OFFSET),
+ DOORBELL_HIGH
+ }
+ };
+
+/** Wait until channel is free.
+
+ @param[in] Channel Pointer to a channel.
+ @param[in] TimeOutInMicroSeconds Time out in micro seconds.
+
+ @retval EFI_SUCCESS Channel is free.
+ @retval EFI_TIMEOUT Time out error.
+**/
+EFI_STATUS
+MtlWaitUntilChannelFree (
+ IN MTL_CHANNEL *Channel,
+ IN UINTN TimeOutInMicroSeconds
+ )
+{
+ while (TimeOutInMicroSeconds != 0) {
+ // If channel is free then we have received the reply.
+ if (Channel->MailBox->ChannelStatus == MTL_CHANNEL_FREE) {
+ return EFI_SUCCESS;
+ }
+ if (TimeOutInMicroSeconds < MTL_POLL_WAIT_TIME) {
+ gBS->Stall (TimeOutInMicroSeconds);
+ break;
+ }
+ // Wait for some arbitrary time.
+ gBS->Stall (MTL_POLL_WAIT_TIME);
+ TimeOutInMicroSeconds -= MTL_POLL_WAIT_TIME;
+ }
+
+ // No response from SCP.
+ if (Channel->MailBox->ChannelStatus != MTL_CHANNEL_FREE) {
+ ASSERT (FALSE);
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Return the address of the message payload.
+
+ @param[in] Channel Pointer to a channel.
+
+ @retval UINT32* Pointer to the payload.
+**/
+UINT32*
+MtlGetChannelPayload (
+ IN MTL_CHANNEL *Channel
+ )
+{
+ return Channel->MailBox->Payload;
+}
+
+/** Return pointer to a channel for the requested channel type.
+
+ @param[in] ChannelType ChannelType, Low or High priority channel.
+ MTL_CHANNEL_TYPE_LOW or
+ MTL_CHANNEL_TYPE_HIGH
+
+ @param[out] Channel Holds pointer to the channel.
+
+ @retval EFI_SUCCESS Pointer to channel is returned.
+ @retval EFI_UNSUPPORTED Requested channel type not supported.
+**/
+EFI_STATUS
+MtlGetChannel (
+ IN MTL_CHANNEL_TYPE ChannelType,
+ OUT MTL_CHANNEL **Channel
+ )
+{
+ if (ChannelType != MTL_CHANNEL_TYPE_LOW
+ && ChannelType != MTL_CHANNEL_TYPE_HIGH) {
+ return EFI_UNSUPPORTED;
+ }
+
+ *Channel = (MTL_CHANNEL*)&Channels[ChannelType];
+
+ return EFI_SUCCESS;
+}
+
+/** Mark the channel busy and ring the doorbell.
+
+ @param[in] Channel Pointer to a channel.
+ @param[in] MessageHeader Message header.
+
+ @param[out] PayloadLength Message length.
+
+ @retval EFI_SUCCESS Message sent successfully.
+ @retval EFI_DEVICE_ERROR Channel is busy.
+**/
+EFI_STATUS
+MtlSendMessage (
+ IN MTL_CHANNEL *Channel,
+ IN UINT32 MessageHeader,
+ OUT UINT32 PayloadLength
+ )
+{
+ MTL_MAILBOX *MailBox = Channel->MailBox;
+
+ if (Channel->MailBox->ChannelStatus != MTL_CHANNEL_FREE) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ // Mark the channel busy before ringing doorbell.
+ Channel->MailBox->ChannelStatus = MTL_CHANNEL_BUSY;
+ ArmDataSynchronizationBarrier ();
+
+ MailBox->Flags = MTL_POLL;
+ MailBox->MessageHeader = MessageHeader;
+
+ // Add length of message header.
+ MailBox->Length = PayloadLength + sizeof (MessageHeader);
+
+ // Ring the doorbell. It sets SET bit of the MHU register.
+ MmioWrite32 (
+ Channel->DoorBell.PhysicalAddress,
+ Channel->DoorBell.ModifyMask
+ );
+
+ return EFI_SUCCESS;
+}
+
+/** Wait for a response on a channel.
+
+ If channel is free after sending message, it implies SCP responded
+ with a response on the channel.
+
+ @param[in] Channel Pointer to a channel.
+
+ @retval EFI_SUCCESS Message received successfully.
+ @retval EFI_TIMEOUT Time out error.
+**/
+EFI_STATUS
+MtlReceiveMessage (
+ IN MTL_CHANNEL *Channel,
+ OUT UINT32 *MessageHeader,
+ OUT UINT32 *PayloadLength
+ )
+{
+ EFI_STATUS Status;
+
+ MTL_MAILBOX *MailBox = Channel->MailBox;
+
+ Status = MtlWaitUntilChannelFree (Channel, RESPONSE_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *MessageHeader = MailBox->MessageHeader;
+
+ // Deduct message header length.
+ *PayloadLength = MailBox->Length - sizeof (*MessageHeader);
+
+ return EFI_SUCCESS;
+}