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