Reviewed-by: Chao Li  <lichao@loongson.cn>


Thanks,
Chao
--------

On 11月 11 2022, at 5:12 δΈ‹εˆ, xianglai li <lixianglai@loongson.cn> wrote:
Serial Port library for LoongarchQemuPkg



REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054



Cc: Bibo Mao <maobibo@loongson.cn>

Cc: Chao Li <lichao@loongson.cn>

Cc: Leif Lindholm <quic_llindhol@quicinc.com>

Cc: Liming Gao <gaoliming@byosoft.com.cn>

Cc: Michael D Kinney <michael.d.kinney@intel.com>

Signed-off-by: xianglai li <lixianglai@loongson.cn>

---

.../LoongArchQemuPkg/Include/Library/Cpu.h | 237 +++++++

.../Include/LoongArchQemuPlatform.h | 95 +++

.../Library/SerialPortLib/SerialPortLib.c | 593 ++++++++++++++++++

.../Library/SerialPortLib/SerialPortLib.inf | 39 ++

4 files changed, 964 insertions(+)

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf



diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h

new file mode 100644

index 0000000000..c6599c6ed7

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h

@@ -0,0 +1,237 @@

+/** @file

+

+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+ @par Glossary:

+ - EXC - Exception

+ - INT - Interrupt

+ - FPU - Floating Point Unit

+ - CSR - CPU Status Register

+ - READQ - Read Quad Word

+**/

+#ifndef LOONGARCH_CPU_H_

+#define LOONGARCH_CPU_H_

+

+/* Exception types decoded by machdep exception decoder */

+#define EXC_INT 0 /* HW interrupt */

+#define EXC_TLBL 1 /* TLB miss on a load */

+#define EXC_TLBS 2 /* TLB miss on a store */

+#define EXC_TLBI 3 /* TLB miss on a ifetch */

+#define EXC_TLBM 4 /* TLB modified fault */

+#define EXC_TLBRI 5 /* TLB Read-Inhibit exception */

+#define EXC_TLBXI 6 /* TLB Execution-Inhibit exception */

+#define EXC_TLBPE 7 /* TLB Privilege Error */

+#define EXC_ADE 8 /* Address Error */

+#define EXC_ALE 9 /* Unalign Access */

+#define EXC_OOB 10 /* Out of bounds */

+#define EXC_SYS 11 /* System call */

+#define EXC_BP 12 /* Breakpoint */

+#define EXC_INE 13 /* Inst. Not Exist */

+#define EXC_IPE 14 /* Inst. Privileged Error */

+#define EXC_FPDIS 15 /* FPU Disabled */

+#define EXC_LSXDIS 16 /* LSX Disabled */

+#define EXC_LASXDIS 17 /* LASX Disabled */

+#define EXC_FPE 18 /* Floating Point Exception */

+#define EXC_WATCH 19 /* Watch address reference */

+#define EXC_BAD 255 /* Undecodeable */

+

+#define COPY_SIGCODE // copy sigcode above user stack in exec

+#define ZERO $r0 /* wired zero */

+#define RA $r1 /* return address */

+#define GP $r2 /* global pointer - caller saved for PIC */

+#define SP $r3 /* stack pointer */

+#define V0 $r4 /* return value - caller saved */

+#define V1 $r5

+#define A0 $r4 /* argument registers */

+#define A1 $r5

+#define A2 $r6

+#define A3 $r7

+#define A4 $r8 /* arg reg 64 bit; caller saved in 32 bit */

+#define A5 $r9

+#define A6 $r10

+#define A7 $r11

+#define T0 $r12 /* caller saved */

+#define T1 $r13

+#define T2 $r14

+#define T3 $r15

+#define T4 $r16 /* callee saved */

+#define T5 $r17

+#define T6 $r18

+#define T7 $r19

+#define T8 $r20 /* caller saved */

+#define TP $r21 /* TLS */

+#define FP $r22 /* frame pointer */

+#define S0 $r23 /* callee saved */

+#define S1 $r24

+#define S2 $r25

+#define S3 $r26

+#define S4 $r27

+#define S5 $r28

+#define S6 $r29

+#define S7 $r30

+#define S8 $r31 /* callee saved */

+

+#define FCSR0 $r0

+

+//

+// Location of the saved registers relative to ZERO.

+// Usage is p->p_regs[XX].

+//

+#define RA_NUM 1

+#define GP_NUM 2

+#define SP_NUM 3

+#define A0_NUM 4

+#define A1_NUM 5

+#define A2_NUM 6

+#define A3_NUM 7

+#define A4_NUM 8

+#define A5_NUM 9

+#define A6_NUM 10

+#define A7_NUM 11

+#define T0_NUM 12

+#define T1_NUM 13

+#define T2_NUM 14

+#define T3_NUM 15

+#define T4_NUM 16

+#define T5_NUM 17

+#define T6_NUM 18

+#define T7_NUM 19

+#define T8_NUM 20

+#define TP_NUM 21

+#define FP_NUM 22

+#define S0_NUM 23

+#define S1_NUM 24

+#define S2_NUM 25

+#define S3_NUM 26

+#define S4_NUM 27

+#define S5_NUM 28

+#define S6_NUM 29

+#define S7_NUM 30

+#define S8_NUM 31

+

+#define FP0_NUM 0

+#define FP1_NUM 1

+#define FP2_NUM 2

+#define FP3_NUM 3

+#define FP4_NUM 4

+#define FP5_NUM 5

+#define FP6_NUM 6

+#define FP7_NUM 7

+#define FP8_NUM 8

+#define FP9_NUM 9

+#define FP10_NUM 10

+#define FP11_NUM 11

+#define FP12_NUM 12

+#define FP13_NUM 13

+#define FP14_NUM 14

+#define FP15_NUM 15

+#define FP16_NUM 16

+#define FP17_NUM 17

+#define FP18_NUM 18

+#define FP19_NUM 19

+#define FP20_NUM 20

+#define FP21_NUM 21

+#define FP22_NUM 22

+#define FP23_NUM 23

+#define FP24_NUM 24

+#define FP25_NUM 25

+#define FP26_NUM 26

+#define FP27_NUM 27

+#define FP28_NUM 28

+#define FP29_NUM 29

+#define FP30_NUM 30

+#define FP31_NUM 31

+#define FCSR_NUM 32

+#define FCC_NUM 33

+

+#ifdef __ASSEMBLY__

+#define _ULCAST_

+#define _U64CAST_

+#else

+#define _ULCAST_ (unsigned long)

+#define _U64CAST_ (u64)

+#endif

+

+#define LOONGARCH_CSR_CRMD 0

+#define LOONGARCH_CSR_PRMD 1

+#define LOONGARCH_CSR_EUEN 2

+#define CSR_EUEN_LBTEN_SHIFT 3

+#define CSR_EUEN_LBTEN (_ULCAST_(0x1) << CSR_EUEN_LBTEN_SHIFT)

+#define CSR_EUEN_LASXEN_SHIFT 2

+#define CSR_EUEN_LASXEN (_ULCAST_(0x1) << CSR_EUEN_LASXEN_SHIFT)

+#define CSR_EUEN_LSXEN_SHIFT 1

+#define CSR_EUEN_LSXEN (_ULCAST_(0x1) << CSR_EUEN_LSXEN_SHIFT)

+#define CSR_EUEN_FPEN_SHIFT 0

+#define CSR_EUEN_FPEN (_ULCAST_(0x1) << CSR_EUEN_FPEN_SHIFT)

+#define LOONGARCH_CSR_ECFG 4

+

+/* Exception status */

+#define LOONGARCH_CSR_ESTAT 5

+#define CSR_ESTAT_ESUBCODE_SHIFT 22

+#define CSR_ESTAT_ESUBCODE_WIDTH 9

+#define CSR_ESTAT_ESUBCODE (_ULCAST_(0x1ff) << CSR_ESTAT_ESUBCODE_SHIFT)

+#define CSR_ESTAT_EXC_SHIFT 16

+#define CSR_ESTAT_EXC_WIDTH 6

+#define CSR_ESTAT_EXC (_ULCAST_(0x3f) << CSR_ESTAT_EXC_SHIFT)

+#define CSR_ESTAT_IS_SHIFT 0

+#define CSR_ESTAT_IS_WIDTH 15

+#define CSR_ESTAT_IS (_ULCAST_(0x7fff) << CSR_ESTAT_IS_SHIFT)

+

+#define LOONGARCH_CSR_EPC 6

+#define LOONGARCH_CSR_BADV 7

+#define LOONGARCH_CSR_BADINST 8

+#define LOONGARCH_CSR_BADI 8

+#define LOONGARCH_CSR_EBASE 0xc /* Exception entry base address */

+#define LOONGARCH_CSR_CPUNUM 0x20 /* CPU core number */

+

+/* register number save in stack on exception */

+#define FP_BASE_NUM 34

+#define BASE_NUM 32

+#define CSR_NUM 10

+#define FP_BASE_INDEX (CSR_NUM + BASE_NUM)

+#define BOOTCORE_ID 0

+

+#define LOONGSON_IOCSR_IPI_STATUS 0x1000

+#define LOONGSON_IOCSR_IPI_EN 0x1004

+#define LOONGSON_IOCSR_IPI_SET 0x1008

+#define LOONGSON_IOCSR_IPI_CLEAR 0x100c

+#define LOONGSON_CSR_MAIL_BUF0 0x1020

+#define LOONGSON_CSR_MAIL_BUF1 0x1028

+#define LOONGSON_CSR_MAIL_BUF2 0x1030

+#define LOONGSON_CSR_MAIL_BUF3 0x1038

+

+/* Bit Domains for CFG registers */

+#define LOONGARCH_CPUCFG4 0x4

+#define LOONGARCH_CPUCFG5 0x5

+

+/* Kscratch registers */

+#define LOONGARCH_CSR_KS0 0x30

+#define LOONGARCH_CSR_KS1 0x31

+

+/* Stable timer registers */

+#define LOONGARCH_CSR_TMCFG 0x41

+#define LOONGARCH_CSR_TMCFG_EN (1ULL << 0)

+#define LOONGARCH_CSR_TMCFG_PERIOD (1ULL << 1)

+#define LOONGARCH_CSR_TMCFG_TIMEVAL (0x3fffffffffffULL << 2)

+#define LOONGARCH_CSR_TVAL 0x42 /* Timer value */

+#define LOONGARCH_CSR_CNTC 0x43 /* Timer offset */

+#define LOONGARCH_CSR_TINTCLR 0x44 /* Timer interrupt clear */

+

+/* TLB refill exception base address */

+#define LOONGARCH_CSR_TLBREBASE 0x88

+#define LOONGARCH_CSR_TLBRSAVE 0x8b /* KScratch for TLB refill exception */

+#define LOONGARCH_CSR_PGD 0x1b /* Page table base */

+

+/* Invalid addr with global=1 or matched asid in current tlb */

+#define INVTLB_ADDR_GTRUE_OR_ASID 0x6

+

+/* Bits 8 and 9 of FPU Status Register specify the rounding mode */

+#define FPU_CSR_RM 0x300

+#define FPU_CSR_RN 0x000 /* nearest */

+#define FPU_CSR_RZ 0x100 /* towards zero */

+#define FPU_CSR_RU 0x200 /* towards +Infinity */

+#define FPU_CSR_RD 0x300 /* towards -Infinity */

+

+#endif

diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h

new file mode 100644

index 0000000000..e942e6a994

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h

@@ -0,0 +1,95 @@

+/** @file

+ LoongArch Qemu Platform macro definition.

+

+ Copyright (c) 2022, Loongson Limited. All rights reserved.

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+ **/

+

+#ifndef LOONGARCH_QEMU_PLATFORM_H_

+#define LOONGARCH_QEMU_PLATFORM_H_

+

+/* Acpi pm device */

+#define LS7A_PCH_REG_BASE 0x10000000UL

+#define LS7A_ACPI_REG_BASE (LS7A_PCH_REG_BASE + 0x000D0000)

+#define LS7A_PM_CNT_BLK (0x14) /* 2 bytes */

+#define LS7A_GPE0_RESET_REG (0x30) /* 4 bytes */

+

+#define ACPI_BITMASK_SLEEP_TYPE 0x1C00

+#define ACPI_BITMASK_SLEEP_ENABLE 0x2000

+

+//---------------------------------------------

+// UART Register Offsets

+//---------------------------------------------

+#define BAUD_LOW_OFFSET 0x00

+#define BAUD_HIGH_OFFSET 0x01

+#define IER_OFFSET 0x01

+#define LCR_SHADOW_OFFSET 0x01

+#define FCR_SHADOW_OFFSET 0x02

+#define IR_CONTROL_OFFSET 0x02

+#define FCR_OFFSET 0x02

+#define EIR_OFFSET 0x02

+#define BSR_OFFSET 0x03

+#define LCR_OFFSET 0x03

+#define MCR_OFFSET 0x04

+#define LSR_OFFSET 0x05

+#define MSR_OFFSET 0x06

+

+/* character format control register */

+#define CFCR_DLAB 0x80 /* divisor latch */

+#define CFCR_SBREAK 0x40 /* send break */

+#define CFCR_PZERO 0x30 /* zero parity */

+#define CFCR_PONE 0x20 /* one parity */

+#define CFCR_PEVEN 0x10 /* even parity */

+#define CFCR_PODD 0x00 /* odd parity */

+#define CFCR_PENAB 0x08 /* parity enable */

+#define CFCR_STOPB 0x04 /* 2 stop bits */

+#define CFCR_8BITS 0x03 /* 8 data bits */

+#define CFCR_7BITS 0x02 /* 7 data bits */

+#define CFCR_6BITS 0x01 /* 6 data bits */

+#define CFCR_5BITS 0x00 /* 5 data bits */

+/* modem control register */

+#define MCR_LOOPBACK 0x10 /* loopback */

+#define MCR_IENABLE 0x08 /* output 2 = int enable */

+#define MCR_DRS 0x04 /* output 1 = xxx */

+#define MCR_RTS 0x02 /* enable RTS */

+#define MCR_DTR 0x01 /* enable DTR */

+

+/* line status register */

+#define LSR_RCV_FIFO 0x80 /* error in receive fifo */

+#define LSR_TSRE 0x40 /* transmitter empty */

+#define LSR_TXRDY 0x20 /* transmitter ready */

+#define LSR_BI 0x10 /* break detected */

+#define LSR_FE 0x08 /* framing error */

+#define LSR_PE 0x04 /* parity error */

+#define LSR_OE 0x02 /* overrun error */

+#define LSR_RXRDY 0x01 /* receiver ready */

+#define LSR_RCV_MASK 0x1f

+

+/* 16550 UART register offsets and bitfields */

+#define R_UART_RXBUF 0

+#define R_UART_TXBUF 0

+#define R_UART_BAUD_LOW 0

+#define R_UART_BAUD_HIGH 1

+#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

+#define UART_BASE_ADDRESS (0x1fe001e0)

+#define UART_BPS (115200)

+#define UART_WAIT_TIMOUT (1000000)

+#endif

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c

new file mode 100644

index 0000000000..7044db81ee

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c

@@ -0,0 +1,593 @@

+/** @file

+ UART Serial Port library functions

+

+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+ @par Glossary:

+ - Bps - Bit Per Second

+ - CTL - Control

+ - Config - Configure

+**/

+

+#include <Base.h>

+#include <Library/Cpu.h>

+#include <Library/IoLib.h>

+#include <Library/SerialPortLib.h>

+#include <LoongArchQemuPlatform.h>

+

+UINTN gUartBase = UART_BASE_ADDRESS;

+UINTN gBps = UART_BPS;

+

+/**

+ Initialize the serial device hardware.

+

+ If no initialization is required, then return RETURN_SUCCESS.

+ If the serial device was successfuly 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 serail device could not be initialized.

+**/

+RETURN_STATUS

+EFIAPI

+SerialPortInitialize (

+ VOID

+ )

+{

+ UINTN TimeOut;

+ //

+ // wait for Tx fifo to completely drain */

+ //

+ TimeOut = UART_WAIT_TIMOUT;

+ while (!(MmioRead8 ((UINTN) gUartBase + LSR_OFFSET) & LSR_TSRE)) {

+ if (--TimeOut == 0) {

+ break;

+ }

+ }

+ //

+ // Set communications format

+ //

+ MmioWrite8 ((UINTN) (gUartBase + LCR_OFFSET), CFCR_DLAB);

+

+ //

+ // Configure baud rate

+ //

+

+ MmioWrite8 ((UINTN) (gUartBase + LCR_OFFSET), CFCR_8BITS);

+ MmioWrite8 ((UINTN) (gUartBase + MCR_OFFSET), MCR_IENABLE | MCR_DTR | MCR_RTS);

+

+ 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 read operation failed.

+**/

+UINTN

+EFIAPI

+UartCtlWrite (

+ IN UINT8 *Buffer,

+ IN UINTN NumberOfBytes,

+ IN UINTN CtlAddr

+)

+{

+ UINTN Result;

+ UINT8 Data;

+

+ if (Buffer == NULL) {

+ return 0;

+ }

+

+ Result = NumberOfBytes;

+

+ while (NumberOfBytes--) {

+ //

+ // Wait for the serail port to be ready.

+ //

+ do {

+ Data = MmioRead8 (CtlAddr + LSR_OFFSET);

+ } while ((Data & LSR_TXRDY) == 0);

+ MmioWrite8 (CtlAddr, *Buffer++);

+ }

+ return Result;

+}

+/**

+ Writes data to serial port.

+

+ @param Buffer Pointer to the data buffer to store the data writed to serial port.

+ @param NumberOfBytes Number of bytes to write to the serial port.

+

+ @retval 0 NumberOfBytes is 0.

+ @retval >0 The number of bytes write the serial port.

+ If this value is less than NumberOfBytes, then the write operation failed.

+**/

+UINTN

+EFIAPI

+SerialPortWrite (

+ IN UINT8 *Buffer,

+ IN UINTN NumberOfBytes

+)

+{

+ return UartCtlWrite (Buffer, NumberOfBytes, gUartBase);

+}

+/**

+ 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

+UartCtlRead (

+ OUT UINT8 *Buffer,

+ IN UINTN NumberOfBytes,

+ IN UINTN CtlAddr

+)

+{

+ UINTN Result;

+ UINT8 Data;

+

+ if (NULL == Buffer) {

+ return 0;

+ }

+

+ Result = NumberOfBytes;

+

+ while (NumberOfBytes--) {

+ //

+ // Wait for the serail port to be ready.

+ //

+ do {

+ Data = MmioRead8 (CtlAddr + LSR_OFFSET);

+ } while ((Data & LSR_RXRDY) == 0);

+

+ *Buffer++ = MmioRead8 (CtlAddr);

+ }

+ return Result;

+}

+/**

+ Read data from serial port.

+

+ @param Buffer Pointer to the data buffer to store the data read from serial port.

+ @param NumberOfBytes Number of bytes to read from the serial port.

+

+ @retval 0 NumberOfBytes is 0.

+ @retval >0 The number of bytes read from the serial port.

+ If this value is less than NumberOfBytes, then the read operation failed.

+**/

+UINTN

+EFIAPI

+SerialPortRead (

+ OUT UINT8 *Buffer,

+ IN UINTN NumberOfBytes

+)

+{

+ return UartCtlRead (Buffer, NumberOfBytes, gUartBase);

+}

+/**

+ 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

+ )

+{

+ UINT8 Data;

+

+ //

+ // Read the serial port status.

+ //

+ Data = MmioRead8 ((UINTN) gUartBase + LSR_OFFSET);

+

+ return (BOOLEAN) ((Data & LSR_RXRDY) != 0);

+}

+/**

+ To get serial register base address.

+

+ @param VOID

+

+ @return serial register base address.

+**/

+UINTN

+GetSerialRegisterBase (

+ VOID

+ )

+{

+ return gUartBase;

+}

+/**

+ Read an 8-bit register.

+ @param Base The base address register of UART device.

+ @param Offset The offset of the register to read.

+

+ @return The value read from the 16550 register.

+**/

+UINT8

+SerialPortReadRegister (

+ UINTN Base,

+ UINTN Offset

+ )

+{

+ return MmioRead8 (Base + Offset);

+}

+

+/**

+ Write an 8-bit register.

+ @param Base The base address register of UART device.

+ @param Offset The offset of the register to write.

+ @param Value The value to write to the register specified by Offset.

+

+ @return The value written to the 16550 register.

+**/

+UINT8

+SerialPortWriteRegister (

+ UINTN Base,

+ UINTN Offset,

+ UINT8 Value

+ )

+{

+ return MmioWrite8 (Base + Offset, Value);

+}

+

+/**

+ 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

+UartCtlConfig (

+ 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,

+ IN UINTN CtlAddr

+ )

+{

+ UINTN SerialRegisterBase;

+ UINT8 Lcr;

+ UINT8 LcrData;

+ UINT8 LcrParity;

+ UINT8 LcrStop;

+

+ SerialRegisterBase = CtlAddr;

+ if (SerialRegisterBase ==0) {

+ return RETURN_UNSUPPORTED;

+ }

+

+ //

+ // Check for default settings and fill in actual values.

+ //

+ if (*BaudRate == 0) {

+ *BaudRate = PcdGet32 (PcdSerialBaudRate);

+ }

+

+ 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;

+ }

+ }

+ SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);

+

+ //

+ // 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;

+}

+/**

+ Set the serial port Attributes.

+

+ @param VOID

+

+ @return serial register base address.

+**/

+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;

+

+ SerialRegisterBase = GetSerialRegisterBase ();

+

+ return UartCtlConfig (&gBps, ReceiveFifoDepth, Timeout, Parity, DataBits, StopBits,

+ SerialRegisterBase);

+}

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf

new file mode 100644

index 0000000000..22cf82cf79

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf

@@ -0,0 +1,39 @@

+## @file

+# UART Serial Port library functions

+#

+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+#

+# SPDX-License-Identifier: BSD-2-Clause-Patent

+#

+##

+

+[Defines]

+ INF_VERSION = 0x00010005

+ BASE_NAME = PcAtSerialPortLib

+ FILE_GUID = f4fb883d-8138-4f29-bb0c-c574e9312c74

+ MODULE_TYPE = BASE

+ VERSION_STRING = 1.0

+ LIBRARY_CLASS = SerialPortLib

+

+#

+# VALID_ARCHITECTURES = LOONGARCH64

+#

+

+[Sources]

+ SerialPortLib.c

+

+[Packages]

+ MdePkg/MdePkg.dec

+ MdeModulePkg/MdeModulePkg.dec

+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec

+

+[LibraryClasses]

+ BaseLib

+ IoLib

+ PcdLib

+

+[Pcd]

+ gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl ## CONSUMES

+ gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate ## CONSUMES

+ gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl ## CONSUMES

+ gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate ## CONSUMES

--

2.31.1