On Fri, May 1, 2026 at 2:05 AM ~lexbaileylowrisc
<[email protected]> wrote:
>
> From: Lex Bailey <[email protected]>
>
> The STATUS register value is now generated on every read instead of being
> tracked in memory since it has such a dynamic value. the receive fifo is now
> implemented, and the system loopback mode. (line loopback is not supported)
>
> Signed-off-by: Lex Bailey <[email protected]>

Reviewed-by: Alistair Francis <[email protected]>

Alistair

> ---
>  hw/char/ot_uart.c | 160 ++++++++++++++++++++++++++++++++--------------
>  1 file changed, 112 insertions(+), 48 deletions(-)
>
> diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
> index 2247db7110..3511a42fbe 100644
> --- a/hw/char/ot_uart.c
> +++ b/hw/char/ot_uart.c
> @@ -140,6 +140,21 @@ static void ot_uart_update_irqs(OtUARTState *s)
>      }
>  }
>
> +static bool ot_uart_is_sys_loopack_enabled(const OtUARTState *s)
> +{
> +    return FIELD_EX32(s->regs[R_CTRL], CTRL, SLPBK);
> +}
> +
> +static bool ot_uart_is_tx_enabled(const OtUARTState *s)
> +{
> +    return FIELD_EX32(s->regs[R_CTRL], CTRL, TX);
> +}
> +
> +static bool ot_uart_is_rx_enabled(const OtUARTState *s)
> +{
> +    return FIELD_EX32(s->regs[R_CTRL], CTRL, RX);
> +}
> +
>  static int ot_uart_can_receive(void *opaque)
>  {
>      OtUARTState *s = opaque;
> @@ -196,7 +211,7 @@ static void ot_uart_reset_rx_fifo(OtUARTState *s)
>      fifo8_reset(&s->rx_fifo);
>      s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;
>      s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;
> -    if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {
> +    if (ot_uart_is_rx_enabled(s)) {
>          qemu_chr_fe_accept_input(&s->chr);
>      }
>  }
> @@ -218,21 +233,37 @@ static void ot_uart_xmit(OtUARTState *s)
>          return;
>      }
>
> -    /* instant drain the fifo when there's no back-end */
> -    if (!qemu_chr_fe_backend_connected(&s->chr)) {
> -        ot_uart_reset_tx_fifo(s);
> -        ot_uart_update_irqs(s);
> -        return;
> -    }
> +    if (ot_uart_is_sys_loopack_enabled(s)) {
> +        /* system loopback mode, just forward to RX FIFO */
> +        uint32_t count = fifo8_num_used(&s->tx_fifo);
> +        buf = fifo8_pop_bufptr(&s->tx_fifo, count, &size);
> +        ot_uart_receive(s, buf, (int)size);
> +        count -= size;
> +        /*
> +         * there may be more data to send if data wraps around the end of TX
> +         * FIFO
> +         */
> +        if (count) {
> +            buf = fifo8_pop_bufptr(&s->tx_fifo, count, &size);
> +            ot_uart_receive(s, buf, (int)size);
> +        }
> +    } else {
> +        /* instant drain the fifo when there's no back-end */
> +        if (!qemu_chr_fe_backend_connected(&s->chr)) {
> +            ot_uart_reset_tx_fifo(s);
> +            ot_uart_update_irqs(s);
> +            return;
> +        }
>
> -    /* get a continuous buffer from the FIFO */
> -    buf =
> -        fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);
> -    /* send as much as possible */
> -    ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
> -    /* if some characters where sent, remove them from the FIFO */
> -    if (ret >= 0) {
> -        fifo8_drop(&s->tx_fifo, ret);
> +        /* get a continuous buffer from the FIFO */
> +        buf =
> +            fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), 
> &size);
> +        /* send as much as possible */
> +        ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
> +        /* if some characters where sent, remove them from the FIFO */
> +        if (ret >= 0) {
> +            fifo8_drop(&s->tx_fifo, ret);
> +        }
>      }
>
>      /* update INTR_STATE */
> @@ -269,7 +300,7 @@ static void uart_write_tx_fifo(OtUARTState *s, uint8_t 
> val)
>          s->tx_watermark_level = 0;
>      }
>
> -    if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {
> +    if (ot_uart_is_tx_enabled(s)) {
>          ot_uart_xmit(s);
>      }
>
> @@ -288,8 +319,6 @@ static void ot_uart_reset_enter(Object *obj, ResetType 
> type)
>
>      memset(&s->regs[0], 0, sizeof(s->regs));
>
> -    s->regs[R_STATUS] = 0x0000003c;
> -
>      s->tx_watermark_level = 0;
>      for (unsigned index = 0; index < ARRAY_SIZE(s->irqs); index++) {
>          qemu_set_irq(s->irqs[index], 0);
> @@ -305,6 +334,27 @@ static void ot_uart_reset_enter(Object *obj, ResetType 
> type)
>      ot_uart_update_irqs(s);
>  }
>
> +static uint8_t ot_uart_read_rx_fifo(OtUARTState *s)
> +{
> +    uint8_t val;
> +
> +    if (!(s->regs[R_CTRL] & R_CTRL_RX_MASK)) {
> +        return 0;
> +    }
> +
> +    if (fifo8_is_empty(&s->rx_fifo)) {
> +        return 0;
> +    }
> +
> +    val = fifo8_pop(&s->rx_fifo);
> +
> +    if (ot_uart_is_rx_enabled(s) && !ot_uart_is_sys_loopack_enabled(s)) {
> +        qemu_chr_fe_accept_input(&s->chr);
> +    }
> +
> +    return val;
> +}
> +
>  static uint64_t ot_uart_get_baud(OtUARTState *s)
>  {
>      uint64_t baud;
> @@ -327,32 +377,50 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, 
> unsigned int size)
>      case R_INTR_ENABLE:
>      case R_CTRL:
>      case R_FIFO_CTRL:
> -    case R_STATUS:
>          retvalue = s->regs[reg];
>          break;
> +    case R_STATUS:
> +        /* assume that UART always report RXIDLE */
> +        retvalue = R_STATUS_RXIDLE_MASK;
> +        /* report RXEMPTY or RXFULL */
> +        switch (fifo8_num_used(&s->rx_fifo)) {
> +        case 0:
> +            retvalue |= R_STATUS_RXEMPTY_MASK;
> +            break;
> +        case OT_UART_RX_FIFO_SIZE:
> +            retvalue |= R_STATUS_RXFULL_MASK;
> +            break;
> +        default:
> +            break;
> +        }
> +        /* report TXEMPTY+TXIDLE or TXFULL */
> +        switch (fifo8_num_used(&s->tx_fifo)) {
> +        case 0:
> +            retvalue |= R_STATUS_TXEMPTY_MASK | R_STATUS_TXIDLE_MASK;
> +            break;
> +        case OT_UART_TX_FIFO_SIZE:
> +            retvalue |= R_STATUS_TXFULL_MASK;
> +            break;
> +        default:
> +            break;
> +        }
> +        if (!ot_uart_is_tx_enabled(s)) {
> +            retvalue |= R_STATUS_TXIDLE_MASK;
> +        }
> +        if (!ot_uart_is_rx_enabled(s)) {
> +            retvalue |= R_STATUS_RXIDLE_MASK;
> +        }
> +        break;
>
>      case R_RDATA:
> -        retvalue = s->regs[R_RDATA];
> -        if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {
> -            qemu_chr_fe_accept_input(&s->chr);
> -
> -            s->rx_level -= 1;
> -            s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;
> -            if (s->rx_level == 0) {
> -                s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;
> -                s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;
> -            }
> -        }
> +        retvalue = (uint32_t)ot_uart_read_rx_fifo(s);
>          break;
>
>      case R_FIFO_STATUS:
> -        retvalue = s->regs[R_FIFO_STATUS];
> -
> -        retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
> -        retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
> -
> -        qemu_log_mask(LOG_UNIMP,
> -                      "%s: RX fifos are not supported\n", __func__);
> +        retvalue =
> +            (fifo8_num_used(&s->rx_fifo) & 0xffu) << 
> R_FIFO_STATUS_RXLVL_SHIFT;
> +        retvalue |=
> +            (fifo8_num_used(&s->tx_fifo) & 0xffu) << 
> R_FIFO_STATUS_TXLVL_SHIFT;
>          break;
>
>      case R_VAL:
> @@ -416,10 +484,6 @@ static void ot_uart_write(void *opaque, hwaddr addr, 
> uint64_t val64,
>              qemu_log_mask(LOG_UNIMP,
>                            "%s: UART_CTRL_NF is not supported\n", __func__);
>          }
> -        if (value & R_CTRL_SLPBK_MASK) {
> -            qemu_log_mask(LOG_UNIMP,
> -                          "%s: UART_CTRL_SLPBK is not supported\n", 
> __func__);
> -        }
>          if (value & R_CTRL_LLPBK_MASK) {
>              qemu_log_mask(LOG_UNIMP,
>                            "%s: UART_CTRL_LLPBK is not supported\n", 
> __func__);
> @@ -445,19 +509,19 @@ static void ot_uart_write(void *opaque, hwaddr addr, 
> uint64_t val64,
>          }
>          break;
>      case R_WDATA:
> -        uart_write_tx_fifo(s, value);
> +        uart_write_tx_fifo(s, (uint8_t)(value & R_WDATA_WDATA_MASK));
>          break;
>
>      case R_FIFO_CTRL:
> -        s->regs[R_FIFO_CTRL] = value;
> -
> +        s->regs[R_FIFO_CTRL] =
> +            value & (R_FIFO_CTRL_RXILVL_MASK | R_FIFO_CTRL_TXILVL_MASK);
>          if (value & R_FIFO_CTRL_RXRST_MASK) {
> -            s->rx_level = 0;
> -            qemu_log_mask(LOG_UNIMP,
> -                          "%s: RX fifos are not supported\n", __func__);
> +            ot_uart_reset_rx_fifo(s);
> +            ot_uart_update_irqs(s);
>          }
>          if (value & R_FIFO_CTRL_TXRST_MASK) {
> -            s->tx_level = 0;
> +            ot_uart_reset_tx_fifo(s);
> +            ot_uart_update_irqs(s);
>          }
>          break;
>      case R_OVRD:
> --
> 2.49.1
>
>

Reply via email to