DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser

The Generic Timer Description Table (GTDT) is a mandatory table
required for booting a standards-based operating system. It
provides an OSPM with information about a system's Generic Timer
configuration. The Generic Timer (GT) is a standard timer interface
implemented on ARM processor-based systems. The GTDT provides OSPM
with information about a system's GT interrupt configurations, for
both per-processor timers, and platform (memory-mapped) timers.

The Generic Timer information is described in the platform Device
Tree. The Device Tree bindings for the Generic timers can be found
at:
 - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml

The FdtHwInfoParser implements a Generic Timer Parser that parses
the platform Device Tree to create a CM_ARM_GENERIC_TIMER_INFO
object. The CM_ARM_GENERIC_TIMER_INFO object is encapsulated in a
Configuration Manager descriptor object and added to the platform
information repository.

The platform Configuration Manager can then utilise this information
when generating the GTDT table.

Note: The Generic Timer Parser currently does not support parsing
of memory-mapped platform timers.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
This commit is contained in:
Pierre Gondois 2021-12-09 10:31:58 +01:00 committed by mergify[bot]
parent 8d2691c3d5
commit 3ebe1ff5c9
2 changed files with 324 additions and 0 deletions

View File

@ -0,0 +1,258 @@
/** @file
Arm generic timer parser.
Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
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;
}

View File

@ -0,0 +1,66 @@
/** @file
Arm generic timer parser.
Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
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_