Skip to contentSkip to conversation

ARM pl011 fixes

PATCH Superseded Rob Herring 3 participants 12 messages Mar 14, 2014 – Mar 17, 2014

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover ARM pl011 fixes

From: Rob Herring <[email protected]>

Intermittent issues have been seen where no serial input occurs. It appears the pl011 gets in a state where the rx interrupt never fires because the rx interrupt only asserts when crossing the fifo trigger level. The fifo state appears to get out of sync when the pl011 is re-configured. This combined with the rx timeout interrupt not being modeled results in no more rx interrupts.

This problem is fixed by the 1st patch. The 3 other patches are problems I noticed while debugging this issue. They are more for correctness of the model than fixing any observed issues.

Rob

Rob Herring (4):
pl011: reset the fifo when enabled or disabled
pl011: fix UARTRSR accesses corrupting the UARTCR value
pl011: fix incorrect logic to set the RXFF flag
pl011: re-evaluate rx interrupt when fifo trigger changes

 hw/char/pl011.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
Signature · 1 line
1.8.3.2
#1 lore raw

PATCH v2 1/4 pl011: reset the fifo when enabled or disabled

From: Rob Herring <[email protected]>

Intermittent issues have been seen where no serial input occurs. It appears the pl011 gets in a state where the rx interrupt never fires because the rx interrupt only asserts when crossing the fifo trigger level. The fifo state appears to get out of sync when the pl011 is re-configured. This combined with the rx timeout interrupt not being modeled results in no more rx interrupts.

Disabling the fifo is the recommended way to clear the tx fifo in the TRM (section 3.3.8). The behavior in this case for the rx fifo is undefined in the TRM, but having fifo contents to be maintained during configuration changes is not likely expected behavior. Reseting the fifo state when the fifo size is changed is the simplest solution.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +4 -0
index a8ae6f4..f0c3fa3 100644
@@ -162,6 +162,10 @@ static void pl011_write(void *opaque, hwaddr offset,
         s->fbrd = value;
         break;
     case 11: /* UARTLCR_H */
+        if (!((s->lcr ^ value) & 0x10)) {
+            s->read_count = 0;
+            s->read_pos = 0;
+        }
         s->lcr = value;
         pl011_set_read_trigger(s);
         break;
Signature · 1 line
1.8.3.2
#2 lore raw

Re: [Qemu-devel] [PATCH v2 1/4] pl011: reset the fifo when enabled or disabled

Quoted 8 lines · Rob Herring

From: Rob Herring <[email protected]>

Intermittent issues have been seen where no serial input occurs. It appears the pl011 gets in a state where the rx interrupt never fires because the rx interrupt only asserts when crossing the fifo trigger level. The fifo state appears to get out of sync when the pl011 is re-configured. This combined with the rx timeout interrupt not being modeled results in no more rx interrupts.

This sounds like the bug is that we're not asserting the rx interrupt when we should, which is not what this patch is trying to change.

Among other things, this patch doesn't actually cause a write to this register to make us recalculate and re-send the interrupt state, because there is no call to pl011_update() in this code path.

Quoted 16 lines

Disabling the fifo is the recommended way to clear the tx fifo in the TRM (section 3.3.8). The behavior in this case for the rx fifo is undefined in the TRM, but having fifo contents to be maintained during configuration changes is not likely expected behavior. Reseting the fifo state when the fifo size is changed is the simplest solution.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +0 -0
index a8ae6f4..f0c3fa3 100644

hw/char/pl011.c static void pl011_write(void *opaque, hwaddr offset, · line 168

Quoted 7 lines
hw/char/pl011.c +4 -0
@@ -162,6 +162,10 @@ static void pl011_write(void *opaque, hwaddr offset,
         s->fbrd = value;
         break;
     case 11: /* UARTLCR_H */
+        if (!((s->lcr ^ value) & 0x10)) {
+            s->read_count = 0;
+            s->read_pos = 0;
+        }

Am I misreading this, or do we clear the fifo on every write to this register where the fifo-enable bit is not changed?

Quoted 7 lines
hw/char/pl011.c +0 -0
         s->lcr = value;
 
         pl011_set_read_trigger(s);
         break;

--
1.8.3.2

I really think this patch is just covering over the actual problem and we need to identify and fix the actual bug.

thanks
-- PMM

#3 lore raw

Re: [Qemu-devel] [PATCH v2 1/4] pl011: reset the fifo when enabled or disabled

Quoted 44 lines · Peter Maydell
Quoted 8 lines · Rob Herring

From: Rob Herring <[email protected]>

Intermittent issues have been seen where no serial input occurs. It appears the pl011 gets in a state where the rx interrupt never fires because the rx interrupt only asserts when crossing the fifo trigger level. The fifo state appears to get out of sync when the pl011 is re-configured. This combined with the rx timeout interrupt not being modeled results in no more rx interrupts.

This sounds like the bug is that we're not asserting the rx interrupt when we should, which is not what this patch is trying to change.

Among other things, this patch doesn't actually cause a write to this register to make us recalculate and re-send the interrupt state, because there is no call to pl011_update() in this code path.

Quoted 16 lines

Disabling the fifo is the recommended way to clear the tx fifo in the TRM (section 3.3.8). The behavior in this case for the rx fifo is undefined in the TRM, but having fifo contents to be maintained during configuration changes is not likely expected behavior. Reseting the fifo state when the fifo size is changed is the simplest solution.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +0 -0
index a8ae6f4..f0c3fa3 100644

hw/char/pl011.c static void pl011_write(void *opaque, hwaddr offset, · line 168

Quoted 7 lines
hw/char/pl011.c +4 -0
@@ -162,6 +162,10 @@ static void pl011_write(void *opaque, hwaddr offset,
         s->fbrd = value;
         break;
     case 11: /* UARTLCR_H */
+        if (!((s->lcr ^ value) & 0x10)) {
+            s->read_count = 0;
+            s->read_pos = 0;
+        }

Am I misreading this, or do we clear the fifo on every write to this register where the fifo-enable bit is not changed?

Quoted 2 lines

I really think this patch is just covering over the actual problem and we need to identify and fix the actual bug.

That said, I'm fairly sure that read_count = read_pos = 0 is indeed the correct behaviour when the FIFO is disabled.

thanks
-- PMM

#4 lore raw

PATCH v2 2/4 pl011: fix UARTRSR accesses corrupting the UARTCR value

In reply to Rob Herring

From: Rob Herring <[email protected]>

Offset 4 is UARTRSR/UARTECR, not the UARTCR. The UARTCR would be corrupted if the UARTRSR is ever written. Fix by implementing a correct model of the UARTRSR/UARTECR register. Reads of this register simply reflect the error bits in data register. Only breaks can be triggered in QEMU. With the pl011_can_receive function, we effectively have flow control between the host and the model. Framing and parity errors simply don't make sense in the model and will never occur.

---
 hw/char/pl011.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
hw/char/pl011.c +6 -4
index f0c3fa3..920ba3f 100644
@@ -20,6 +20,7 @@ typedef struct PL011State {
     uint32_t readbuff;
     uint32_t flags;
     uint32_t lcr;
+    uint32_t rsr;
     uint32_t cr;
     uint32_t dmacr;
     uint32_t int_enabled;
@@ -81,13 +82,14 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         }
         if (s->read_count == s->read_trigger - 1)
             s->int_level &= ~ PL011_INT_RX;
+        s->rsr = c >> 8;
         pl011_update(s);
         if (s->chr) {
             qemu_chr_accept_input(s->chr);
         }
         return c;
-    case 1: /* UARTCR */
-        return 0;
+    case 1: /* UARTRSR */
+        return s->rsr;
     case 6: /* UARTFR */
         return s->flags;
     case 8: /* UARTILPR */
@@ -146,8 +148,8 @@ static void pl011_write(void *opaque, hwaddr offset,
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
         break;
-    case 1: /* UARTCR */
-        s->cr = value;
+    case 1: /* UARTRSR/UARTECR */
+        s->rsr = 0;
         break;
     case 6: /* UARTFR */
         /* Writes to Flag register are ignored.  */
Signature · 1 line
1.8.3.2
#5 lore raw

Re: [Qemu-devel] [PATCH v2 2/4] pl011: fix UARTRSR accesses corrupting the UARTCR value

Quoted 20 lines · Rob Herring

From: Rob Herring <[email protected]>

Offset 4 is UARTRSR/UARTECR, not the UARTCR. The UARTCR would be corrupted if the UARTRSR is ever written. Fix by implementing a correct model of the UARTRSR/UARTECR register. Reads of this register simply reflect the error bits in data register. Only breaks can be triggered in QEMU. With the pl011_can_receive function, we effectively have flow control between the host and the model. Framing and parity errors simply don't make sense in the model and will never occur.

---
 hw/char/pl011.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
hw/char/pl011.c +0 -0
index f0c3fa3..920ba3f 100644

hw/char/pl011.c typedef struct PL011State { · line 23

Quoted 4 lines
hw/char/pl011.c +1 -0
@@ -20,6 +20,7 @@ typedef struct PL011State {
     uint32_t readbuff;
     uint32_t flags;
     uint32_t lcr;
+    uint32_t rsr;

This needs a corresponding entry in the vmstate (and the vmstate version_id/minimum_version_id/minimum_version_id_old all need to be incremented).

Quoted 32 lines
hw/char/pl011.c +5 -5
     uint32_t cr;
     uint32_t dmacr;
     uint32_t int_enabled;
@@ -81,13 +82,14 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
         }
         if (s->read_count == s->read_trigger - 1)
             s->int_level &= ~ PL011_INT_RX;
+        s->rsr = c >> 8;
         pl011_update(s);
         if (s->chr) {
             qemu_chr_accept_input(s->chr);
         }
         return c;
-    case 1: /* UARTCR */
-        return 0;
+    case 1: /* UARTRSR */
+        return s->rsr;
     case 6: /* UARTFR */
         return s->flags;
     case 8: /* UARTILPR */
@@ -146,8 +148,8 @@ static void pl011_write(void *opaque, hwaddr offset,
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
         break;
-    case 1: /* UARTCR */
-        s->cr = value;
+    case 1: /* UARTRSR/UARTECR */
+        s->rsr = 0;
         break;
     case 6: /* UARTFR */
         /* Writes to Flag register are ignored.  */
--
Quoted 2 lines

1.8.3.2

thanks
-- PMM

#6 lore raw

PATCH v2 3/4 pl011: fix incorrect logic to set the RXFF flag

In reply to Rob Herring

From: Rob Herring <[email protected]>

The receive fifo full bit should be set when 1 character is received and the fifo is disabled or when 16 characters are in the fifo.

---
 hw/char/pl011.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
hw/char/pl011.c +1 -1
index 920ba3f..5e664f4 100644
@@ -220,7 +220,7 @@ static void pl011_put_fifo(void *opaque, uint32_t value)
     s->read_fifo[slot] = value;
     s->read_count++;
     s->flags &= ~PL011_FLAG_RXFE;
-    if (s->cr & 0x10 || s->read_count == 16) {
+    if (!(s->lcr & 0x10) || s->read_count == 16) {
         s->flags |= PL011_FLAG_RXFF;
     }
     if (s->read_count == s->read_trigger) {
Signature · 1 line
1.8.3.2
#8 lore raw

PATCH v2 4/4 pl011: re-evaluate rx interrupt when fifo trigger changes

In reply to Rob Herring

From: Rob Herring <[email protected]>

When setting the fifo trigger level, the rx interrupt needs to be asserted if the current fifo level matches. This is more for correctness as the level is currently never changed.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +4 -0
index 5e664f4..3903933 100644
@@ -131,6 +131,10 @@ static void pl011_set_read_trigger(PL011State *s)
     else
 #endif
         s->read_trigger = 1;
+
+    if (s->read_count == s->read_trigger) {
+        s->int_level |= PL011_INT_RX;
+    }
 }
 
 static void pl011_write(void *opaque, hwaddr offset,
Signature · 1 line
1.8.3.2
#9 lore raw

Re: [Qemu-devel] [PATCH v2 4/4] pl011: re-evaluate rx interrupt when fifo trigger changes

Quoted 24 lines · Rob Herring

From: Rob Herring <[email protected]>

When setting the fifo trigger level, the rx interrupt needs to be asserted if the current fifo level matches. This is more for correctness as the level is currently never changed.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +4 -0
index 5e664f4..3903933 100644
@@ -131,6 +131,10 @@ static void pl011_set_read_trigger(PL011State *s)
     else
 #endif
         s->read_trigger = 1;
+
+    if (s->read_count == s->read_trigger) {
+        s->int_level |= PL011_INT_RX;
+    }
 
Quoted 1 line

=, surely?

Also if you're updating int_level then you need to call pl011_update().

thanks
-- PMM

#10 lore raw

Re: [Qemu-devel] [PATCH v2 4/4] pl011: re-evaluate rx interrupt when fifo trigger changes

Quoted 25 lines · Peter Maydell
Quoted 23 lines · Rob Herring

From: Rob Herring <[email protected]>

When setting the fifo trigger level, the rx interrupt needs to be asserted if the current fifo level matches. This is more for correctness as the level is currently never changed.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +4 -0
index 5e664f4..3903933 100644
@@ -131,6 +131,10 @@ static void pl011_set_read_trigger(PL011State *s)
     else
 #endif
         s->read_trigger = 1;
+
+    if (s->read_count == s->read_trigger) {
+        s->int_level |= PL011_INT_RX;
+    }
Quoted 1 line
Quoted 1 line

=, surely?

On closer inspection of the TRM it's not quite that simple (section 2.8.2). We need to assert the interrupt only if the change in the read_trigger means the FIFO is now above the new threshold but wasn't above the old one, and only if the FIFOs are enabled -- if the FIFO is disabled the threshold doesn't figure into the interrupt level.

thanks
-- PMM

#11 lore raw

Re: [Qemu-devel] [PATCH v2 4/4] pl011: re-evaluate rx interrupt when fifo trigger changes

Quoted 26 lines · Peter Maydell
Quoted 25 lines · Peter Maydell
Quoted 23 lines · Rob Herring

From: Rob Herring <[email protected]>

When setting the fifo trigger level, the rx interrupt needs to be asserted if the current fifo level matches. This is more for correctness as the level is currently never changed.

---
 hw/char/pl011.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/char/pl011.c +4 -0
index 5e664f4..3903933 100644
@@ -131,6 +131,10 @@ static void pl011_set_read_trigger(PL011State *s)
     else
 #endif
         s->read_trigger = 1;
+
+    if (s->read_count == s->read_trigger) {
+        s->int_level |= PL011_INT_RX;
+    }
Quoted 3 lines
Quoted 1 line
Quoted 1 line

=, surely?

On closer inspection of the TRM it's not quite that simple (section 2.8.2).

Humm, a section not in r1p4 rev I was looking at, but in r1p5.

Quoted 4 lines

We need to assert the interrupt only if the change in the read_trigger means the FIFO is now above the new threshold but wasn't above the old one, and only if the FIFOs are enabled -- if the FIFO is disabled the threshold doesn't figure into the interrupt level.

I'm not even sure that is correct. I read it as the interrupt would only assert when receiving a character that causes the fifo to be equal to the new trigger level. I think changing the fifo trigger level on the fly is just asking for trouble. It is all just speculation and I don't have hardware to test actual behavior.

Rob