ARM pl011 fixes
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
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
@@ -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
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.
Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+)hw/char/pl011.c +0
-0
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
-0s->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
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.
Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+)hw/char/pl011.c +0
-0hw/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
PATCH v2 2/4 pl011: fix UARTRSR accesses corrupting the UARTCR value
In reply to Rob HerringFrom: 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
@@ -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
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_receivefunction, 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.Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)hw/char/pl011.c +0
-0
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
-5uint32_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
PATCH v2 3/4 pl011: fix incorrect logic to set the RXFF flag
In reply to Rob HerringFrom: 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
@@ -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
Re: [Qemu-devel] [PATCH v2 3/4] pl011: fix incorrect logic to set the RXFF flag
Quoted 6 lines · 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.
Signed-off-by Rob Herring <[email protected]>
thanks
-- PMM
PATCH v2 4/4 pl011: re-evaluate rx interrupt when fifo trigger changes
In reply to Rob HerringFrom: 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
@@ -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
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.
Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+)hw/char/pl011.c +4
-0@@ -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
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.
Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+)hw/char/pl011.c +4
-0@@ -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
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.
Signed-off-by Rob Herring <[email protected]>--- hw/char/pl011.c | 4 ++++ 1 file changed, 4 insertions(+)hw/char/pl011.c +4
-0@@ -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_triggermeans 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