summary refs log tree commit diff stats
path: root/hw/char/imx_serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char/imx_serial.c')
-rw-r--r--hw/char/imx_serial.c44
1 files changed, 33 insertions, 11 deletions
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 70405ccf8b..1e5540472b 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -37,8 +37,8 @@
 
 static const VMStateDescription vmstate_imx_serial = {
     .name = TYPE_IMX_SERIAL,
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .fields = (VMStateField[]) {
         VMSTATE_INT32(readbuff, IMXSerialState),
         VMSTATE_UINT32(usr1, IMXSerialState),
@@ -50,22 +50,36 @@ static const VMStateDescription vmstate_imx_serial = {
         VMSTATE_UINT32(ubmr, IMXSerialState),
         VMSTATE_UINT32(ubrc, IMXSerialState),
         VMSTATE_UINT32(ucr3, IMXSerialState),
+        VMSTATE_UINT32(ucr4, IMXSerialState),
         VMSTATE_END_OF_LIST()
     },
 };
 
 static void imx_update(IMXSerialState *s)
 {
-    uint32_t flags;
+    uint32_t usr1;
+    uint32_t usr2;
+    uint32_t mask;
 
-    flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
-    if (s->ucr1 & UCR1_TXMPTYEN) {
-        flags |= (s->uts1 & UTS1_TXEMPTY);
-    } else {
-        flags &= ~USR1_TRDY;
-    }
+    /*
+     * Lucky for us TRDY and RRDY has the same offset in both USR1 and
+     * UCR1, so we can get away with something as simple as the
+     * following:
+     */
+    usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY);
+    /*
+     * Bits that we want in USR2 are not as conveniently laid out,
+     * unfortunately.
+     */
+    mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
+    /*
+     * TCEN and TXDC are both bit 3
+     */
+    mask |= s->ucr4 & UCR4_TCEN;
 
-    qemu_set_irq(s->irq, !!flags);
+    usr2 = s->usr2 & mask;
+
+    qemu_set_irq(s->irq, usr1 || usr2);
 }
 
 static void imx_serial_reset(IMXSerialState *s)
@@ -155,6 +169,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
         return s->ucr3;
 
     case 0x23: /* UCR4 */
+        return s->ucr4;
+
     case 0x29: /* BRM Incremental */
         return 0x0; /* TODO */
 
@@ -183,8 +199,10 @@ static void imx_serial_write(void *opaque, hwaddr offset,
              * qemu_chr_fe_write and background I/O callbacks */
             qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->usr1 &= ~USR1_TRDY;
+            s->usr2 &= ~USR2_TXDC;
             imx_update(s);
             s->usr1 |= USR1_TRDY;
+            s->usr2 |= USR2_TXDC;
             imx_update(s);
         }
         break;
@@ -257,8 +275,12 @@ static void imx_serial_write(void *opaque, hwaddr offset,
         s->ucr3 = value & 0xffff;
         break;
 
-    case 0x2d: /* UTS1 */
     case 0x23: /* UCR4 */
+        s->ucr4 = value & 0xffff;
+        imx_update(s);
+        break;
+
+    case 0x2d: /* UTS1 */
         qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
                       HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
         /* TODO */