From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by spool.mail.gandi.net (Postfix) with ESMTPS id 05300940FFF for ; Mon, 6 Nov 2023 03:29:51 +0000 (UTC) DKIM-Signature: a=rsa-sha256; bh=52dPuAde1UNTL7RAKS1QvIRxjrN28+v4TnCjX+dT0JU=; c=relaxed/simple; d=groups.io; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:MIME-Version:Precedence:List-Subscribe:List-Help:Sender:List-Id:Mailing-List:Delivered-To:Reply-To:List-Unsubscribe-Post:List-Unsubscribe:Content-Transfer-Encoding; s=20140610; t=1699241390; v=1; b=reFlKQZ46wQsYcqJ7Q1STAePJdfcg2JUnqS03695eFwwO5Rzp4CfyoCUIWCBkgQArw7J/iQB eISgigyIAdhVrZZV6gbEks0sFd6nfguXifPUnvXkkuPWk9ingoTwmEacNQ2wJFTD1HbV83sXsiu VrlqvfPZ90L90FX3ABo0mslU= X-Received: by 127.0.0.2 with SMTP id iKRMYY7687511xGTWh5CPSry; Sun, 05 Nov 2023 19:29:50 -0800 X-Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by mx.groups.io with SMTP id smtpd.web10.47087.1699241389462584182 for ; Sun, 05 Nov 2023 19:29:50 -0800 X-Received: from loongson.cn (unknown [10.2.9.245]) by gateway (Coremail) with SMTP id _____8Dx_+urXUhlgzc3AA--.41618S3; Mon, 06 Nov 2023 11:29:47 +0800 (CST) X-Received: from code-server.gen (unknown [10.2.9.245]) by localhost.localdomain (Coremail) with SMTP id AQAAf8BxL92qXUhlaxI6AA--.63484S2; Mon, 06 Nov 2023 11:29:46 +0800 (CST) From: "Chao Li" To: devel@edk2.groups.io Cc: Ard Biesheuvel , Jiewen Yao , Jordan Justen , Gerd Hoffmann , Xianglai Li Subject: [edk2-devel] [PATCH v2 20/30] OvmfPkg/LoongArchVirt: Add serial port library Date: Mon, 6 Nov 2023 11:29:45 +0800 Message-Id: <20231106032945.2285855-1-lichao@loongson.cn> In-Reply-To: <20231106032521.2251143-1-lichao@loongson.cn> References: <20231106032521.2251143-1-lichao@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8BxL92qXUhlaxI6AA--.63484S2 X-CM-SenderInfo: xolfxt3r6o00pqjv00gofq/1tbiAQAGCGVFqihSvgBLsK X-Coremail-Antispam: 1Uk129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7 ZEXasCq-sGcSsGvfJ3UbIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnUUvcSsGvfC2Kfnx nUUI43ZEXa7xR_UUUUUUUUU== Precedence: Bulk List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,lichao@loongson.cn List-Unsubscribe-Post: List-Unsubscribe=One-Click List-Unsubscribe: X-Gm-Message-State: QdeBTa8EJR71OEGCq1AXhG5Qx7686176AA= Content-Transfer-Encoding: 8bit X-GND-Status: LEGIT Authentication-Results: spool.mail.gandi.net; dkim=pass header.d=groups.io header.s=20140610 header.b=reFlKQZ4; dmarc=none; spf=pass (spool.mail.gandi.net: domain of bounce@groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce@groups.io Add two libraries named Fdt16550SerialPortHookLib and EarlyFdtSerialPortLib16550, requested by LoongArch virtual machine. The Fdt16550SerialProtHookLib is referenced from ArmVirtPkg. BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584 Cc: Ard Biesheuvel Cc: Jiewen Yao Cc: Jordan Justen Cc: Gerd Hoffmann Signed-off-by: Chao Li Co-authored-by: Xianglai Li --- .../EarlyFdtSerialPortLib16550.c | 824 ++++++++++++++++++ .../EarlyFdtSerialPortLib16550.inf | 47 + .../EarlyFdt16550SerialPortHookLib.c | 136 +++ .../EarlyFdt16550SerialPortHookLib.inf | 36 + .../Fdt16550SerialPortHookLib.c | 41 + .../Fdt16550SerialPortHookLib.inf | 33 + .../Fdt16550SerialPortHookLib.uni | 13 + 7 files changed, 1130 insertions(+) create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni diff --git a/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c new file mode 100644 index 0000000000..77ced19566 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c @@ -0,0 +1,824 @@ +/** @file + 16550 UART Serial Port library functions + + Copyright (c) 2023, Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// PCI Defintions. +// +#define PCI_BRIDGE_32_BIT_IO_SPACE 0x01 + +// +// 16550 UART register offsets and bitfields +// +#define R_UART_RXBUF 0 // LCR_DLAB = 0 +#define R_UART_TXBUF 0 // LCR_DLAB = 0 +#define R_UART_BAUD_LOW 0 // LCR_DLAB = 1 +#define R_UART_BAUD_HIGH 1 // LCR_DLAB = 1 +#define R_UART_IER 1 // LCR_DLAB = 0 +#define R_UART_FCR 2 +#define B_UART_FCR_FIFOE BIT0 +#define B_UART_FCR_FIFO64 BIT5 +#define R_UART_LCR 3 +#define B_UART_LCR_DLAB BIT7 +#define R_UART_MCR 4 +#define B_UART_MCR_DTRC BIT0 +#define B_UART_MCR_RTS BIT1 +#define R_UART_LSR 5 +#define B_UART_LSR_RXRDY BIT0 +#define B_UART_LSR_TXRDY BIT5 +#define B_UART_LSR_TEMT BIT6 +#define R_UART_MSR 6 +#define B_UART_MSR_CTS BIT4 +#define B_UART_MSR_DSR BIT5 +#define B_UART_MSR_RI BIT6 +#define B_UART_MSR_DCD BIT7 + +EFI_STATUS +EFIAPI +GetSerialConsolePortAddress ( + IN CONST VOID *Fdt, + OUT UINT64 *SerialConsoleAddress + ); + +/** + Read an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is read from + MMIO space. If PcdSerialUseMmio is FALSE, then the value is read from I/O space. The + parameter Offset is added to the base address of the 16550 registers that is specified + by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMIO space access + width and defaults to 8 bit access, and supports 8 or 32 bit access. + + @param Base The base address register of UART device. + @param Offset The offset of the 16550 register to read. + + @return The value read from the 16550 register. +**/ +UINT8 +SerialPortReadRegister ( + UINTN Base, + UINTN Offset + ) +{ + if (PcdGetBool (PcdSerialUseMmio)) { + if (PcdGet8 (PcdSerialRegisterAccessWidth) == 32) { + return (UINT8)MmioRead32 (Base + Offset * PcdGet32 (PcdSerialRegisterStride)); + } + + return MmioRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride)); + } else { + return IoRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride)); + } +} + +/** + Write an 8-bit 16550 register. If PcdSerialUseMmio is TRUE, then the value is written to + MMIO space. If PcdSerialUseMmio is FALSE, then the value is written to I/O space. The + parameter Offset is added to the base address of the 16550 registers that is specified + by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMIO space access + width and defaults to 8 bit access, and supports 8 or 32 bit access. + + @param Base The base address register of UART device. + @param Offset The offset of the 16550 register to write. + @param Value The value to write to the 16550 register specified by Offset. + + @return The value written to the 16550 register. +**/ +UINT8 +SerialPortWriteRegister ( + UINTN Base, + UINTN Offset, + UINT8 Value + ) +{ + if (PcdGetBool (PcdSerialUseMmio)) { + if (PcdGet8 (PcdSerialRegisterAccessWidth) == 32) { + return (UINT8)MmioWrite32 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), (UINT8)Value); + } + + return MmioWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value); + } else { + return IoWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value); + } +} + +/** + Retrieve the I/O or MMIO base address register for the PCI UART device. + + This function assumes Root Bus Numer is Zero, and enables I/O and MMIO in PCI UART + Device if they are not already enabled. + + @return The base address register of the UART device. +**/ +UINTN +GetSerialRegisterBase ( + VOID + ) +{ + VOID *Base; + RETURN_STATUS Status; + UINT64 SerialConsoleAddress; + + Base = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress); + Status = GetSerialConsolePortAddress (Base, &SerialConsoleAddress); + if (RETURN_ERROR (Status)) { + return (UINTN)0; + } + + return SerialConsoleAddress; +} + +/** + Return whether the hardware flow control signal allows writing. + + @param SerialRegisterBase The base address register of UART device. + + @retval TRUE The serial port is writable. + @retval FALSE The serial port is not writable. +**/ +BOOLEAN +SerialPortWritable ( + UINTN SerialRegisterBase + ) +{ + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + if (PcdGetBool (PcdSerialDetectCable)) { + // + // Wait for both DSR and CTS to be set + // DSR is set if a cable is connected. + // CTS is set if it is ok to transmit data + // + // DSR CTS Description Action + // === === ======================================== ======== + // 0 0 No cable connected. Wait + // 0 1 No cable connected. Wait + // 1 0 Cable connected, but not clear to send. Wait + // 1 1 Cable connected, and clear to send. Transmit + // + return (BOOLEAN)((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) == (B_UART_MSR_DSR | B_UART_MSR_CTS)); + } else { + // + // Wait for both DSR and CTS to be set OR for DSR to be clear. + // DSR is set if a cable is connected. + // CTS is set if it is ok to transmit data + // + // DSR CTS Description Action + // === === ======================================== ======== + // 0 0 No cable connected. Transmit + // 0 1 No cable connected. Transmit + // 1 0 Cable connected, but not clear to send. Wait + // 1 1 Cable connected, and clar to send. Transmit + // + return (BOOLEAN)((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) != (B_UART_MSR_DSR)); + } + } + + return TRUE; +} + +/** + Initialize the serial device hardware. + + If no initialization is required, then return RETURN_SUCCESS. + If the serial device was successfully initialized, then return RETURN_SUCCESS. + If the serial device could not be initialized, then return RETURN_DEVICE_ERROR. + + @retval RETURN_SUCCESS The serial device was initialized. + @retval RETURN_DEVICE_ERROR The serial device could not be initialized. +**/ +RETURN_STATUS +EFIAPI +SerialPortInitialize ( + VOID + ) +{ + UINTN SerialRegisterBase; + UINT32 Divisor; + UINT32 CurrentDivisor; + BOOLEAN Initialized; + + // + // Calculate divisor for baud generator + // Ref_Clk_Rate / Baud_Rate / 16 + // + Divisor = PcdGet32 (PcdSerialClockRate) / (PcdGet32 (PcdSerialBaudRate) * 16); + if ((PcdGet32 (PcdSerialClockRate) % (PcdGet32 (PcdSerialBaudRate) * 16)) >= PcdGet32 (PcdSerialBaudRate) * 8) { + Divisor++; + } + + // + // Get the base address of the serial port in either I/O or MMIO space + // + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return RETURN_DEVICE_ERROR; + } + + // + // See if the serial port is already initialized + // + Initialized = TRUE; + if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & 0x3F) != (PcdGet8 (PcdSerialLineControl) & 0x3F)) { + Initialized = FALSE; + } + + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) | B_UART_LCR_DLAB)); + CurrentDivisor = SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_HIGH) << 8; + CurrentDivisor |= (UINT32)SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_LOW); + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & ~B_UART_LCR_DLAB)); + if (CurrentDivisor != Divisor) { + Initialized = FALSE; + } + + if (Initialized) { + return RETURN_SUCCESS; + } + + // + // Wait for the serial port to be ready. + // Verify that both the transmit FIFO and the shift register are empty. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) { + } + + // + // Configure baud rate + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8)(Divisor >> 8)); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Divisor & 0xff)); + + // + // Clear DLAB and configure Data Bits, Parity, and Stop Bits. + // Strip reserved bits from PcdSerialLineControl + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3F)); + + // + // Enable and reset FIFOs + // Strip reserved bits from PcdSerialFifoControl + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, 0x00); + SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64))); + + // + // Set FIFO Polled Mode by clearing IER after setting FCR + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_IER, 0x00); + + // + // Put Modem Control Register(MCR) into its reset state of 0x00. + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, 0x00); + + return RETURN_SUCCESS; +} + +/** + Write data from buffer to serial device. + + Writes NumberOfBytes data bytes from Buffer to the serial device. + The number of bytes actually written to the serial device is returned. + If the return value is less than NumberOfBytes, then the write operation failed. + + If Buffer is NULL, then ASSERT(). + + If NumberOfBytes is zero, then return 0. + + @param Buffer Pointer to the data buffer to be written. + @param NumberOfBytes Number of bytes to written to the serial device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes written to the serial device. + If this value is less than NumberOfBytes, then the write operation failed. + +**/ +UINTN +EFIAPI +SerialPortWrite ( + IN UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + UINTN SerialRegisterBase; + UINTN Result; + UINTN Index; + UINTN FifoSize; + + if (Buffer == NULL) { + return 0; + } + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return 0; + } + + if (NumberOfBytes == 0) { + // + // Flush the hardware + // + + // + // Wait for both the transmit FIFO and shift register empty. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) { + } + + // + // Wait for the hardware flow control signal + // + while (!SerialPortWritable (SerialRegisterBase)) { + } + + return 0; + } + + // + // Compute the maximum size of the Tx FIFO + // + FifoSize = 1; + if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFOE) != 0) { + if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFO64) == 0) { + FifoSize = 16; + } else { + FifoSize = PcdGet32 (PcdSerialExtendedTxFifoSize); + } + } + + Result = NumberOfBytes; + while (NumberOfBytes != 0) { + // + // Wait for the serial port to be ready, to make sure both the transmit FIFO + // and shift register empty. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) { + } + + // + // Fill then entire Tx FIFO + // + for (Index = 0; Index < FifoSize && NumberOfBytes != 0; Index++, NumberOfBytes--, Buffer++) { + // + // Wait for the hardware flow control signal + // + while (!SerialPortWritable (SerialRegisterBase)) { + } + + // + // Write byte to the transmit buffer. + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_TXBUF, *Buffer); + } + } + + return Result; +} + +/** + Reads data from a serial device into a buffer. + + @param Buffer Pointer to the data buffer to store the data read from the serial device. + @param NumberOfBytes Number of bytes to read from the serial device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes read from the serial device. + If this value is less than NumberOfBytes, then the read operation failed. +**/ +UINTN +EFIAPI +SerialPortRead ( + OUT UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + UINTN SerialRegisterBase; + UINTN Result; + UINT8 Mcr; + + if (NULL == Buffer) { + return 0; + } + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return 0; + } + + Mcr = (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS); + + for (Result = 0; NumberOfBytes-- != 0; Result++, Buffer++) { + // + // Wait for the serial port to have some data. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) == 0) { + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Set RTS to let the peer send some data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(Mcr | B_UART_MCR_RTS)); + } + } + + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Clear RTS to prevent peer from sending data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr); + } + + // + // Read byte from the receive buffer. + // + *Buffer = SerialPortReadRegister (SerialRegisterBase, R_UART_RXBUF); + } + + return Result; +} + +/** + Polls a serial device to see if there is any data waiting to be read. + + Polls aserial device to see if there is any data waiting to be read. + If there is data waiting to be read from the serial device, then TRUE is returned. + If there is no data waiting to be read from the serial device, then FALSE is returned. + + @retval TRUE Data is waiting to be read from the serial device. + @retval FALSE There is no data waiting to be read from the serial device. +**/ +BOOLEAN +EFIAPI +SerialPortPoll ( + VOID + ) +{ + UINTN SerialRegisterBase; + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return FALSE; + } + + // + // Read the serial port status + // + if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) != 0) { + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Clear RTS to prevent peer from sending data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS)); + } + + return TRUE; + } + + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Set RTS to let the peer send some data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) | B_UART_MCR_RTS)); + } + + return FALSE; +} + +/** + Sets the control bits on a serial device. + + @param Control Sets the bits of Control that are settable. + + @retval RETURN_SUCCESS The new control bits were set on the serial device. + @retval RETURN_UNSUPPORTED The serial device does not support this operation. + @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly. +**/ +RETURN_STATUS +EFIAPI +SerialPortSetControl ( + IN UINT32 Control + ) +{ + UINTN SerialRegisterBase; + UINT8 Mcr; + + // + // First determine the parameter is invalid. + // + if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY | + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) + { + return RETURN_UNSUPPORTED; + } + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return RETURN_UNSUPPORTED; + } + + // + // Read the Modem Control Register. + // + Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR); + Mcr &= (~(B_UART_MCR_DTRC | B_UART_MCR_RTS)); + + if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) { + Mcr |= B_UART_MCR_DTRC; + } + + if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) { + Mcr |= B_UART_MCR_RTS; + } + + // + // Write the Modem Control Register. + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr); + + return RETURN_SUCCESS; +} + +/** + Retrieve the status of the control bits on a serial device. + + @param Control A pointer to return the current control signals from the serial device. + + @retval RETURN_SUCCESS The control bits were read from the serial device. + @retval RETURN_UNSUPPORTED The serial device does not support this operation. + @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly. +**/ +RETURN_STATUS +EFIAPI +SerialPortGetControl ( + OUT UINT32 *Control + ) +{ + UINTN SerialRegisterBase; + UINT8 Msr; + UINT8 Mcr; + UINT8 Lsr; + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return RETURN_UNSUPPORTED; + } + + *Control = 0; + + // + // Read the Modem Status Register. + // + Msr = SerialPortReadRegister (SerialRegisterBase, R_UART_MSR); + + if ((Msr & B_UART_MSR_CTS) == B_UART_MSR_CTS) { + *Control |= EFI_SERIAL_CLEAR_TO_SEND; + } + + if ((Msr & B_UART_MSR_DSR) == B_UART_MSR_DSR) { + *Control |= EFI_SERIAL_DATA_SET_READY; + } + + if ((Msr & B_UART_MSR_RI) == B_UART_MSR_RI) { + *Control |= EFI_SERIAL_RING_INDICATE; + } + + if ((Msr & B_UART_MSR_DCD) == B_UART_MSR_DCD) { + *Control |= EFI_SERIAL_CARRIER_DETECT; + } + + // + // Read the Modem Control Register. + // + Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR); + + if ((Mcr & B_UART_MCR_DTRC) == B_UART_MCR_DTRC) { + *Control |= EFI_SERIAL_DATA_TERMINAL_READY; + } + + if ((Mcr & B_UART_MCR_RTS) == B_UART_MCR_RTS) { + *Control |= EFI_SERIAL_REQUEST_TO_SEND; + } + + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + + // + // Read the Line Status Register. + // + Lsr = SerialPortReadRegister (SerialRegisterBase, R_UART_LSR); + + if ((Lsr & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) == (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) { + *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + } + + if ((Lsr & B_UART_LSR_RXRDY) == 0) { + *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + + return RETURN_SUCCESS; +} + +/** + Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, + data bits, and stop bits on a serial device. + + @param BaudRate The requested baud rate. A BaudRate value of 0 will use the + device's default interface speed. + On output, the value actually set. + @param ReveiveFifoDepth The requested depth of the FIFO on the receive side of the + serial interface. A ReceiveFifoDepth value of 0 will use + the device's default FIFO depth. + On output, the value actually set. + @param Timeout The requested time out for a single character in microseconds. + This timeout applies to both the transmit and receive side of the + interface. A Timeout value of 0 will use the device's default time + out value. + On output, the value actually set. + @param Parity The type of parity to use on this serial device. A Parity value of + DefaultParity will use the device's default parity value. + On output, the value actually set. + @param DataBits The number of data bits to use on the serial device. A DataBits + vaule of 0 will use the device's default data bit setting. + On output, the value actually set. + @param StopBits The number of stop bits to use on this serial device. A StopBits + value of DefaultStopBits will use the device's default number of + stop bits. + On output, the value actually set. + + @retval RETURN_SUCCESS The new attributes were set on the serial device. + @retval RETURN_UNSUPPORTED The serial device does not support this operation. + @retval RETURN_INVALID_PARAMETER One or more of the attributes has an unsupported value. + @retval RETURN_DEVICE_ERROR The serial device is not functioning correctly. +**/ +RETURN_STATUS +EFIAPI +SerialPortSetAttributes ( + IN OUT UINT64 *BaudRate, + IN OUT UINT32 *ReceiveFifoDepth, + IN OUT UINT32 *Timeout, + IN OUT EFI_PARITY_TYPE *Parity, + IN OUT UINT8 *DataBits, + IN OUT EFI_STOP_BITS_TYPE *StopBits + ) +{ + UINTN SerialRegisterBase; + UINT32 SerialBaudRate; + UINTN Divisor; + UINT8 Lcr; + UINT8 LcrData; + UINT8 LcrParity; + UINT8 LcrStop; + + SerialRegisterBase = GetSerialRegisterBase (); + if (SerialRegisterBase == 0) { + return RETURN_UNSUPPORTED; + } + + // + // Check for default settings and fill in actual values. + // + if (*BaudRate == 0) { + *BaudRate = PcdGet32 (PcdSerialBaudRate); + } + + SerialBaudRate = (UINT32)*BaudRate; + + if (*DataBits == 0) { + LcrData = (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3); + *DataBits = LcrData + 5; + } else { + if ((*DataBits < 5) || (*DataBits > 8)) { + return RETURN_INVALID_PARAMETER; + } + + // + // Map 5..8 to 0..3 + // + LcrData = (UINT8)(*DataBits - (UINT8)5); + } + + if (*Parity == DefaultParity) { + LcrParity = (UINT8)((PcdGet8 (PcdSerialLineControl) >> 3) & 0x7); + switch (LcrParity) { + case 0: + *Parity = NoParity; + break; + + case 3: + *Parity = EvenParity; + break; + + case 1: + *Parity = OddParity; + break; + + case 7: + *Parity = SpaceParity; + break; + + case 5: + *Parity = MarkParity; + break; + + default: + break; + } + } else { + switch (*Parity) { + case NoParity: + LcrParity = 0; + break; + + case EvenParity: + LcrParity = 3; + break; + + case OddParity: + LcrParity = 1; + break; + + case SpaceParity: + LcrParity = 7; + break; + + case MarkParity: + LcrParity = 5; + break; + + default: + return RETURN_INVALID_PARAMETER; + } + } + + if (*StopBits == DefaultStopBits) { + LcrStop = (UINT8)((PcdGet8 (PcdSerialLineControl) >> 2) & 0x1); + switch (LcrStop) { + case 0: + *StopBits = OneStopBit; + break; + + case 1: + if (*DataBits == 5) { + *StopBits = OneFiveStopBits; + } else { + *StopBits = TwoStopBits; + } + + break; + + default: + break; + } + } else { + switch (*StopBits) { + case OneStopBit: + LcrStop = 0; + break; + + case OneFiveStopBits: + case TwoStopBits: + LcrStop = 1; + break; + + default: + return RETURN_INVALID_PARAMETER; + } + } + + // + // Calculate divisor for baud generator + // Ref_Clk_Rate / Baud_Rate / 16 + // + Divisor = PcdGet32 (PcdSerialClockRate) / (SerialBaudRate * 16); + if ((PcdGet32 (PcdSerialClockRate) % (SerialBaudRate * 16)) >= SerialBaudRate * 8) { + Divisor++; + } + + // + // Configure baud rate + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8)(Divisor >> 8)); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Divisor & 0xff)); + + // + // Clear DLAB and configure Data Bits, Parity, and Stop Bits. + // Strip reserved bits from line control value + // + Lcr = (UINT8)((LcrParity << 3) | (LcrStop << 2) | LcrData); + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(Lcr & 0x3F)); + + return RETURN_SUCCESS; +} diff --git a/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf new file mode 100644 index 0000000000..3a0d856af7 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf @@ -0,0 +1,47 @@ +## @file +# SerialPortLib instance for 16550 UART. +# +# Copyright (c) 2023, Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EarlySerialPortLib16550 + FILE_GUID = f4fb883d-8138-4f29-bb0c-c574e9312c74 + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = SerialPortLib + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + IoLib + PcdLib + FdtLib + PlatformHookLib + +[Sources] + EarlyFdtSerialPortLib16550.c + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterAccessWidth ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseMmio ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialDetectCable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialFifoControl ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress ## CONSUMES diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c new file mode 100644 index 0000000000..e19f90c509 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c @@ -0,0 +1,136 @@ +/** @file + PEI Phase Early Platform Hook Library instance for 16550 Uart. + + Copyright (c) 2020 - 2023, Arm Ltd. All rights reserved.
+ Copyright (c) 2023 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +/** Get the UART base address of the console serial-port from the DT. + + This function fetches the node referenced in the "stdout-path" + property of the "chosen" node and returns the base address of + the console UART. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [out] SerialConsoleAddress If success, contains the base address + of the console serial-port. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND Console serial-port info not found in DT. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +GetSerialConsolePortAddress ( + IN CONST VOID *Fdt, + OUT UINT64 *SerialConsoleAddress + ) +{ + CONST CHAR8 *Prop; + INT32 PropSize; + CONST CHAR8 *Path; + INT32 PathLen; + INT32 ChosenNode; + INT32 SerialConsoleNode; + INT32 Len; + CONST CHAR8 *NodeStatus; + CONST UINT64 *RegProperty; + + if ((Fdt == NULL) || (fdt_check_header (Fdt) != 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 (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 = AsciiStrLen (Prop); + } else { + PathLen = 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); + } else { + // 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); + } + + NodeStatus = fdt_getprop (Fdt, SerialConsoleNode, "status", &Len); + if ((NodeStatus != NULL) && (AsciiStrCmp (NodeStatus, "okay") != 0)) { + return EFI_NOT_FOUND; + } + + RegProperty = fdt_getprop (Fdt, SerialConsoleNode, "reg", &Len); + if (Len != 16) { + return EFI_INVALID_PARAMETER; + } + + *SerialConsoleAddress = fdt64_to_cpu (ReadUnaligned64 (RegProperty)); + + return EFI_SUCCESS; +} + +/** Platform hook to retrieve the 16550 UART base address from the platform + Device tree and store it in the reigster LOONGARCH_CSR_KS1. + + @retval RETURN_SUCCESS Success. + @retval RETURN_INVALID_PARAMETER A parameter was invalid. + @retval RETURN_NOT_FOUND Serial port information not found. + +**/ +RETURN_STATUS +EFIAPI +PlatformHookSerialPortInitialize ( + VOID + ) +{ + RETURN_STATUS Status; + VOID *DeviceTreeBase; + UINT64 SerialConsoleAddress; + + if (PcdGet64 (PcdSerialRegisterBase) != 0) { + return RETURN_SUCCESS; + } + + DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress); + if (DeviceTreeBase == NULL) { + return RETURN_NOT_FOUND; + } + + Status = GetSerialConsolePortAddress (DeviceTreeBase, &SerialConsoleAddress); + if (RETURN_ERROR (Status)) { + return Status; + } + + CsrWrite (LOONGARCH_CSR_KS1, (UINTN)SerialConsoleAddress); + + return RETURN_SUCCESS; +} diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf new file mode 100644 index 0000000000..d37c1f6402 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf @@ -0,0 +1,36 @@ +## @file +# PEI Phase Early Platform Hook Library instance for 16550 Uart. +# +# Copyright (c) 2020, ARM Ltd. All rights reserved.
+# Copyright (c) 2023 Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = EarlyFdt16550SerialPortHookLib + MODULE_UNI_FILE = Fdt16550SerialPortHookLib.uni + FILE_GUID = 6A5FEBCB-C676-A7C1-A96C-B79D4860EEC5 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformHookLib|SEC PEI_CORE PEIM + +[Sources] + EarlyFdt16550SerialPortHookLib.c + +[LibraryClasses] + BaseLib + PcdLib + FdtLib + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c new file mode 100644 index 0000000000..46e159d7c2 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c @@ -0,0 +1,41 @@ +/** @file + Platform Hook Library instance for 16550 Uart. + + Copyright (c) 2020, ARM Ltd. All rights reserved.
+ Copyright (c) 2023 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +/** Platform hook to retrieve the 16550 UART base address from register + LOONGARCH_CSR_KS1 that caches the UART base address from early boot + stage and store it in PcdSerialRegisterBase. + + @retval RETURN_SUCCESS Success. + @retval RETURN_NOT_FOUND Serial Port information not found. + +**/ +RETURN_STATUS +EFIAPI +PlatformHookSerialPortInitialize ( + VOID + ) +{ + UINT64 *UartBase; + + if (PcdGet64 (PcdSerialRegisterBase) != 0) { + return RETURN_SUCCESS; + } + + *UartBase = CsrRead (LOONGARCH_CSR_KS1); + + return (RETURN_STATUS)PcdSet64S (PcdSerialRegisterBase, (UINTN)*UartBase); +} diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf new file mode 100644 index 0000000000..3db9de4f7e --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf @@ -0,0 +1,33 @@ +## @file +# Platform Hook Library instance for 16550 Uart. +# +# Copyright (c) 2020, ARM Ltd. All rights reserved.
+# Copyright (c) 2023 Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = Fdt16550SerialPortHookLib + MODULE_UNI_FILE = Fdt16550SerialPortHookLib.uni + FILE_GUID = 808335DB-220E-A353-887C-9AA1B7D433A1 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformHookLib|DXE_CORE DXE_DRIVER UEFI_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION + CONSTRUCTOR = PlatformHookSerialPortInitialize + +[Sources] + Fdt16550SerialPortHookLib.c + +[LibraryClasses] + BaseLib + PcdLib + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni new file mode 100644 index 0000000000..70356534e9 --- /dev/null +++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni @@ -0,0 +1,13 @@ +// /** @file +// Platform Hook Library instance for 16550 Uart. +// +// +// Copyright (c) 2020, ARM Ltd. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "Platform Hook Library instance for 16550 Uart." + +#string STR_MODULE_DESCRIPTION #language en-US "Platform Hook Library instance for 16550 Uart." -- 2.27.0 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#110724): https://edk2.groups.io/g/devel/message/110724 Mute This Topic: https://groups.io/mt/102413885/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=-