summary refs log tree commit diff stats
path: root/hw/char/pl011.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-03-05 13:54:54 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-03-05 13:54:54 +0000
commitdb596ae19040574e41d086e78469014191d7d7fc (patch)
tree8c2b6253966f2dfed3983b6aea86b83b6c840900 /hw/char/pl011.c
parent7d4e29ef805f7ebc03e547bb8ae82a86ee69c41b (diff)
parent7558300c53057126514ee0fd5cf629c65ccc20e1 (diff)
downloadfocaccia-qemu-db596ae19040574e41d086e78469014191d7d7fc.tar.gz
focaccia-qemu-db596ae19040574e41d086e78469014191d7d7fc.zip
Merge tag 'pull-target-arm-20240305' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue:
 * raspi: Implement Broadcom Serial Controller (BSC) for BCM2835 boards
 * hw/char/pl011: Add support for loopback
 * STM32L4x5: Implement RCC clock control device
 * target/arm: Do memory type alignment checks
 * atomic.h: Reword confusing comment for qatomic_cmpxchg
 * qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXnI4gZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3p5ED/wOtAHA3PK+WbQhVhnji3+k
# gdhvVcldf+HLaI2v4tfaW152xjY80/j3PQFNkzptoXENA9U51F47oNYOIfULLZZX
# FTKmw/mjTBc2LqJ8gLpS8Wkr/PFtDq9JJzDwZd0MwguXpzIJp31JJpESvXlAqjjv
# FhuAcqNNuGwI2SXCBmp2lPoEMn8ExLDoG9rmzjxVZeZCyzUjVnJYM61ykhC4ByvK
# j5+/a7pUcpgHSX5cbq7kFloPOx3JXI5lS6xUKhGXXk75qHRwiQIsxMcPq8PD1+ok
# yrmp7cySwK8I7AlIPdDjpJmhU0OiBu+PkYiXmHlF2nvaUy6M0nVX2lSTzqj6VpVV
# 7yYhvWXHrtIA9AUspqTRsX7tP7iMJkco7qWfKSzYl+3pTbxS4+rEoee4jNR3hqsU
# lbWC47sNVtTN507qIL1dcsu+BaeSsYVftfxtFql3odTqRB+ticsjDfKg69dRSFyk
# SS0t8Zy3TdomcEoQkAv/ZSpkQnQUGavbRumCG58lJdiTwTuJUmGi1ufKBrD/GeKj
# IlDEl9yvKiR8uvdjj6EQqr5kOj09mmN5nvokNsq5a4aNXBYoesszWK2xodzXE2x5
# M9DHJ3S8xnN++p1idS2bikwEklG1XVQ/q52bDXQkUmQSNerVS1PCvg9hzYqA+x53
# ihJtMcsmGVfxY8aQHyHweA==
# =isAe
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 05 Mar 2024 13:52:08 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20240305' of https://git.linaro.org/people/pmaydell/qemu-arm:
  qemu-options.hx: Don't claim "-serial" has limit of 4 serial ports
  atomic.h: Reword confusing comment for qatomic_cmpxchg
  target/arm: Do memory type alignment check when translation enabled
  target/arm: Do memory type alignment check when translation disabled
  accel/tcg: Add TLB_CHECK_ALIGNED
  accel/tcg: Add tlb_fill_flags to CPUTLBEntryFull
  exec/memattrs: Remove target_tlb_bit*
  target/arm: Support 32-byte alignment in pow2_align
  tests/qtest/stm32l4x5_rcc-test.c: Add tests for the STM32L4x5_RCC
  hw/arm/stm32l4x5_soc.c: Use the RCC Sysclk
  hw/misc/stm32l4x5_rcc: Add write protections to CR register
  hw/misc/stm32l4x5_rcc: Handle Register Updates
  hw/misc/stm32l4x5_rcc: Initialize PLLs and clock multiplexers
  hw/misc/stm32l4x5_rcc: Add an internal PLL Clock object
  hw/misc/stm32l4x5_rcc: Add an internal clock multiplexer object
  hw/misc/stm32l4x5_rcc: Implement STM32L4x5_RCC skeleton
  hw/char/pl011: Add support for loopback
  tests/qtest: Add testcase for BCM2835 BSC
  hw/arm: Connect BSC to BCM2835 board as I2C0, I2C1 and I2C2
  hw/i2c: Implement Broadcom Serial Controller (BSC)

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/char/pl011.c')
-rw-r--r--hw/char/pl011.c110
1 files changed, 108 insertions, 2 deletions
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 855cb82d08..8753b84a84 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);
     }
 }