From c4c12ee48709aeb896ebb60163e8eb26cdaa3d65 Mon Sep 17 00:00:00 2001 From: Arnaud Minier Date: Fri, 29 Mar 2024 18:44:00 +0100 Subject: hw/char/stm32l4x5_usart: Add options for serial parameters setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to change the settings of the serial connection. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Peter Maydell Message-id: 20240329174402.60382-4-arnaud.minier@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/char/stm32l4x5_usart.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++ hw/char/trace-events | 1 + 2 files changed, 99 insertions(+) (limited to 'hw/char') diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 755fc0b..2627aab 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -271,6 +271,92 @@ static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) } } +static void stm32l4x5_update_params(Stm32l4x5UsartBaseState *s) +{ + int speed, parity, data_bits, stop_bits; + uint32_t value, usart_div; + QEMUSerialSetParams ssp; + + /* Select the parity type */ + if (s->cr1 & R_CR1_PCE_MASK) { + if (s->cr1 & R_CR1_PS_MASK) { + parity = 'O'; + } else { + parity = 'E'; + } + } else { + parity = 'N'; + } + + /* Select the number of stop bits */ + switch (FIELD_EX32(s->cr2, CR2, STOP)) { + case 0: + stop_bits = 1; + break; + case 2: + stop_bits = 2; + break; + default: + qemu_log_mask(LOG_UNIMP, + "UNIMPLEMENTED: fractionnal stop bits; CR2[13:12] = %u", + FIELD_EX32(s->cr2, CR2, STOP)); + return; + } + + /* Select the length of the word */ + switch ((FIELD_EX32(s->cr1, CR1, M1) << 1) | FIELD_EX32(s->cr1, CR1, M0)) { + case 0: + data_bits = 8; + break; + case 1: + data_bits = 9; + break; + case 2: + data_bits = 7; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: invalid word length, CR1.M = 0b11"); + return; + } + + /* Select the baud rate */ + value = FIELD_EX32(s->brr, BRR, BRR); + if (value < 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: BRR less than 16: %u", value); + return; + } + + if (FIELD_EX32(s->cr1, CR1, OVER8) == 0) { + /* + * Oversampling by 16 + * BRR = USARTDIV + */ + usart_div = value; + } else { + /* + * Oversampling by 8 + * - BRR[2:0] = USARTDIV[3:0] shifted 1 bit to the right. + * - BRR[3] must be kept cleared. + * - BRR[15:4] = USARTDIV[15:4] + * - The frequency is multiplied by 2 + */ + usart_div = ((value & 0xFFF0) | ((value & 0x0007) << 1)) / 2; + } + + speed = clock_get_hz(s->clk) / usart_div; + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + trace_stm32l4x5_usart_update_params(speed, parity, data_bits, stop_bits); +} + static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -369,16 +455,19 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, switch (addr) { case A_CR1: s->cr1 = value; + stm32l4x5_update_params(s); stm32l4x5_update_irq(s); return; case A_CR2: s->cr2 = value; + stm32l4x5_update_params(s); return; case A_CR3: s->cr3 = value; return; case A_BRR: s->brr = value; + stm32l4x5_update_params(s); return; case A_GTPR: s->gtpr = value; @@ -447,10 +536,19 @@ static void stm32l4x5_usart_base_init(Object *obj) s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); } +static int stm32l4x5_usart_base_post_load(void *opaque, int version_id) +{ + Stm32l4x5UsartBaseState *s = (Stm32l4x5UsartBaseState *)opaque; + + stm32l4x5_update_params(s); + return 0; +} + static const VMStateDescription vmstate_stm32l4x5_usart_base = { .name = TYPE_STM32L4X5_USART_BASE, .version_id = 1, .minimum_version_id = 1, + .post_load = stm32l4x5_usart_base_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), diff --git a/hw/char/trace-events b/hw/char/trace-events index f22f0ee..8875758 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -116,6 +116,7 @@ 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" +stm32l4x5_usart_update_params(int speed, uint8_t parity, int data, int stop) "USART: speed: %d, parity: %c, data bits: %d, stop bits: %d" # 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" -- cgit v1.1