diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c new file mode 100644 index 0000000000..988a81221d --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c @@ -0,0 +1,258 @@ +/** @file + Arm generic timer parser. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml +**/ + +#include "FdtHwInfoParser.h" +#include "CmObjectDescUtility.h" +#include "GenericTimer/ArmGenericTimerParser.h" +#include "Gic/ArmGicDispatcher.h" + +/** List of "compatible" property values for timer nodes. + + Other "compatible" values are not supported by this module. +*/ +STATIC CONST COMPATIBILITY_STR TimerCompatibleStr[] = { + { "arm,armv7-timer" }, + { "arm,armv8-timer" } +}; + +/** Timer compatiblity information. +*/ +STATIC CONST COMPATIBILITY_INFO TimerCompatibleInfo = { + ARRAY_SIZE (TimerCompatibleStr), + TimerCompatibleStr +}; + +/** Parse a timer node. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] TimerNode Offset of a timer node. + @param [in] GenericTimerInfo The CM_ARM_BOOT_ARCH_INFO to populate. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +TimerNodeParser ( + IN CONST VOID *Fdt, + IN INT32 TimerNode, + IN CM_ARM_GENERIC_TIMER_INFO *GenericTimerInfo + ) +{ + EFI_STATUS Status; + CONST UINT32 *Data; + INT32 IntcNode; + UINT32 GicVersion; + INT32 DataSize; + INT32 IntCells; + BOOLEAN AlwaysOnTimer; + + if ((Fdt == NULL) || + (GenericTimerInfo == NULL)) + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Data = fdt_getprop (Fdt, TimerNode, "always-on", &DataSize); + if ((Data == NULL) || (DataSize < 0)) { + AlwaysOnTimer = FALSE; + } else { + AlwaysOnTimer = TRUE; + } + + // Get the associated interrupt-controller. + Status = FdtGetIntcParentNode (Fdt, TimerNode, &IntcNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check that the interrupt-controller node is a Gic. + Status = GetGicVersion (Fdt, IntcNode, &GicVersion); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Get the number of cells used to encode an interrupt. + Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells); + if (EFI_ERROR (Status)) { + ASSERT (0); + if (Status == EFI_NOT_FOUND) { + // Should have found the node. + Status = EFI_ABORTED; + } + + return Status; + } + + Data = fdt_getprop (Fdt, TimerNode, "interrupts", &DataSize); + if ((Data == NULL) || + (DataSize != (FdtMaxTimerItem * IntCells * sizeof (UINT32)))) + { + // If error or not FdtMaxTimerItem interrupts. + ASSERT (0); + return EFI_ABORTED; + } + + GenericTimerInfo->SecurePL1TimerGSIV = + FdtGetInterruptId (&Data[FdtSecureTimerIrq * IntCells]); + GenericTimerInfo->SecurePL1TimerFlags = + FdtGetInterruptFlags (&Data[FdtSecureTimerIrq * IntCells]); + GenericTimerInfo->NonSecurePL1TimerGSIV = + FdtGetInterruptId (&Data[FdtNonSecureTimerIrq * IntCells]); + GenericTimerInfo->NonSecurePL1TimerFlags = + FdtGetInterruptFlags (&Data[FdtNonSecureTimerIrq * IntCells]); + GenericTimerInfo->VirtualTimerGSIV = + FdtGetInterruptId (&Data[FdtVirtualTimerIrq * IntCells]); + GenericTimerInfo->VirtualTimerFlags = + FdtGetInterruptFlags (&Data[FdtVirtualTimerIrq * IntCells]); + GenericTimerInfo->NonSecurePL2TimerGSIV = + FdtGetInterruptId (&Data[FdtHypervisorTimerIrq * IntCells]); + GenericTimerInfo->NonSecurePL2TimerFlags = + FdtGetInterruptFlags (&Data[FdtHypervisorTimerIrq * IntCells]); + + if (AlwaysOnTimer) { + GenericTimerInfo->SecurePL1TimerFlags |= BIT2; + GenericTimerInfo->NonSecurePL1TimerFlags |= BIT2; + GenericTimerInfo->VirtualTimerFlags |= BIT2; + GenericTimerInfo->NonSecurePL2TimerFlags |= BIT2; + } + + // Setup default values + // The CntControlBase & CntReadBase Physical Address are optional if + // the system implements EL3 (Security Extensions). So, initialise + // these to their default value. + GenericTimerInfo->CounterControlBaseAddress = 0xFFFFFFFFFFFFFFFF; + GenericTimerInfo->CounterReadBaseAddress = 0xFFFFFFFFFFFFFFFF; + + // For systems not implementing ARMv8.1 VHE, this field is 0. + GenericTimerInfo->VirtualPL2TimerGSIV = 0; + GenericTimerInfo->VirtualPL2TimerFlags = 0; + + return EFI_SUCCESS; +} + +/** CM_ARM_GENERIC_TIMER_INFO parser function. + + The following structure is populated: + typedef struct CmArmGenericTimerInfo { + UINT64 CounterControlBaseAddress; // {default} + UINT64 CounterReadBaseAddress; // {default} + UINT32 SecurePL1TimerGSIV; // {Populated} + UINT32 SecurePL1TimerFlags; // {Populated} + UINT32 NonSecurePL1TimerGSIV; // {Populated} + UINT32 NonSecurePL1TimerFlags; // {Populated} + UINT32 VirtualTimerGSIV; // {Populated} + UINT32 VirtualTimerFlags; // {Populated} + UINT32 NonSecurePL2TimerGSIV; // {Populated} + UINT32 NonSecurePL2TimerFlags; // {Populated} + UINT32 VirtualPL2TimerGSIV; // {default} + UINT32 VirtualPL2TimerFlags; // {default} + } CM_ARM_GENERIC_TIMER_INFO; + + A parser parses a Device Tree to populate a specific CmObj type. None, + one or many CmObj can be created by the parser. + The created CmObj are then handed to the parser's caller through the + HW_INFO_ADD_OBJECT interface. + This can also be a dispatcher. I.e. a function that not parsing a + Device Tree but calling other parsers. + + @param [in] FdtParserHandle A handle to the parser instance. + @param [in] FdtBranch When searching for DT node name, restrict + the search to this Device Tree branch. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Not found. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +ArmGenericTimerInfoParser ( + IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, + IN INT32 FdtBranch + ) +{ + EFI_STATUS Status; + UINT32 Index; + INT32 TimerNode; + UINT32 TimerNodeCount; + CM_ARM_GENERIC_TIMER_INFO GenericTimerInfo; + VOID *Fdt; + + if (FdtParserHandle == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Fdt = FdtParserHandle->Fdt; + Status = FdtCountCompatNodeInBranch ( + Fdt, + FdtBranch, + &TimerCompatibleInfo, + &TimerNodeCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (TimerNodeCount == 0) { + return EFI_NOT_FOUND; + } + + // Parse each timer node in the branch. + TimerNode = FdtBranch; + for (Index = 0; Index < TimerNodeCount; Index++) { + ZeroMem (&GenericTimerInfo, sizeof (CM_ARM_GENERIC_TIMER_INFO)); + + Status = FdtGetNextCompatNodeInBranch ( + Fdt, + FdtBranch, + &TimerCompatibleInfo, + &TimerNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + if (Status == EFI_NOT_FOUND) { + // Should have found the node. + Status = EFI_ABORTED; + } + + return Status; + } + + Status = TimerNodeParser (Fdt, TimerNode, &GenericTimerInfo); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Add the CmObj to the Configuration Manager. + Status = AddSingleCmObj ( + FdtParserHandle, + CREATE_CM_ARM_OBJECT_ID (EArmObjGenericTimerInfo), + &GenericTimerInfo, + sizeof (CM_ARM_GENERIC_TIMER_INFO), + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } // for + + return Status; +} diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h new file mode 100644 index 0000000000..d7fa278c90 --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h @@ -0,0 +1,66 @@ +/** @file + Arm generic timer parser. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml +**/ + +#ifndef ARM_GENERIC_TIMER_PARSER_H_ +#define ARM_GENERIC_TIMER_PARSER_H_ + +/** An enum listing the FDT interrupt items. +*/ +typedef enum FdtTimerInterruptItems { + FdtSecureTimerIrq, ///< Secure timer IRQ + FdtNonSecureTimerIrq, ///< Non-secure timer IRQ + FdtVirtualTimerIrq, ///< Virtual timer IRQ + FdtHypervisorTimerIrq, ///< Hypervisor timer IRQ + FdtMaxTimerItem ///< Max timer item +} FDT_TIMER_INTERRUPT_ITEMS; + +/** CM_ARM_BOOT_ARCH_INFO parser function. + + The following structure is populated: + typedef struct CmArmGenericTimerInfo { + UINT64 CounterControlBaseAddress; // {default} + UINT64 CounterReadBaseAddress; // {default} + UINT32 SecurePL1TimerGSIV; // {Populated} + UINT32 SecurePL1TimerFlags; // {Populated} + UINT32 NonSecurePL1TimerGSIV; // {Populated} + UINT32 NonSecurePL1TimerFlags; // {Populated} + UINT32 VirtualTimerGSIV; // {Populated} + UINT32 VirtualTimerFlags; // {Populated} + UINT32 NonSecurePL2TimerGSIV; // {Populated} + UINT32 NonSecurePL2TimerFlags; // {Populated} + UINT32 VirtualPL2TimerGSIV; // {default} + UINT32 VirtualPL2TimerFlags; // {default} + } CM_ARM_GENERIC_TIMER_INFO; + + A parser parses a Device Tree to populate a specific CmObj type. None, + one or many CmObj can be created by the parser. + The created CmObj are then handed to the parser's caller through the + HW_INFO_ADD_OBJECT interface. + This can also be a dispatcher. I.e. a function that not parsing a + Device Tree but calling other parsers. + + @param [in] FdtParserHandle A handle to the parser instance. + @param [in] FdtBranch When searching for DT node name, restrict + the search to this Device Tree branch. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND Not found. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +ArmGenericTimerInfoParser ( + IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, + IN INT32 FdtBranch + ); + +#endif // ARM_GENERIC_TIMER_PARSER_H_