From nobody Mon Sep 16 18:57:43 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+96449+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+96449+1787277+3901457@groups.io ARC-Seal: i=1; a=rsa-sha256; t=1668652804; cv=none; d=zohomail.com; s=zohoarc; b=N0kBLMMyqxr3/PSI0AxIajatJz5Bs26TU6pikzEmOZJ2b9XhZ2zKUryV+1uHp6EADbDZZTMhcdsoH0C/KJmdYkMzLiVffp6Tuox4PCm+8J68xV8umOl16iTTCOhUxauSG75dePKpD0zR75LzEGt3IatyPk7GadHeMQtNsa5qVng= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1668652804; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=OSFgTkYwZTlj2NLIibbJZedJ37vP0CpOL5khEz32Cwo=; b=c7OBnBWjZ0dHGx8GRURq47+jK392S+y3yJGS9gK5iQWF6+c282rO2I6qVyslX4Ow81Vouv0injg6mpM3mpnvera1H5Q6vasu9l4IOzxYQw4tqFDT8uLArUiygjVQAVNWjHSdTdmQxczKRyHSxeVMzBTS2qzJcDBOVclAuLyuPhg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+96449+1787277+3901457@groups.io Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1668652803937216.412222201227; Wed, 16 Nov 2022 18:40:03 -0800 (PST) Return-Path: X-Received: by 127.0.0.2 with SMTP id hQneYY1788612xI4IN7dESro; Wed, 16 Nov 2022 18:40:03 -0800 X-Received: from loongson.cn (loongson.cn [114.242.206.163]) by mx.groups.io with SMTP id smtpd.web11.6321.1668652800888360549 for ; Wed, 16 Nov 2022 18:40:02 -0800 X-Received: from loongson.cn (unknown [10.2.5.185]) by gateway (Coremail) with SMTP id _____8Cxjdr8nnVj8CMIAA--.23102S3; Thu, 17 Nov 2022 10:39:56 +0800 (CST) X-Received: from localhost.localdomain (unknown [10.2.5.185]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxLeD5nnVjCpcVAA--.56818S3; Thu, 17 Nov 2022 10:39:56 +0800 (CST) From: "xianglai" To: devel@edk2.groups.io Cc: Ard Biesheuvel , Bibo Mao , Chao Li , Leif Lindholm , Liming Gao , Michael D Kinney Subject: [edk2-devel] [edk2-platforms][PATCH V6 01/16] Platform/Loongson: Add Serial Port library Date: Thu, 17 Nov 2022 10:39:27 +0800 Message-Id: <5a0ce4e0c0ef2adde6017074c59813d34f5cdf34.1668652102.git.lixianglai@loongson.cn> In-Reply-To: References: MIME-Version: 1.0 X-CM-TRANSID: AQAAf8DxLeD5nnVjCpcVAA--.56818S3 X-CM-SenderInfo: 5ol0xt5qjotxo6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBjvAXoWDWw4DGrWDGFy8Cw18tFWxXrb_yoWrWF13Xo W8Ja1fCw15Jw1xuws5Kw17Wr18Xw4I9rn5JF4YgFWUX3Z7Xws8uw1DX3sYgwn3Gryjqr15 Gr1xtas7JFZay34rn29KB7ZKAUJUUUU5529EdanIXcx71UUUUU7KY7ZEXasCq-sGcSsGvf J3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnRJU UUqq1xkIjI8I6I8E6xAIw20EY4v20xvaj40_Wr0E3s1l8cAvFVAK0II2c7xJM28CjxkF64 kEwVA0rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVW8JVW5JwA2z4x0Y4vE2Ix0cI8IcVCY 1x0267AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E87Iv6x kF7I0E14v26r4UJVWxJr1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0cIa020E x4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E74AGY7Cv6cx26rWlOx8S6xCaFV Cjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JMxAIw28IcxkI7VAKI48JMxAIw28IcVCjz48v 1sIEY20_WwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I3I 0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAI cVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Jr0_Gr1lIxAIcV CF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIE c7CjxVAFwI0_Jr0_GrUvcSsGvfC2KfnxnUUI43ZEXa7xRE6wZ7UUUUU== Precedence: Bulk List-Unsubscribe: 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,lixianglai@loongson.cn X-Gm-Message-State: G44Hm9Y0h43NExN6b7NfWxBAx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1668652803; bh=CgH0rWuJ/SDaWlMSjgKaNU8zotOGcYIsOw7FhhGQM7I=; h=Cc:Date:From:Reply-To:Subject:To; b=uYhlJYESJBtCCMQCpxGStRLdJChGT/KnJlintfPrJFIMVmE5f1r0ut+H+T3i2SasnMj 22nb7+KZ/q5gkUWEO+l4lillxJywA1rgt9Zbaq73nNrXwVoe12N5CuEOjQbYLB/knbDv2 UPOgEuqSeF3wbPVSbLL1BNKzW+qp+0F7OMs= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1668652805848100017 Content-Type: text/plain; charset="utf-8" Serial Port library for LoongarchQemuPkg REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D4054 Cc: Ard Biesheuvel Cc: Bibo Mao Cc: Chao Li Cc: Leif Lindholm Cc: Liming Gao Cc: Michael D Kinney Signed-off-by: xianglai li Reviewed-by: Chao Li --- .../Include/Guid/Early16550UartBaseAddress.h | 22 + .../LoongArchQemuPkg/Include/Library/Cpu.h | 237 +++++ .../Fdt16550SerialPortHookLib.c | 57 ++ .../Fdt16550SerialPortHookLib.inf | 38 + .../SerialPortLib/EarlySerialPortLib16550.c | 900 ++++++++++++++++++ .../SerialPortLib/EarlySerialPortLib16550.inf | 46 + 6 files changed, 1300 insertions(+) create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Guid/Early16= 550UartBaseAddress.h create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/Fdt16550Seri= alPortHookLib/Fdt16550SerialPortHookLib.c create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/Fdt16550Seri= alPortHookLib/Fdt16550SerialPortHookLib.inf create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLi= b/EarlySerialPortLib16550.c create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLi= b/EarlySerialPortLib16550.inf diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Guid/Early16550Uart= BaseAddress.h b/Platform/Loongson/LoongArchQemuPkg/Include/Guid/Early16550U= artBaseAddress.h new file mode 100644 index 0000000000..95aa8f4bc0 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Include/Guid/Early16550UartBaseAdd= ress.h @@ -0,0 +1,22 @@ +/** @file + GUID for the HOB that caches the base address of the 16550 serial port, = for + when PCD access is not available. + + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights r= eserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EARLY_16550_UART_BASE_ADDRESS_H__ +#define EARLY_16550_UART_BASE_ADDRESS_H__ + +#define EARLY_16550_UART_BASE_ADDRESS_GUID { \ + 0xea67ca3e, 0x1f54, 0x436b, { \ + 0x97, 0x88, 0xd4, 0xeb, 0x29, 0xc3, 0x42, 0x67 \ + } \ + } + +extern EFI_GUID gEarly16550UartBaseAddressGuid; + +#endif // EARLY_16550_UART_BASE_ADDRESS_H__ diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h b/Pla= tform/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 r= eserved.
+ + 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 excep= tion */ +#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 f= or 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_SHIF= T) +#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_SHIF= T) + +#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 addres= s */ +#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 exc= eption */ +#define LOONGARCH_CSR_PGD 0x1b /* Page table base */ + +/* Invalid addr with global=3D1 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/Library/Fdt16550SerialPortH= ookLib/Fdt16550SerialPortHookLib.c b/Platform/Loongson/LoongArchQemuPkg/Lib= rary/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c new file mode 100644 index 0000000000..2b766e10dc --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Library/Fdt16550SerialPortHookLib/= Fdt16550SerialPortHookLib.c @@ -0,0 +1,57 @@ +/** @file + Platform Hook Library instance for 16550 Uart. + + Copyright (c) 2022, Loongson Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +/** Platform hook to retrieve the 16550 UART base address from the GUID Hob + 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 + ) +{ + VOID *Hob; + UINT64 *UartBase; + + if (PcdGet64 (PcdSerialRegisterBase) !=3D 0) { + return RETURN_SUCCESS; + } + + Hob =3D GetFirstGuidHob (&gEarly16550UartBaseAddressGuid); + if ((Hob =3D=3D NULL) || (GET_GUID_HOB_DATA_SIZE (Hob) !=3D sizeof (*Uar= tBase))) { + return RETURN_NOT_FOUND; + } + + UartBase =3D GET_GUID_HOB_DATA (Hob); + if ((UINTN)*UartBase =3D=3D 0) { + return RETURN_NOT_FOUND; + } + + return (RETURN_STATUS)PcdSet64S (PcdSerialRegisterBase, (UINTN)*UartBase= ); +} diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/Fdt16550SerialPortH= ookLib/Fdt16550SerialPortHookLib.inf b/Platform/Loongson/LoongArchQemuPkg/L= ibrary/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf new file mode 100644 index 0000000000..cbc99864be --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Library/Fdt16550SerialPortHookLib/= Fdt16550SerialPortHookLib.inf @@ -0,0 +1,38 @@ +## @file +# Platform Hook Library instance for 16550 Uart. +# +# Copyright (c) 2022, Loongson Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x0001001B + BASE_NAME =3D Fdt16550SerialPortHookLib + MODULE_UNI_FILE =3D Fdt16550SerialPortHookLib.uni + FILE_GUID =3D C6DFD3F0-179D-4376-89A5-F641A2E7EFB5 + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D PlatformHookLib|DXE_CORE DXE_DRIVER U= EFI_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION + CONSTRUCTOR =3D PlatformHookSerialPortInitialize + +[Sources] + Fdt16550SerialPortHookLib.c + +[LibraryClasses] + BaseLib + PcdLib + HobLib + +[Packages] + Platform/Loongson/LoongArchQemuPkg/Loongson.dec + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase + +[Guids] + gEarly16550UartBaseAddressGuid diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/Early= SerialPortLib16550.c b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPor= tLib/EarlySerialPortLib16550.c new file mode 100644 index 0000000000..c713c6e9d6 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/EarlySerialP= ortLib16550.c @@ -0,0 +1,900 @@ +/** @file + 16550 UART Serial Port library functions + + Copyright (c) 2022, Loongson 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 =3D 0 +#define R_UART_TXBUF 0 // LCR_DLAB =3D 0 +#define R_UART_BAUD_LOW 0 // LCR_DLAB =3D 1 +#define R_UART_BAUD_HIGH 1 // LCR_DLAB =3D 1 +#define R_UART_IER 1 // LCR_DLAB =3D 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 val= ue 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 tha= t is specified + by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMI= O 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) =3D=3D 32) { + return (UINT8)MmioRead32 (Base + Offset * PcdGet32 (PcdSerialRegiste= rStride)); + } + + 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 va= lue 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 tha= t is specified + by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMI= O 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 Of= fset. + + @return The value written to the 16550 register. +**/ +UINT8 +SerialPortWriteRegister ( + UINTN Base, + UINTN Offset, + UINT8 Value + ) +{ + if (PcdGetBool (PcdSerialUseMmio)) { + if (PcdGet8 (PcdSerialRegisterAccessWidth) =3D=3D 32) { + return (UINT8)MmioWrite32 (Base + Offset * PcdGet32 (PcdSerialRegist= erStride), (UINT8)Value); + } + + return MmioWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride),= Value); + } else { + return IoWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), V= alue); + } +} + +/** 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. +**/ +STATIC +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 =3D=3D NULL) || (fdt_check_header (Fdt) !=3D 0)) { + return EFI_INVALID_PARAMETER; + } + + // The "chosen" node resides at the root of the DT. Fetch it. + ChosenNode =3D fdt_path_offset (Fdt, "/chosen"); + if (ChosenNode < 0) { + return EFI_NOT_FOUND; + } + + Prop =3D 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 =3D ScanMem8 (Prop, ':', PropSize); + if (Path =3D=3D NULL) { + PathLen =3D AsciiStrLen (Prop); + } else { + PathLen =3D Path - Prop; + } + + // Aliases cannot start with a '/', so it must be the actual path. + if (Prop[0] =3D=3D '/') { + SerialConsoleNode =3D fdt_path_offset_namelen (Fdt, Prop, PathLen); + } else { + // Lookup the alias, as this contains the actual path. + Path =3D fdt_get_alias_namelen (Fdt, Prop, PathLen); + if (Path =3D=3D NULL) { + return EFI_NOT_FOUND; + } + + SerialConsoleNode =3D fdt_path_offset (Fdt, Path); + } + + NodeStatus =3D fdt_getprop (Fdt, SerialConsoleNode, "status", &Len); + if ((NodeStatus !=3D NULL) && (AsciiStrCmp (NodeStatus, "okay") !=3D 0))= { + return EFI_NOT_FOUND; + } + + RegProperty =3D fdt_getprop (Fdt, SerialConsoleNode, "reg", &Len); + if (Len !=3D 16) { + return EFI_INVALID_PARAMETER; + } + + *SerialConsoleAddress =3D fdt64_to_cpu (ReadUnaligned64 (RegProperty)); + + return EFI_SUCCESS; +} + +/** + 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 i= n 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 =3D (VOID*)(UINTN)PcdGet64 (PcdDeviceTreeBase); + Status =3D 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 + // =3D=3D=3D =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D + // 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)) =3D=3D (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 + // =3D=3D=3D =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D + // 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)) !=3D (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_SU= CCESS. + 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 =3D PcdGet32 (PcdSerialClockRate) / (PcdGet32 (PcdSerialBaudRate= ) * 16); + if ((PcdGet32 (PcdSerialClockRate) % (PcdGet32 (PcdSerialBaudRate) * 16)= ) >=3D PcdGet32 (PcdSerialBaudRate) * 8) { + Divisor++; + } + + // + // Get the base address of the serial port in either I/O or MMIO space + // + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return RETURN_DEVICE_ERROR; + } + + // + // See if the serial port is already initialized + // + Initialized =3D TRUE; + if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & 0x3F) != =3D (PcdGet8 (PcdSerialLineControl) & 0x3F)) { + Initialized =3D FALSE; + } + + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialP= ortReadRegister (SerialRegisterBase, R_UART_LCR) | B_UART_LCR_DLAB)); + CurrentDivisor =3D SerialPortReadRegister (SerialRegisterBase, R_UART_= BAUD_HIGH) << 8; + CurrentDivisor |=3D (UINT32)SerialPortReadRegister (SerialRegisterBase, = R_UART_BAUD_LOW); + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialP= ortReadRegister (SerialRegisterBase, R_UART_LCR) & ~B_UART_LCR_DLAB)); + if (CurrentDivisor !=3D Divisor) { + Initialized =3D 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_UAR= T_LSR_TEMT | B_UART_LSR_TXRDY)) !=3D (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)(D= ivisor >> 8)); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Di= visor & 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 devic= e. + 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 =3D=3D NULL) { + return 0; + } + + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return 0; + } + + if (NumberOfBytes =3D=3D 0) { + // + // Flush the hardware + // + + // + // Wait for both the transmit FIFO and shift register empty. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_U= ART_LSR_TEMT | B_UART_LSR_TXRDY)) !=3D (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 =3D 1; + if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFOE) !=3D 0) { + if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFO64) =3D=3D 0) { + FifoSize =3D 16; + } else { + FifoSize =3D PcdGet32 (PcdSerialExtendedTxFifoSize); + } + } + + Result =3D NumberOfBytes; + while (NumberOfBytes !=3D 0) { + // + // Wait for the serial port to be ready, to make sure both the transmi= t FIFO + // and shift register empty. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_U= ART_LSR_TEMT | B_UART_LSR_TXRDY)) !=3D (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)= ) { + } + + // + // Fill then entire Tx FIFO + // + for (Index =3D 0; Index < FifoSize && NumberOfBytes !=3D 0; Index++, N= umberOfBytes--, 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 re= ad 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 =3D=3D Buffer) { + return 0; + } + + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return 0; + } + + Mcr =3D (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) = & ~B_UART_MCR_RTS); + + for (Result =3D 0; NumberOfBytes-- !=3D 0; Result++, Buffer++) { + // + // Wait for the serial port to have some data. + // + while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UA= RT_LSR_RXRDY) =3D=3D 0) { + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Set RTS to let the peer send some data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(M= cr | 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 =3D 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 FALS= E is returned. + + @retval TRUE Data is waiting to be read from the serial devi= ce. + @retval FALSE There is no data waiting to be read from the se= rial device. +**/ +BOOLEAN +EFIAPI +SerialPortPoll ( + VOID + ) +{ + UINTN SerialRegisterBase; + + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return FALSE; + } + + // + // Read the serial port status + // + if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LS= R_RXRDY) !=3D 0) { + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + // + // Clear RTS to prevent peer from sending data + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(Ser= ialPortReadRegister (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)(Seria= lPortReadRegister (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 seria= l device. + @retval RETURN_UNSUPPORTED The serial device does not support this op= eration. + @retval RETURN_DEVICE_ERROR The serial device is not functioning corre= ctly. +**/ +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))) !=3D 0) + { + return RETURN_UNSUPPORTED; + } + + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return RETURN_UNSUPPORTED; + } + + // + // Read the Modem Control Register. + // + Mcr =3D SerialPortReadRegister (SerialRegisterBase, R_UART_MCR); + Mcr &=3D (~(B_UART_MCR_DTRC | B_UART_MCR_RTS)); + + if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) =3D=3D EFI_SERIAL_DATA_TE= RMINAL_READY) { + Mcr |=3D B_UART_MCR_DTRC; + } + + if ((Control & EFI_SERIAL_REQUEST_TO_SEND) =3D=3D EFI_SERIAL_REQUEST_TO_= SEND) { + Mcr |=3D 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 si= gnals 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 op= eration. + @retval RETURN_DEVICE_ERROR The serial device is not functioning corre= ctly. +**/ +RETURN_STATUS +EFIAPI +SerialPortGetControl ( + OUT UINT32 *Control + ) +{ + UINTN SerialRegisterBase; + UINT8 Msr; + UINT8 Mcr; + UINT8 Lsr; + + SerialRegisterBase =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return RETURN_UNSUPPORTED; + } + + *Control =3D 0; + + // + // Read the Modem Status Register. + // + Msr =3D SerialPortReadRegister (SerialRegisterBase, R_UART_MSR); + + if ((Msr & B_UART_MSR_CTS) =3D=3D B_UART_MSR_CTS) { + *Control |=3D EFI_SERIAL_CLEAR_TO_SEND; + } + + if ((Msr & B_UART_MSR_DSR) =3D=3D B_UART_MSR_DSR) { + *Control |=3D EFI_SERIAL_DATA_SET_READY; + } + + if ((Msr & B_UART_MSR_RI) =3D=3D B_UART_MSR_RI) { + *Control |=3D EFI_SERIAL_RING_INDICATE; + } + + if ((Msr & B_UART_MSR_DCD) =3D=3D B_UART_MSR_DCD) { + *Control |=3D EFI_SERIAL_CARRIER_DETECT; + } + + // + // Read the Modem Control Register. + // + Mcr =3D SerialPortReadRegister (SerialRegisterBase, R_UART_MCR); + + if ((Mcr & B_UART_MCR_DTRC) =3D=3D B_UART_MCR_DTRC) { + *Control |=3D EFI_SERIAL_DATA_TERMINAL_READY; + } + + if ((Mcr & B_UART_MCR_RTS) =3D=3D B_UART_MCR_RTS) { + *Control |=3D EFI_SERIAL_REQUEST_TO_SEND; + } + + if (PcdGetBool (PcdSerialUseHardwareFlowControl)) { + *Control |=3D EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE; + } + + // + // Read the Line Status Register. + // + Lsr =3D SerialPortReadRegister (SerialRegisterBase, R_UART_LSR); + + if ((Lsr & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) =3D=3D (B_UART_LSR_TEMT= | B_UART_LSR_TXRDY)) { + *Control |=3D EFI_SERIAL_OUTPUT_BUFFER_EMPTY; + } + + if ((Lsr & B_UART_LSR_RXRDY) =3D=3D 0) { + *Control |=3D EFI_SERIAL_INPUT_BUFFER_EMPTY; + } + + return RETURN_SUCCESS; +} + +/** + Sets the baud rate, receive FIFO depth, transmit/receice time out, parit= y, + 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 d= evice's default time + out value. + On output, the value actually set. + @param Parity The type of parity to use on this serial devic= e. A Parity value of + DefaultParity will use the device's default pa= rity value. + On output, the value actually set. + @param DataBits The number of data bits to use on the serial d= evice. 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 ser= ial device. + @retval RETURN_UNSUPPORTED The serial device does not support thi= s operation. + @retval RETURN_INVALID_PARAMETER One or more of the attributes has an u= nsupported value. + @retval RETURN_DEVICE_ERROR The serial device is not functioning c= orrectly. +**/ +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 =3D GetSerialRegisterBase (); + if (SerialRegisterBase =3D=3D 0) { + return RETURN_UNSUPPORTED; + } + + // + // Check for default settings and fill in actual values. + // + if (*BaudRate =3D=3D 0) { + *BaudRate =3D PcdGet32 (PcdSerialBaudRate); + } + + SerialBaudRate =3D (UINT32)*BaudRate; + + if (*DataBits =3D=3D 0) { + LcrData =3D (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3); + *DataBits =3D LcrData + 5; + } else { + if ((*DataBits < 5) || (*DataBits > 8)) { + return RETURN_INVALID_PARAMETER; + } + + // + // Map 5..8 to 0..3 + // + LcrData =3D (UINT8)(*DataBits - (UINT8)5); + } + + if (*Parity =3D=3D DefaultParity) { + LcrParity =3D (UINT8)((PcdGet8 (PcdSerialLineControl) >> 3) & 0x7); + switch (LcrParity) { + case 0: + *Parity =3D NoParity; + break; + + case 3: + *Parity =3D EvenParity; + break; + + case 1: + *Parity =3D OddParity; + break; + + case 7: + *Parity =3D SpaceParity; + break; + + case 5: + *Parity =3D MarkParity; + break; + + default: + break; + } + } else { + switch (*Parity) { + case NoParity: + LcrParity =3D 0; + break; + + case EvenParity: + LcrParity =3D 3; + break; + + case OddParity: + LcrParity =3D 1; + break; + + case SpaceParity: + LcrParity =3D 7; + break; + + case MarkParity: + LcrParity =3D 5; + break; + + default: + return RETURN_INVALID_PARAMETER; + } + } + + if (*StopBits =3D=3D DefaultStopBits) { + LcrStop =3D (UINT8)((PcdGet8 (PcdSerialLineControl) >> 2) & 0x1); + switch (LcrStop) { + case 0: + *StopBits =3D OneStopBit; + break; + + case 1: + if (*DataBits =3D=3D 5) { + *StopBits =3D OneFiveStopBits; + } else { + *StopBits =3D TwoStopBits; + } + + break; + + default: + break; + } + } else { + switch (*StopBits) { + case OneStopBit: + LcrStop =3D 0; + break; + + case OneFiveStopBits: + case TwoStopBits: + LcrStop =3D 1; + break; + + default: + return RETURN_INVALID_PARAMETER; + } + } + + // + // Calculate divisor for baud generator + // Ref_Clk_Rate / Baud_Rate / 16 + // + Divisor =3D PcdGet32 (PcdSerialClockRate) / (SerialBaudRate * 16); + if ((PcdGet32 (PcdSerialClockRate) % (SerialBaudRate * 16)) >=3D SerialB= audRate * 8) { + Divisor++; + } + + // + // Configure baud rate + // + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB= ); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8)(D= ivisor >> 8)); + SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Di= visor & 0xff)); + + // + // Clear DLAB and configure Data Bits, Parity, and Stop Bits. + // Strip reserved bits from line control value + // + Lcr =3D (UINT8)((LcrParity << 3) | (LcrStop << 2) | LcrData); + SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(Lcr & 0= x3F)); + + return RETURN_SUCCESS; +} diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/Early= SerialPortLib16550.inf b/Platform/Loongson/LoongArchQemuPkg/Library/SerialP= ortLib/EarlySerialPortLib16550.inf new file mode 100644 index 0000000000..ee7b5fda18 --- /dev/null +++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/EarlySerialP= ortLib16550.inf @@ -0,0 +1,46 @@ +## @file +# SerialPortLib instance for 16550 UART. +# +# Copyright (c) 2022, Loongson Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D EarlySerialPortLib16550 + FILE_GUID =3D f4fb883d-8138-4f29-bb0c-c574e9312c74 + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.1 + LIBRARY_CLASS =3D SerialPortLib + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + Platform/Loongson/LoongArchQemuPkg/Loongson.dec + +[LibraryClasses] + BaseLib + IoLib + PcdLib + FdtLib + +[Sources] + EarlySerialPortLib16550.c + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterAccessWidth ## SOMET= IMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseMmio ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialDetectCable ## SOMET= IMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialFifoControl ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize ## CONSU= MES + gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride ## CONSU= MES + gLoongArchQemuPkgTokenSpaceGuid.PcdDeviceTreeBase ## CONSU= MES --=20 2.31.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#96449): https://edk2.groups.io/g/devel/message/96449 Mute This Topic: https://groups.io/mt/95082586/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-