[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 3/6] hw/char/stm32l4x5_usart: Enable serial read and write
From: |
Arnaud Minier |
Subject: |
[PATCH v2 3/6] hw/char/stm32l4x5_usart: Enable serial read and write |
Date: |
Sun, 24 Mar 2024 17:55:43 +0100 |
Implement the ability to read and write characters to the
usart using the serial port.
The character transmission is based on the
cmsdk-apb-uart implementation.
Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
---
hw/char/stm32l4x5_usart.c | 139 ++++++++++++++++++++++++++++++
hw/char/trace-events | 7 ++
include/hw/char/stm32l4x5_usart.h | 1 +
3 files changed, 147 insertions(+)
diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c
index 46e69bb096..ec8c2f6e63 100644
--- a/hw/char/stm32l4x5_usart.c
+++ b/hw/char/stm32l4x5_usart.c
@@ -154,6 +154,119 @@ REG32(RDR, 0x24)
REG32(TDR, 0x28)
FIELD(TDR, TDR, 0, 9)
+static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s)
+{
+ if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) ||
+ ((s->isr & R_ISR_CMF_MASK) && (s->cr1 & R_CR1_CMIE_MASK)) ||
+ ((s->isr & R_ISR_ABRF_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) ||
+ ((s->isr & R_ISR_EOBF_MASK) && (s->cr1 & R_CR1_EOBIE_MASK)) ||
+ ((s->isr & R_ISR_RTOF_MASK) && (s->cr1 & R_CR1_RTOIE_MASK)) ||
+ ((s->isr & R_ISR_CTSIF_MASK) && (s->cr3 & R_CR3_CTSIE_MASK)) ||
+ ((s->isr & R_ISR_LBDF_MASK) && (s->cr2 & R_CR2_LBDIE_MASK)) ||
+ ((s->isr & R_ISR_TXE_MASK) && (s->cr1 & R_CR1_TXEIE_MASK)) ||
+ ((s->isr & R_ISR_TC_MASK) && (s->cr1 & R_CR1_TCIE_MASK)) ||
+ ((s->isr & R_ISR_RXNE_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) ||
+ ((s->isr & R_ISR_IDLE_MASK) && (s->cr1 & R_CR1_IDLEIE_MASK)) ||
+ ((s->isr & R_ISR_ORE_MASK) &&
+ ((s->cr1 & R_CR1_RXNEIE_MASK) || (s->cr3 & R_CR3_EIE_MASK))) ||
+ /* TODO: Handle NF ? */
+ ((s->isr & R_ISR_FE_MASK) && (s->cr3 & R_CR3_EIE_MASK)) ||
+ ((s->isr & R_ISR_PE_MASK) && (s->cr1 & R_CR1_PEIE_MASK))) {
+ qemu_irq_raise(s->irq);
+ trace_stm32l4x5_usart_irq_raised(s->isr);
+ } else {
+ qemu_irq_lower(s->irq);
+ trace_stm32l4x5_usart_irq_lowered();
+ }
+}
+
+static int stm32l4x5_usart_base_can_receive(void *opaque)
+{
+ Stm32l4x5UsartBaseState *s = opaque;
+
+ if (!(s->isr & R_ISR_RXNE_MASK)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void stm32l4x5_usart_base_receive(void *opaque, const uint8_t *buf, int
size)
+{
+ Stm32l4x5UsartBaseState *s = opaque;
+
+ if (!((s->cr1 & R_CR1_UE_MASK) && (s->cr1 & R_CR1_RE_MASK))) {
+ trace_stm32l4x5_usart_receiver_not_enabled(
+ FIELD_EX32(s->cr1, CR1, UE), FIELD_EX32(s->cr1, CR1, RE));
+ return;
+ }
+
+ /* Check if overrun detection is enabled and if there is an overrun */
+ if (!(s->cr3 & R_CR3_OVRDIS_MASK) && (s->isr & R_ISR_RXNE_MASK)) {
+ /*
+ * A character has been received while
+ * the previous has not been read = Overrun.
+ */
+ s->isr |= R_ISR_ORE_MASK;
+ trace_stm32l4x5_usart_overrun_detected(s->rdr, *buf);
+ } else {
+ /* No overrun */
+ s->rdr = *buf;
+ s->isr |= R_ISR_RXNE_MASK;
+ trace_stm32l4x5_usart_rx(s->rdr);
+ }
+
+ stm32l4x5_update_irq(s);
+}
+
+/* Try to send tx data, and arrange to be called back later if
+ * we can't (ie the char backend is busy/blocking).
+ */
+static gboolean usart_transmit(void *do_not_use, GIOCondition cond, void
*opaque)
+{
+ Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(opaque);
+ int ret;
+ /* TODO: Handle 9 bits transmission */
+ uint8_t ch = s->tdr;
+
+ s->watch_tag = 0;
+
+ if (!(s->cr1 & R_CR1_TE_MASK) || (s->isr & R_ISR_TXE_MASK)) {
+ return G_SOURCE_REMOVE;
+ }
+
+ ret = qemu_chr_fe_write(&s->chr, &ch, 1);
+ if (ret <= 0) {
+ s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
+ usart_transmit, s);
+ if (!s->watch_tag) {
+ /* Most common reason to be here is "no chardev backend":
+ * just insta-drain the buffer, so the serial output
+ * goes into a void, rather than blocking the guest.
+ */
+ goto buffer_drained;
+ }
+ /* Transmit pending */
+ trace_stm32l4x5_usart_tx_pending();
+ return G_SOURCE_REMOVE;
+ }
+
+buffer_drained:
+ /* Character successfully sent */
+ trace_stm32l4x5_usart_tx(ch);
+ s->isr |= R_ISR_TC_MASK | R_ISR_TXE_MASK;
+ stm32l4x5_update_irq(s);
+ return G_SOURCE_REMOVE;
+}
+
+static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s)
+{
+ if (s->watch_tag) {
+ g_source_remove(s->watch_tag);
+ s->watch_tag = 0;
+ }
+}
+
static void stm32l4x5_usart_base_reset_hold(Object *obj)
{
Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj);
@@ -167,6 +280,22 @@ static void stm32l4x5_usart_base_reset_hold(Object *obj)
s->isr = 0x020000C0;
s->rdr = 0x00000000;
s->tdr = 0x00000000;
+
+ usart_cancel_transmit(s);
+ stm32l4x5_update_irq(s);
+}
+
+static void usart_update_rqr(Stm32l4x5UsartBaseState *s, uint32_t value)
+{
+ /* TXFRQ */
+ /* Reset RXNE flag */
+ if (value & R_RQR_RXFRQ_MASK) {
+ s->isr &= ~R_ISR_RXNE_MASK;
+ }
+ /* MMRQ */
+ /* SBKRQ */
+ /* ABRRQ */
+ stm32l4x5_update_irq(s);
}
static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr,
@@ -209,6 +338,7 @@ static uint64_t stm32l4x5_usart_base_read(void *opaque,
hwaddr addr,
retvalue = FIELD_EX32(s->rdr, RDR, RDR);
/* Reset RXNE flag */
s->isr &= ~R_ISR_RXNE_MASK;
+ stm32l4x5_update_irq(s);
break;
case A_TDR:
retvalue = FIELD_EX32(s->tdr, TDR, TDR);
@@ -235,6 +365,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr
addr,
switch (addr) {
case A_CR1:
s->cr1 = value;
+ stm32l4x5_update_irq(s);
return;
case A_CR2:
s->cr2 = value;
@@ -252,6 +383,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr
addr,
s->rtor = value;
return;
case A_RQR:
+ usart_update_rqr(s, value);
return;
case A_ISR:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -260,6 +392,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr
addr,
case A_ICR:
/* Clear the status flags */
s->isr &= ~value;
+ stm32l4x5_update_irq(s);
return;
case A_RDR:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -267,6 +400,8 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr
addr,
return;
case A_TDR:
s->tdr = value;
+ s->isr &= ~R_ISR_TXE_MASK;
+ usart_transmit(NULL, G_IO_OUT, s);
return;
default:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -336,6 +471,10 @@ static void stm32l4x5_usart_base_realize(DeviceState *dev,
Error **errp)
error_setg(errp, "USART clock must be wired up by SoC code");
return;
}
+
+ qemu_chr_fe_set_handlers(&s->chr, stm32l4x5_usart_base_can_receive,
+ stm32l4x5_usart_base_receive, NULL, NULL,
+ s, NULL, true);
}
static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 689bed9e02..f22f0ee2bc 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -109,6 +109,13 @@ sh_serial_write(char *id, unsigned size, uint64_t offs,
uint64_t val) "%s size %
# stm32l4x5_usart.c
stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64
"> -> 0x%" PRIx32 ""
stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64
"> <- 0x%" PRIx32 ""
+stm32l4x5_usart_rx(uint8_t c) "USART: got character 0x%x from backend"
+stm32l4x5_usart_tx(uint8_t c) "USART: character 0x%x sent to backend"
+stm32l4x5_usart_tx_pending(void) "USART: character send to backend pending"
+stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32
+stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered"
+stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART:
Overrun detected, RDR='0x%x', received 0x%x"
+stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART:
Receiver not enabled, UE=0x%x, RE=0x%x"
# xen_console.c
xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int
port, unsigned int limit) "idx %u ring_ref %u port %u limit %u"
diff --git a/include/hw/char/stm32l4x5_usart.h
b/include/hw/char/stm32l4x5_usart.h
index 8d38a85a6e..dd3866682a 100644
--- a/include/hw/char/stm32l4x5_usart.h
+++ b/include/hw/char/stm32l4x5_usart.h
@@ -55,6 +55,7 @@ struct Stm32l4x5UsartBaseState {
Clock *clk;
CharBackend chr;
qemu_irq irq;
+ guint watch_tag;
};
struct Stm32l4x5UsartBaseClass {
--
2.34.1
- [PATCH v2 0/6] hw/char: Implement the STM32L4x5 USART, UART and LPUART, Arnaud Minier, 2024/03/24
- [PATCH v2 1/6] hw/misc/stm32l4x5_rcc: Propagate period when enabling a clock, Arnaud Minier, 2024/03/24
- [PATCH v2 2/6] hw/char: Implement STM32L4x5 USART skeleton, Arnaud Minier, 2024/03/24
- [PATCH v2 3/6] hw/char/stm32l4x5_usart: Enable serial read and write,
Arnaud Minier <=
- [PATCH v2 4/6] hw/char/stm32l4x5_usart: Add options for serial parameters setting, Arnaud Minier, 2024/03/24
- [PATCH v2 5/6] hw/arm: Add the USART to the stm32l4x5 SoC, Arnaud Minier, 2024/03/24
- [PATCH v2 6/6] tests/qtest: Add tests for the STM32L4x5 USART, Arnaud Minier, 2024/03/24
- Re: [PATCH v2 0/6] hw/char: Implement the STM32L4x5 USART, UART and LPUART, Peter Maydell, 2024/03/28