hw/char/stm32l4x5_usart: Enable serial read and write

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>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240329174402.60382-3-arnaud.minier@telecom-paris.fr
[PMM: fixed a few checkpatch nits]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Arnaud Minier 2024-03-29 18:43:59 +01:00 committed by Peter Maydell
parent 4fb37aea7e
commit 87b77e6e01
3 changed files with 151 additions and 0 deletions

View File

@ -154,6 +154,123 @@ REG32(RDR, 0x24)
REG32(TDR, 0x28) REG32(TDR, 0x28)
FIELD(TDR, TDR, 0, 9) 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, ResetType type) static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type)
{ {
Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj);
@ -167,6 +284,22 @@ static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type)
s->isr = 0x020000C0; s->isr = 0x020000C0;
s->rdr = 0x00000000; s->rdr = 0x00000000;
s->tdr = 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, static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr,
@ -209,6 +342,7 @@ static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr,
retvalue = FIELD_EX32(s->rdr, RDR, RDR); retvalue = FIELD_EX32(s->rdr, RDR, RDR);
/* Reset RXNE flag */ /* Reset RXNE flag */
s->isr &= ~R_ISR_RXNE_MASK; s->isr &= ~R_ISR_RXNE_MASK;
stm32l4x5_update_irq(s);
break; break;
case A_TDR: case A_TDR:
retvalue = FIELD_EX32(s->tdr, TDR, TDR); retvalue = FIELD_EX32(s->tdr, TDR, TDR);
@ -235,6 +369,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr,
switch (addr) { switch (addr) {
case A_CR1: case A_CR1:
s->cr1 = value; s->cr1 = value;
stm32l4x5_update_irq(s);
return; return;
case A_CR2: case A_CR2:
s->cr2 = value; s->cr2 = value;
@ -252,6 +387,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr,
s->rtor = value; s->rtor = value;
return; return;
case A_RQR: case A_RQR:
usart_update_rqr(s, value);
return; return;
case A_ISR: case A_ISR:
qemu_log_mask(LOG_GUEST_ERROR, qemu_log_mask(LOG_GUEST_ERROR,
@ -260,6 +396,7 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr,
case A_ICR: case A_ICR:
/* Clear the status flags */ /* Clear the status flags */
s->isr &= ~value; s->isr &= ~value;
stm32l4x5_update_irq(s);
return; return;
case A_RDR: case A_RDR:
qemu_log_mask(LOG_GUEST_ERROR, qemu_log_mask(LOG_GUEST_ERROR,
@ -267,6 +404,8 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr,
return; return;
case A_TDR: case A_TDR:
s->tdr = value; s->tdr = value;
s->isr &= ~R_ISR_TXE_MASK;
usart_transmit(NULL, G_IO_OUT, s);
return; return;
default: default:
qemu_log_mask(LOG_GUEST_ERROR, qemu_log_mask(LOG_GUEST_ERROR,
@ -336,6 +475,10 @@ static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp)
error_setg(errp, "USART clock must be wired up by SoC code"); error_setg(errp, "USART clock must be wired up by SoC code");
return; 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) static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data)

View File

@ -109,6 +109,13 @@ sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %
# stm32l4x5_usart.c # stm32l4x5_usart.c
stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" 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_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.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" 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"

View File

@ -55,6 +55,7 @@ struct Stm32l4x5UsartBaseState {
Clock *clk; Clock *clk;
CharBackend chr; CharBackend chr;
qemu_irq irq; qemu_irq irq;
guint watch_tag;
}; };
struct Stm32l4x5UsartBaseClass { struct Stm32l4x5UsartBaseClass {