This patch adds loopback for sent characters as well as
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.

Signed-off-by: Tong Ho <tong...@amd.com>
Signed-off-by: Francisco Iglesias <francisco.igles...@amd.com>
---
 hw/char/pl011.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 855cb82d08..3c0e07aa35 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -121,6 +121,51 @@ static void pl011_update(PL011State *s)
     }
 }
 
+static void pl011_put_fifo(void *opaque, uint32_t value);
+
+static bool pl011_is_loopback(PL011State *s)
+{
+    return !!(s->cr & (1U << 7));
+}
+
+static void pl011_tx_loopback(PL011State *s, uint32_t value)
+{
+    if (pl011_is_loopback(s)) {
+        pl011_put_fifo(s, value);
+    }
+}
+
+static uint32_t pl011_cr_loopback(PL011State *s, bool update)
+{
+    uint32_t cr = s->cr;
+    uint32_t fr = s->flags;
+    uint32_t ri = 1 << 8, dcd = 1 << 2, dsr = 1 << 1, cts = 0;
+    uint32_t out2 = 1 << 13, out1 = 1 << 12, rts = 1 << 11, dtr = 1 << 10;
+
+    if (!pl011_is_loopback(s)) {
+        return fr;
+    }
+
+    fr &= ~(ri | dcd | dsr | cts);
+    fr |= (cr & out2) ?  ri : 0;   /* FR.RI  <= CR.Out2 */
+    fr |= (cr & out1) ? dcd : 0;   /* FR.DCD <= CR.Out1 */
+    fr |= (cr &  rts) ? cts : 0;   /* FR.CTS <= CR.RTS */
+    fr |= (cr &  dtr) ? dsr : 0;   /* FR.DSR <= CR.DTR */
+
+    if (!update) {
+        return fr;
+    }
+
+    s->int_level &= ~(INT_DSR | INT_DCD | INT_CTS | INT_RI);
+    s->int_level |= (fr & dsr) ? INT_DSR : 0;
+    s->int_level |= (fr & dcd) ? INT_DCD : 0;
+    s->int_level |= (fr & cts) ? INT_CTS : 0;
+    s->int_level |= (fr &  ri) ? INT_RI  : 0;
+    pl011_update(s);
+
+    return fr;
+}
+
 static bool pl011_is_fifo_enabled(PL011State *s)
 {
     return (s->lcr & LCR_FEN) != 0;
@@ -172,7 +217,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         r = s->rsr;
         break;
     case 6: /* UARTFR */
-        r = s->flags;
+        r = pl011_cr_loopback(s, false);
         break;
     case 8: /* UARTILPR */
         r = s->ilpr;
@@ -267,6 +312,7 @@ static void pl011_write(void *opaque, hwaddr offset,
          * qemu_chr_fe_write and background I/O callbacks */
         qemu_chr_fe_write_all(&s->chr, &ch, 1);
         s->int_level |= INT_TX;
+        pl011_tx_loopback(s, ch);
         pl011_update(s);
         break;
     case 1: /* UARTRSR/UARTECR */
@@ -300,8 +346,9 @@ static void pl011_write(void *opaque, hwaddr offset,
         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_cr_loopback(s, true);
         break;
     case 13: /* UARTIFS */
         s->ifl = value;
-- 
2.25.1


Reply via email to