mirror of https://github.com/acidanthera/audk.git
634 lines
19 KiB
C
634 lines
19 KiB
C
/** @file
|
|
Arm Serial Port Parser.
|
|
|
|
Copyright (c) 2021 - 2023, Arm Limited. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
@par Reference(s):
|
|
- linux/Documentation/devicetree/bindings/serial/serial.yaml
|
|
- linux/Documentation/devicetree/bindings/serial/8250.txt
|
|
- linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
|
|
- linux/Documentation/devicetree/bindings/serial/pl011.yaml
|
|
**/
|
|
|
|
#include <IndustryStandard/DebugPort2Table.h>
|
|
|
|
#include "CmObjectDescUtility.h"
|
|
#include "FdtHwInfoParser.h"
|
|
#include "Serial/ArmSerialPortParser.h"
|
|
|
|
/** List of "compatible" property values for serial port nodes.
|
|
|
|
Any other "compatible" value is not supported by this module.
|
|
*/
|
|
STATIC CONST COMPATIBILITY_STR SerialCompatibleStr[] = {
|
|
{ "ns16550a" },
|
|
{ "arm,sbsa-uart" },
|
|
{ "arm,pl011" }
|
|
};
|
|
|
|
/** COMPATIBILITY_INFO structure for the SerialCompatible.
|
|
*/
|
|
CONST COMPATIBILITY_INFO SerialCompatibleInfo = {
|
|
ARRAY_SIZE (SerialCompatibleStr),
|
|
SerialCompatibleStr
|
|
};
|
|
|
|
/** 16550 UART compatible strings.
|
|
|
|
Any string of this list must be part of SerialCompatible.
|
|
*/
|
|
STATIC CONST COMPATIBILITY_STR Serial16550CompatibleStr[] = {
|
|
{ "ns16550a" }
|
|
};
|
|
|
|
/** COMPATIBILITY_INFO structure for the Serial16550Compatible.
|
|
*/
|
|
CONST COMPATIBILITY_INFO Serial16550CompatibleInfo = {
|
|
ARRAY_SIZE (Serial16550CompatibleStr),
|
|
Serial16550CompatibleStr
|
|
};
|
|
|
|
/** SBSA UART compatible strings.
|
|
|
|
Include PL011 as SBSA uart is a subset of PL011.
|
|
|
|
Any string of this list must be part of SerialCompatible.
|
|
*/
|
|
STATIC CONST COMPATIBILITY_STR SerialSbsaCompatibleStr[] = {
|
|
{ "arm,sbsa-uart" },
|
|
{ "arm,pl011" }
|
|
};
|
|
|
|
/** COMPATIBILITY_INFO structure for the SerialSbsaCompatible.
|
|
*/
|
|
CONST COMPATIBILITY_INFO SerialSbsaCompatibleInfo = {
|
|
ARRAY_SIZE (SerialSbsaCompatibleStr),
|
|
SerialSbsaCompatibleStr
|
|
};
|
|
|
|
/** Parse a serial port node.
|
|
|
|
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
|
|
@param [in] SerialPortNode Offset of a serial-port node.
|
|
@param [in] SerialPortInfo The CM_ARM_SERIAL_PORT_INFO to populate.
|
|
|
|
@retval EFI_SUCCESS The function completed successfully.
|
|
@retval EFI_ABORTED An error occurred.
|
|
@retval EFI_INVALID_PARAMETER Invalid parameter.
|
|
@retval EFI_UNSUPPORTED Unsupported.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SerialPortNodeParser (
|
|
IN CONST VOID *Fdt,
|
|
IN INT32 SerialPortNode,
|
|
IN CM_ARM_SERIAL_PORT_INFO *SerialPortInfo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
INT32 IntcNode;
|
|
CONST UINT8 *SizeValue;
|
|
|
|
INT32 AddressCells;
|
|
INT32 SizeCells;
|
|
INT32 IntCells;
|
|
|
|
CONST UINT8 *Data;
|
|
INT32 DataSize;
|
|
UINT8 AccessSize;
|
|
|
|
if ((Fdt == NULL) ||
|
|
(SerialPortInfo == NULL))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = FdtGetParentAddressInfo (
|
|
Fdt,
|
|
SerialPortNode,
|
|
&AddressCells,
|
|
&SizeCells
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
// Don't support more than 64 bits and less than 32 bits addresses.
|
|
if ((AddressCells < 1) ||
|
|
(AddressCells > 2) ||
|
|
(SizeCells < 1) ||
|
|
(SizeCells > 2))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
Data = fdt_getprop (Fdt, SerialPortNode, "reg", &DataSize);
|
|
if ((Data == NULL) ||
|
|
(DataSize < (INT32)(sizeof (UINT32) *
|
|
GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)) - 1))
|
|
{
|
|
// If error or not enough space.
|
|
ASSERT (0);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (AddressCells == 2) {
|
|
SerialPortInfo->BaseAddress = fdt64_to_cpu (*(UINT64 *)Data);
|
|
} else {
|
|
SerialPortInfo->BaseAddress = fdt32_to_cpu (*(UINT32 *)Data);
|
|
}
|
|
|
|
SizeValue = Data + (sizeof (UINT32) *
|
|
GET_DT_REG_SIZE_OFFSET (0, AddressCells, SizeCells));
|
|
if (SizeCells == 2) {
|
|
SerialPortInfo->BaseAddressLength = fdt64_to_cpu (*(UINT64 *)SizeValue);
|
|
} else {
|
|
SerialPortInfo->BaseAddressLength = fdt32_to_cpu (*(UINT32 *)SizeValue);
|
|
}
|
|
|
|
// Get the associated interrupt-controller.
|
|
Status = FdtGetIntcParentNode (Fdt, SerialPortNode, &IntcNode);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
// Should have found the node.
|
|
Status = EFI_ABORTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
// Get the number of cells used to encode an interrupt.
|
|
Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
Data = fdt_getprop (Fdt, SerialPortNode, "interrupts", &DataSize);
|
|
if ((Data == NULL) || (DataSize != (IntCells * sizeof (UINT32)))) {
|
|
// If error or not 1 interrupt.
|
|
ASSERT (0);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
SerialPortInfo->Interrupt = FdtGetInterruptId ((CONST UINT32 *)Data);
|
|
|
|
// Note: clock-frequency is optional for SBSA UART.
|
|
Data = fdt_getprop (Fdt, SerialPortNode, "clock-frequency", &DataSize);
|
|
if (Data != NULL) {
|
|
if (DataSize < sizeof (UINT32)) {
|
|
// If error or not enough space.
|
|
ASSERT (0);
|
|
return EFI_ABORTED;
|
|
} else if (fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*Data)) >= 0) {
|
|
// "clock-frequency" can be a "clocks phandle to refer to the clk used".
|
|
// This is not supported.
|
|
ASSERT (0);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
SerialPortInfo->Clock = fdt32_to_cpu (*(UINT32 *)Data);
|
|
}
|
|
|
|
if (FdtNodeIsCompatible (Fdt, SerialPortNode, &Serial16550CompatibleInfo)) {
|
|
SerialPortInfo->PortSubtype =
|
|
EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_16550_WITH_GAS;
|
|
|
|
/* reg-io-width:
|
|
description: |
|
|
The size (in bytes) of the IO accesses that should be performed on the
|
|
device. There are some systems that require 32-bit accesses to the
|
|
UART.
|
|
*/
|
|
Data = fdt_getprop (Fdt, SerialPortNode, "reg-io-width", &DataSize);
|
|
if (Data != NULL) {
|
|
if (DataSize < sizeof (UINT32)) {
|
|
// If error or not enough space.
|
|
ASSERT (0);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
AccessSize = fdt32_to_cpu (*(UINT32 *)Data);
|
|
if (AccessSize > EFI_ACPI_6_3_QWORD) {
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
SerialPortInfo->AccessSize = AccessSize;
|
|
} else {
|
|
// 8250/16550 defaults to byte access.
|
|
SerialPortInfo->AccessSize = EFI_ACPI_6_3_BYTE;
|
|
}
|
|
} else if (FdtNodeIsCompatible (
|
|
Fdt,
|
|
SerialPortNode,
|
|
&SerialSbsaCompatibleInfo
|
|
))
|
|
{
|
|
SerialPortInfo->PortSubtype =
|
|
EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART;
|
|
} else {
|
|
ASSERT (0);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
// Set Baudrate to 115200 by default
|
|
SerialPortInfo->BaudRate = 115200;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/** Find the console serial-port node in the DT.
|
|
|
|
This function fetches the node referenced in the "stdout-path"
|
|
property of the "chosen" node.
|
|
|
|
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
|
|
@param [out] SerialConsoleNode If success, contains the node offset
|
|
of the console serial-port node.
|
|
|
|
@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.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetSerialConsoleNode (
|
|
IN CONST VOID *Fdt,
|
|
OUT INT32 *SerialConsoleNode
|
|
)
|
|
{
|
|
CONST CHAR8 *Prop;
|
|
INT32 PropSize;
|
|
CONST CHAR8 *Path;
|
|
INT32 PathLen;
|
|
INT32 ChosenNode;
|
|
|
|
if ((Fdt == NULL) ||
|
|
(SerialConsoleNode == NULL))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// The "chosen" node resides at the root of the DT. Fetch it.
|
|
ChosenNode = fdt_path_offset (Fdt, "/chosen");
|
|
if (ChosenNode < 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Prop = fdt_getprop (Fdt, ChosenNode, "stdout-path", &PropSize);
|
|
if ((Prop == NULL) || (PropSize < 0)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Determine the actual path length, as a colon terminates the path.
|
|
Path = ScanMem8 (Prop, PropSize, ':');
|
|
if (Path == NULL) {
|
|
PathLen = (UINT32)AsciiStrLen (Prop);
|
|
} else {
|
|
PathLen = (INT32)(Path - Prop);
|
|
}
|
|
|
|
// Aliases cannot start with a '/', so it must be the actual path.
|
|
if (Prop[0] == '/') {
|
|
*SerialConsoleNode = fdt_path_offset_namelen (Fdt, Prop, PathLen);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// Lookup the alias, as this contains the actual path.
|
|
Path = fdt_get_alias_namelen (Fdt, Prop, PathLen);
|
|
if (Path == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*SerialConsoleNode = fdt_path_offset (Fdt, Path);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/** CM_ARM_SERIAL_PORT_INFO dispatcher function (for a generic serial-port).
|
|
|
|
@param [in] FdtParserHandle A handle to the parser instance.
|
|
@param [in] GenericSerialInfo Pointer to a serial port info list.
|
|
@param [in] NodeCount Count of serial ports to dispatch.
|
|
@param [in] SerialObjectId Serial port object ID.
|
|
|
|
@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.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmSerialPortInfoDispatch (
|
|
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
|
|
IN CM_ARM_SERIAL_PORT_INFO *GenericSerialInfo,
|
|
IN INT32 NodeCount,
|
|
IN EARM_OBJECT_ID SerialObjectId
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CM_OBJ_DESCRIPTOR *NewCmObjDesc;
|
|
|
|
if ((GenericSerialInfo == NULL) || (NodeCount == 0)) {
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((SerialObjectId != EArmObjSerialPortInfo) &&
|
|
(SerialObjectId != EArmObjSerialDebugPortInfo) &&
|
|
(SerialObjectId != EArmObjSerialConsolePortInfo))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Dispatch the Generic Serial ports
|
|
Status = CreateCmObjDesc (
|
|
CREATE_CM_ARM_OBJECT_ID (SerialObjectId),
|
|
NodeCount,
|
|
GenericSerialInfo,
|
|
sizeof (CM_ARM_SERIAL_PORT_INFO) * NodeCount,
|
|
&NewCmObjDesc
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
// Add all the CmObjs to the Configuration Manager.
|
|
Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
FreeCmObjDesc (NewCmObjDesc);
|
|
return Status;
|
|
}
|
|
|
|
/** CM_ARM_SERIAL_PORT_INFO parser function (for debug/console serial-port).
|
|
|
|
This parser expects FdtBranch to be the debug serial-port node.
|
|
At most one CmObj is created.
|
|
The following structure is populated:
|
|
typedef struct CmArmSerialPortInfo {
|
|
UINT64 BaseAddress; // {Populated}
|
|
UINT32 Interrupt; // {Populated}
|
|
UINT64 BaudRate; // {default}
|
|
UINT32 Clock; // {Populated}
|
|
UINT16 PortSubtype; // {Populated}
|
|
UINT64 BaseAddressLength // {Populated}
|
|
} CM_ARM_SERIAL_PORT_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.
|
|
@param [in] SerialObjectId ArmNamespace Object ID for the serial port.
|
|
|
|
@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.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmSerialPortInfoParser (
|
|
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
|
|
IN INT32 FdtBranch,
|
|
IN EARM_OBJECT_ID SerialObjectId
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CM_ARM_SERIAL_PORT_INFO SerialInfo;
|
|
|
|
if ((SerialObjectId != EArmObjSerialDebugPortInfo) &&
|
|
(SerialObjectId != EArmObjSerialConsolePortInfo))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem (&SerialInfo, sizeof (SerialInfo));
|
|
|
|
Status = SerialPortNodeParser (
|
|
FdtParserHandle->Fdt,
|
|
FdtBranch,
|
|
&SerialInfo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
Status = ArmSerialPortInfoDispatch (
|
|
FdtParserHandle,
|
|
&SerialInfo,
|
|
1,
|
|
SerialObjectId
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
/** SerialPort dispatcher.
|
|
|
|
This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
|
|
the following CM_OBJ_ID:
|
|
- EArmObjSerialConsolePortInfo
|
|
- EArmObjSerialDebugPortInfo
|
|
- EArmObjSerialPortInfo
|
|
|
|
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
|
|
SerialPortDispatcher (
|
|
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
|
|
IN INT32 FdtBranch
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
INT32 SerialConsoleNode;
|
|
INT32 SerialDebugNode;
|
|
INT32 SerialNode;
|
|
UINT32 Index;
|
|
UINT32 SerialNodeCount;
|
|
UINT32 SerialNodesRemaining;
|
|
CM_ARM_SERIAL_PORT_INFO *GenericSerialInfo;
|
|
UINT32 GenericSerialIndex;
|
|
VOID *Fdt;
|
|
|
|
if (FdtParserHandle == NULL) {
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Fdt = FdtParserHandle->Fdt;
|
|
|
|
// Count the number of serial-ports.
|
|
Status = FdtCountCompatNodeInBranch (
|
|
Fdt,
|
|
FdtBranch,
|
|
&SerialCompatibleInfo,
|
|
&SerialNodeCount
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
if (SerialNodeCount == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Track remaining nodes separately as SerialNodeCount
|
|
// is used in for loop below and reducing SerialNodeCount
|
|
// would result in the Generic Serial port nodes not
|
|
// being found if the serial console port node is among
|
|
// the first few serial nodes.
|
|
SerialNodesRemaining = SerialNodeCount;
|
|
|
|
// Identify the serial console port.
|
|
Status = GetSerialConsoleNode (Fdt, &SerialConsoleNode);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
// No serial console.
|
|
SerialConsoleNode = -1;
|
|
} else if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
} else {
|
|
// Parse the console serial-port.
|
|
Status = ArmSerialPortInfoParser (
|
|
FdtParserHandle,
|
|
SerialConsoleNode,
|
|
EArmObjSerialConsolePortInfo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
return Status;
|
|
}
|
|
|
|
SerialNodesRemaining--;
|
|
}
|
|
|
|
GenericSerialInfo = NULL;
|
|
if (SerialNodesRemaining > 1) {
|
|
// We have more than one serial port remaining.
|
|
// This means that the first serial port will
|
|
// be reserved as a debug port, and the remaining
|
|
// will be for general purpose use.
|
|
SerialNodesRemaining--;
|
|
GenericSerialInfo = AllocateZeroPool (
|
|
SerialNodesRemaining *
|
|
sizeof (CM_ARM_SERIAL_PORT_INFO)
|
|
);
|
|
if (GenericSerialInfo == NULL) {
|
|
ASSERT (0);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
SerialNode = FdtBranch;
|
|
SerialDebugNode = -1;
|
|
GenericSerialIndex = 0;
|
|
for (Index = 0; Index < SerialNodeCount; Index++) {
|
|
// Search the next serial-port node in the branch.
|
|
Status = FdtGetNextCompatNodeInBranch (
|
|
Fdt,
|
|
FdtBranch,
|
|
&SerialCompatibleInfo,
|
|
&SerialNode
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
// Should have found the node.
|
|
Status = EFI_ABORTED;
|
|
}
|
|
|
|
goto exit_handler;
|
|
}
|
|
|
|
// Ignore the serial console node.
|
|
if (SerialNode == SerialConsoleNode) {
|
|
continue;
|
|
} else if (SerialDebugNode == -1) {
|
|
// The first serial-port node, not being the console serial-port,
|
|
// will be the debug serial-port.
|
|
SerialDebugNode = SerialNode;
|
|
Status = ArmSerialPortInfoParser (
|
|
FdtParserHandle,
|
|
SerialDebugNode,
|
|
EArmObjSerialDebugPortInfo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
goto exit_handler;
|
|
}
|
|
} else {
|
|
if (GenericSerialInfo == NULL) {
|
|
// Should not be possible.
|
|
ASSERT (0);
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler;
|
|
}
|
|
|
|
Status = SerialPortNodeParser (
|
|
Fdt,
|
|
SerialNode,
|
|
&GenericSerialInfo[GenericSerialIndex++]
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (0);
|
|
goto exit_handler;
|
|
}
|
|
}
|
|
} // for
|
|
|
|
if (GenericSerialIndex > 0) {
|
|
Status = ArmSerialPortInfoDispatch (
|
|
FdtParserHandle,
|
|
GenericSerialInfo,
|
|
GenericSerialIndex,
|
|
EArmObjSerialPortInfo
|
|
);
|
|
}
|
|
|
|
exit_handler:
|
|
if (GenericSerialInfo != NULL) {
|
|
FreePool (GenericSerialInfo);
|
|
}
|
|
|
|
return Status;
|
|
}
|