From nobody Mon Sep 28 00:10:26 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1787417772; cv=none; d=zohomail.com; s=zohoarc; b=CvSxKjNmkYyn0y3klrZVstSpaOqDlCJV3p2FBnTnkA3b0xnjobGtwlBwDEO3kCgRl7lMmc+AGDPDpnShyjb+GrWBomOkT3FRcJswuRUJAJGl1+F4xgvrsp5f9YP82II3aa0jKQfEJ6D11CkeISiARNhE1SFmlST+xAYRy163SGk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787417772; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=T0xE1KANjQjJZlPwU/txw1/fcNoRFudGxhkULaXzqPI=; b=gtdgFoEjcVMyV0g6OEVYBZqLJtY4yKOFsN3boR+HnVN+IED3biSm/xrla2jBr5l3YnNhV/oNmsUydrW1tYyet0RwqsAkBkRVCMr+iK1nuEQbfzJEpkdg7F7FOnhWR4dG2+KhPOWON1ymiBuojANDvFMcfH4AOSriAFKjIaObPKY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787417772222220.13961291784642; Sat, 22 Aug 2026 09:56:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wxozi-0002de-3d; Sat, 22 Aug 2026 12:54:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozg-0002c7-Bd for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:54:56 -0400 Received: from mail-pl1-x636.google.com ([2607:f8b0:4864:20::636]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozc-0006ds-V1 for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:54:56 -0400 Received: by mail-pl1-x636.google.com with SMTP id d9443c01a7336-2cace91f112so22908445ad.0 for ; Sat, 22 Aug 2026 09:54:52 -0700 (PDT) Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa ([117.55.237.216]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d2d1esm8688456eec.17.2026.08.22.09.54.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Aug 2026 09:54:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787417691; x=1788022491; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=T0xE1KANjQjJZlPwU/txw1/fcNoRFudGxhkULaXzqPI=; b=rSiHMRkJbt17/+Y5DCiqsSdd3fLFbaoz9k8S1+yQsc2Y68gFJx21tzkVoLhpvnHJsU g5/AYiPYQkDAAP46FeoLLcHWakv4n5IMoPb+zQwfXqnqEgbmByT3u8ECl2/t997cgTrb A1QAiYGAim02YuLX0NoI3sG1D1SDX5Md1wEtHb+7rTIVhSEJT99uit1/TqGs/jV6zO3A 2Yq6Fp89PN5Fo1lp+b5l+P7w9hsZJTFtCO0PvSZPhuLgs+OwSjdM429kx0XicrpRdr6T DCmUzxq4WlcTOcj/skJEwGspKtxpL4lQScQK8fZFuExcv61Md6ZXbW/pRlOpOU9R4Ow9 bWUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787417691; x=1788022491; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=T0xE1KANjQjJZlPwU/txw1/fcNoRFudGxhkULaXzqPI=; b=ABZPITZyEW4b0Tqb0KZSiTZ1+npFj6qUJvaCMHPz6AakAn9MpEkh61E55vPSPO0ZWb CD07/kfDqqnpFAvk6F9XQU2e0YOS0kL5xxUIT0CuL9X3xHdiy+75/OD7e9R+oz1e4akA ZRrcMNisorKSXMpB+wo0mk5B91Y5ZVFi6QvwB/4Jn6xv3FU4OiFLByapTQ8x5GOWGi6W xiOYGOwMzzBsiMwrsOiJpqJPc7UwD48DxgtNrk6Vn+h8INxpPjqO8k/k2XDXIOk+9czk 2qE0XOFwSoVZ92jGK3tyNb8Plnuh54CQ9REThUmHuNlfOYf9kaTYDg+M1c5sDAusnYxR FjNA== X-Gm-Message-State: AFuF++lD8G6Y0+Qe/He7bzy3AEKhPvuHYYdUzxgA91JKLhAjUu7atDlO 7t5dTltusO2MhH4drlIv2E1JGEWm7uh2nkLfbr2f6CNobT8Dz7MoQskJ X-Gm-Gg: AR+sD10e5AQowHMeg0gkcnS2+EMyRidaNUEQgPOF0jGpRIb+o/Wyh4t3MR+WfVbHsk0 PxkdO7tZo5cxHJe+5/OULI5hH+aaaN/UMESryDBDVE38bGMpZJJ2ToA1VWQr1s4FiDlwaaIFtKQ mqshJNBRuHI6tmk4IIm8EGDYZ5j+621JiUq08ie7oj8Ur0iZh3hrsWWv4oil3GNtvaw77/TyR/b p7AT5Q+MVY3UGU6R78pm4ByI6GsqBo1fHSlOu29DurfYI83/1WFusq97TZntVVE5/geHCIlhKxE BtSfJ4grvobaeAkrjyekm34XxgnHVwne1NO5y4ccUclNYG8HSjbgYLiQvZLwNvEv2gTkyKnb8ba vrsdyBeIo6J/+pzS4cEQ873M72orbfcYDlnbB0xENpRTUE8lSW486Ol3sP1xy+vHVyB5H0fEr0n /p/ABXa0ubo/9gDKJOpUM20/C0I+T1Tam2kGVYz8ETEH5sYfLeSnWHAV/d4C8Xbq/jQD+mynohy GDwaNsgEF7yFM1LqyLrWwPk2YRJD9Cw/NHYJyG8FPPbHRqBSlWqYy+xoLtVDkgbDjPEmN8= X-Received: by 2002:a17:902:ceca:b0:2d3:6fac:d67c with SMTP id d9443c01a7336-2d670c980e1mr95821195ad.10.1787417690424; Sat, 22 Aug 2026 09:54:50 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> Date: Sun, 23 Aug 2026 00:54:35 +0800 Subject: [PATCH RFC v2 1/5] hw/gpio: add STM32F1xx GPIO device model in Rust MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260823-stm32f100-gpio-v2-final-v2-1-02e31f589dd0@gmail.com> References: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> In-Reply-To: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , qemu-rust@nongnu.org, Peter Maydell , Alexandre Iooss , Alistair Francis , qemu-arm@nongnu.org, Fabiano Rosas , Laurent Vivier X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=19912; i=163wangjack@gmail.com; h=from:subject:message-id; bh=aTOA2nKXadaYHcYn7zlDRiKQ/nrcmrFXHHTST5uYI34=; b=owEBiQJ2/ZANAwAIARVa4Yx0u1BNAcsmYgBqidRSu8yq6tqphqilCkL6IRbEk3tubFBdtYXYq Q45OUSLB/yJAk8EAAEIADkWIQQlXkw4qdbVYyKFm20VWuGMdLtQTQUCaonUUhsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwwLDMACgkQFVrhjHS7UE2Dgw//a6VCqbvlJGacSWS1WL89+ptfM65uy9U G5ART/3Tm3L2GwuWxDH/LIBSXNvXeH80IfUNorv4OuyJirQx63w2B/EXej9m2WpXdYPOLGUNTyu cun9uK6sebR3PygsTd6IWrQ9PUip33D210quvyTd3dwKS9PYFsPLZfNZd5D76ZUbwk3M9Wehoyc qztzdCeukksx/VF4mVB6zobIf4kN/UoVW8g3BGhacXY3tTOjeIffbS0IJzdocrrEnCBY1Lqpq1k WCbgJBKAUk1QOr+XDqXkGPkO5e5/gvO+OIn6wFa6qVWabHkWSD15qc6Obnk0Aqn27GXuvAoQrb5 uhDcRq/k66DIJK9LmEUTacRDXZGA52Nrqf4y3L/heUE2c8CI76Xr4pwaS9EjbMUOx4zzQJ1IKbD W1B4QOHafN2kRQ92st5O/vu9XkkYLyflV1mHmNzITUN+55FSETEczXEhbcUP9o2+mZZfT52QMUr qLlyaSXRd5Mk37yTJL0zqGBlJDn8O4XZoCMRJb6VoCXDVq+0qgMuaiNRIHy9EM7niuv18XSEhLF d5upCEumgecTuz5PAAaPyJrJzEwSaKPlcXHaaTJeTjDdWF3YBEVc3XnGROB50pWrFA/Irfqpy0Z Qr467R5KQuLQfpGQNm0F3Lujz2zJsFXw1x8mYMx9lEvJlZNUc1aE= X-Developer-Key: i=163wangjack@gmail.com; a=openpgp; fpr=255E4C38A9D6D56322859B6D155AE18C74BB504D Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::636; envelope-from=163wangjack@gmail.com; helo=mail-pl1-x636.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 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, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1787417774214158500 Add a reusable STM32F1xx GPIO port model implemented in Rust and based on t= he STM32F100xx RM0041 register definition. The device implements the CRL/CRH configuration registers, IDR/ODR and atom= ic BSRR/BRR updates. Restrict accesses to aligned 32-bit words and model an= alog input, push-pull output and released open-drain input sampling. Altern= ate-function routing is not implemented, so AF outputs are not driven from = ODR. LCKR configuration locking, AFIO routing and EXTI integration remain unimpl= emented. Ignore LCKR writes rather than reporting a false lock. Signed-off-by: Jack Wang <163wangjack@gmail.com> --- hw/gpio/Kconfig | 5 + hw/gpio/trace-events | 7 + rust/hw/gpio/Kconfig | 4 + rust/hw/gpio/src/bindings.rs | 19 ++ rust/hw/gpio/src/gpio.rs | 491 +++++++++++++++++++++++++++++++++++++++= ++++ rust/hw/gpio/src/lib.rs | 3 + 6 files changed, 529 insertions(+) diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index fcc7c70bd5..c0b97f2fb2 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -16,6 +16,11 @@ config SIFIVE_GPIO config STM32L4X5_GPIO bool =20 +config STM32F1XX_GPIO + bool + depends on HAVE_RUST + select X_STM32F1XX_GPIO_RUST + config PCA9552 bool depends on I2C diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 80ca783a03..480beea606 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -51,3 +51,10 @@ stm32l4x5_gpio_pins(char *gpio, uint16_t disconnected, u= int16_t high) "GPIO%s di cdns_gpio_read(const char *path, uint64_t offset, uint32_t value) "%s:reg[= 0x%04" PRIx64 "] -> 0x%" PRIx32 cdns_gpio_write(const char *path, uint64_t offset, uint64_t value) "%s:reg= [0x%04" PRIx64 "] <- 0x%04" PRIx64 cdns_gpio_set(const char *path, int line, int level) "%s:[%d] <- %d" + +# stm32f1xx GPIO Rust device +stm32f1xx_gpio_instance_init(void) "instance init" +stm32f1xx_gpio_reset(void) "reset" +stm32f1xx_gpio_read(uint64_t offset, uint64_t value, unsigned size) "offse= t 0x%" PRIx64 " value 0x%" PRIx64 " size %u" +stm32f1xx_gpio_write(uint64_t offset, uint64_t value, unsigned size) "offs= et 0x%" PRIx64 " value 0x%" PRIx64 " size %u" +stm32f1xx_gpio_irq(unsigned line, int level) "line %u level %d" diff --git a/rust/hw/gpio/Kconfig b/rust/hw/gpio/Kconfig new file mode 100644 index 0000000000..acedd7a004 --- /dev/null +++ b/rust/hw/gpio/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config X_STM32F1XX_GPIO_RUST + bool diff --git a/rust/hw/gpio/src/bindings.rs b/rust/hw/gpio/src/bindings.rs new file mode 100644 index 0000000000..b0189065b4 --- /dev/null +++ b/rust/hw/gpio/src/bindings.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unnecessary_transmutes, + unsafe_op_in_unsafe_fn, + clippy::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc, + clippy::too_many_arguments +)] diff --git a/rust/hw/gpio/src/gpio.rs b/rust/hw/gpio/src/gpio.rs new file mode 100644 index 0000000000..6a66608a1d --- /dev/null +++ b/rust/hw/gpio/src/gpio.rs @@ -0,0 +1,491 @@ +// STM32F1xx GPIO port device model (Rust) +// Copyright 2026, Jack Wang +// Author(s): Jack Wang <163wangjack@gmail.com> +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Reference: ST RM0041, section 7. + +use std::ffi::CStr; + +use bql::prelude::*; +use common::prelude::*; +use hwcore::prelude::*; +use migration::prelude::*; +use qom::prelude::*; +use system::prelude::*; +use util::prelude::*; + +pub const GPIO_NUM_PINS: usize =3D 16; + +pub const TYPE_STM32F1XX_GPIO_RUST: &CStr =3D c"stm32f1xx-gpio-rust"; +qom_isa!(Stm32f1xxGpioState: SysBusDevice, DeviceState, Object); + +unsafe impl ObjectType for Stm32f1xxGpioState { + type Class =3D ::Class; + const TYPE_NAME: &'static CStr =3D TYPE_STM32F1XX_GPIO_RUST; +} + +::trace::include_trace!("hw_gpio"); + +pub const REG_CRL: u64 =3D 0x00; +pub const REG_CRH: u64 =3D 0x04; +pub const REG_IDR: u64 =3D 0x08; +pub const REG_ODR: u64 =3D 0x0C; +pub const REG_BSRR: u64 =3D 0x10; +pub const REG_BRR: u64 =3D 0x14; +pub const REG_LCKR: u64 =3D 0x18; + +pub const RESET_CRL: u32 =3D 0x4444_4444; +pub const RESET_CRH: u32 =3D 0x4444_4444; + +#[repr(C)] +#[derive(Debug)] +pub struct GpioRegisters { + crl: BqlCell, + crh: BqlCell, + idr: BqlCell, + odr: BqlCell, + lckr: BqlCell, + + // Set bits have no external driver. + disconnected_pins: BqlCell, + pins_connected_high: BqlCell, +} + +impl Default for GpioRegisters { + fn default() -> Self { + Self::new() + } +} + +/// Changed output pins and their new levels. +#[derive(Debug, Default, Clone, Copy)] +pub struct PinUpdate { + pub mask: u32, + pub levels: u32, +} + +impl GpioRegisters { + pub const fn new() -> Self { + Self { + crl: BqlCell::new(RESET_CRL), + crh: BqlCell::new(RESET_CRH), + idr: BqlCell::new(0), + odr: BqlCell::new(0), + lckr: BqlCell::new(0), + disconnected_pins: BqlCell::new(0xFFFF), + pins_connected_high: BqlCell::new(0), + } + } + + pub fn idr(&self) -> u32 { + self.idr.get() + } + pub fn odr(&self) -> u32 { + self.odr.get() + } + pub fn crl(&self) -> u32 { + self.crl.get() + } + pub fn crh(&self) -> u32 { + self.crh.get() + } + pub fn disconnected_pins(&self) -> u16 { + self.disconnected_pins.get() + } + + fn extract(value: u32, start: u32, length: u32) -> u32 { + assert!(length > 0 && length <=3D 32 - start); + (value >> start) & (!0u32 >> (32 - length)) + } + + fn get_modecnf(&self, pin: usize) -> u8 { + let reg =3D if pin < 8 { + self.crl.get() + } else { + self.crh.get() + }; + let shift =3D ((pin % 8) * 4) as u32; + Self::extract(reg, shift, 4) as u8 + } + + fn is_output(&self, pin: usize) -> bool { + (self.get_modecnf(pin) & 0x03) !=3D 0 + } + + fn is_alternate_open_drain(&self, pin: usize) -> bool { + let modecnf =3D self.get_modecnf(pin); + ((modecnf & 0x03) !=3D 0) && ((modecnf >> 2) =3D=3D 0x03) + } + + fn is_alternate_push_pull(&self, pin: usize) -> bool { + let modecnf =3D self.get_modecnf(pin); + ((modecnf & 0x03) !=3D 0) && ((modecnf >> 2) =3D=3D 0x02) + } + + fn is_general_push_pull(&self, pin: usize) -> bool { + let modecnf =3D self.get_modecnf(pin); + ((modecnf & 0x03) !=3D 0) && ((modecnf >> 2) =3D=3D 0x00) + } + + fn is_general_open_drain(&self, pin: usize) -> bool { + let modecnf =3D self.get_modecnf(pin); + ((modecnf & 0x03) !=3D 0) && ((modecnf >> 2) =3D=3D 0x01) + } + + fn is_analog_input(&self, pin: usize) -> bool { + self.get_modecnf(pin) =3D=3D 0b0000 + } + + fn is_odr_high(&self, pin: usize) -> bool { + self.odr.get() & (1 << pin) !=3D 0 + } + + fn is_input_pull_up_down_mode(&self, pin: usize) -> bool { + self.get_modecnf(pin) =3D=3D 0b1000 + } + + fn is_input_pullup(&self, pin: usize) -> bool { + self.is_input_pull_up_down_mode(pin) && ((self.odr.get() & (1 << p= in)) !=3D 0) + } + + fn is_input_pulldown(&self, pin: usize) -> bool { + self.is_input_pull_up_down_mode(pin) && ((self.odr.get() & (1 << p= in)) =3D=3D 0) + } + + fn check_and_warn_af(&self, start_pin: usize) { + for i in 0..8 { + let pin =3D start_pin + i; + if self.is_alternate_push_pull(pin) || self.is_alternate_open_= drain(pin) { + log_mask_ln!( + Log::Unimp, + "stm32f1xx_gpio: alternate-function routing for pin {}= is not implemented", + pin + ); + } + } + } + + pub fn reset(&self) -> PinUpdate { + self.crl.set(RESET_CRL); + self.crh.set(RESET_CRH); + self.idr.set(0); + self.odr.set(0); + self.lckr.set(0); + self.disconnected_pins.set(0xFFFF); + self.pins_connected_high.set(0); + + // RM0041 leaves IDR undefined; use low and deassert every output. + PinUpdate { + mask: 0xFFFF, + levels: 0, + } + } + + pub fn gpio_set(&self, line: usize, level: u32) -> PinUpdate { + let push_pull =3D self.is_general_push_pull(line) || self.is_alter= nate_push_pull(line); + let open_drain_drives_low =3D self.is_general_open_drain(line) && = !self.is_odr_high(line); + + if push_pull || (open_drain_drives_low && level !=3D 0) { + log_mask_ln!( + Log::GuestError, + "stm32f1xx_gpio: line {} cannot be driven to {} externally= ", + line, + level + ); + return PinUpdate::default(); + } + + self.disconnected_pins + .set(self.disconnected_pins.get() & !(1 << line)); + + if level !=3D 0 { + self.pins_connected_high + .set(self.pins_connected_high.get() | (1 << line)); + } else { + self.pins_connected_high + .set(self.pins_connected_high.get() & !(1 << line)); + } + + self.update_gpio_idr() + } + + // Retain an open-drain source so it can be sampled after release. + fn get_gpio_pinmask_to_disconnect(&self) -> u16 { + let mut pins_to_disconnect: u16 =3D 0; + for i in 0..GPIO_NUM_PINS { + if (self.disconnected_pins.get() & (1 << i) =3D=3D 0) && self.= is_output(i) { + if self.is_general_push_pull(i) + || self.is_alternate_push_pull(i) + || self.is_alternate_open_drain(i) + { + pins_to_disconnect |=3D 1 << i; + log_mask_ln!( + Log::GuestError, + "stm32f1xx_gpio: Line {} can't be driven externall= y", + i + ); + } + } + } + pins_to_disconnect + } + + fn disconnect_gpio_pins(&self, lines: u16) -> PinUpdate { + self.disconnected_pins + .set(self.disconnected_pins.get() | lines); + self.update_gpio_idr() + } + + pub fn write(&self, offset: u64, data: u32) -> PinUpdate { + match offset { + REG_CRL =3D> { + self.crl.set(data); + self.check_and_warn_af(0); + self.disconnect_gpio_pins(self.get_gpio_pinmask_to_disconn= ect()) + } + REG_CRH =3D> { + self.crh.set(data); + self.check_and_warn_af(8); + self.disconnect_gpio_pins(self.get_gpio_pinmask_to_disconn= ect()) + } + REG_ODR =3D> { + self.odr.set(data & 0xFFFF); + self.update_gpio_idr() + } + REG_BRR =3D> { + let bits_to_reset =3D data & 0xFFFF; + self.odr.set(self.odr.get() & !bits_to_reset); + self.update_gpio_idr() + } + REG_BSRR =3D> { + let bits_to_set =3D data & 0xFFFF; + let bits_to_reset =3D (data >> 16) & 0xFFFF; + + // If both BSx and BRx are set, BSx has priority. + let mut current_odr =3D self.odr.get(); + current_odr &=3D !bits_to_reset; + current_odr |=3D bits_to_set; + self.odr.set(current_odr); + + self.update_gpio_idr() + } + REG_LCKR =3D> { + log_mask_ln!( + Log::Unimp, + "stm32f1xx_gpio: LCKR configuration locking is not imp= lemented" + ); + PinUpdate::default() + } + _ =3D> { + log_mask_ln!( + Log::GuestError, + "stm32f1xx_gpio_write: Bad offset 0x{:x}", + offset + ); + PinUpdate::default() + } + } + } + + pub fn read(&self, offset: u64) -> u32 { + match offset { + REG_CRL =3D> self.crl.get(), + REG_CRH =3D> self.crh.get(), + REG_IDR =3D> self.idr.get(), + REG_ODR =3D> self.odr.get(), + REG_BSRR =3D> 0, + REG_BRR =3D> 0, + REG_LCKR =3D> 0, + _ =3D> { + log_mask_ln!( + Log::GuestError, + "stm32f1xx_gpio_read: Bad offset 0x{:x}", + offset + ); + 0 + } + } + } + + fn update_gpio_idr(&self) -> PinUpdate { + let old_idr =3D self.idr.get(); + let mut new_idr =3D old_idr; + let mut new_idr_mask =3D 0u32; + + for i in 0..GPIO_NUM_PINS { + let bit =3D 1u32 << i; + let pin_bit =3D 1u16 << i; + let external_connected =3D self.disconnected_pins.get() & pin_= bit =3D=3D 0; + let external_high =3D self.pins_connected_high.get() & pin_bit= !=3D 0; + + if self.is_analog_input(i) { + new_idr &=3D !bit; + new_idr_mask |=3D bit; + } else if self.is_alternate_push_pull(i) || self.is_alternate_= open_drain(i) { + // AF routing is not modelled. + new_idr &=3D !bit; + new_idr_mask |=3D bit; + } else if self.is_general_push_pull(i) { + if self.is_odr_high(i) { + new_idr |=3D bit; + } else { + new_idr &=3D !bit; + } + new_idr_mask |=3D bit; + } else if self.is_general_open_drain(i) { + if !self.is_odr_high(i) { + new_idr &=3D !bit; + } else if external_connected && external_high { + new_idr |=3D bit; + } else { + new_idr &=3D !bit; + } + new_idr_mask |=3D bit; + } else if external_connected { + if external_high { + new_idr |=3D bit; + } else { + new_idr &=3D !bit; + } + new_idr_mask |=3D bit; + } else if self.is_input_pullup(i) { + new_idr |=3D bit; + new_idr_mask |=3D bit; + } else if self.is_input_pulldown(i) { + new_idr &=3D !bit; + new_idr_mask |=3D bit; + } + } + + let resolved_idr =3D (old_idr & !new_idr_mask) | (new_idr & new_id= r_mask); + self.idr.set(resolved_idr); + + PinUpdate { + mask: (old_idr ^ resolved_idr) & new_idr_mask, + levels: resolved_idr, + } + } +} + +impl_vmstate_struct!( + GpioRegisters, + VMStateDescriptionBuilder::::new() + .name(c"stm32f1xx-gpio/regs") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(GpioRegisters, crl), + vmstate_of!(GpioRegisters, crh), + vmstate_of!(GpioRegisters, idr), + vmstate_of!(GpioRegisters, odr), + vmstate_of!(GpioRegisters, lckr), + vmstate_of!(GpioRegisters, disconnected_pins), + vmstate_of!(GpioRegisters, pins_connected_high), + }) + .build() +); + +#[repr(C)] +#[derive(qom::Object, hwcore::Device)] +pub struct Stm32f1xxGpioState { + parent_obj: ParentField, + mmio: MemoryRegion, + + regs: GpioRegisters, + + outlines: [InterruptSource; GPIO_NUM_PINS], +} + +impl Stm32f1xxGpioState { + unsafe fn init(mut this: ParentInit) { + static STM32F1XX_GPIO_OPS: MemoryRegionOps =3D + MemoryRegionOpsBuilder::::new() + .read(&Stm32f1xxGpioState::read) + .write(&Stm32f1xxGpioState::write) + .little_endian() + .valid_sizes(4, 4) + .impl_sizes(4, 4) + .build(); + + MemoryRegion::init_io( + &mut uninit_field_mut!(*this, mmio), + &STM32F1XX_GPIO_OPS, + "stm32f1xx-gpio-rust", + 0x400, + ); + + uninit_field_mut!(*this, regs).write(GpioRegisters::new()); + uninit_field_mut!(*this, outlines).write(Default::default()); + + trace::trace_stm32f1xx_gpio_instance_init(); + } + + fn post_init(&self) { + self.init_mmio(&self.mmio); + // Pin interrupts are routed through EXTI, not a sysbus IRQ. + self.init_gpio_in(GPIO_NUM_PINS as u32, Self::gpio_set); + self.init_gpio_out(&self.outlines); + } + + fn apply_pin_update(&self, upd: PinUpdate) { + for i in 0..GPIO_NUM_PINS { + if (upd.mask & (1 << i)) !=3D 0 { + let level =3D (upd.levels & (1 << i)) !=3D 0; + self.outlines[i].set(level); + trace::trace_stm32f1xx_gpio_irq(i as u32, i32::from(level)= ); + } + } + } + + fn reset_hold(&self, _type: ResetType) { + trace::trace_stm32f1xx_gpio_reset(); + let upd =3D self.regs.reset(); + self.apply_pin_update(upd); + } + + fn gpio_set(&self, line: u32, level: u32) { + let upd =3D self.regs.gpio_set(line as usize, level); + self.apply_pin_update(upd); + } + + fn write(&self, offset: hwaddr, data: u64, size: u32) { + trace::trace_stm32f1xx_gpio_write(offset, data, size); + let upd =3D self.regs.write(offset, data as u32); + self.apply_pin_update(upd); + } + + fn read(&self, offset: hwaddr, size: u32) -> u64 { + let value =3D self.regs.read(offset); + trace::trace_stm32f1xx_gpio_read(offset, u64::from(value), size); + u64::from(value) + } +} + +impl ObjectImpl for Stm32f1xxGpioState { + type ParentType =3D SysBusDevice; + + const CLASS_INIT: fn(&mut Self::Class) =3D Self::Class::class_init::; + const INSTANCE_INIT: Option)> =3D Some(Self= ::init); + const INSTANCE_POST_INIT: Option =3D Some(Self::post_init); +} + +impl DeviceImpl for Stm32f1xxGpioState { + const VMSTATE: Option> =3D Some(VMSTATE_STM32= F1XX_GPIO); +} + +impl SysBusDeviceImpl for Stm32f1xxGpioState {} + +impl ResettablePhasesImpl for Stm32f1xxGpioState { + const HOLD: Option =3D Some(Self::reset_hold); +} + +const VMSTATE_STM32F1XX_GPIO: VMStateDescription =3D + VMStateDescriptionBuilder::::new() + .name(c"stm32f1xx-gpio") + .version_id(1) + .minimum_version_id(1) + .fields(vmstate_fields! { + vmstate_of!(Stm32f1xxGpioState, regs), + }) + .build(); diff --git a/rust/hw/gpio/src/lib.rs b/rust/hw/gpio/src/lib.rs new file mode 100644 index 0000000000..57a1420bb1 --- /dev/null +++ b/rust/hw/gpio/src/lib.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pub mod bindings; +pub mod gpio; --=20 2.53.0 From nobody Mon Sep 28 00:10:26 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1787417739; cv=none; d=zohomail.com; s=zohoarc; b=Yhk8w3Qeny9aFVF9bPwlExfsSCaRWiwy7cpNvuiWwPsxKfwz4OXomYyc49x/0o9CnPafmHT+fWiTdJ28xhrxpMVws0eXTkjCwMn+8xxs0xcE42lG+iQ84drQYq8QIPVOJL7mVbNQ3Dg0UE2LMlIo2twMmnVnfOARx2LmTmJcZvE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787417739; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=mhEU6aF3+QjuNaMTF/g3TcPOXmaDcXqZHgRUc5HuoC0=; b=XrUCYoleDniiwXhM8cmnPAZwPd4YcXWUh6frmn5MaY0hhhuQQ4bicIHnzavP2AB7UDQH2Eq7uAAob67fBOOlzbsEe5vgID3TDXdzfIrmATJIneokzM0u4IgEQxJtn6hURkdI+YRm5RL8owbeJUqhP2zuaweh5YuqQ+8QjVf+2zY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787417739375665.5242426320439; Sat, 22 Aug 2026 09:55:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wxozj-0002eT-VZ; Sat, 22 Aug 2026 12:54:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozi-0002dy-Te for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:54:58 -0400 Received: from mail-pg1-x536.google.com ([2607:f8b0:4864:20::536]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozh-0006gK-4x for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:54:58 -0400 Received: by mail-pg1-x536.google.com with SMTP id 41be03b00d2f7-c966b9ee9cbso1476516a12.1 for ; Sat, 22 Aug 2026 09:54:56 -0700 (PDT) Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa ([117.55.237.216]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d2d1esm8688456eec.17.2026.08.22.09.54.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Aug 2026 09:54:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787417694; x=1788022494; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=mhEU6aF3+QjuNaMTF/g3TcPOXmaDcXqZHgRUc5HuoC0=; b=hwy9NsdfegxBy7R1NPBnPpWsDPwjQhEjZND8Dy0/OD19zC6qFoOCAuUWI5su5p04f7 XiZ6p3JxMhLxCckinzahYlH4KuRrEaxS76IKVg6HtHcMcydH+9Lf/lrSX7QQckb4dD8B Zav9a7/Rfs5xURbsdqU1QLR2K0QQDeii42otjWk9DOP1nrIuwYXr11zwk3MaSE0MoyaP +97SnktsdFPFot8arLfWuHsX2BUU0yeWTtytAcCdlEeCFAZ0SZyMfjyTN3OZ1yE0m0yy 2tGna+Jbw5w4Ev5TcFSfj7i7kd/qTB101ZN3XG9NbRuPyhoFAw1cl2vmhLq+CgCVPBvX rHVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787417694; x=1788022494; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=mhEU6aF3+QjuNaMTF/g3TcPOXmaDcXqZHgRUc5HuoC0=; b=KnyZC35yZhCFgALy3sbG0j0cS5nlMuQatsGehCKiRbHPFrekS6+W1kgApdDL69cWht NlTHvK3+DZq4TJTJb3nePlCy7KNC//fgAT2141lrk/vWIVbPYICFP9/bksEwBXLPIsyc 7YU3WF828G/QsHWP9eyTsy3ZdklcCdHsLs0fvegdCyD7n7gYMSMnOUhFucjl7fN2+cBv jyMImeljxyvVzJ95GrAFfEZ9JczN6DOry+nCqEVqPioMemmsTziLGJIGyE4PAM0N/Vh3 lHW2YNIoNxoFlliwqnATYMnj+nT2nCJ/IvqP5J2HMrmR7HM/eeU9KgJ4uScEiNu3355k qeFg== X-Gm-Message-State: AFuF++mQ+DBxZWWa+BP5Rb26DW2obUFJFdAYKIZizFhf9CQz8woA3x57 F5C5N2bo5jgsbGBxnQjs2qqoRT/2DvC8Li8cJvGVKrcGeq/P6EAccJEZ X-Gm-Gg: AR+sD10IGpvoBL74WBTZsklDI9n9umjIlVO+lmGz5r5flMfJMwaAWvoTcktYPiRgOzm 8XD8iil3VTn4iSzv4wamOhUmBPkFxp+cEznhvI6EbA6vLFgEju3wdPfGbVYHYAt48b6ntUQG8Rb yWQXZd4KcP5s48rHTvJUlPdVmv7idJfgJrCWXOCJVzLORFIzNMQfFJkQx13aI8/F1mn84F+O2MB OOLTDyNZ3JS/dEJP1CP2lUadIZnZqYzkLUj2sfBV1kYrv7aANvVwDhv0E6uNYCDo0C+bQu2tXsa ipOIo8tVMkY4hF7Qdni10Wb7iaB12723qcKkMJ3osK9+h/y7rto0D1fVCfDjZakIoXxu0p8OOr9 rAN5djPuWEoVYKM4GH4AXBFvG4EfKU7sohjHPEvc0821/BfIQtRagb7h6ztEbVmUCPmPGhgCQ3W Q3PkieW0x9qK1TYwIfSM/S/un8PVzqYYyAwSvUKg680ORteik8EEC0iKzno74QX0egx+Z3QnkoR rBd21sFKDIzjHLb23u035bF5Fnvd6xZNyfCONwNRSsrpFux7ca2LFKqbuEQQE9zsQfP9ZQ= X-Received: by 2002:a05:6a20:9392:b0:3c4:3112:45 with SMTP id adf61e73a8af0-3cd4b9ea446mr11720814637.1.1787417694153; Sat, 22 Aug 2026 09:54:54 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> Date: Sun, 23 Aug 2026 00:54:36 +0800 Subject: [PATCH RFC v2 2/5] rust: add hw/gpio crate to the workspace MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260823-stm32f100-gpio-v2-final-v2-2-02e31f589dd0@gmail.com> References: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> In-Reply-To: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , qemu-rust@nongnu.org, Peter Maydell , Alexandre Iooss , Alistair Francis , qemu-arm@nongnu.org, Fabiano Rosas , Laurent Vivier X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3324; i=163wangjack@gmail.com; h=from:subject:message-id; bh=ekHy3ZWWMO/wiOwjaWf2p1JWI24Gmw3v+ipxsBvt78Q=; b=owEBiQJ2/ZANAwAIARVa4Yx0u1BNAcsmYgBqidRS9rNpFg5k0DNoktbWx76plhb5uvJOI1OCi wKdBHZ0TuKJAk8EAAEIADkWIQQlXkw4qdbVYyKFm20VWuGMdLtQTQUCaonUUhsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwwLDMACgkQFVrhjHS7UE2NRQ/+JBZLNv1fAa4d9zwUZqP86FEmr5PGobL 9joRSe+h0DFE9Jj0qWWgH7XPqEogfvSie1BLTixOH06AhbDCMLPkvYSbc6oDioy1xemqD6V4rcr 3fIqPZBRIvbP/NZPmTn/PHNZcXISTcM0XRLB5w8XsWo+1qhuwDamUJ+9NWrpqLjbLo6oIRoFsEj G+4YA7cbXvjjSrFKK6CZkHFgprU2h2Tdii6QvaP1mCpywJhmdIkhvncqedp0gji2bLUV8pNC20b s+t0tQAUdpwUT7hsi2t4GEtosowVqNb6I0bmYzPzYuU4Lu9cB4kOfshIE5uJxyBgnROlDZVxa3L z1+/rDIrC9gTR2v1s5PtLjPzeE8Zh8SZmnuQFIVr4A7wEfzGtpj81UPsOTzJ61KOk79nwIcpkjX /kNth5IDhIw0B+KhYRiUMUv+dwM1e+6agxpRZAgsuLWxkf4vJjA8HM6HYIduzVrjVDXSPdrbu3H nPhBhxQiXrm5S5+CqPjb4w0dn2/VNeVge7A3qKMVMCrrW1B8+VkNWedd0aJcttlByKDt+n9sceD aYTlWkMHUMsZKj4jCwsCz8zLiO2dp00SLtk10Ux9qy4+8eSNISeYcZ76q3/LsfUDSnBktsMn6Pk vaw4v9JnMIsaOFEdTz7gsD30XjlwjoWgJWJoYhnuyt0Rp3k0eDPw= X-Developer-Key: i=163wangjack@gmail.com; a=openpgp; fpr=255E4C38A9D6D56322859B6D155AE18C74BB504D Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::536; envelope-from=163wangjack@gmail.com; helo=mail-pg1-x536.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 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, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1787417740733158500 Add the stm32f1xx_gpio crate to the QEMU Rust workspace and Meson/Kconfig b= uild graph. Build it only when Rust support and the STM32F1xx GPIO configur= ation are enabled. Keep native test registration out of this commit; it is added together with= the test source later in the series. Signed-off-by: Jack Wang <163wangjack@gmail.com> --- Cargo.lock | 15 +++++++++++++++ Cargo.toml | 1 + rust/hw/Kconfig | 1 + rust/hw/gpio/Cargo.toml | 30 ++++++++++++++++++++++++++++++ rust/hw/gpio/meson.build | 8 ++++++++ rust/hw/meson.build | 1 + 6 files changed, 56 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index ad664a812d..507257384e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,6 +314,21 @@ version =3D "1.15.2" source =3D "registry+https://github.com/rust-lang/crates.io-index" checksum =3D "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa= 7c90" =20 +[[package]] +name =3D "stm32f1xx_gpio" +version =3D "0.1.0" +dependencies =3D [ + "bql", + "common", + "glib-sys", + "hwcore", + "migration", + "qom", + "system", + "trace", + "util", +] + [[package]] name =3D "syn" version =3D "2.0.104" diff --git a/Cargo.toml b/Cargo.toml index b35704faab..5e25c307c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver =3D "2" members =3D [ "rust/hw/char/pl011", + "rust/hw/gpio", "rust/hw/timer/hpet", "rust/tests", ] diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig index 36f92ec028..ba1297ca2d 100644 --- a/rust/hw/Kconfig +++ b/rust/hw/Kconfig @@ -1,3 +1,4 @@ # devices Kconfig source char/Kconfig +source gpio/Kconfig source timer/Kconfig diff --git a/rust/hw/gpio/Cargo.toml b/rust/hw/gpio/Cargo.toml new file mode 100644 index 0000000000..f6a1407984 --- /dev/null +++ b/rust/hw/gpio/Cargo.toml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +[package] +name =3D "stm32f1xx_gpio" +version =3D "0.1.0" +description =3D "STM32F1xx GPIO device model for QEMU" +resolver =3D "2" +publish =3D false + +edition.workspace =3D true +homepage.workspace =3D true +license.workspace =3D true +repository.workspace =3D true +rust-version.workspace =3D true + +[dependencies] +glib-sys.workspace =3D true + +# QEMU Rust foundations crate +common =3D { path =3D "../../common" } +util =3D { path =3D "../../util" } +bql =3D { path =3D "../../bql" } +migration =3D { path =3D "../../migration" } +qom =3D { path =3D "../../qom" } +system =3D { path =3D "../../system" } +hwcore =3D { path =3D "../../hw/core" } +trace =3D { path =3D "../../trace" } + +[lints] +workspace =3D true diff --git a/rust/hw/gpio/meson.build b/rust/hw/gpio/meson.build new file mode 100644 index 0000000000..e359d7e329 --- /dev/null +++ b/rust/hw/gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +_libstm32f1xx_gpio_rs =3D cargo_ws.package('stm32f1xx_gpio').library() + +rust_devices_ss.add(when: 'CONFIG_X_STM32F1XX_GPIO_RUST', if_true: [declar= e_dependency( + link_whole: [_libstm32f1xx_gpio_rs], + variables: {'crate': 'stm32f1xx_gpio'}, +)]) diff --git a/rust/hw/meson.build b/rust/hw/meson.build index 9749d4adfc..d6b273170e 100644 --- a/rust/hw/meson.build +++ b/rust/hw/meson.build @@ -1,2 +1,3 @@ subdir('char') +subdir('gpio') subdir('timer') --=20 2.53.0 From nobody Mon Sep 28 00:10:26 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1787417738; cv=none; d=zohomail.com; s=zohoarc; b=kHlOO0PtdO09x93ikKLbv98m8ZdK5Jb32fC9cJ4rdq5z5YlZXlZ/OgBXVNXDTK5EhPXTpisoowJUNaBLQimd7xOiijabY0RpaYn3leX2cy+dHkgIpKjxIdIbOagNSI55cIMTvDqsSP1r1QCqFCDtOwXudccb5Xblg4d2a25EWNI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787417738; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=f2PQRSPvPljDWMrjBTrlNl9RDQbiYdM8LPCBein4cVY=; b=EkaNAdz2lh82721Ftfr42ku26TqyPwYyFuRwRBuLkgd+cdoqLhBgUH+B5OoiHd1ZjFuHu9PyAJuZkbCqF1o7IRzVCMFjtFja2Pg2eGwfpXQ8S2eiQYPFaTaJtq32eVO1S4ttSPClZzWJBipE0WyipY+sZkTorDaA86ziu1O+Lb0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787417738844664.2418125414404; Sat, 22 Aug 2026 09:55:38 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wxozt-0002i1-I8; Sat, 22 Aug 2026 12:55:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozr-0002h0-2L for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:07 -0400 Received: from mail-pg1-x52d.google.com ([2607:f8b0:4864:20::52d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozn-0006iJ-Tp for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:06 -0400 Received: by mail-pg1-x52d.google.com with SMTP id 41be03b00d2f7-cbee846deecso2414703a12.1 for ; Sat, 22 Aug 2026 09:55:03 -0700 (PDT) Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa ([117.55.237.216]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d2d1esm8688456eec.17.2026.08.22.09.54.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Aug 2026 09:54:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787417698; x=1788022498; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=f2PQRSPvPljDWMrjBTrlNl9RDQbiYdM8LPCBein4cVY=; b=QZVXuG4BwPnAw+5cM6VpKSqWGp+sZWqZI04Nx/4ndL0DfYUie3QZnI1e35phC1LKWx vgb500j0burunPScTy2zHfiyTcKvpAtJla0jMKswz6e+qu4+hrrBV8u029VkAhbrdBfx borJF0/lpsVqtW778acTiCk1zGN9vLw5f81zbwRuAULi/6aapDa5Qz7tSzPWi1r/wLyV tQDUsnZOCGgUUqNuup3kDUiVCWHdVVW46iqMGroe3qtabq0enBrqXkdgV69VwSwYSJeQ nUTsmjXLxAxqIrmLuIsuZ5H4xvYcJLfkWBllVzFjHPM7LhMDE3Zf3snrOyiRn9mUZWZg WH3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787417698; x=1788022498; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=f2PQRSPvPljDWMrjBTrlNl9RDQbiYdM8LPCBein4cVY=; b=jxniyQhn4FFLvUEWvcLHYPJu3Dq4yCWh1ihi5v5Cjxa/DD9vVBuoXFjohSszNS89eO Sjfeq5GEFxvaAluEZp2v+N0wdL9G0xAXPUoPuIk/nraEj4FO8lOh3adIdRK4dBQlp6a5 s0PuwqFASg/jsD3Tl9BpmLl3f5hNvac2BxlBpNoVElPbqjjrwb3gRosVLnqz0v/Hn8Dt V7ZmRejSLWEFF+mq3MwGSHF5z+H+S4/YtkGyAfqvMxxYN4VbpoJ0zNjwR7M2HV3PoptK orapUEC8ZzHGkvM3KcOuJAjxSnmUMFhSl/Bc2wwn1vT7vqt62YJRcD1kwB9O5vg12Lav DBHQ== X-Gm-Message-State: AFuF++nKjHhkY/d1qqRqG9GTEuY6ZEEjZVdt33NqRy6AyQNAeq40Hgpq OOgFsenFRKc9FlbLSu4JQHNFYEPnq8ZT3HVbzsU072cpcn7wOYH6J2hh X-Gm-Gg: AR+sD130XJsCx/Y5Zsp4uJtKuXwlxa+/TxVHrrEd/Dkx2iB3j076UTsjBPDcVH+Rt4X 2jtrVXQ+tjmLo41iTg9NO88ACdsPe6t5/CYC/Iz4loLIbgjy5soXXk5TwUX36T6ePGbJtygISsc phNYmlDrSZ9PVp5+Jpwai8M4d6zDkWXCB0kUBGrPogKdJE1bl+SHuBaHNP7Bc3pt3EMVQxYWRHl 4Zh7NP78IbV6kblr9wJDN9hcfI9JvuiqoNwbPaen4mf933LZBfDDheOplShTSkDFvjcGg58Vq0c zLfeA+BQc4OTuMcdQTZXGBlOo97Dy19a679hXNl9Qz7HvXrg6UOSBkWqjtlQFgmd5aoHS5MFZzL kyTDHsCOERBTG254MSG+3OKIbkrnzeB/UPFs9KkCd0xCqywewzT2MBEDaHyeMngwrVN/LGszzPu zFWyH/WMJ0DVzW15T61IPMDCvYzM7d1y10MMp7nMr0JFt57/v+CQDVppWwHRfxWrcvW5IWLD4w+ ikXe14CZtsQbJB1OD8erB5mYnbVRULwAM2ky5T6mGRK6Ah1hUSJWTYOzRzWbtN1c6Hzb+P4E4XI 0igc9g== X-Received: by 2002:a05:6a20:e607:b0:3c3:89ce:b5bc with SMTP id adf61e73a8af0-3cd3009a920mr29213877637.15.1787417697631; Sat, 22 Aug 2026 09:54:57 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> Date: Sun, 23 Aug 2026 00:54:37 +0800 Subject: [PATCH RFC v2 3/5] hw/arm/stm32f100: instantiate and map GPIO ports MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260823-stm32f100-gpio-v2-final-v2-3-02e31f589dd0@gmail.com> References: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> In-Reply-To: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , qemu-rust@nongnu.org, Peter Maydell , Alexandre Iooss , Alistair Francis , qemu-arm@nongnu.org, Fabiano Rosas , Laurent Vivier X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4850; i=163wangjack@gmail.com; h=from:subject:message-id; bh=mP8WffifX+6XqQ5W3gSIX1W/Wm1uvmeNzY6B0uMYAJU=; b=owEBiQJ2/ZANAwAIARVa4Yx0u1BNAcsmYgBqidRSayA+zoSRZzV3j4TyFdxMtwn3h765CkFZe CaYvxiUPuyJAk8EAAEIADkWIQQlXkw4qdbVYyKFm20VWuGMdLtQTQUCaonUUhsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwwLDMACgkQFVrhjHS7UE2UQBAAks/cuHe+IXUf/WsS0uPcqT3N0kb5YM/ jTZm4/PPWP5wWqfma8SXrkDWuhS5pR6RJJSTWDQcwwM71xg+YkGyihLQGhfpap8R0sR81fiva5Z T+3sRJydN4tywr2oG4ahynnKf+kHoBU4ReJzgu0Z81hNzzZ9L8ctxuZhVdNFdiMUG/+OCpb4A6f 4oAN9HFXI+MroH36VS/cfkqYrPxgFo23D1ohyYendUpK5hYhlaXJqt3uPBgsfEjID7B1+kWfveD 9vRFEoxDBjwEk83m+D8kIthJePGYrTtNquyYWQ4b9DUHVvSzpcDUEmO5QOM5FIDx0XDYqrT1D4p w6IFF36FWE1AXgSdgJhbcN7yx204VkizdPSyXIF+479mgtX0/c/8I9LFZqPW324HkHH4yQVmTmv wVHpSOFkO/tIS0CIE4yAnOJv0bVZtKhKzHwSY1vOG8hAeJlxe08J/hBujz07Oyy51Ky1lkO1dnZ I5MmxV8rljSAIh2lB2aI51vM9cx88gA8SPv2z5VJwNW/IelM9Fi6KeXQXzNjwcFx3w2Syzu4+rJ tJSTRfYx9nfTezU7ePiJW2M/Y/0mOefjsuWm2BnH8U/dBpfR87l432v8O4R8Ewat19BEcNSyV5F CU4mRibBeBrJzzyPDW9v0uI4hBK2iZYxt3ZLRS5fXnzjXx/v9I4g= X-Developer-Key: i=163wangjack@gmail.com; a=openpgp; fpr=255E4C38A9D6D56322859B6D155AE18C74BB504D Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52d; envelope-from=163wangjack@gmail.com; helo=mail-pg1-x52d.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 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, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1787417740742158500 Instantiate the Rust STM32F1xx GPIO device for GPIOA through GPIOE on the S= TM32F100RBT6 used by the stm32vldiscovery machine. Use a fixed-size address table tied to STM32F100_NUM_GPIOS. When the Rust t= ype is unavailable, retain the existing unimplemented-device regions so a -= -disable-rust build does not leave the GPIO windows unbacked. Do not connect the GPIO ports directly to the NVIC because external interru= pts are routed through the currently unmodelled EXTI. Signed-off-by: Jack Wang <163wangjack@gmail.com> --- hw/arm/Kconfig | 1 + hw/arm/stm32f100_soc.c | 35 ++++++++++++++++++++++++++++++----- include/hw/arm/stm32f100_soc.h | 2 ++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 260d2f0751..6333c0ca09 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -373,6 +373,7 @@ config STM32F100_SOC select ARM_V7M select STM32F2XX_USART select STM32F2XX_SPI + select STM32F1XX_GPIO if HAVE_RUST =20 config STM32F205_SOC bool diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index 2358368dba..769f8e5ff0 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qom/object.h" #include "hw/arm/boot.h" #include "system/address-spaces.h" #include "hw/arm/stm32f100_soc.h" @@ -36,10 +37,20 @@ =20 /* stm32f100_soc implementation is derived from stm32f205_soc */ =20 +#define TYPE_STM32F1XX_GPIO_RUST "stm32f1xx-gpio-rust" + static const uint32_t usart_addr[STM_NUM_USARTS] =3D { 0x40013800, 0x40004= 400, 0x40004800 }; static const uint32_t spi_addr[STM_NUM_SPIS] =3D { 0x40013000, 0x40003800 = }; =20 +static const uint32_t gpio_addr[STM32F100_NUM_GPIOS] =3D { + 0x40010800, 0x40010c00, 0x40011000, 0x40011400, 0x40011800, +}; + +static const char *const gpio_name[STM32F100_NUM_GPIOS] =3D { + "GPIOA", "GPIOB", "GPIOC", "GPIOD", "GPIOE", +}; + static const int usart_irq[STM_NUM_USARTS] =3D {37, 38, 39}; static const int spi_irq[STM_NUM_SPIS] =3D {35, 36}; =20 @@ -149,6 +160,25 @@ static void stm32f100_soc_realize(DeviceState *dev_soc= , Error **errp) sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i])= ); } =20 + /* GPIO interrupts are routed through the currently unmodelled EXTI. */ + if (object_class_by_name(TYPE_STM32F1XX_GPIO_RUST)) { + for (i =3D 0; i < STM32F100_NUM_GPIOS; i++) { + g_autofree char *name =3D g_strdup_printf("gpio%c", 'a' + i); + + s->gpio[i] =3D qdev_new(TYPE_STM32F1XX_GPIO_RUST); + object_property_add_child(OBJECT(s), name, OBJECT(s->gpio[i])); + busdev =3D SYS_BUS_DEVICE(s->gpio[i]); + if (!sysbus_realize_and_unref(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, gpio_addr[i]); + } + } else { + for (i =3D 0; i < STM32F100_NUM_GPIOS; i++) { + create_unimplemented_device(gpio_name[i], gpio_addr[i], 0x400); + } + } + create_unimplemented_device("timer[2]", 0x40000000, 0x400); create_unimplemented_device("timer[3]", 0x40000400, 0x400); create_unimplemented_device("timer[4]", 0x40000800, 0x400); @@ -165,11 +195,6 @@ static void stm32f100_soc_realize(DeviceState *dev_soc= , Error **errp) create_unimplemented_device("CEC", 0x40007800, 0x400); create_unimplemented_device("AFIO", 0x40010000, 0x400); create_unimplemented_device("EXTI", 0x40010400, 0x400); - create_unimplemented_device("GPIOA", 0x40010800, 0x400); - create_unimplemented_device("GPIOB", 0x40010C00, 0x400); - create_unimplemented_device("GPIOC", 0x40011000, 0x400); - create_unimplemented_device("GPIOD", 0x40011400, 0x400); - create_unimplemented_device("GPIOE", 0x40011800, 0x400); create_unimplemented_device("ADC1", 0x40012400, 0x400); create_unimplemented_device("timer[1]", 0x40012C00, 0x400); create_unimplemented_device("timer[15]", 0x40014000, 0x400); diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h index 4c113d6774..401794242d 100644 --- a/include/hw/arm/stm32f100_soc.h +++ b/include/hw/arm/stm32f100_soc.h @@ -36,6 +36,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC) =20 #define STM_NUM_USARTS 3 #define STM_NUM_SPIS 2 +#define STM32F100_NUM_GPIOS 5 =20 #define FLASH_BASE_ADDRESS 0x08000000 #define FLASH_SIZE (128 * 1024) @@ -49,6 +50,7 @@ struct STM32F100State { =20 STM32F2XXUsartState usart[STM_NUM_USARTS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; + DeviceState *gpio[STM32F100_NUM_GPIOS]; =20 MemoryRegion sram; MemoryRegion flash; --=20 2.53.0 From nobody Mon Sep 28 00:10:26 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1787417814; cv=none; d=zohomail.com; s=zohoarc; b=F3Eou/QX0CqwOKB0yi8hE5fNwn8O4c/NZYtAAhWdxOICS2f0QW5UMpkCOPlEQP8+ET5UhKYCxNkac6AP3mxbiDhiKPJ8MGdie713Q1LLqx6Bz9hiYpPGq052+WLWy+m0wWeiaU5C+AWedzUdNfOnr1oBM9UDfwTtMBXlDmcnnFM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787417814; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=If1q46hsy+fNHIxv/NY6fJEaikfzuAFWzoaODXwM524=; b=RnuREOvnziZWPDdR6lGwhaao+5F3qiKWCuqEgkqoDbS41/k79aPA5DlOXmidx3ONy/d6c66i5cAMFUwmmcyi3XMNA3lADCOvGdA4hjx6bMwSsK2ldiKdt1dkk5MA3iHsOjO3uAz5JLPWQ8SV1q0ahO6AKaAyON9krvn9JVKzNVM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787417814878143.8709234350988; Sat, 22 Aug 2026 09:56:54 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wxozr-0002hS-R7; Sat, 22 Aug 2026 12:55:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozp-0002gO-Sn for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:05 -0400 Received: from mail-pj1-x1032.google.com ([2607:f8b0:4864:20::1032]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozn-0006i9-Jq for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:05 -0400 Received: by mail-pj1-x1032.google.com with SMTP id 98e67ed59e1d1-38dc4553f62so2688846a91.0 for ; Sat, 22 Aug 2026 09:55:02 -0700 (PDT) Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa ([117.55.237.216]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d2d1esm8688456eec.17.2026.08.22.09.54.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Aug 2026 09:55:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787417701; x=1788022501; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=If1q46hsy+fNHIxv/NY6fJEaikfzuAFWzoaODXwM524=; b=YwwjKUVSxgkRDZopTFtzspcU/6JvlMiwvtcnJOrP49SFSKkTcvwqJNuKMh7d9LRWtB AqsTlXx1ZBdXT+GG95/nWvL+tH7KLtSn1rt49HtJt8yOjwrhXPop/CGB4hzuNnLX/l2R 3d8VqqskW0byqF4QJPsosCPwX+mwicZrElnr8gLD9YQQmSEjgMZ+6XSfQmXaF0i+nRZi PlM2lvRuk/XUZd+3FYmx0gX9QkMNGhGzy/2kIw2tQt/lyIQLNSbJjX/NYUg0dX/yoZLs diP2tizVxU7vpDvIyHSWPyimBz95qcjrq8V6mX3gygO1lEKT8K8bZre84m40SaosGr6g loTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787417701; x=1788022501; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=If1q46hsy+fNHIxv/NY6fJEaikfzuAFWzoaODXwM524=; b=FXSiYY7+RWCHaXcugPrZDtGsU2lMoNeyjL4PZSEjDQUJWNKsS8M2LL21Ir3cm3vGg1 bTv8+mUKNidP1BiTtkeZCiBSQy2wQ1NOKd8zvSwrptMKqvVtXBLOkcEKXQ4DlkqN9Ac9 gU9a5lyYt2GC6zAj58NQHlDA9GVRUqQgUZZmw1bqsKPOa0t+vEWw3JU0y+J9SF7US5yL SEsD1ETv1nB8A/8tkNPZ1GDQD9vUUXshk3EzWxxf1uxqDPd41BsEvqIDoExWNRLh6RcO OTa1x926hOjOngztkCHhv6XF1ONH5GEoFDwfxKPDzPPW/+toAUISSZKltyROrseSW+US ka2g== X-Gm-Message-State: AFuF++lc624ZmUS6/rorqIe5uzTcEFhPsewMZvumt3XGD2CVLBRrZh/t 0Sju0UFZ4PuE56VF0i6jWp3w3CGeeZcKP9dT/I7YyLJMOAUHOz0bcGvO X-Gm-Gg: AR+sD11Q0gTdBbq54P4/X8i01Y2UB/HuKGfCPEhySj1RAWN0Yf7qtQNVNYfhb6MWg6e qypSh/awJ44uA47SfYB05yAKENfiCAUaCk99nBkG4sBbVjZs/fiSbihf4ExXKRWCNTBvIYvnQQ8 ZksFkQaOIDfrZUSDCdVIIXFATo/F3pIW/IoIy4NEEm2GCDJvjgJ/zAL2E4SMZ7BRoUYdaNh+7BO RN13PtdT9pwh5Ws0GB5E4rjntdu49PVBC9LCWmdAW0M0yYUOhcoJek0Wop9Q9XUKZcW2UleLhzv gcHyprrEAFsDhgdsPZza7234HG+2XCQl1mmzATb9CM1otuEn+TdqR0bqzfGsbMB/xY2chIOyX1r vt54RJ4JyaMp7Ryo8B2e08Pz8am1J5mjXsk9WrkWp7SYW8aB+/jn7KqYh+iG4DULNBnE8jIPog1 e7lwjTWPHig2HdzpDe0AbU5jen/mkZoRnY6C8Mdh57DDGoGf2W4pVv6YLXdZPhFY6HsK3LJjBBa m3INJMWj+fqyzSGbNNWN1K6KFA06fcTbziiGBCLPq71ZpDZu50y8Ui1eMVcz6S3ZbkMyf8= X-Received: by 2002:a17:90b:384d:b0:38e:6aa7:68ad with SMTP id 98e67ed59e1d1-395c3503b04mr23027179a91.5.1787417701124; Sat, 22 Aug 2026 09:55:01 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> Date: Sun, 23 Aug 2026 00:54:38 +0800 Subject: [PATCH RFC v2 4/5] tests/qtest: add STM32F100 GPIO tests MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260823-stm32f100-gpio-v2-final-v2-4-02e31f589dd0@gmail.com> References: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> In-Reply-To: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , qemu-rust@nongnu.org, Peter Maydell , Alexandre Iooss , Alistair Francis , qemu-arm@nongnu.org, Fabiano Rosas , Laurent Vivier X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=13486; i=163wangjack@gmail.com; h=from:subject:message-id; bh=IE9tGBd8c/9oqC9mEVDgI2WpJzFdjT8+Z1kPz4HWt/Q=; b=owEBiQJ2/ZANAwAIARVa4Yx0u1BNAcsmYgBqidRSgcyTvc9sHN8+JjttDh8I0qgno3F5pYC6e TelsLa0oI+JAk8EAAEIADkWIQQlXkw4qdbVYyKFm20VWuGMdLtQTQUCaonUUhsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwwLDMACgkQFVrhjHS7UE3rWA/+Kfm1lQY2dnyVDk7bvU4NH/+X6sVQfRN sateym7tVJWhlqA1vcpMS0q8TBHePGwlOxvmYCw+xgvvks9SytK6sHJDHrnwroli8eM1U90MrR0 07IBa0NSxwNVKcdYuQBY30Ak8vgyIQD+kOBXW6lsoDYYydB26fyDgBWmm8D7WRLKuB5OMycjXhO DdQ3TPSwULw8ex8VO4Zjikk8icO/XAyP1lvEk9K8uTo/d8QnM4u7OGnUxDMmIq4PzxGoOgFNn73 ejZ67w+EZ0t/se3gxVURaOxECHCc+mKgBgVTTzqYTBAfVkyc+pV1d1Y7S3iWOk2kWEYMLjqph+v 8uX7W7u+SFKvYGcYDXfsAUjMkRuvOpk+JKUloVrJKD8qn8w1bLr7NSsrF7R1QCblQN3pFzDDLlW AvYnuCfdsNaXEWKggyx6wwLbMeQidQASTc05aByj6/FcuolHxIbf9Bpl/rzGq+GoVVI8d9RLaVv 52KCWdJ/BmgJQ7hsD52nNA1KIOCyZePEW5lMsAlCYZ0EU1R/SDSI+VXjsyli+hvG40I6rs2WIdh Yag26ziNOumGaC26tBmUZg0Q/GOQ58oNFH1MW1BVmQ/Sri1p0X1RT8MYWyuELmlGQG2KU5c/V4n AculGMh3enxQIitfDa3ACXRvoZl3gkFiH83ZQgDFddUNMGQc8vEo= X-Developer-Key: i=163wangjack@gmail.com; a=openpgp; fpr=255E4C38A9D6D56322859B6D155AE18C74BB504D Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1032; envelope-from=163wangjack@gmail.com; helo=mail-pj1-x1032.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 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, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1787417816440158500 Add integration coverage for the STM32F1xx GPIO device on the stm32vldiscov= ery machine and its five GPIOA-E instances. Retain the v1 register and GPIO-line coverage and add focused cases for ana= log input, released open-drain sampling, AF-output isolation, reset output = deassertion, word-only MMIO and explicitly unimplemented LCKR behavior. When the Rust device is unavailable, keep a machine-start smoke test for th= e unimplemented-device fallback. Signed-off-by: Jack Wang <163wangjack@gmail.com> --- tests/qtest/meson.build | 4 + tests/qtest/stm32f100_gpio-test.c | 380 ++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 384 insertions(+) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 12ed368997..d05ce065cd 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -239,6 +239,9 @@ qtests_stm32l4x5 =3D \ 'stm32l4x5_gpio-test', 'stm32l4x5_usart-test'] =20 +qtests_stm32f100 =3D \ + ['stm32f100_gpio-test'] + qtests_arm =3D \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-= dualtimer-test'] : []) + \ @@ -254,6 +257,7 @@ qtests_arm =3D \ (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : = []) + \ (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : [])= + \ (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 := []) + \ + (config_all_devices.has_key('CONFIG_STM32F100_SOC') ? qtests_stm32f100 := []) + \ (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-= test'] : []) + \ (config_all_devices.has_key('CONFIG_CAN_FLEXCAN') ? ['flexcan-test'] : [= ]) + \ (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and diff --git a/tests/qtest/stm32f100_gpio-test.c b/tests/qtest/stm32f100_gpio= -test.c new file mode 100644 index 0000000000..ea8483c1e4 --- /dev/null +++ b/tests/qtest/stm32f100_gpio-test.c @@ -0,0 +1,380 @@ +/* + * QTest testcase for STM32F100 GPIO (Rust device "stm32f1xx-gpio-rust") + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2026 QEMU contributors + * + * The STM32F1 GPIO differs from the L4 family: each pin is configured by a + * 4-bit field MODE[1:0]+CNF[1:0] packed into CRL (pins 0-7) and CRH (pins + * 8-15), instead of the separate MODER/OTYPER/PUPDR registers of the L4. + * See ST RM0041 for the register map and reset values. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define GPIO_BASE_ADDR 0x40010800 +#define GPIO_SIZE 0x400 +#define NUM_GPIOS 5 +#define NUM_GPIO_PINS 16 + +#define GPIO_A 0x40010800 +#define GPIO_B 0x40010C00 +#define GPIO_C 0x40011000 +#define GPIO_D 0x40011400 +#define GPIO_E 0x40011800 + +#define CRL 0x00 +#define CRH 0x04 +#define IDR 0x08 +#define ODR 0x0C +#define BSRR 0x10 +#define BRR 0x14 +#define LCKR 0x18 + +#define CFG_INPUT_FLOATING 0x4 +#define CFG_INPUT_ANALOG 0x0 +#define CFG_INPUT_PULL 0x8 +#define CFG_OUTPUT_PP 0x1 +#define CFG_OUTPUT_OD 0x5 +#define CFG_AF_PP 0x9 + +#define CR_RESET 0x44444444 + +static uint32_t gpio_readl(unsigned int gpio, unsigned int offset) +{ + return readl(gpio + offset); +} + +static void gpio_writel(unsigned int gpio, unsigned int offset, uint32_t v= alue) +{ + writel(gpio + offset, value); +} + +static void gpio_set_config(unsigned int gpio, unsigned int pin, uint8_t c= fg) +{ + unsigned int reg =3D (pin < 8) ? CRL : CRH; + unsigned int shift =3D (pin % 8) * 4; + uint32_t mask =3D ~(0xFu << shift); + uint32_t val =3D (gpio_readl(gpio, reg) & mask) | ((uint32_t)cfg << sh= ift); + gpio_writel(gpio, reg, val); +} + +static void gpio_set_odr_bit(unsigned int gpio, unsigned int pin, + uint32_t value) +{ + uint32_t mask =3D ~(0x1u << pin); + gpio_writel(gpio, ODR, (gpio_readl(gpio, ODR) & mask) | (value << pin)= ); +} + +static unsigned int get_gpio_id(uint32_t gpio_addr) +{ + return (gpio_addr - GPIO_BASE_ADDR) / GPIO_SIZE; +} + +static const char *gpio_path(uint32_t gpio) +{ + static char buf[32]; + snprintf(buf, sizeof(buf), "/machine/soc/gpio%c", 'a' + get_gpio_id(gp= io)); + return buf; +} + +static void gpio_set_irq(unsigned int gpio, int num, int level) +{ + qtest_set_irq_in(global_qtest, gpio_path(gpio), NULL, num, level); +} + +/* qtest can intercept one device's outputs per session; use GPIOA. */ +static void intercept_out(unsigned int gpio) +{ + qtest_irq_intercept_out(global_qtest, gpio_path(gpio)); +} + +static void reset_gpio(unsigned int gpio) +{ + gpio_writel(gpio, CRL, CR_RESET); + gpio_writel(gpio, CRH, CR_RESET); + gpio_writel(gpio, ODR, 0); +} + +static void test_reset_values(void) +{ + gpio_writel(GPIO_A, CRL, 0xDEADBEEF); + gpio_writel(GPIO_A, CRH, 0xDEADBEEF); + gpio_writel(GPIO_A, ODR, 0xDEADBEEF); + + gpio_writel(GPIO_C, CRL, 0x12345678); + gpio_writel(GPIO_C, ODR, 0x0000FFFF); + + qtest_system_reset(global_qtest); + + g_assert_cmphex(gpio_readl(GPIO_A, CRL), =3D=3D, CR_RESET); + g_assert_cmphex(gpio_readl(GPIO_A, CRH), =3D=3D, CR_RESET); + g_assert_cmphex(gpio_readl(GPIO_A, IDR), =3D=3D, 0); + g_assert_cmphex(gpio_readl(GPIO_A, ODR), =3D=3D, 0); + + g_assert_cmphex(gpio_readl(GPIO_C, CRL), =3D=3D, CR_RESET); + g_assert_cmphex(gpio_readl(GPIO_C, IDR), =3D=3D, 0); + g_assert_cmphex(gpio_readl(GPIO_C, ODR), =3D=3D, 0); +} + +static void test_output_mode(const void *data) +{ + unsigned int pin =3D (uintptr_t)data & 0xF; + uint32_t gpio =3D (uintptr_t)data & ~(GPIO_SIZE - 1); + bool observe =3D (gpio =3D=3D GPIO_A); + + reset_gpio(gpio); + if (observe) { + intercept_out(gpio); + } + + gpio_set_odr_bit(gpio, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + if (observe) { + g_assert_false(get_irq(pin)); + } + + gpio_set_config(gpio, pin, CFG_OUTPUT_PP); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, (1u << pin)); + if (observe) { + g_assert_true(get_irq(pin)); + } + + gpio_set_odr_bit(gpio, pin, 0); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + if (observe) { + g_assert_false(get_irq(pin)); + } + + reset_gpio(gpio); +} + +static void test_input_mode(const void *data) +{ + unsigned int pin =3D (uintptr_t)data & 0xF; + uint32_t gpio =3D (uintptr_t)data & ~(GPIO_SIZE - 1); + bool observe =3D (gpio =3D=3D GPIO_A); + + reset_gpio(gpio); + if (observe) { + intercept_out(gpio); + } + + gpio_set_config(gpio, pin, CFG_INPUT_FLOATING); + + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, (1u << pin)); + if (observe) { + g_assert_true(get_irq(pin)); + } + + gpio_set_irq(gpio, pin, 0); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + if (observe) { + g_assert_false(get_irq(pin)); + } + + reset_gpio(gpio); +} + +static void test_pull_up_down(const void *data) +{ + unsigned int pin =3D (uintptr_t)data & 0xF; + uint32_t gpio =3D (uintptr_t)data & ~(GPIO_SIZE - 1); + + reset_gpio(gpio); + intercept_out(gpio); + + gpio_set_odr_bit(gpio, pin, 1); + gpio_set_config(gpio, pin, CFG_INPUT_PULL); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, (1u << pin)); + g_assert_true(get_irq(pin)); + + gpio_set_odr_bit(gpio, pin, 0); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + g_assert_false(get_irq(pin)); + + reset_gpio(gpio); +} + +static void test_bsrr_brr(const void *data) +{ + unsigned int pin =3D (uintptr_t)data & 0xF; + uint32_t gpio =3D (uintptr_t)data & ~(GPIO_SIZE - 1); + + reset_gpio(gpio); + + gpio_writel(gpio, BSRR, (1u << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), =3D=3D, (1u << pin)); + + gpio_writel(gpio, BSRR, (1u << (pin + 16))); + g_assert_cmphex(gpio_readl(gpio, ODR), =3D=3D, 0); + + gpio_writel(gpio, BSRR, (1u << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), =3D=3D, (1u << pin)); + + gpio_writel(gpio, BRR, (1u << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), =3D=3D, 0); + + /* Set has priority over reset in BSRR. */ + gpio_writel(gpio, BSRR, (1u << pin) | (1u << (pin + 16))); + g_assert_cmphex(gpio_readl(gpio, ODR), =3D=3D, (1u << pin)); + + reset_gpio(gpio); +} + +static void test_push_pull_disconnect(const void *data) +{ + unsigned int pin =3D (uintptr_t)data & 0xF; + uint32_t gpio =3D (uintptr_t)data & ~(GPIO_SIZE - 1); + + reset_gpio(gpio); + + gpio_set_config(gpio, pin, CFG_INPUT_FLOATING); + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, (1u << pin)); + + gpio_set_odr_bit(gpio, pin, 0); + gpio_set_config(gpio, pin, CFG_OUTPUT_PP); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), =3D=3D, 0); + + reset_gpio(gpio); +} + +static void test_analog_input(void) +{ + reset_gpio(GPIO_A); + gpio_set_config(GPIO_A, 0, CFG_INPUT_ANALOG); + gpio_set_irq(GPIO_A, 0, 1); + + g_assert_cmphex(gpio_readl(GPIO_A, IDR) & 1, =3D=3D, 0); + + reset_gpio(GPIO_A); +} + +static void test_open_drain_released(void) +{ + reset_gpio(GPIO_A); + gpio_set_config(GPIO_A, 1, CFG_OUTPUT_OD); + gpio_set_odr_bit(GPIO_A, 1, 1); + + gpio_set_irq(GPIO_A, 1, 1); + g_assert_cmphex(gpio_readl(GPIO_A, IDR) & (1u << 1), =3D=3D, 1u << 1); + + gpio_set_irq(GPIO_A, 1, 0); + g_assert_cmphex(gpio_readl(GPIO_A, IDR) & (1u << 1), =3D=3D, 0); + + reset_gpio(GPIO_A); +} + +static void test_af_output_not_driven_by_odr(void) +{ + reset_gpio(GPIO_A); + gpio_set_config(GPIO_A, 2, CFG_AF_PP); + gpio_set_odr_bit(GPIO_A, 2, 1); + + g_assert_cmphex(gpio_readl(GPIO_A, IDR) & (1u << 2), =3D=3D, 0); + + reset_gpio(GPIO_A); +} + +static void test_reset_deasserts_output(void) +{ + reset_gpio(GPIO_A); + intercept_out(GPIO_A); + gpio_set_config(GPIO_A, 3, CFG_OUTPUT_PP); + gpio_set_odr_bit(GPIO_A, 3, 1); + g_assert_true(get_irq(3)); + + qtest_system_reset(global_qtest); + + g_assert_false(get_irq(3)); + g_assert_cmphex(gpio_readl(GPIO_A, ODR), =3D=3D, 0); +} + +static void test_lckr_unimplemented(void) +{ + reset_gpio(GPIO_A); + gpio_writel(GPIO_A, LCKR, 0x00010001); + g_assert_cmphex(gpio_readl(GPIO_A, LCKR), =3D=3D, 0); +} + +static void test_word_access_only(void) +{ + gpio_writel(GPIO_A, ODR, 0x55aa); + + writeb(GPIO_A + ODR, 0xff); + g_assert_cmphex(gpio_readl(GPIO_A, ODR), =3D=3D, 0x55aa); + + writew(GPIO_A + ODR, 0xffff); + g_assert_cmphex(gpio_readl(GPIO_A, ODR), =3D=3D, 0x55aa); + + writel(GPIO_A + ODR + 1, 0xffffffff); + g_assert_cmphex(gpio_readl(GPIO_A, ODR), =3D=3D, 0x55aa); + + reset_gpio(GPIO_A); +} + +static void test_machine_started(void) +{ + g_assert_nonnull(global_qtest); +} + +static void register_gpio_tests(void) +{ + qtest_add_func("stm32f100/gpio/reset_values", test_reset_values); + qtest_add_data_func("stm32f100/gpio/output_mode_a5", + (void *)(uintptr_t)(GPIO_A | 5), test_output_mode); + qtest_add_data_func("stm32f100/gpio/output_mode_c13", + (void *)(uintptr_t)(GPIO_C | 13), test_output_mode= ); + qtest_add_data_func("stm32f100/gpio/input_mode_b6", + (void *)(uintptr_t)(GPIO_B | 6), test_input_mode); + qtest_add_data_func("stm32f100/gpio/input_mode_d10", + (void *)(uintptr_t)(GPIO_D | 10), test_input_mode); + qtest_add_data_func("stm32f100/gpio/pull_up_down_a0", + (void *)(uintptr_t)(GPIO_A | 0), test_pull_up_down= ); + qtest_add_data_func("stm32f100/gpio/bsrr_brr_a1", + (void *)(uintptr_t)(GPIO_A | 1), test_bsrr_brr); + qtest_add_data_func("stm32f100/gpio/bsrr_brr_e12", + (void *)(uintptr_t)(GPIO_E | 12), test_bsrr_brr); + qtest_add_data_func("stm32f100/gpio/push_pull_disconnect_e7", + (void *)(uintptr_t)(GPIO_E | 7), + test_push_pull_disconnect); + qtest_add_func("stm32f100/gpio/analog_input", test_analog_input); + qtest_add_func("stm32f100/gpio/open_drain_released", + test_open_drain_released); + qtest_add_func("stm32f100/gpio/af_output_not_driven_by_odr", + test_af_output_not_driven_by_odr); + qtest_add_func("stm32f100/gpio/reset_deasserts_output", + test_reset_deasserts_output); + qtest_add_func("stm32f100/gpio/lckr_unimplemented", + test_lckr_unimplemented); + qtest_add_func("stm32f100/gpio/word_access_only", + test_word_access_only); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + if (qtest_has_device("stm32f1xx-gpio-rust")) { + register_gpio_tests(); + } else { + qtest_add_func("stm32f100/gpio/no_rust_machine_start", + test_machine_started); + } + + qtest_start("-machine stm32vldiscovery"); + ret =3D g_test_run(); + qtest_end(); + + return ret; +} --=20 2.53.0 From nobody Mon Sep 28 00:10:26 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1787417809; cv=none; d=zohomail.com; s=zohoarc; b=cdH4qFpp70FBH8lK/iVACEQQY+RAiEaIyo3LZS3Ar77CRhRk3/hGAOABmKqJIGse1i2b/P6WdaIiX5GgcMf5FYf+qHWiw3pwA3VLw9hb7zaUA6DWbCL+XgnxA5Pzhto/ulmkNX9tfzq/lIDML+VCqm3KbLMi2vKV5o7yw4e9Uik= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787417809; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=4bkFOugZCHA30/oOrSCQXtGUjmus+VNjc/uLTyUbkVc=; b=EbtsmWcHrCJ/e9sxZ8SDNd0ZFoopGZWm2ZNe5Jazs++9gV8Bqd6pSaygRihQGRCzLlXPRmoAqZkrAGagkHNIsXaLOxFHKK9Fmza13OM9BA/mJ1Nmbi18ZV/d533fUazYgGs9XOCmwsTG1JmXeru53hTpcarqz+BjFmnZNLNXQyk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787417809285139.84099941196962; Sat, 22 Aug 2026 09:56:49 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wxozv-0002jL-Dy; Sat, 22 Aug 2026 12:55:11 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozt-0002hm-9U for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:09 -0400 Received: from mail-pg1-x52a.google.com ([2607:f8b0:4864:20::52a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wxozq-0006tz-SO for qemu-devel@nongnu.org; Sat, 22 Aug 2026 12:55:09 -0400 Received: by mail-pg1-x52a.google.com with SMTP id 41be03b00d2f7-cbedf433a99so2023567a12.2 for ; Sat, 22 Aug 2026 09:55:06 -0700 (PDT) Received: from 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa ([117.55.237.216]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d2d1esm8688456eec.17.2026.08.22.09.55.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Aug 2026 09:55:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787417705; x=1788022505; darn=nongnu.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=4bkFOugZCHA30/oOrSCQXtGUjmus+VNjc/uLTyUbkVc=; b=TKZS2GNhTMDOqJ6BdYCnnzYWQEDHLRVg6jnvB4kKYqssJYZDxGbNKGiyu++C/7QC35 jALQ5S6i1AelBZhzoVHhvyzJPmZs0f+KaMa+p2pCN0xULSs8usrbuDUvsV4bMDuzFDsk WXBeVM886s306halF5D/akE64t2bPYerwcAXtVwPr4JSbFInrMd1xkIeP06mMf4id1/L 17FaJN/8oI1NMDp+uDXzYcNX6w9xQneWWWC+LhvcPMkcktMaQhc6uB6awWawSwrCvaHB Q2rfqaqKbIs0WsNvHY4qF1YF9nvVVA/8SuPfufgU8HdMTSI+PuXgybHLUWihw2aDuYIe /USg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787417705; x=1788022505; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=4bkFOugZCHA30/oOrSCQXtGUjmus+VNjc/uLTyUbkVc=; b=gG7NMJ+et9asLdder6SIOoTHqTzXhG9sRgQTXt42PPmHcvRxa+YLIgWDit+bwHu6U0 80MrASHDnP1K8E512HDusBm9LdCs1sQG/WSt3G+3SYzCjNT4xgqmNDwLaHvoOXdfrEB0 G4Upmi+zsjAVk7qgO+r9tM4S2EYCWQ7D9gNc1asDXmODTCqHzsAsdqxCZzeqF/UdWsc9 XvttCxfgg2tgHd5OQ098B0tfqQGZ6QwZKWa5J3JOPaPaJjykLrJDIP7pPIK5+amWBfQ8 tZSMUCXbN8AW7hz85vg5onpIUYG9tPvI7b+xyveKMDwpqZKUgwJ3/lWU3vAcSarKpR7v jewQ== X-Gm-Message-State: AFuF++kXEadgmE40JRhQOONQCWbIG5K3ddGgK+G+gZTMdMeNnHnSgZzx nNDRMnZiBj77CipzkPWu0R5P/Bha1YJyVJX4TcTSbxYeb5SD2xvv3+H1 X-Gm-Gg: AR+sD10VYUaBQznLJ4Ot3AMpsvrLcdp5lGnkY1tn/H7j3kculDLFm8eooebdUPaDwcH kaNl8h7RBQRUfyb1R1ppLIhh802IInirXoIT2pUY43n2Ub5hKmmW9/ikuEAsozgAovJalBBS/RV Hf1RvL/LuDDt0o41KcePH9XHLoYzZY+fWrd2ret3pb11eIuArUQ245IYd/zRVqBAXRzJdZD12Wh 6yqeKQRm2V76uAF0VZSAt798ksEewvJFv9GAZDk61UI1oSG2PLi+gk0jgYxIm6ELQKqR3Fk61Vc IGQ9t5TmVRvslJ+XjC9ewUA7ru9X+wJJ9UvUu7Q9GpwlR0zS6IGAIo3XbaYDMP7q5x66gkBxen8 /Xe7Tbsd1Jqrbxs8Qjvxx815WY6n2U7iNgNwZsvqtrwrh5YeCJYULmTqx+v8m8A1Z4AuP6ACg8s ltFDzOF3bwvlUsYsuRNuRFVkkc56GPpL4BCw37905fVhReHtYwFcEyjBLBsQEOGFKL8TH9o8qPp JZV5wY9R0OyRhfpKQe/NTt6BIjR9vMsmw7Z5ByTOZySamcyl3BRPU3b0NROHq2pXvzKd7E= X-Received: by 2002:a05:6a20:1605:b0:3c3:b57b:6285 with SMTP id adf61e73a8af0-3cd3003dc4fmr30205314637.13.1787417704776; Sat, 22 Aug 2026 09:55:04 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> Date: Sun, 23 Aug 2026 00:54:39 +0800 Subject: [PATCH RFC v2 5/5] rust/hw/gpio: add native unit tests MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260823-stm32f100-gpio-v2-final-v2-5-02e31f589dd0@gmail.com> References: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> In-Reply-To: <20260823-stm32f100-gpio-v2-final-v2-0-02e31f589dd0@gmail.com> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , qemu-rust@nongnu.org, Peter Maydell , Alexandre Iooss , Alistair Francis , qemu-arm@nongnu.org, Fabiano Rosas , Laurent Vivier X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7737; i=163wangjack@gmail.com; h=from:subject:message-id; bh=yVYaUseLeN8GbT/eETcCIH3Pg/3pKTO9OpyITwmHEYY=; b=owEBiQJ2/ZANAwAIARVa4Yx0u1BNAcsmYgBqidRSVSM2VblINnot2yuUKr7JfwzbuOWvcM8SM 8+nb0s9jmmJAk8EAAEIADkWIQQlXkw4qdbVYyKFm20VWuGMdLtQTQUCaonUUhsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwwLDMACgkQFVrhjHS7UE0E8g/9ECvfLDioTMcPVb4kdC8z2C0ZukRLrt2 +6bSoaIFWc6TSKG9VfLp4aO/uhcgU3vOZ2Ki9XZ2kued/Oo59Z7qye5Qson8Hky6Msy/OuWMnA6 JErbzF0mBm+/CyU0f0KBna1cvnMXXUpgnADTpWVmF4XukA33sgso2REA7nCUmCDxSrPi6P9WRrj nMNsFJLxvsUfRnftTAv6RtyToym39E1jUp7fZDlhswSCAMjd4X7oFEMcb//AoORBsd8PcveVXUs +1hZYV93ptRK3GsMmK0HbiVkf4IrPsrvo3U4UlUWFv9sjB0IcuSbC/TPLC9BrO9G4UF7l10pPih Lj0cAGVIBrnqxhdoAjzspzuyGu9ggOfHVkzV2b/BsVb4mIr4/v5fgBcZgHBJlna5JTkfLLhaVtS 91iYv5FqfocrrPwnI3sQEWucTIQyPzw9+X0k2W+dRhADJCXzS3MUWH/PEDX4NJw8U+P/nApp3SS AK/d+LW7sy9aHoX+AFdEtO14xp5G/36uOxM2Mcdc1yZ/jSRig7MTEmJ7ne+lQRJrDLY2t+gSjh3 +DqZ9nSZkFzUD/SytEHlGuSg4rNkNUtHDLG4+BNFbsAJ0+Jejp5Hmrl34GGnGp/OuYR0YfUGBhc CXeczQ/TFGb3empci8gLW5tolOues8ASWmJD9VRGIy4I/aGVMmjU= X-Developer-Key: i=163wangjack@gmail.com; a=openpgp; fpr=255E4C38A9D6D56322859B6D155AE18C74BB504D Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52a; envelope-from=163wangjack@gmail.com; helo=mail-pg1-x52a.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 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, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1787417810458158500 Add native tests for the STM32F1xx GPIO register logic based on STM32F100xx= RM0041. Retain the v1 reset, push-pull, floating-input, pull and BSRR/BRR coverage.= Add focused cases for analog input, released open-drain sampling, AF-outpu= t isolation, reset output deassertion and explicitly unimplemented LCKR beh= avior. Register the native test target together with its source so intermediate co= mmits do not reference a missing file. Signed-off-by: Jack Wang <163wangjack@gmail.com> --- rust/hw/gpio/meson.build | 41 +++++++++ rust/hw/gpio/tests/tests.rs | 216 ++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 257 insertions(+) diff --git a/rust/hw/gpio/meson.build b/rust/hw/gpio/meson.build index e359d7e329..f0dd7d4acb 100644 --- a/rust/hw/gpio/meson.build +++ b/rust/hw/gpio/meson.build @@ -6,3 +6,44 @@ rust_devices_ss.add(when: 'CONFIG_X_STM32F1XX_GPIO_RUST', = if_true: [declare_depe link_whole: [_libstm32f1xx_gpio_rs], variables: {'crate': 'stm32f1xx_gpio'}, )]) + +stm32f1xx_gpio_rs =3D declare_dependency( + link_with: [_libstm32f1xx_gpio_rs], + dependencies: [ + common_rs, + qom_rs, + hwcore_rs, + bql_rs, + migration_rs, + system_rs, + util_rs, + ], +) + +test( + 'rust-stm32f1xx-gpio-integration', + executable( + 'rust-stm32f1xx-gpio-integration', + files('tests/tests.rs'), + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_args: ['--test'], + install: false, + dependencies: [ + stm32f1xx_gpio_rs, + common_rs, + qom_rs, + hwcore_rs, + bql_rs, + migration_rs, + system_rs, + util_rs, + ], + ), + args: [ + '--test', + '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust'], +) diff --git a/rust/hw/gpio/tests/tests.rs b/rust/hw/gpio/tests/tests.rs new file mode 100644 index 0000000000..df71d205e0 --- /dev/null +++ b/rust/hw/gpio/tests/tests.rs @@ -0,0 +1,216 @@ +// Copyright 2026, Jack Wang +// Author(s): Jack Wang <163wangjack@gmail.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +use stm32f1xx_gpio::gpio::{ + GpioRegisters, PinUpdate, REG_BRR, REG_BSRR, REG_CRH, REG_CRL, REG_IDR= , REG_LCKR, REG_ODR, + RESET_CRH, RESET_CRL, +}; + +fn regs() -> GpioRegisters { + bql::start_test(); + GpioRegisters::new() +} + +const CFG_INPUT_ANALOG: u32 =3D 0x0; +const CFG_INPUT_FLOATING: u32 =3D 0x4; +const CFG_INPUT_PULL: u32 =3D 0x8; +const CFG_OUTPUT_PP: u32 =3D 0x1; +const CFG_OUTPUT_OD: u32 =3D 0x5; +const CFG_AF_PP: u32 =3D 0x9; + +fn set_config(r: &GpioRegisters, pin: usize, cfg: u32) { + let reg =3D if pin < 8 { REG_CRL } else { REG_CRH }; + let shift =3D ((pin % 8) * 4) as u32; + let old =3D r.read(reg); + r.write(reg, (old & !(0xF << shift)) | (cfg << shift)); +} + +fn set_odr_bit(r: &GpioRegisters, pin: usize, value: u32) { + let old =3D r.read(REG_ODR); + r.write(REG_ODR, (old & !(1 << pin)) | (value << pin)); +} + +fn pin_changed_to(upd: PinUpdate, pin: usize, level: bool) -> bool { + (upd.mask & (1 << pin)) !=3D 0 && (((upd.levels >> pin) & 1) !=3D 0) = =3D=3D level +} + +#[test] +fn test_reset_values() { + let r =3D regs(); + assert_eq!(r.read(REG_CRL), RESET_CRL); + assert_eq!(r.read(REG_CRH), RESET_CRH); + assert_eq!(r.read(REG_IDR), 0); + assert_eq!(r.read(REG_ODR), 0); +} + +#[test] +fn test_reset_restores() { + let r =3D regs(); + r.write(REG_CRL, 0xDEAD_BEEF); + r.write(REG_ODR, 0x0000_FFFF); + assert_ne!(r.read(REG_CRL), RESET_CRL); + + r.reset(); + + assert_eq!(r.read(REG_CRL), RESET_CRL); + assert_eq!(r.read(REG_CRH), RESET_CRH); + assert_eq!(r.read(REG_IDR), 0); + assert_eq!(r.read(REG_ODR), 0); +} + +#[test] +fn test_output_mode() { + let r =3D regs(); + let pin =3D 5; + + let idle =3D set_odr_and_report(&r, pin, 1); + assert_eq!(r.read(REG_IDR), 0); + assert_eq!(idle.mask & (1 << pin), 0); + + let old =3D r.read(REG_CRL); + let to_output =3D r.write( + REG_CRL, + (old & !(0xF << (pin * 4))) | (CFG_OUTPUT_PP << (pin * 4)), + ); + assert_eq!(r.read(REG_IDR), 1 << pin); + assert!(pin_changed_to(to_output, pin, true)); + + let to_low =3D set_odr_and_report(&r, pin, 0); + assert_eq!(r.read(REG_IDR), 0); + assert!(pin_changed_to(to_low, pin, false)); +} + +fn set_odr_and_report(r: &GpioRegisters, pin: usize, value: u32) -> PinUpd= ate { + let old =3D r.read(REG_ODR); + r.write(REG_ODR, (old & !(1 << pin)) | (value << pin)) +} + +#[test] +fn test_input_mode() { + let r =3D regs(); + let pin =3D 6usize; + + set_config(&r, pin, CFG_INPUT_FLOATING); + + let drive_high =3D r.gpio_set(pin, 1); + assert_eq!(r.read(REG_IDR), 1 << pin); + assert!(pin_changed_to(drive_high, pin, true)); + + let drive_low =3D r.gpio_set(pin, 0); + assert_eq!(r.read(REG_IDR), 0); + assert!(pin_changed_to(drive_low, pin, false)); +} + +#[test] +fn test_pull_up_down() { + let r =3D regs(); + let pin =3D 0; + + set_odr_bit(&r, pin, 1); + set_config(&r, pin, CFG_INPUT_PULL); + assert_eq!(r.read(REG_IDR), 1 << pin); + + set_odr_bit(&r, pin, 0); + assert_eq!(r.read(REG_IDR), 0); +} + +#[test] +fn test_bsrr_brr() { + let r =3D regs(); + let pin =3D 1; + + r.write(REG_BSRR, 1 << pin); + assert_eq!(r.read(REG_ODR), 1 << pin); + + r.write(REG_BSRR, 1 << (pin + 16)); + assert_eq!(r.read(REG_ODR), 0); + + r.write(REG_BSRR, 1 << pin); + r.write(REG_BRR, 1 << pin); + assert_eq!(r.read(REG_ODR), 0); + + // Set has priority over reset within one BSRR write. + r.write(REG_BSRR, (1 << pin) | (1 << (pin + 16))); + assert_eq!(r.read(REG_ODR), 1 << pin); +} + +#[test] +fn test_push_pull_disconnect() { + let r =3D regs(); + let pin =3D 7usize; + + set_config(&r, pin, CFG_INPUT_FLOATING); + r.gpio_set(pin, 1); + assert_eq!(r.read(REG_IDR), 1 << pin); + assert_eq!(r.disconnected_pins() & (1 << pin), 0); + + set_odr_bit(&r, pin, 0); + set_config(&r, pin, CFG_OUTPUT_PP); + assert_eq!(r.read(REG_IDR), 0); + assert_ne!(r.disconnected_pins() & (1 << pin), 0); + + r.gpio_set(pin, 1); + assert_eq!(r.read(REG_IDR), 0); +} + +#[test] +fn test_analog_input_is_digitally_low() { + let r =3D regs(); + let pin =3D 0; + + set_config(&r, pin, CFG_INPUT_ANALOG); + r.gpio_set(pin, 1); + + assert_eq!(r.read(REG_IDR) & (1 << pin), 0); +} + +#[test] +fn test_open_drain_released_samples_external_input() { + let r =3D regs(); + let pin =3D 1; + + set_config(&r, pin, CFG_OUTPUT_OD); + set_odr_bit(&r, pin, 1); + + r.gpio_set(pin, 1); + assert_eq!(r.read(REG_IDR) & (1 << pin), 1 << pin); + + r.gpio_set(pin, 0); + assert_eq!(r.read(REG_IDR) & (1 << pin), 0); +} + +#[test] +fn test_af_output_does_not_follow_odr() { + let r =3D regs(); + let pin =3D 2; + + set_config(&r, pin, CFG_AF_PP); + set_odr_bit(&r, pin, 1); + + assert_eq!(r.read(REG_IDR) & (1 << pin), 0); +} + +#[test] +fn test_reset_deasserts_previous_high_output() { + let r =3D regs(); + let pin =3D 3; + + set_config(&r, pin, CFG_OUTPUT_PP); + set_odr_bit(&r, pin, 1); + assert_eq!(r.read(REG_IDR) & (1 << pin), 1 << pin); + + let update =3D r.reset(); + + assert_ne!(update.mask & (1 << pin), 0); + assert_eq!(update.levels & (1 << pin), 0); +} + +#[test] +fn test_lckr_is_explicitly_unimplemented() { + let r =3D regs(); + + r.write(REG_LCKR, 0x0001_0001); + + assert_eq!(r.read(REG_LCKR), 0); +} --=20 2.53.0