[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 04/20] hw/char/pl011: Add support for loopback
From: |
Peter Maydell |
Subject: |
[PULL 04/20] hw/char/pl011: Add support for loopback |
Date: |
Tue, 5 Mar 2024 13:52:21 +0000 |
From: Tong Ho <tong.ho@amd.com>
This patch adds loopback for sent characters, sent BREAK,
and modem-control signals.
Loopback of send and modem-control is often used for uart
self tests in real hardware but missing from current pl011
model, resulting in self-test failures when running in QEMU.
This implementation matches what is observed in real pl011
hardware placed in loopback mode:
1. Input characters and BREAK events from serial backend
are ignored, but
2. Both TX characters and BREAK events are still sent to
serial backend, in addition to be looped back to RX.
Signed-off-by: Tong Ho <tong.ho@amd.com>
Signed-off-by: Francisco Iglesias <francisco.iglesias@amd.com>
Message-id: 20240227054855.44204-1-tong.ho@amd.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/char/pl011.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 108 insertions(+), 2 deletions(-)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 855cb82d08d..8753b84a842 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -49,10 +49,14 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq,
Chardev *chr)
}
/* Flag Register, UARTFR */
+#define PL011_FLAG_RI 0x100
#define PL011_FLAG_TXFE 0x80
#define PL011_FLAG_RXFF 0x40
#define PL011_FLAG_TXFF 0x20
#define PL011_FLAG_RXFE 0x10
+#define PL011_FLAG_DCD 0x04
+#define PL011_FLAG_DSR 0x02
+#define PL011_FLAG_CTS 0x01
/* Data Register, UARTDR */
#define DR_BE (1 << 10)
@@ -76,6 +80,13 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev
*chr)
#define LCR_FEN (1 << 4)
#define LCR_BRK (1 << 0)
+/* Control Register, UARTCR */
+#define CR_OUT2 (1 << 13)
+#define CR_OUT1 (1 << 12)
+#define CR_RTS (1 << 11)
+#define CR_DTR (1 << 10)
+#define CR_LBE (1 << 7)
+
static const unsigned char pl011_id_arm[8] =
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
static const unsigned char pl011_id_luminary[8] =
@@ -251,6 +262,89 @@ static void pl011_trace_baudrate_change(const PL011State
*s)
s->ibrd, s->fbrd);
}
+static bool pl011_loopback_enabled(PL011State *s)
+{
+ return !!(s->cr & CR_LBE);
+}
+
+static void pl011_loopback_mdmctrl(PL011State *s)
+{
+ uint32_t cr, fr, il;
+
+ if (!pl011_loopback_enabled(s)) {
+ return;
+ }
+
+ /*
+ * Loopback software-driven modem control outputs to modem status inputs:
+ * FR.RI <= CR.Out2
+ * FR.DCD <= CR.Out1
+ * FR.CTS <= CR.RTS
+ * FR.DSR <= CR.DTR
+ *
+ * The loopback happens immediately even if this call is triggered
+ * by setting only CR.LBE.
+ *
+ * CTS/RTS updates due to enabled hardware flow controls are not
+ * dealt with here.
+ */
+ cr = s->cr;
+ fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
+ PL011_FLAG_DSR | PL011_FLAG_CTS);
+ fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0;
+ fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
+ fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0;
+ fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0;
+
+ /* Change interrupts based on updated FR */
+ il = s->int_level & ~(INT_DSR | INT_DCD | INT_CTS | INT_RI);
+ il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
+ il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
+ il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
+ il |= (fr & PL011_FLAG_RI) ? INT_RI : 0;
+
+ s->flags = fr;
+ s->int_level = il;
+ pl011_update(s);
+}
+
+static void pl011_put_fifo(void *opaque, uint32_t value);
+
+static void pl011_loopback_tx(PL011State *s, uint32_t value)
+{
+ if (!pl011_loopback_enabled(s)) {
+ return;
+ }
+
+ /*
+ * Caveat:
+ *
+ * In real hardware, TX loopback happens at the serial-bit level
+ * and then reassembled by the RX logics back into bytes and placed
+ * into the RX fifo. That is, loopback happens after TX fifo.
+ *
+ * Because the real hardware TX fifo is time-drained at the frame
+ * rate governed by the configured serial format, some loopback
+ * bytes in TX fifo may still be able to get into the RX fifo
+ * that could be full at times while being drained at software
+ * pace.
+ *
+ * In such scenario, the RX draining pace is the major factor
+ * deciding which loopback bytes get into the RX fifo, unless
+ * hardware flow-control is enabled.
+ *
+ * For simplicity, the above described is not emulated.
+ */
+ pl011_put_fifo(s, value);
+}
+
+static void pl011_loopback_break(PL011State *s, int brk_enable)
+{
+ if (brk_enable) {
+ pl011_loopback_tx(s, DR_BE);
+ }
+}
+
static void pl011_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
@@ -266,6 +360,7 @@ static void pl011_write(void *opaque, hwaddr offset,
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&s->chr, &ch, 1);
+ pl011_loopback_tx(s, ch);
s->int_level |= INT_TX;
pl011_update(s);
break;
@@ -295,13 +390,15 @@ static void pl011_write(void *opaque, hwaddr offset,
int break_enable = value & LCR_BRK;
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
&break_enable);
+ pl011_loopback_break(s, break_enable);
}
s->lcr = value;
pl011_set_read_trigger(s);
break;
case 12: /* UARTCR */
- /* ??? Need to implement the enable and loopback bits. */
+ /* ??? Need to implement the enable bit. */
s->cr = value;
+ pl011_loopback_mdmctrl(s);
break;
case 13: /* UARTIFS */
s->ifl = value;
@@ -361,12 +458,21 @@ static void pl011_put_fifo(void *opaque, uint32_t value)
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
{
+ /*
+ * In loopback mode, the RX input signal is internally disconnected
+ * from the entire receiving logics; thus, all inputs are ignored,
+ * and BREAK detection on RX input signal is also not performed.
+ */
+ if (pl011_loopback_enabled(opaque)) {
+ return;
+ }
+
pl011_put_fifo(opaque, *buf);
}
static void pl011_event(void *opaque, QEMUChrEvent event)
{
- if (event == CHR_EVENT_BREAK) {
+ if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) {
pl011_put_fifo(opaque, DR_BE);
}
}
--
2.34.1
- [PULL 08/20] hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers, (continued)
- [PULL 08/20] hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers, Peter Maydell, 2024/03/05
- [PULL 16/20] accel/tcg: Add TLB_CHECK_ALIGNED, Peter Maydell, 2024/03/05
- [PULL 07/20] hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object, Peter Maydell, 2024/03/05
- [PULL 09/20] hw/misc/stm32l4x5_rcc: Handle Register Updates, Peter Maydell, 2024/03/05
- [PULL 02/20] hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2, Peter Maydell, 2024/03/05
- [PULL 14/20] exec/memattrs: Remove target_tlb_bit*, Peter Maydell, 2024/03/05
- [PULL 20/20] qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports, Peter Maydell, 2024/03/05
- [PULL 01/20] hw/i2c: Implement Broadcom Serial Controller (BSC), Peter Maydell, 2024/03/05
- [PULL 11/20] hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk, Peter Maydell, 2024/03/05
- [PULL 05/20] hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton, Peter Maydell, 2024/03/05
- [PULL 04/20] hw/char/pl011: Add support for loopback,
Peter Maydell <=
- [PULL 18/20] target/arm: Do memory type alignment check when translation enabled, Peter Maydell, 2024/03/05
- [PULL 13/20] target/arm: Support 32-byte alignment in pow2_align, Peter Maydell, 2024/03/05
- Re: [PULL 00/20] target-arm queue, Peter Maydell, 2024/03/05
- [PULL 00/20] target-arm queue, Peter Maydell, 2024/03/11
- [PULL 03/20] contrib/elf2dmp: Assume error by default, Peter Maydell, 2024/03/11
- [PULL 08/20] contrib/elf2dmp: Fix error reporting style in pdb.c, Peter Maydell, 2024/03/11
- [PULL 15/20] contrib/elf2dmp: Use rol64() to decode, Peter Maydell, 2024/03/11
- [PULL 12/20] contrib/elf2dmp: Always destroy PA space, Peter Maydell, 2024/03/11
- [PULL 18/20] contrib/elf2dmp: Clamp QEMU note to file size, Peter Maydell, 2024/03/11
- [PULL 11/20] contrib/elf2dmp: Always check for PA resolution failure, Peter Maydell, 2024/03/11