[v2] hw/char/stm32f2xx_usart: fix TXE/TC bit handling

Message ID 1518555259-26463-1-git-send-email-rbraun@sceen.net
State New
Headers show
Series
  • [v2] hw/char/stm32f2xx_usart: fix TXE/TC bit handling
Related show

Commit Message

Richard Braun Feb. 13, 2018, 8:54 p.m.
I/O currently being synchronous, there is no reason to ever clear the
SR_TXE bit. However the SR_TC bit may be cleared by software writing
to the SR register, so set it on each write.

In addition, fix the reset value of the USART status register.

Signed-off-by: Richard Braun <rbraun@sceen.net>
---
 hw/char/stm32f2xx_usart.c         | 12 ++++++++----
 include/hw/char/stm32f2xx_usart.h |  7 ++++++-
 2 files changed, 14 insertions(+), 5 deletions(-)

Comments

Alistair Francis Feb. 15, 2018, 10:27 p.m. | #1
On Tue, Feb 13, 2018 at 12:54 PM, Richard Braun <rbraun@sceen.net> wrote:
> I/O currently being synchronous, there is no reason to ever clear the
> SR_TXE bit. However the SR_TC bit may be cleared by software writing
> to the SR register, so set it on each write.
>
> In addition, fix the reset value of the USART status register.
>
> Signed-off-by: Richard Braun <rbraun@sceen.net>

Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>

Alistair

> ---
>  hw/char/stm32f2xx_usart.c         | 12 ++++++++----
>  include/hw/char/stm32f2xx_usart.h |  7 ++++++-
>  2 files changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
> index 07b462d4b6..032b5fda13 100644
> --- a/hw/char/stm32f2xx_usart.c
> +++ b/hw/char/stm32f2xx_usart.c
> @@ -96,12 +96,10 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
>      switch (addr) {
>      case USART_SR:
>          retvalue = s->usart_sr;
> -        s->usart_sr &= ~USART_SR_TC;
>          qemu_chr_fe_accept_input(&s->chr);
>          return retvalue;
>      case USART_DR:
>          DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
> -        s->usart_sr |= USART_SR_TXE;
>          s->usart_sr &= ~USART_SR_RXNE;
>          qemu_chr_fe_accept_input(&s->chr);
>          qemu_set_irq(s->irq, 0);
> @@ -137,7 +135,9 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
>      switch (addr) {
>      case USART_SR:
>          if (value <= 0x3FF) {
> -            s->usart_sr = value;
> +            /* I/O being synchronous, TXE is always set. In addition, it may
> +               only be set by hardware, so keep it set here. */
> +            s->usart_sr = value | USART_SR_TXE;
>          } else {
>              s->usart_sr &= value;
>          }
> @@ -151,8 +151,12 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
>              /* 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);
> +            /* XXX I/O are currently synchronous, making it impossible for
> +               software to observe transient states where TXE or TC aren't
> +               set. Unlike TXE however, which is read-only, software may
> +               clear TC by writing 0 to the SR register, so set it again
> +               on each write. */
>              s->usart_sr |= USART_SR_TC;
> -            s->usart_sr &= ~USART_SR_TXE;
>          }
>          return;
>      case USART_BRR:
> diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h
> index 9d03a7527c..7ea7448813 100644
> --- a/include/hw/char/stm32f2xx_usart.h
> +++ b/include/hw/char/stm32f2xx_usart.h
> @@ -37,7 +37,12 @@
>  #define USART_CR3  0x14
>  #define USART_GTPR 0x18
>
> -#define USART_SR_RESET 0x00C00000
> +/*
> + * XXX The reset value mentioned in "24.6.1 Status register" seems bogus.
> + * Looking at "Table 98 USART register map and reset values", it seems it
> + * should be 0xc0, and that's how real hardware behaves.
> + */
> +#define USART_SR_RESET (USART_SR_TXE | USART_SR_TC)
>
>  #define USART_SR_TXE  (1 << 7)
>  #define USART_SR_TC   (1 << 6)
> --
> 2.11.0
>
>

Patch

diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 07b462d4b6..032b5fda13 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -96,12 +96,10 @@  static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
     switch (addr) {
     case USART_SR:
         retvalue = s->usart_sr;
-        s->usart_sr &= ~USART_SR_TC;
         qemu_chr_fe_accept_input(&s->chr);
         return retvalue;
     case USART_DR:
         DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
-        s->usart_sr |= USART_SR_TXE;
         s->usart_sr &= ~USART_SR_RXNE;
         qemu_chr_fe_accept_input(&s->chr);
         qemu_set_irq(s->irq, 0);
@@ -137,7 +135,9 @@  static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
     switch (addr) {
     case USART_SR:
         if (value <= 0x3FF) {
-            s->usart_sr = value;
+            /* I/O being synchronous, TXE is always set. In addition, it may
+               only be set by hardware, so keep it set here. */
+            s->usart_sr = value | USART_SR_TXE;
         } else {
             s->usart_sr &= value;
         }
@@ -151,8 +151,12 @@  static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
             /* 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);
+            /* XXX I/O are currently synchronous, making it impossible for
+               software to observe transient states where TXE or TC aren't
+               set. Unlike TXE however, which is read-only, software may
+               clear TC by writing 0 to the SR register, so set it again
+               on each write. */
             s->usart_sr |= USART_SR_TC;
-            s->usart_sr &= ~USART_SR_TXE;
         }
         return;
     case USART_BRR:
diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h
index 9d03a7527c..7ea7448813 100644
--- a/include/hw/char/stm32f2xx_usart.h
+++ b/include/hw/char/stm32f2xx_usart.h
@@ -37,7 +37,12 @@ 
 #define USART_CR3  0x14
 #define USART_GTPR 0x18
 
-#define USART_SR_RESET 0x00C00000
+/*
+ * XXX The reset value mentioned in "24.6.1 Status register" seems bogus.
+ * Looking at "Table 98 USART register map and reset values", it seems it
+ * should be 0xc0, and that's how real hardware behaves.
+ */
+#define USART_SR_RESET (USART_SR_TXE | USART_SR_TC)
 
 #define USART_SR_TXE  (1 << 7)
 #define USART_SR_TC   (1 << 6)