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 78024740034 for ; Wed, 10 Jan 2024 01:25:27 +0000 (UTC) DKIM-Signature: a=rsa-sha256; bh=h51Yq1o+bKbzNc52xNfA69gzvnOWBbKSqzc29LvK90Q=; c=relaxed/simple; d=groups.io; h=Subject:To:Cc:References:From:Message-ID:Date:User-Agent:MIME-Version:In-Reply-To:Precedence:List-Subscribe:List-Help:Sender:List-Id:Mailing-List:Delivered-To:Reply-To:List-Unsubscribe-Post:List-Unsubscribe:Content-Type:Content-Language:Content-Transfer-Encoding; s=20140610; t=1704849926; v=1; b=SY5k3Gi97HrCTbcWPlRPoLQrO2rHsjtadvRLEcubn/ro8u6FmHp3QJ10z59tDnzI3mKTvCCO aZE6oCpfE16vyMEwv9eQ0A04PNOHfM1+q0WiGprJs9G/0HYsgj2zTjdYrlXdwxtnzbgY0vsP6tw VjHsGVlN3cgpmmJR0b12J2B8= X-Received: by 127.0.0.2 with SMTP id DRK8YY7687511xYE4lQKHzPf; Tue, 09 Jan 2024 17:25:26 -0800 X-Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by mx.groups.io with SMTP id smtpd.web10.2090.1704849924461307584 for ; Tue, 09 Jan 2024 17:25:25 -0800 X-Received: from loongson.cn (unknown [10.20.42.173]) by gateway (Coremail) with SMTP id _____8Bxnrv_8Z1loq4DAA--.3071S3; Wed, 10 Jan 2024 09:25:19 +0800 (CST) X-Received: from [10.20.42.173] (unknown [10.20.42.173]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxG9798Z1lWO8JAA--.26417S3; Wed, 10 Jan 2024 09:25:17 +0800 (CST) Subject: Re: [edk2-devel] [PATCH v6 28/36] OvmfPkg/LoongArchVirt: Add the early serial port output library To: Chao Li , devel@edk2.groups.io Cc: Ard Biesheuvel , Jiewen Yao , Jordan Justen , Gerd Hoffmann , Dongyan Qian , Xianglai Li References: <20240105094118.2279380-1-lichao@loongson.cn> <20240105094559.2281750-1-lichao@loongson.cn> From: "maobibo" Message-ID: <190c82d5-a6f4-30d7-f5ce-1b08b11b9319@loongson.cn> Date: Wed, 10 Jan 2024 09:25:17 +0800 User-Agent: Mozilla/5.0 (X11; Linux loongarch64; rv:68.0) Gecko/20100101 Thunderbird/68.7.0 MIME-Version: 1.0 In-Reply-To: <20240105094559.2281750-1-lichao@loongson.cn> X-CM-TRANSID: AQAAf8DxG9798Z1lWO8JAA--.26417S3 X-CM-SenderInfo: xpdruxter6z05rqj20fqof0/ X-Coremail-Antispam: 1Uk129KBj9fXoWftFyxXFyrJr4rZw1rKr4ftFc_yoW5XFWrto WrtwsIyr15J34UurZ5t34xWr48Jws2vwn3AFWDKr4DXFn7Zan5uwnrXwn5trs3Gr1jqFn5 Wr1xJas7JFWkta4rl-sFpf9Il3svdjkaLaAFLSUrUUUUbb8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYx7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUXVWUAwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26r1I6r4UM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26r1j6r4UM28EF7xvwVC2z280aVAFwI0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIEc7CjxVAF wI0_Gr1j6F4UJwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYIkI8VC2zVCFFI 0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUAVWUtwAv7VC2z280 aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcVAKI48JMxk0xIA0c2IEe2 xFo4CEbIxvr21l42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAq x4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r 43MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF 7I0E14v26r1j6r4UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxV WUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v26r1j6r4UYxBIdaVFxhVjvjDU0xZFpf9x07j8 3kZUUUUU= 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,maobibo@loongson.cn List-Unsubscribe-Post: List-Unsubscribe=One-Click List-Unsubscribe: X-Gm-Message-State: Ev4cVC6FbJJB6vDPzR86x5aWx7686176AA= Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US 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=SY5k3Gi9; 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 On 2024/1/5 下午5:45, Chao Li wrote: > Add a early serial port output library into LoongArchVirt that named > EarlyFdtSerialPortLib16550, this library is referenced from > MdeModulePkg. > > This library is used in the PEI phase. Since the serial port address can > not be saved in memory of the LoongArch QEMU virtual machine in the PEI > phase, the serial prot base address will be obtained from the FDT before > each output. > > BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584 > > Cc: Ard Biesheuvel > Cc: Jiewen Yao > Cc: Jordan Justen > Cc: Gerd Hoffmann > Cc: Bibo Mao > Cc: Dongyan Qian > Signed-off-by: Chao Li > Co-authored-by: Xianglai Li > --- > .../EarlyFdtSerialPortLib16550.c | 815 ++++++++++++++++++ > .../EarlyFdtSerialPortLib16550.inf | 46 + > 2 files changed, 861 insertions(+) > create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c > create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf > > diff --git a/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c > new file mode 100644 > index 0000000000..8cc108501c > --- /dev/null > +++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c > @@ -0,0 +1,815 @@ > +/** @file > + 16550 UART Serial Port library functions > + > + Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#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 > + > +/** > + 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 = FdtSerialGetConsolePort (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..443c92de67 > --- /dev/null > +++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf > @@ -0,0 +1,46 @@ > +## @file > +# SerialPortLib instance for 16550 UART. > +# > +# Copyright (c) 2024, 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 > + FdtSerialPortAddressLib > + IoLib > + PcdLib > + > +[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 > Reviewed-by: Bibo Mao -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#113500): https://edk2.groups.io/g/devel/message/113500 Mute This Topic: https://groups.io/mt/103540127/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=-