public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "maobibo" <maobibo@loongson.cn>
To: xianglai li <lixianglai@loongson.cn>, devel@edk2.groups.io
Cc: quic_llindhol@quicinc.com, michael.d.kinney@intel.com,
	kraxel@redhat.com, ardb@kernel.org
Subject: Re: [edk2-platforms][PATCH V4 01/14] Platform/Loongson: Add Serial Port library
Date: Sat, 29 Oct 2022 11:01:29 +0800	[thread overview]
Message-ID: <95df5042-2386-1ec7-19e4-4dd6447df5b8@loongson.cn> (raw)
In-Reply-To: <47c25bb6e08ced7ede28908892636df4177df883.1666335216.git.lixianglai@loongson.cn>


Hi xianglai,

I reply inline.

在 2022/10/21 15:11, xianglai li 写道:
> Serial Port library for LoongarchQemuPkg
> 
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
> 
> Signed-off-by: xianglai li <lixianglai@loongson.cn>
> ---
>  .../LoongArchQemuPkg/Include/Library/Cpu.h    | 387 +++++++++++
>  .../Include/LoongArchQemuPlatform.h           |  97 +++
>  .../Library/SerialPortLib/SerialPortLib.c     | 612 ++++++++++++++++++
>  .../Library/SerialPortLib/SerialPortLib.inf   |  36 ++
>  4 files changed, 1132 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..8c3c21bd96
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/Cpu.h
> @@ -0,0 +1,387 @@
> +/** @file
> +
> +  Copyright (c) 2021 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 */
> +
> +/* 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 */
> +
> +/*
> +  Reads data from the specified CSR register.
> +
> +  @param[OUT]  val   The value is read from the CSR specified register.
> +  @param[IN]  reg    Specifies the register number of the CSR to read the data.
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_CSR_READQ(val, reg)                   \
> +do {                                                    \
> +  UINT64 __res;                                         \
> +  /* csrrd rd, csr_num */                               \
> +  __asm__ __volatile__(                                 \
> +          "csrrd  %0, %1 \n\t"                          \
> +          :"=r"(__res)                                  \
> +          :"i"(reg)                                     \
> +          :                                             \
> +          );                                            \
> +  (val) = __res;                                        \
> +} while(0)
> +#endif
> +
> +/*
> +  Write data to the specified CSR register.
> +
> +  @param[OUT]  val   The value is write to the CSR specified register.
> +  @param[IN]  reg    Specifies the register number of the CSR to write the data.
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_CSR_WRITEQ(val, reg)                 \
> +do {                                                   \
> +  UINT64 __val = val;                                  \
> +  /* csrwr rd, csr_num */                              \
> +  __asm__ __volatile__(                                \
> +          "csrwr  %0, %1 \n\t"                         \
> +          : "+r"(__val)                                \
> +          : "i"(reg), "r"(__val)                       \
> +          : "memory"                                   \
> +          );                                           \
> +} while(0)
> +#endif
> +
> +/*
> +  Exchange specified bit data with the specified CSR registers
> +
> +  @param[IN]  val   The value Exchanged with the CSR specified register.
> +  @param[IN]  mask   Specifies the mask for swapping bits
> +  @param[IN]  reg    Specifies the register number of the CSR to Exchange the data.
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_CSR_XCHGQ(val, mask, reg)          \
> +do {                                                 \
> +  UINT64 __val = val;                                \
> +  UINT64 __mask = mask;                              \
> +  /* csrwr rd, csr_num */                            \
> +  __asm__ __volatile__(                              \
> +          "csrxchg  %0, %1, %2 \n\t"                 \
> +          : "+r"(__val)                              \
> +          : "r"(__mask), "i"(reg), "r"(__val)        \
> +          : "memory"                                 \
> +          );                                         \
> +} while(0)
> +#endif
> +
> +/*
> +  Search for tlb
> +
> +  @param  VOID
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_TLB_SRCH()                         \
> +do {                                                 \
> +  /* tlbsrch */                                      \
> +  __asm__ __volatile__(                              \
> +          "tlbsrch"                                  \
> +          );                                         \
> +} while(0)
> +#endif
> +
> +/*
> +  Writes data to the specified tlb table entry.
> +
> +  @param  VOID
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_TLB_WR()                         \
> +do {                                               \
> +  /* tlbwr */                                      \
> +  __asm__ __volatile__(                            \
> +          "tlbwr"                                  \
> +          );                                       \
> +} while(0)
> +#endif
> +/*
> +  Reads data from the specified CPUCFG register.
> +
> +  @param[OUT]  val   The value is read from the CPUCFG specified register.
> +  @param[IN]  reg    Specifies the register number of the CPUCFG to read the data.
> +
> +  @retval  VOID
> + */
> +#ifdef __GNUC__
> +#define LOONGARCH_GET_CPUCFG(val, reg)               \
> +do {                                                 \
> +  UINT64 __res;                                      \
> +  /* cpucfg rd, rj */                                \
> +  __asm__ __volatile__(                              \
> +          "cpucfg  %0, %1 \n\t"                      \
> +          :"=r"(__res)                               \
> +          :"r"(reg)                                  \
> +          :                                          \
> +          );                                         \
> +  val = (UINT32)__res;                               \
> +} while(0)
> +#endif
> +
> +/*
> +  Enables  floating-point unit
> +
> +  @param  VOID
> +
> +  @retval  VOID
> + */
> +#define LOONGARCH_ENABLR_FPU()                       \
> +do {                                                 \
> +  LOONGARCH_CSR_XCHGQ(CSR_EUEN_FPEN,                 \
> +                      CSR_EUEN_FPEN,                 \
> +                      LOONGARCH_CSR_EUEN);           \
> +} while (0)
> +
> +/*
> +  Disable  floating-point unit
> +
> +  @param  VOID
> +
> +  @retval  VOID
> + */
> +#define LOONGARCH_DISABLE_FPU()                      \
> +do {                                                 \
> +  LOONGARCH_CSR_XCHGQ(0,                             \
> +                      CSR_EUEN_FPEN,                 \
> +                      LOONGARCH_CSR_EUEN);           \
> +} while (0)

Can we remove these inline assemble language lines and put it in ASM code?

regards
bibo, mao
> +
> +#endif
> diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
> new file mode 100644
> index 0000000000..ed672844b4
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Include/LoongArchQemuPlatform.h
> @@ -0,0 +1,97 @@
> +/** @file
> +   LoongArch Qemu Platform macro definition.
> +
> +   Copyright (c) 2021, 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..eb1ce17d69
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.c
> @@ -0,0 +1,612 @@
> +/** @file
> +  UART Serial Port library functions
> +
> +  Copyright (c) 2021 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);
> +  //
> +  // if enable interrupt the kernel of lemote will error in STR mode during wake up phase.
> +  //
> +  //MmioWrite8 ((UINTN) (gUartBase + IER_OFFSET), CFCR_8BITS);
> +
> +  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..6c4674151b
> --- /dev/null
> +++ b/Platform/Loongson/LoongArchQemuPkg/Library/SerialPortLib/SerialPortLib.inf
> @@ -0,0 +1,36 @@
> +## @file
> +#  UART Serial Port library functions
> +#
> +#  Copyright (c) 2021 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
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  IoLib
> +  PcdLib
> +
> +[Sources]
> +  SerialPortLib.c
> +
> +[Pcd]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl  ## CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate                ## CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl             ## CONSUMES
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate               ## CONSUMES


  reply	other threads:[~2022-10-29  3:01 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-21  7:11 [edk2-platforms][PATCH V4 00/14] Platform: Add Loongson support xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 07/14] Platform/Loongson: Support PEI phase xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 08/14] Platform/Loongson: Add CPU DXE driver xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 09/14] Platform/Loongson: Add PciCpuIoDxe driver xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 10/14] Platform/Loongson: Add timer Dxe driver xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 11/14] Platform/Loongson: Add RealTime Clock lib xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 12/14] Platform/Loongson: Add Platform Boot Manager Lib xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 13/14] Platform/Loongson: Add Reset System Lib xianglai
2022-10-21  7:11 ` [edk2-platforms][PATCH V4 14/14] Platform/Loongson: Support Dxe xianglai
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 01/14] Platform/Loongson: Add Serial Port library xianglai
2022-10-29  3:01   ` maobibo [this message]
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 02/14] Platform/Loongson: Support SEC And Add Readme.md xianglai
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 03/14] Platform/Loongson: Add PeiServicesTablePointerLib xianglai
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 04/14] Platform/Loongson: Add QemuFwCfgLib xianglai
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 05/14] Platform/Loongson: Add MmuLib xianglai
2022-10-29  6:45   ` maobibo
2022-10-24  9:25 ` [edk2-platforms][PATCH V4 06/14] Platform/Loongson: Add StableTimerLib xianglai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=95df5042-2386-1ec7-19e4-4dd6447df5b8@loongson.cn \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox