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:
parent
4fb37aea7e
commit
87b77e6e01
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user