From patchwork Sun Mar 17 10:39:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912879 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=QXXWQoJn; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyDzx5hnzz1yWv for ; Sun, 17 Mar 2024 21:41:17 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnw0-0001i6-7n; Sun, 17 Mar 2024 06:40:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnvx-0001hJ-Jm; Sun, 17 Mar 2024 06:40:05 -0400 Received: from zproxy3.enst.fr ([137.194.2.222]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnvu-00010v-R6; Sun, 17 Mar 2024 06:40:05 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy3.enst.fr (Postfix) with ESMTP id 149E3A06DC; Sun, 17 Mar 2024 11:39:59 +0100 (CET) Received: from zproxy3.enst.fr ([IPv6:::1]) by localhost (zproxy3.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id LF9tRo2h-9DW; Sun, 17 Mar 2024 11:39:58 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy3.enst.fr (Postfix) with ESMTP id 9F25EA084B; Sun, 17 Mar 2024 11:39:58 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy3.enst.fr 9F25EA084B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710671998; bh=z8vP61lOqCHtPIe/NMBtNrdjHVIxDoNiy4/4Xzg3S5o=; h=From:To:Date:Message-Id:MIME-Version; b=QXXWQoJnJAWu1igVq/rk9IRMaWIH8tHuQTj1+R1vCNVwEwPM09UdjGMePFiMw/teJ pOr0ExBUf4IoHnGMib9vIFbCTtfmj9ct3u3Tfbdk6elIsOjp8OhQ/IXXmA5s93aSSS /hKwhCITgN6LHJlsMwKWL5tJV9eauJNQgmKCXIuw= X-Virus-Scanned: amavis at enst.fr Received: from zproxy3.enst.fr ([IPv6:::1]) by localhost (zproxy3.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id hO9EbYk_ZZsy; Sun, 17 Mar 2024 11:39:58 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy3.enst.fr (Postfix) with ESMTPSA id 3778DA06DC; Sun, 17 Mar 2024 11:39:58 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 1/7] hw/misc/stm32l4x5_rcc: Propagate period when enabling a clock Date: Sun, 17 Mar 2024 11:39:12 +0100 Message-Id: <20240317103918.44375-2-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=137.194.2.222; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy3.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The "clock_set_mul_div" function doesn't propagate the clock period to the children if it is changed (e.g. by enabling/disabling a clock multiplexer). This was overlooked during the implementation due to late changes. This commit propagates the change if the multiplier or divider changes. The usart tests will ensure that this behavior will not regress. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- hw/misc/stm32l4x5_rcc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index bc2d63528b..4725ba4f1c 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -59,7 +59,12 @@ static void clock_mux_update(RccClockMuxState *mux, bool bypass_source) freq_multiplier = mux->divider; } - clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier); + if ((mux->out->multiplier != freq_multiplier) || + mux->out->divider != mux->multiplier) { + clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier); + clock_propagate(mux->out); + } + clock_update(mux->out, clock_get(current_source)); src_freq = clock_get_hz(current_source); From patchwork Sun Mar 17 10:39:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912880 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=PoMu9UGk; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF044TmQz1yWv for ; Sun, 17 Mar 2024 21:41:24 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnw0-0001iC-EC; Sun, 17 Mar 2024 06:40:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnvx-0001hI-JQ; Sun, 17 Mar 2024 06:40:05 -0400 Received: from zproxy3.enst.fr ([137.194.2.222]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnvu-000116-Kd; Sun, 17 Mar 2024 06:40:05 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy3.enst.fr (Postfix) with ESMTP id BFC19A084B; Sun, 17 Mar 2024 11:40:00 +0100 (CET) Received: from zproxy3.enst.fr ([IPv6:::1]) by localhost (zproxy3.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id Fb2fK10BLPwf; Sun, 17 Mar 2024 11:40:00 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy3.enst.fr (Postfix) with ESMTP id EDE39A086D; Sun, 17 Mar 2024 11:39:59 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy3.enst.fr EDE39A086D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710671999; bh=8Nf4X0B32h13JLSPnzzPytGT5PPczqQ3+PNldp7K26g=; h=From:To:Date:Message-Id:MIME-Version; b=PoMu9UGk/S9HBmxaHxDubNEpKyV4tLNnKcYmwpQo8N+1Zy0ghf8kZmPj25fitEfj5 xvbQ/mgQhXum35VrMHNY5tbb8qWOcE6fLABON8cAAJWLGlnEcijmK9UeWu4I3EuLS3 q1GOIv0eP3jm17K+1uIhx0deWBdvTOz3euBhlVYc= X-Virus-Scanned: amavis at enst.fr Received: from zproxy3.enst.fr ([IPv6:::1]) by localhost (zproxy3.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id Jt0ynEqFTqpF; Sun, 17 Mar 2024 11:39:59 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy3.enst.fr (Postfix) with ESMTPSA id 7F15CA0859; Sun, 17 Mar 2024 11:39:59 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 2/7] hw/char: Implement STM32L4x5 USART skeleton Date: Sun, 17 Mar 2024 11:39:13 +0100 Message-Id: <20240317103918.44375-3-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=137.194.2.222; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy3.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add the basic infrastructure (register read/write, type...) to implement the STM32L4x5 USART. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- MAINTAINERS | 1 + hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/stm32l4x5_usart.c | 364 ++++++++++++++++++++++++++++++ hw/char/trace-events | 10 + include/hw/char/stm32l4x5_usart.h | 49 ++++ 6 files changed, 428 insertions(+) create mode 100644 hw/char/stm32l4x5_usart.c create mode 100644 include/hw/char/stm32l4x5_usart.h diff --git a/MAINTAINERS b/MAINTAINERS index 409d7db4d4..deba4a54ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1128,6 +1128,7 @@ M: Inès Varhol L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32l4x5_soc.c +F: hw/char/stm32l4x5_usart.c F: hw/misc/stm32l4x5_exti.c F: hw/misc/stm32l4x5_syscfg.c F: hw/misc/stm32l4x5_rcc.c diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6b6cf2fc1d..4fd74ea878 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -41,6 +41,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32L4X5_USART + bool + config CMSDK_APB_UART bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 006d20f1e2..e5b13b6958 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -31,6 +31,7 @@ system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_usart.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c new file mode 100644 index 0000000000..b56fee5b3a --- /dev/null +++ b/hw/char/stm32l4x5_usart.c @@ -0,0 +1,364 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" +#include "migration/vmstate.h" +#include "hw/char/stm32l4x5_usart.h" +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" +#include "trace.h" + + +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) /* Word length (part 2, see M0)*/ + FIELD(CR1, EOBIE, 27, 1) /* End of Block interrupt enable */ + FIELD(CR1, RTOIE, 26, 1) /* Receiver timeout interrupt enable */ + FIELD(CR1, DEAT, 21, 5) /* Driver Enable assertion time */ + FIELD(CR1, DEDT, 16, 5) /* Driver Enable de-assertion time */ + FIELD(CR1, OVER8, 15, 1) /* Oversampling mode */ + FIELD(CR1, CMIE, 14, 1) /* Character match interrupt enable */ + FIELD(CR1, MME, 13, 1) /* Mute mode enable */ + FIELD(CR1, M0, 12, 1) /* Word length (part 1, see M1) */ + FIELD(CR1, WAKE, 11, 1) /* Receiver wakeup method */ + FIELD(CR1, PCE, 10, 1) /* Parity control enable */ + FIELD(CR1, PS, 9, 1) /* Parity selection */ + FIELD(CR1, PEIE, 8, 1) /* PE interrupt enable */ + FIELD(CR1, TXEIE, 7, 1) /* TXE interrupt enable */ + FIELD(CR1, TCIE, 6, 1) /* Transmission complete interrupt enable */ + FIELD(CR1, RXNEIE, 5, 1) /* RXNE interrupt enable */ + FIELD(CR1, IDLEIE, 4, 1) /* IDLE interrupt enable */ + FIELD(CR1, TE, 3, 1) /* Transmitter enable */ + FIELD(CR1, RE, 2, 1) /* Receiver enable */ + FIELD(CR1, UESM, 1, 1) /* USART enable in Stop mode */ + FIELD(CR1, UE, 0, 1) /* USART enable */ +REG32(CR2, 0x04) + FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */ + FIELD(CR2, ADD_0, 24, 1) /* ADD[3:0] */ + FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */ + FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */ + FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */ + FIELD(CR2, MSBFIRST, 19, 1) /* Most significant bit first */ + FIELD(CR2, DATAINV, 18, 1) /* Binary data inversion */ + FIELD(CR2, TXINV, 17, 1) /* TX pin active level inversion */ + FIELD(CR2, RXINV, 16, 1) /* RX pin active level inversion */ + FIELD(CR2, SWAP, 15, 1) /* Swap RX/TX pins */ + FIELD(CR2, LINEN, 14, 1) /* LIN mode enable */ + FIELD(CR2, STOP, 12, 2) /* STOP bits */ + FIELD(CR2, CLKEN, 11, 1) /* Clock enable */ + FIELD(CR2, CPOL, 10, 1) /* Clock polarity */ + FIELD(CR2, CPHA, 9, 1) /* Clock phase */ + FIELD(CR2, LBCL, 8, 1) /* Last bit clock pulse */ + FIELD(CR2, LBDIE, 6, 1) /* LIN break detection interrupt enable */ + FIELD(CR2, LBDL, 5, 1) /* LIN break detection length */ + FIELD(CR2, ADDM7, 4, 1) /* 7-bit Address Detection / + * 4-bit Address Detection */ +REG32(CR3, 0x08) + /* TCBGTIE only on STM32L496xx/4A6xx devices */ + FIELD(CR3, UCESM, 23, 1) /* USART Clock Enable in Stop Mode */ + FIELD(CR3, WUFIE, 22, 1) /* Wakeup from Stop mode interrupt enable */ + FIELD(CR3, WUS, 20, 2) /* Wakeup from Stop mode interrupt flag selection */ + FIELD(CR3, SCARCNT, 17, 3) /* Smartcard auto-retry count */ + FIELD(CR3, DEP, 15, 1) /* Driver enable polarity selection */ + FIELD(CR3, DEM, 14, 1) /* Driver enable mode */ + FIELD(CR3, DDRE, 13, 1) /* DMA Disable on Reception Error */ + FIELD(CR3, OVRDIS, 12, 1) /* Overrun Disable */ + FIELD(CR3, ONEBIT, 11, 1) /* One sample bit method enable */ + FIELD(CR3, CTSIE, 10, 1) /* CTS interrupt enable */ + FIELD(CR3, CTSE, 9, 1) /* CTS enable */ + FIELD(CR3, RTSE, 8, 1) /* RTS enable */ + FIELD(CR3, DMAT, 7, 1) /* DMA enable transmitter */ + FIELD(CR3, DMAR, 6, 1) /* DMA enable receiver */ + FIELD(CR3, SCEN, 5, 1) /* Smartcard mode enable */ + FIELD(CR3, NACK, 4, 1) /* Smartcard NACK enable */ + FIELD(CR3, HDSEL, 3, 1) /* Half-duplex selection */ + FIELD(CR3, IRLP, 2, 1) /* IrDA low-power */ + FIELD(CR3, IREN, 1, 1) /* IrDA mode enable */ + FIELD(CR3, EIE, 0, 1) /* Error interrupt enable */ +REG32(BRR, 0x0C) + FIELD(BRR, BRR, 0, 16) +REG32(GTPR, 0x10) + FIELD(GTPR, GT, 8, 8) /* Guard time value */ + FIELD(GTPR, PSC, 0, 8) /* Prescaler value */ +REG32(RTOR, 0x14) + FIELD(RTOR, BLEN, 24, 8) /* Block Length */ + FIELD(RTOR, RTO, 0, 24) /* Receiver timeout value */ +REG32(RQR, 0x18) + FIELD(RQR, TXFRQ, 4, 1) /* Transmit data flush request */ + FIELD(RQR, RXFRQ, 3, 1) /* Receive data flush request */ + FIELD(RQR, MMRQ, 2, 1) /* Mute mode request */ + FIELD(RQR, SBKRQ, 1, 1) /* Send break request */ + FIELD(RQR, ABBRRQ, 0, 1) /* Auto baud rate request */ +REG32(ISR, 0x1C) + /* TCBGT only for STM32L475xx/476xx/486xx devices */ + FIELD(ISR, REACK, 22, 1) /* Receive enable acknowledge flag */ + FIELD(ISR, TEACK, 21, 1) /* Transmit enable acknowledge flag */ + FIELD(ISR, WUF, 20, 1) /* Wakeup from Stop mode flag */ + FIELD(ISR, RWU, 19, 1) /* Receiver wakeup from Mute mode */ + FIELD(ISR, SBKF, 18, 1) /* Send break flag */ + FIELD(ISR, CMF, 17, 1) /* Character match flag */ + FIELD(ISR, BUSY, 16, 1) /* Busy flag */ + FIELD(ISR, ABRF, 15, 1) /* Auto Baud rate flag */ + FIELD(ISR, ABRE, 14, 1) /* Auto Baud rate error */ + FIELD(ISR, EOBF, 12, 1) /* End of block flag */ + FIELD(ISR, RTOF, 11, 1) /* Receiver timeout */ + FIELD(ISR, CTS, 10, 1) /* CTS flag */ + FIELD(ISR, CTSIF, 9, 1) /* CTS interrupt flag */ + FIELD(ISR, LBDF, 8, 1) /* LIN break detection flag */ + FIELD(ISR, TXE, 7, 1) /* Transmit data register empty */ + FIELD(ISR, TC, 6, 1) /* Transmission complete */ + FIELD(ISR, RXNE, 5, 1) /* Read data register not empty */ + FIELD(ISR, IDLE, 4, 1) /* Idle line detected */ + FIELD(ISR, ORE, 3, 1) /* Overrun error */ + FIELD(ISR, NF, 2, 1) /* START bit Noise detection flag */ + FIELD(ISR, FE, 1, 1) /* Framing Error*/ + FIELD(ISR, PE, 0, 1) /* Parity Error*/ +REG32(ICR, 0x20) + FIELD(ICR, WUCF, 20, 1) /* Wakeup from Stop mode clear flag */ + FIELD(ICR, CMCF, 17, 1) /* Character match clear flag */ + FIELD(ICR, EOBCF, 12, 1) /* End of block clear flag */ + FIELD(ICR, RTOCF, 11, 1) /* Receiver timeout clear flag */ + FIELD(ICR, CTSCF, 9, 1) /* CTS clear flag */ + FIELD(ICR, LBDCF, 8, 1) /* LIN break detection clear flag */ + /* TCBGTCF only on STM32L496xx/4A6xx devices */ + FIELD(ICR, TCCF, 6, 1) /* Transmission complete clear flag */ + FIELD(ICR, IDLECF, 4, 1) /* Idle line detected clear flag */ + FIELD(ICR, ORECF, 3, 1) /* Overrun error clear flag */ + FIELD(ICR, NCF, 2, 1) /* Noise detected clear flag */ + FIELD(ICR, FECF, 1, 1) /* Framing error clear flag */ + FIELD(ICR, PECF, 0, 1) /* Parity error clear flag */ +REG32(RDR, 0x24) + FIELD(RDR, RDR, 0, 8) +REG32(TDR, 0x28) + FIELD(TDR, TDR, 0, 8) + +static void stm32l4x5_usart_reset_hold(Object *obj) +{ + STM32L4X5UsartState *s = STM32L4X5_USART(obj); + + s->cr1 = 0x00000000; + s->cr2 = 0x00000000; + s->cr3 = 0x00000000; + s->brr = 0x00000000; + s->gtpr = 0x00000000; + s->rtor = 0x00000000; + /* I/O being synchronous, TXE is always set. */ + s->isr = 0x020000C0 | R_ISR_TXE_MASK; + s->rdr = 0x00000000; + s->tdr = 0x00000000; +} + +static uint64_t stm32l4x5_usart_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32L4X5UsartState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case A_CR1: + retvalue = s->cr1; + break; + case A_CR2: + retvalue = s->cr2; + break; + case A_CR3: + retvalue = s->cr3; + break; + case A_BRR: + retvalue = FIELD_EX32(s->brr, BRR, BRR); + break; + case A_GTPR: + retvalue = s->gtpr; + break; + case A_RTOR: + retvalue = s->rtor; + break; + case A_RQR: + /* RQR is a write only register */ + retvalue = 0x00000000; + break; + case A_ISR: + retvalue = s->isr; + break; + case A_ICR: + /* ICR is a clear register */ + retvalue = 0x00000000; + break; + case A_RDR: + retvalue = FIELD_EX32(s->rdr, RDR, RDR); + /* Reset RXNE flag */ + s->isr &= ~USART_ISR_RXNE; + break; + case A_TDR: + retvalue = FIELD_EX32(s->tdr, TDR, TDR); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + trace_stm32l4x5_usart_read(addr, retvalue); + + return retvalue; +} + +static void stm32l4x5_usart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32L4X5UsartState *s = opaque; + const uint32_t value = val64; + uint8_t ch; + + trace_stm32l4x5_usart_write(addr, value); + + switch (addr) { + case A_CR1: + s->cr1 = value; + return; + case A_CR2: + s->cr2 = value; + return; + case A_CR3: + s->cr3 = value; + return; + case A_BRR: + s->brr = value; + return; + case A_GTPR: + s->gtpr = value; + return; + case A_RTOR: + s->rtor = value; + return; + case A_RQR: + return; + case A_ISR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ISR is read only !\n", __func__); + return; + case A_ICR: + /* Clear the status flags */ + s->isr &= ~value; + return; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDR is read only !\n", __func__); + return; + case A_TDR: + s->tdr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_usart_ops = { + .read = stm32l4x5_usart_read, + .write = stm32l4x5_usart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, + .impl = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, +}; + +static Property stm32l4x5_usart_properties[] = { + DEFINE_PROP_CHR("chardev", STM32L4X5UsartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_usart_init(Object *obj) +{ + STM32L4X5UsartState *s = STM32L4X5_USART(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_ops, s, + TYPE_STM32L4X5_USART, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); +} + +static const VMStateDescription vmstate_stm32l4x5_usart = { + .name = TYPE_STM32L4X5_USART, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr1, STM32L4X5UsartState), + VMSTATE_UINT32(cr2, STM32L4X5UsartState), + VMSTATE_UINT32(cr3, STM32L4X5UsartState), + VMSTATE_UINT32(brr, STM32L4X5UsartState), + VMSTATE_UINT32(gtpr, STM32L4X5UsartState), + VMSTATE_UINT32(rtor, STM32L4X5UsartState), + VMSTATE_UINT32(isr, STM32L4X5UsartState), + VMSTATE_UINT32(rdr, STM32L4X5UsartState), + VMSTATE_UINT32(tdr, STM32L4X5UsartState), + VMSTATE_CLOCK(clk, STM32L4X5UsartState), + VMSTATE_END_OF_LIST() + } +}; + + +static void stm32l4x5_usart_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + STM32L4X5UsartState *s = STM32L4X5_USART(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "USART clock must be wired up by SoC code"); + return; + } +} + +static void stm32l4x5_usart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = stm32l4x5_usart_reset_hold; + device_class_set_props(dc, stm32l4x5_usart_properties); + dc->realize = stm32l4x5_usart_realize; + dc->vmsd = &vmstate_stm32l4x5_usart; +} + +static const TypeInfo stm32l4x5_usart_types[] = { + { + .name = TYPE_STM32L4X5_USART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32L4X5UsartState), + .instance_init = stm32l4x5_usart_init, + .class_init = stm32l4x5_usart_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_usart_types) diff --git a/hw/char/trace-events b/hw/char/trace-events index 7a398c82a5..9f5fda0eb8 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -106,6 +106,16 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 +# 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_update_params(int speed, uint8_t parity, int data, int stop, uint64_t wordtime) "USART: speed: %d, parity: %c, data bits: %d, stop bits: %d wordtime: %"PRId64"ns" +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_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 +stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" +stm32l4x5_usart_error(const char* error) "USART Error: %s" + # 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_disconnect(unsigned int idx) "idx %u" diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h new file mode 100644 index 0000000000..8ec1b7afed --- /dev/null +++ b/include/hw/char/stm32l4x5_usart.h @@ -0,0 +1,49 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#ifndef HW_STM32L4X5_USART_H +#define HW_STM32L4X5_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_USART "stm32l4x5-usart" +OBJECT_DECLARE_SIMPLE_TYPE(STM32L4X5UsartState, STM32L4X5_USART) + +struct STM32L4X5UsartState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t cr1; + uint32_t cr2; + uint32_t cr3; + uint32_t brr; + uint32_t gtpr; + uint32_t rtor; + /* rqr is write-only */ + uint32_t isr; + /* icr is a clear register */ + uint32_t rdr; + uint32_t tdr; + + Clock *clk; + CharBackend chr; + qemu_irq irq; +}; +#endif /* HW_STM32L4X5_USART_H */ From patchwork Sun Mar 17 10:39:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912883 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=ryd0oXLo; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF0f32Q5z1yWv for ; Sun, 17 Mar 2024 21:41:54 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnxR-00036L-1b; Sun, 17 Mar 2024 06:41:38 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnx7-0002j0-SY; Sun, 17 Mar 2024 06:41:19 -0400 Received: from zproxy4.enst.fr ([137.194.2.223]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnx3-0001PG-6W; Sun, 17 Mar 2024 06:41:16 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 958B7205A4; Sun, 17 Mar 2024 11:41:10 +0100 (CET) Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id ZyZmxp1mnQN3; Sun, 17 Mar 2024 11:41:09 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id C858B2061D; Sun, 17 Mar 2024 11:41:08 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy4.enst.fr C858B2061D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710672068; bh=5GDvnMu2ewIpK7fNk9UMMPgm5GCnbCl9hPbVOlmLn+A=; h=From:To:Date:Message-Id:MIME-Version; b=ryd0oXLo5rPZ4Uxto/c1tWLq/Jh8kJZZ6J+J0uvRR2kNH8n4/c0znnSSQJONtJhm9 0T40wNskuI/yqvuy/+t0rKd3jbpEjKQP8vBq7K/LEMn7essjUpeksh70CdErZvvRuq KXd58KwAQY53FF6LfdqaYozhDb33bRvOicTJPytc= X-Virus-Scanned: amavis at enst.fr Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id LYl-nYQxjC9L; Sun, 17 Mar 2024 11:41:08 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy4.enst.fr (Postfix) with ESMTPSA id CB612205F4; Sun, 17 Mar 2024 11:41:07 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 3/7] hw/char/stm32l4x5_usart: Add USART, UART, LPUART types Date: Sun, 17 Mar 2024 11:39:14 +0100 Message-Id: <20240317103918.44375-4-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=137.194.2.223; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy4.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Create different types for the USART, UART and LPUART of the STM32L4x5 to deduplicate code and enable the implementation of different behaviors depending on the type. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- hw/char/stm32l4x5_usart.c | 113 +++++++++++++++++++----------- include/hw/char/stm32l4x5_usart.h | 21 +++++- 2 files changed, 92 insertions(+), 42 deletions(-) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index b56fee5b3a..f58bd56875 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -154,9 +154,9 @@ REG32(RDR, 0x24) REG32(TDR, 0x28) FIELD(TDR, TDR, 0, 8) -static void stm32l4x5_usart_reset_hold(Object *obj) +static void stm32l4x5_usart_base_reset_hold(Object *obj) { - STM32L4X5UsartState *s = STM32L4X5_USART(obj); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); s->cr1 = 0x00000000; s->cr2 = 0x00000000; @@ -170,10 +170,10 @@ static void stm32l4x5_usart_reset_hold(Object *obj) s->tdr = 0x00000000; } -static uint64_t stm32l4x5_usart_read(void *opaque, hwaddr addr, - unsigned int size) +static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, + unsigned int size) { - STM32L4X5UsartState *s = opaque; + Stm32l4x5UsartBaseState *s = opaque; uint64_t retvalue = 0; switch (addr) { @@ -225,10 +225,10 @@ static uint64_t stm32l4x5_usart_read(void *opaque, hwaddr addr, return retvalue; } -static void stm32l4x5_usart_write(void *opaque, hwaddr addr, +static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { - STM32L4X5UsartState *s = opaque; + Stm32l4x5UsartBaseState *s = opaque; const uint32_t value = val64; uint8_t ch; @@ -276,9 +276,9 @@ static void stm32l4x5_usart_write(void *opaque, hwaddr addr, } } -static const MemoryRegionOps stm32l4x5_usart_ops = { - .read = stm32l4x5_usart_read, - .write = stm32l4x5_usart_write, +static const MemoryRegionOps stm32l4x5_usart_base_ops = { + .read = stm32l4x5_usart_base_read, + .write = stm32l4x5_usart_base_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .max_access_size = 4, @@ -292,72 +292,105 @@ static const MemoryRegionOps stm32l4x5_usart_ops = { }, }; -static Property stm32l4x5_usart_properties[] = { - DEFINE_PROP_CHR("chardev", STM32L4X5UsartState, chr), +static Property stm32l4x5_usart_base_properties[] = { + DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), DEFINE_PROP_END_OF_LIST(), }; -static void stm32l4x5_usart_init(Object *obj) +static void stm32l4x5_usart_base_init(Object *obj) { - STM32L4X5UsartState *s = STM32L4X5_USART(obj); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_ops, s, - TYPE_STM32L4X5_USART, 0x400); + memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_base_ops, s, + TYPE_STM32L4X5_USART_BASE, 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); } -static const VMStateDescription vmstate_stm32l4x5_usart = { - .name = TYPE_STM32L4X5_USART, +static const VMStateDescription vmstate_stm32l4x5_usart_base = { + .name = TYPE_STM32L4X5_USART_BASE, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(cr1, STM32L4X5UsartState), - VMSTATE_UINT32(cr2, STM32L4X5UsartState), - VMSTATE_UINT32(cr3, STM32L4X5UsartState), - VMSTATE_UINT32(brr, STM32L4X5UsartState), - VMSTATE_UINT32(gtpr, STM32L4X5UsartState), - VMSTATE_UINT32(rtor, STM32L4X5UsartState), - VMSTATE_UINT32(isr, STM32L4X5UsartState), - VMSTATE_UINT32(rdr, STM32L4X5UsartState), - VMSTATE_UINT32(tdr, STM32L4X5UsartState), - VMSTATE_CLOCK(clk, STM32L4X5UsartState), + VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr3, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(brr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(gtpr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rtor, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(isr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rdr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(tdr, Stm32l4x5UsartBaseState), + VMSTATE_CLOCK(clk, Stm32l4x5UsartBaseState), VMSTATE_END_OF_LIST() } }; -static void stm32l4x5_usart_realize(DeviceState *dev, Error **errp) +static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); - STM32L4X5UsartState *s = STM32L4X5_USART(dev); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(dev); if (!clock_has_source(s->clk)) { error_setg(errp, "USART clock must be wired up by SoC code"); return; } } -static void stm32l4x5_usart_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); - rc->phases.hold = stm32l4x5_usart_reset_hold; - device_class_set_props(dc, stm32l4x5_usart_properties); - dc->realize = stm32l4x5_usart_realize; - dc->vmsd = &vmstate_stm32l4x5_usart; + rc->phases.hold = stm32l4x5_usart_base_reset_hold; + device_class_set_props(dc, stm32l4x5_usart_base_properties); + dc->realize = stm32l4x5_usart_base_realize; + dc->vmsd = &vmstate_stm32l4x5_usart_base; +} + +static void stm32l4x5_usart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_USART; +} + +static void stm32l4x5_uart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_UART; +} + +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_LPUART; } static const TypeInfo stm32l4x5_usart_types[] = { { - .name = TYPE_STM32L4X5_USART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(STM32L4X5UsartState), - .instance_init = stm32l4x5_usart_init, - .class_init = stm32l4x5_usart_class_init, + .name = TYPE_STM32L4X5_USART_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5UsartBaseState), + .instance_init = stm32l4x5_usart_base_init, + .class_init = stm32l4x5_usart_base_class_init, + }, { + .name = TYPE_STM32L4X5_USART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_usart_class_init, + }, { + .name = TYPE_STM32L4X5_UART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_uart_class_init, + }, { + .name = TYPE_STM32L4X5_LPUART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_lpuart_class_init, } }; diff --git a/include/hw/char/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h index 8ec1b7afed..8d38a85a6e 100644 --- a/include/hw/char/stm32l4x5_usart.h +++ b/include/hw/char/stm32l4x5_usart.h @@ -22,10 +22,20 @@ #include "chardev/char-fe.h" #include "qom/object.h" +#define TYPE_STM32L4X5_USART_BASE "stm32l4x5-usart-base" #define TYPE_STM32L4X5_USART "stm32l4x5-usart" -OBJECT_DECLARE_SIMPLE_TYPE(STM32L4X5UsartState, STM32L4X5_USART) +#define TYPE_STM32L4X5_UART "stm32l4x5-uart" +#define TYPE_STM32L4X5_LPUART "stm32l4x5-lpuart" +OBJECT_DECLARE_TYPE(Stm32l4x5UsartBaseState, Stm32l4x5UsartBaseClass, + STM32L4X5_USART_BASE) -struct STM32L4X5UsartState { +typedef enum { + STM32L4x5_USART, + STM32L4x5_UART, + STM32L4x5_LPUART, +} Stm32l4x5UsartType; + +struct Stm32l4x5UsartBaseState { SysBusDevice parent_obj; MemoryRegion mmio; @@ -46,4 +56,11 @@ struct STM32L4X5UsartState { CharBackend chr; qemu_irq irq; }; + +struct Stm32l4x5UsartBaseClass { + SysBusDeviceClass parent_class; + + Stm32l4x5UsartType type; +}; + #endif /* HW_STM32L4X5_USART_H */ From patchwork Sun Mar 17 10:39:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912881 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=rMuEp7/z; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF0Y46x8z1yWv for ; Sun, 17 Mar 2024 21:41:49 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnxT-0003Ar-2n; Sun, 17 Mar 2024 06:41:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnxA-0002qF-GR; Sun, 17 Mar 2024 06:41:23 -0400 Received: from zproxy4.enst.fr ([2001:660:330f:2::df]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnx7-0001PO-HY; Sun, 17 Mar 2024 06:41:19 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 42FA42061D; Sun, 17 Mar 2024 11:41:11 +0100 (CET) Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id ldRA11B_8oXQ; Sun, 17 Mar 2024 11:41:10 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 6B29C205F4; Sun, 17 Mar 2024 11:41:10 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy4.enst.fr 6B29C205F4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710672070; bh=1M07QmUdcjDgX6E7/IQG4GlxgxJ8TiHLTW/QmWMAN0I=; h=From:To:Date:Message-Id:MIME-Version; b=rMuEp7/zJypxhWjhhPpaylgBOM4EkQ1raNGGnNelJA8fOy2hN82UsG///qf4X1pTF WUAu7jc4DuyNzy7DfYIUUy6ROpYdGlwbbg1XUpJQVjkTnccE08HrD8YxsPtHBZfoBI EdfITSwIbVYCzIH+JAzlGRjb5s3ubF1Yr5KjI+no= X-Virus-Scanned: amavis at enst.fr Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id yWCRL7Ps1xFw; Sun, 17 Mar 2024 11:41:10 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy4.enst.fr (Postfix) with ESMTPSA id 036B9205A4; Sun, 17 Mar 2024 11:41:09 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 4/7] hw/char/stm32l4x5_usart: Enable serial read and write Date: Sun, 17 Mar 2024 11:39:15 +0100 Message-Id: <20240317103918.44375-5-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=2001:660:330f:2::df; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy4.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Implement the ability to read and write characters to the usart using the serial port. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- hw/char/stm32l4x5_usart.c | 105 +++++++++++++++++++++++++++++++++++++- hw/char/trace-events | 1 + 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index f58bd56875..958d05a56d 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -154,6 +154,71 @@ REG32(RDR, 0x24) REG32(TDR, 0x28) FIELD(TDR, TDR, 0, 8) +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_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 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))) { + /* USART not enabled - drop the chars */ + trace_stm32l4x5_usart_error("Dropping the chars\n"); + 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); +} + static void stm32l4x5_usart_base_reset_hold(Object *obj) { Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); @@ -168,6 +233,21 @@ static void stm32l4x5_usart_base_reset_hold(Object *obj) s->isr = 0x020000C0 | R_ISR_TXE_MASK; s->rdr = 0x00000000; s->tdr = 0x00000000; + + 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,7 +289,8 @@ static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, case A_RDR: retvalue = FIELD_EX32(s->rdr, RDR, RDR); /* Reset RXNE flag */ - s->isr &= ~USART_ISR_RXNE; + s->isr &= ~R_ISR_RXNE_MASK; + stm32l4x5_update_irq(s); break; case A_TDR: retvalue = FIELD_EX32(s->tdr, TDR, TDR); @@ -237,6 +318,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; @@ -254,6 +336,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, @@ -262,6 +345,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, @@ -269,6 +353,21 @@ static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, return; case A_TDR: s->tdr = value; + ch = value & R_TDR_TDR_MASK; + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); + /* + * XXX I/O are currently synchronous, making it impossible for + * software to observe transient states where TXE or TC aren't + * set. Unlike TXE however, which is read-only, software may + * clear TC by writing 0 to the ISR register, so set it again + * on each write. + */ + s->isr |= R_ISR_TC_MASK; + stm32l4x5_update_irq(s); return; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -338,6 +437,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 9f5fda0eb8..0cda3dfed0 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -114,6 +114,7 @@ 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_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_error(const char* error) "USART Error: %s" # xen_console.c From patchwork Sun Mar 17 10:39:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912882 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=JgxK8xvP; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF0Y6RRmz1yX0 for ; Sun, 17 Mar 2024 21:41:49 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnxV-0003Io-Mt; Sun, 17 Mar 2024 06:41:41 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnxA-0002qG-Gf; Sun, 17 Mar 2024 06:41:23 -0400 Received: from zproxy4.enst.fr ([137.194.2.223]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnx5-0001PV-T8; Sun, 17 Mar 2024 06:41:18 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 59231207EB; Sun, 17 Mar 2024 11:41:12 +0100 (CET) Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id UDVJRWpCNm4Y; Sun, 17 Mar 2024 11:41:11 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 822FB205F4; Sun, 17 Mar 2024 11:41:11 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy4.enst.fr 822FB205F4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710672071; bh=1onEwCpqfWs3uwQN5dyXddcXZJMtBvmrzTYx9qNJtUc=; h=From:To:Date:Message-Id:MIME-Version; b=JgxK8xvPKc0WlTe97dUlOEyaF8teNCGdPK5hNnJnyA3GRjl/8H9kRb7PRBKAvzb8p EYQHE8Bd4Q02Kbl++jsIK7qqdhN7QwpMHlTwdPN615j3iTDQD+nBcQ9FLy8YZPmE2W 7ZVR+zmYe2PXViM//d9Yimx2y27bY6ilUi2faUF4= X-Virus-Scanned: amavis at enst.fr Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id Lg13qi5U1sfI; Sun, 17 Mar 2024 11:41:11 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy4.enst.fr (Postfix) with ESMTPSA id E754520619; Sun, 17 Mar 2024 11:41:10 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 5/7] hw/char/stm32l4x5_usart: Add options for serial parameters setting Date: Sun, 17 Mar 2024 11:39:16 +0100 Message-Id: <20240317103918.44375-6-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=137.194.2.223; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy4.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add a function to change the settings of the serial connection. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- hw/char/stm32l4x5_usart.c | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index 958d05a56d..95e792d09d 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -165,6 +165,91 @@ static int stm32l4x5_usart_base_can_receive(void *opaque) return 0; } +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 */ + value = FIELD_EX32(s->cr2, CR2, STOP); + if (value == 0b00) { + stop_bits = 1; + } else if (value == 0b10) { + stop_bits = 2; + } else { + /* TODO: raise an error here */ + stop_bits = 1; + error_report( + "UNIMPLEMENTED: fractionnal stop bits; CR2[13:12] = %x", + value); + return; + } + + /* Select the length of the word */ + value = (FIELD_EX32(s->cr1, CR1, M1) << 1) | FIELD_EX32(s->cr1, CR1, M0); + if (value == 0b00) { + data_bits = 8; + } else if (value == 0b01) { + data_bits = 9; + } else if (value == 0b01) { + data_bits = 7; + } else { + /* TODO: Raise an error here */ + data_bits = 8; + error_report("UNDEFINED: invalid word length, CR1.M = 0b11"); + return; + } + + /* Select the baud rate */ + value = FIELD_EX32(s->brr, BRR, BRR); + if (value < 16) { + /* TODO: Raise an error here */ + error_report("UNDEFINED: BRR lesser 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, 0); +} + static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s) { if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) || @@ -318,16 +403,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; @@ -409,10 +497,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), From patchwork Sun Mar 17 10:39:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912885 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=5R6ne1Iy; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF2C38gXz1yWv for ; Sun, 17 Mar 2024 21:43:15 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnyG-0005bf-BD; Sun, 17 Mar 2024 06:42:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnyE-0005aZ-G8; Sun, 17 Mar 2024 06:42:26 -0400 Received: from zproxy4.enst.fr ([2001:660:330f:2::df]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnyC-0001XD-DM; Sun, 17 Mar 2024 06:42:26 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 0C1A1207D7; Sun, 17 Mar 2024 11:42:23 +0100 (CET) Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id E_tKn1gy7pl9; Sun, 17 Mar 2024 11:42:22 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 09BBA208DF; Sun, 17 Mar 2024 11:42:22 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy4.enst.fr 09BBA208DF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710672142; bh=1cJQ4lTgvmyfnsaioqzW1qu+OsgkD51B2G7lmtK9uLw=; h=From:To:Date:Message-Id:MIME-Version; b=5R6ne1Iy586f1niRqGvUEeXb5Y3CEVE7c1GAi9KIAbSitmqqbaBYH8gDPk1jx+HgH 3rFfks57kyq95S1ZoAy3M6jj0+RcN62h9e86Hkt0ejEvfswuDU05xu5melQytw0Nz5 STh3qzVrpozad6apD65i9WVY1yPQYSlznQ3Z8zh8= X-Virus-Scanned: amavis at enst.fr Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id FWINl1CaxWPy; Sun, 17 Mar 2024 11:42:21 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy4.enst.fr (Postfix) with ESMTPSA id 57299208D9; Sun, 17 Mar 2024 11:42:21 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 6/7] hw/arm: Add the USART to the stm32l4x5 SoC Date: Sun, 17 Mar 2024 11:39:17 +0100 Message-Id: <20240317103918.44375-7-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=2001:660:330f:2::df; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy4.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, WEIRD_QUOTING=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add the USART to the SoC and connect it to the other implemented devices. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- docs/system/arm/b-l475e-iot01a.rst | 2 +- hw/arm/Kconfig | 1 + hw/arm/stm32l4x5_soc.c | 88 +++++++++++++++++++++++++++--- include/hw/arm/stm32l4x5_soc.h | 13 +++++ 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index 0afef8e4f4..a76c9976c5 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -19,13 +19,13 @@ Currently B-L475E-IOT01A machine's only supports the following devices: - STM32L4x5 SYSCFG (System configuration controller) - STM32L4x5 RCC (Reset and clock control) - STM32L4x5 GPIOs (General-purpose I/Os) +- STM32L4x5 USARTs, UARTs and LPUART (Serial ports) Missing devices """"""""""""""" The B-L475E-IOT01A does *not* support the following devices: -- Serial ports (UART) - Analog to Digital Converter (ADC) - SPI controller - Timer controller (TIMER) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 893a7bff66..098d043375 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -477,6 +477,7 @@ config STM32L4X5_SOC select STM32L4X5_SYSCFG select STM32L4X5_RCC select STM32L4X5_GPIO + select STM32L4X5_USART config XLNX_ZYNQMP_ARM bool diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 40e294f838..ca5279c40e 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -28,6 +28,7 @@ #include "sysemu/sysemu.h" #include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" +#include "hw/char/stm32l4x5_usart.h" #include "hw/gpio/stm32l4x5_gpio.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" @@ -116,6 +117,22 @@ static const struct { { 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 }, }; +static const hwaddr usart_addr[] = { + 0x40013800, /* "USART1", 0x400 */ + 0x40004400, /* "USART2", 0x400 */ + 0x40004800, /* "USART3", 0x400 */ +}; +static const hwaddr uart_addr[] = { + 0x40004C00, /* "UART4" , 0x400 */ + 0x40005000 /* "UART5" , 0x400 */ +}; + +#define LPUART_BASE_ADDRESS 0x40008000 + +static const int usart_irq[] = { 37, 38, 39 }; +static const int uart_irq[] = { 52, 53 }; +#define LPUART_IRQ 70 + static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); @@ -132,6 +149,18 @@ static void stm32l4x5_soc_initfn(Object *obj) g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i); object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO); } + + for (int i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32L4X5_USART); + } + + for (int i = 0; i < STM_NUM_UARTS; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], + TYPE_STM32L4X5_UART); + } + object_initialize_child(obj, "lpuart1", &s->lpuart, + TYPE_STM32L4X5_LPUART); } static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) @@ -143,6 +172,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) DeviceState *armv7m, *dev; SysBusDevice *busdev; uint32_t pin_index; + g_autofree char *name; if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash", sc->flash_size, errp)) { @@ -185,7 +215,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) /* GPIOs */ for (unsigned i = 0; i < NUM_GPIOS; i++) { - g_autofree char *name = g_strdup_printf("%c", 'A' + i); + name = g_strdup_printf("%c", 'A' + i); dev = DEVICE(&s->gpio[i]); qdev_prop_set_string(dev, "name", name); qdev_prop_set_uint32(dev, "mode-reset", @@ -199,6 +229,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) name = g_strdup_printf("gpio%c-out", 'a' + i); qdev_connect_clock_in(DEVICE(&s->gpio[i]), "clk", qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + g_free(name); if (!sysbus_realize(busdev, errp)) { return; } @@ -279,6 +310,55 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ)); + /* USART devices */ + for (int i = 0; i < STM_NUM_USARTS; i++) { + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + name = g_strdup_printf("usart%d-out", i + 1); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + g_free(name); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* + * TODO: Connect the USARTs, UARTs and LPUART to the EXTI once the EXTI + * can handle other gpio-in than the gpios. (e.g. Direct Lines for the usarts) + */ + + /* UART devices */ + for (int i = 0; i < STM_NUM_UARTS; i++) { + dev = DEVICE(&(s->uart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + i)); + name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + g_free(name); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, uart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, uart_irq[i])); + } + + /* LPUART device*/ + dev = DEVICE(&(s->lpuart)); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + STM_NUM_UARTS)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "lpuart1-out")); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, LPUART_IRQ)); + /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); create_unimplemented_device("TIM3", 0x40000400, 0x400); @@ -294,10 +374,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("SPI2", 0x40003800, 0x400); create_unimplemented_device("SPI3", 0x40003C00, 0x400); /* RESERVED: 0x40004000, 0x400 */ - create_unimplemented_device("USART2", 0x40004400, 0x400); - create_unimplemented_device("USART3", 0x40004800, 0x400); - create_unimplemented_device("UART4", 0x40004C00, 0x400); - create_unimplemented_device("UART5", 0x40005000, 0x400); create_unimplemented_device("I2C1", 0x40005400, 0x400); create_unimplemented_device("I2C2", 0x40005800, 0x400); create_unimplemented_device("I2C3", 0x40005C00, 0x400); @@ -308,7 +384,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("DAC1", 0x40007400, 0x400); create_unimplemented_device("OPAMP", 0x40007800, 0x400); create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); - create_unimplemented_device("LPUART1", 0x40008000, 0x400); /* RESERVED: 0x40008400, 0x400 */ create_unimplemented_device("SWPMI1", 0x40008800, 0x400); /* RESERVED: 0x40008C00, 0x800 */ @@ -325,7 +400,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("TIM1", 0x40012C00, 0x400); create_unimplemented_device("SPI1", 0x40013000, 0x400); create_unimplemented_device("TIM8", 0x40013400, 0x400); - create_unimplemented_device("USART1", 0x40013800, 0x400); /* RESERVED: 0x40013C00, 0x400 */ create_unimplemented_device("TIM15", 0x40014000, 0x400); create_unimplemented_device("TIM16", 0x40014400, 0x400); diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index ee5f362405..a94ddbd19c 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -21,6 +21,12 @@ * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html */ +/* + * The STM32L4X5 is heavily inspired by the stm32f405 by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + #ifndef HW_ARM_STM32L4x5_SOC_H #define HW_ARM_STM32L4x5_SOC_H @@ -31,6 +37,7 @@ #include "hw/misc/stm32l4x5_exti.h" #include "hw/misc/stm32l4x5_rcc.h" #include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/char/stm32l4x5_usart.h" #include "qom/object.h" #define TYPE_STM32L4X5_SOC "stm32l4x5-soc" @@ -41,6 +48,9 @@ OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) #define NUM_EXTI_OR_GATES 4 +#define STM_NUM_USARTS 3 +#define STM_NUM_UARTS 2 + struct Stm32l4x5SocState { SysBusDevice parent_obj; @@ -51,6 +61,9 @@ struct Stm32l4x5SocState { Stm32l4x5SyscfgState syscfg; Stm32l4x5RccState rcc; Stm32l4x5GpioState gpio[NUM_GPIOS]; + Stm32l4x5UsartBaseState usart[STM_NUM_USARTS]; + Stm32l4x5UsartBaseState uart[STM_NUM_UARTS]; + Stm32l4x5UsartBaseState lpuart; MemoryRegion sram1; MemoryRegion sram2; From patchwork Sun Mar 17 10:39:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arnaud Minier X-Patchwork-Id: 1912884 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=telecom-paris.fr header.i=@telecom-paris.fr header.a=rsa-sha256 header.s=A35C7578-1106-11E5-A17F-C303FDDA8F2E header.b=2jOm1Jxn; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=patchwork.ozlabs.org) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TyF1Y6YyYz1yWv for ; Sun, 17 Mar 2024 21:42:41 +1100 (AEDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rlnyI-0005c6-TU; Sun, 17 Mar 2024 06:42:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnyH-0005bp-D6; Sun, 17 Mar 2024 06:42:29 -0400 Received: from zproxy4.enst.fr ([2001:660:330f:2::df]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rlnyF-0001XY-6S; Sun, 17 Mar 2024 06:42:29 -0400 Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id C128620804; Sun, 17 Mar 2024 11:42:25 +0100 (CET) Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10032) with ESMTP id 5nm9788iPow0; Sun, 17 Mar 2024 11:42:24 +0100 (CET) Received: from localhost (localhost [IPv6:::1]) by zproxy4.enst.fr (Postfix) with ESMTP id 9CDC1208D9; Sun, 17 Mar 2024 11:42:24 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.10.3 zproxy4.enst.fr 9CDC1208D9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telecom-paris.fr; s=A35C7578-1106-11E5-A17F-C303FDDA8F2E; t=1710672144; bh=+SWkX0o2QReO37iop26P/T3kwRpNYHtksoAyb30/1Y0=; h=From:To:Date:Message-Id:MIME-Version; b=2jOm1JxnaRa6qBjk17zq1zD+NDdOFvq1Gq9Chu5VHK0aj09k1cnQMVZcEetyTitOD +TYpEok3lpW1MaMbHMUHq/I6aFFveHe56deaEB0xts19CNQlNPRF4Ql+Y3d1d21nN0 S9FsFK39BnJWZKspBJRO/CFnzHfGoSH0/jPK+VTg= X-Virus-Scanned: amavis at enst.fr Received: from zproxy4.enst.fr ([IPv6:::1]) by localhost (zproxy4.enst.fr [IPv6:::1]) (amavis, port 10026) with ESMTP id 2h29AJvT_Bw7; Sun, 17 Mar 2024 11:42:24 +0100 (CET) Received: from AM-Inspiron-3585.. (cust-west-par-46-193-4-103.cust.wifirst.net [46.193.4.103]) by zproxy4.enst.fr (Postfix) with ESMTPSA id E805E20804; Sun, 17 Mar 2024 11:42:23 +0100 (CET) From: Arnaud Minier To: qemu-devel@nongnu.org Cc: Samuel Tardieu , =?utf-8?q?Marc-Andr?= =?utf-8?q?=C3=A9_Lureau?= , Alistair Francis , Paolo Bonzini , =?utf-8?q?In=C3=A8s_Varhol?= , Peter Maydell , qemu-arm@nongnu.org, Laurent Vivier , Thomas Huth , Arnaud Minier Subject: [PATCH 7/7] tests/qtest: Add tests for the STM32L4x5 USART Date: Sun, 17 Mar 2024 11:39:18 +0100 Message-Id: <20240317103918.44375-8-arnaud.minier@telecom-paris.fr> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> References: <20240317103918.44375-1-arnaud.minier@telecom-paris.fr> MIME-Version: 1.0 Received-SPF: pass client-ip=2001:660:330f:2::df; envelope-from=arnaud.minier@telecom-paris.fr; helo=zproxy4.enst.fr X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Test: - read/write from/to the usart registers - send/receive a character/string over the serial port The test to detect overrun is implemented but disabled because overruns are currently impossible due to how we signal in the USART when we are ready to receive a new character. Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol --- tests/qtest/meson.build | 3 +- tests/qtest/stm32l4x5_usart-test.c | 399 +++++++++++++++++++++++++++++ 2 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/stm32l4x5_usart-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 36c5c13a7b..e0d72ee91e 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -205,7 +205,8 @@ qtests_stm32l4x5 = \ ['stm32l4x5_exti-test', 'stm32l4x5_syscfg-test', 'stm32l4x5_rcc-test', - 'stm32l4x5_gpio-test'] + 'stm32l4x5_gpio-test', + 'stm32l4x5_usart-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c new file mode 100644 index 0000000000..b19b89c9ff --- /dev/null +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -0,0 +1,399 @@ +/* + * QTest testcase for STML4X5_USART + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" +#include "hw/registerfields.h" + +/* + * All page references in the following test + * refer to the ST RM0351 Reference Manuel. + */ + +#define RCC_BASE_ADDR 0x40021000 +/* Use USART 1 ADDR, assume the others work the same */ +#define USART1_BASE_ADDR 0x40013800 + +/* See stm32l4x5_usart for definitions */ +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) + FIELD(CR1, OVER8, 15, 1) + FIELD(CR1, M0, 12, 1) + FIELD(CR1, PCE, 10, 1) + FIELD(CR1, TXEIE, 7, 1) + FIELD(CR1, RXNEIE, 5, 1) + FIELD(CR1, TE, 3, 1) + FIELD(CR1, RE, 2, 1) + FIELD(CR1, UE, 0, 1) +REG32(CR2, 0x04) +REG32(CR3, 0x08) + FIELD(CR3, OVRDIS, 12, 1) +REG32(BRR, 0x0C) +REG32(GTPR, 0x10) +REG32(RTOR, 0x14) +REG32(RQR, 0x18) +REG32(ISR, 0x1C) + FIELD(ISR, TXE, 7, 1) + FIELD(ISR, RXNE, 5, 1) + FIELD(ISR, ORE, 3, 1) +REG32(ICR, 0x20) +REG32(RDR, 0x24) +REG32(TDR, 0x28) + +#define NVIC_ISPR1 0XE000E204 +#define NVIC_ICPR1 0xE000E284 +#define USART1_IRQ 37 + +static bool check_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + if (n < 32) { + return false; + } + n -= 32; + return qtest_readl(qts, NVIC_ISPR1) & (1 << n); +} + +static bool clear_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + if (n < 32) { + return false; + } + n -= 32; + qtest_writel(qts, NVIC_ICPR1, (1 << n)); + return true; +} + +static void usart_writel(unsigned int offset, uint32_t value) +{ + writel(USART1_BASE_ADDR + offset, value); +} + +static uint32_t usart_readl(unsigned int offset) +{ + return readl(USART1_BASE_ADDR + offset); +} + +static bool usart_wait_for_flag(QTestState *qts, uint32_t event_addr, uint32_t flag) +{ + /* Wait at most 5 seconds */ + for (int i = 0; i < 5000; i++) { + if ((qtest_readl(qts, event_addr) & flag)) { + return true; + } + g_usleep(1000); + } + + return false; +} + +static void usart_receive_string(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(send(sock_fd, in, in_len, 0) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK)); + out[i] = qtest_readl(qts, USART1_BASE_ADDR + A_RDR); + } + out[i] = '\0'; +} + +static void usart_send_string(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, in[i]); + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_TXE_MASK)); + } +} + +/* Init the RCC clocks to run at 80 MHz */ +static void init_clocks(QTestState *qts) +{ + uint32_t value; + + /* MSIRANGE can be set only when MSI is OFF or READY */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), R_CR_MSION_MASK); + + /* Clocking from MSI, in case MSI was not the default source */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* + * Update PLL and set MSI as the source clock. + * PLLM = 1 --> 000 + * PLLN = 40 --> 40 + * PPLLR = 2 --> 00 + * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1) + * SRC = MSI --> 01 + */ + qtest_writel(qts, (RCC_BASE_ADDR + A_PLLCFGR), R_PLLCFGR_PLLREN_MASK | + (40 << R_PLLCFGR_PLLN_SHIFT) | + (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); + + /* PLL activation */ + + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), value | R_CR_PLLON_MASK); + + /* RCC_CFGR is OK by defaut */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* CCIPR : no periph clock by default */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Switches on the PLL clock source */ + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CFGR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), (value & ~R_CFGR_SW_MASK) | + (0b11 << R_CFGR_SW_SHIFT)); + + /* Enable SYSCFG clock enabled */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), R_APB2ENR_SYSCFGEN_MASK); + + /* Enable the IO port B clock (See p.252) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_AHB2ENR), R_AHB2ENR_GPIOBEN_MASK); + + /* Enable the clock for USART1 (cf p.259) */ + /* We rewrite SYSCFGEN to not disable it */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), + R_APB2ENR_SYSCFGEN_MASK | R_APB2ENR_USART1EN_MASK); + + /* TODO: Enable usart via gpio */ + /* Set Pin 6 & 7 of port B in Alternate Function mode */ + /* GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODE6_Msk | + GPIO_MODER_MODE7_Msk)) | + (AF_MODE << GPIO_MODER_MODE6_Pos) | + (AF_MODE << GPIO_MODER_MODE7_Pos); */ + + /* Specify Alternate Function mode n°7 for Pin 6 & 7 of port B */ + /* GPIOB->AFR[0] = (GPIOB->AFR[0] & ~(GPIO_AFRL_AFSEL6_Msk | + GPIO_AFRL_AFSEL7_Msk )) | + (AF7 << GPIO_AFRL_AFSEL6_Pos) | + (AF7 << GPIO_AFRL_AFSEL7_Pos); */ + + /* Set PCLK as the clock for USART1(cf p.272) i.e. reset both bits */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Reset USART1 (see p.249) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 1 << 14); + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 0); +} + +static void init_uart(QTestState *qts) +{ + uint32_t cr1; + + init_clocks(qts); + + /* + * For 115200 bauds, see p.1349. + * The clock has a frequency of 80Mhz, + * for 115200, we have to put a divider of 695 = 0x2B7. + */ + qtest_writel(qts, (USART1_BASE_ADDR + A_BRR), 0x2B7); + + /* + * Set the oversampling by 16, + * disable the parity control and + * set the word length to 8. (cf p.1377) + */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 &= ~(R_CR1_M1_MASK | R_CR1_M0_MASK | R_CR1_OVER8_MASK | R_CR1_PCE_MASK); + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), cr1); + + /* Enable the transmitter, the receiver and the USART. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); +} + +static void test_write_read(void) +{ + /* Test that we can write and retrieve a value from the device */ + usart_writel(A_TDR, 0xFFFFFFFF); + const uint32_t tdr = usart_readl(A_TDR); + g_assert_cmpuint(tdr, ==, 0x000000FF); +} + +static void test_receive_char(void) +{ + int sock_fd; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + g_assert_true(send(sock_fd, "a", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'a'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_RXNEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + g_assert_true(send(sock_fd, "b", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'b'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_char(void) +{ + int sock_fd; + char s[1]; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'c'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'c'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_TXEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'd'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'd'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_receive_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_receive_string(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_send_string(qts, "world"); + g_assert_true(recv(sock_fd, s, 10, 0) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_overrun(void) +{ + int sock_fd; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* + * These tests fail for now because we cannot write an new charcter + * while RXNE is high (see stm32l4x_5_usart_can_receive), + * thus preventing any overrun to happen. + * TODO: Find a way to indicate that we can only receive 1 character at + * a time without using RXNE. + */ + + /* Try without initializing IRQ: Test presence of overrun error flag */ + g_assert_true(send(sock_fd, "e", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + g_assert_true(send(sock_fd, "f", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_ORE_MASK); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'e'); + + /* Now disable Overrun detection */ + qtest_writel(qts, USART1_BASE_ADDR + A_CR3, R_CR3_OVRDIS_MASK); + g_assert_true(send(sock_fd, "g", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + g_assert_true(send(sock_fd, "h", 1, 0) == 1); + g_usleep(1000); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + g_assert_cmphex(qtest_readl(qts, + USART1_BASE_ADDR + A_ISR) & R_ISR_ORE_MASK, ==, 0); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'h'); + + /* Now, enable irq from overrun with RXNEIE */ + g_assert_true(send(sock_fd, "i", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_RXNEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + g_assert_true(send(sock_fd, "j", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_ORE_MASK); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'i'); + + close(sock_fd); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/usart/write_read", test_write_read); + qtest_add_func("stm32l4x5/usart/receive_char", test_receive_char); + qtest_add_func("stm32l4x5/usart/send_char", test_send_char); + qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); + qtest_add_func("stm32l4x5/usart/send_str", test_send_str); + /* Disabled tests */ + if (false) { + qtest_add_func("stm32l4x5/usart/overrun", test_overrun); + } + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} +